< Summary

Line coverage
100%
Covered lines: 938
Uncovered lines: 0
Coverable lines: 938
Total lines: 1913
Line coverage: 100%
Branch coverage
100%
Covered branches: 221
Total branches: 221
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: get_Center()100%11100%
File 2: set_Center(...)100%22100%
File 2: get_Proportions()100%11100%
File 2: set_Proportions(...)100%11100%
File 2: get_Scope()100%11100%
File 2: get_State()100%11100%
File 2: set_State(...)100%11100%
File 2: FromMinMax(...)100%11100%
File 2: FromCenterAndSize(...)100%11100%
File 2: FromCenterAndScope(...)100%11100%
File 2: FromCenterAndSizeClippedToDomain(...)100%11100%
File 2: FromCenterAndScopeClippedToDomain(...)100%11100%
File 2: FromRelativeRotatedBoundsClippedToDomain(...)100%44100%
File 2: Orient(...)100%22100%
File 2: Resize(...)100%11100%
File 2: SetMinMax(...)100%11100%
File 2: SetBoundingBox(...)100%11100%
File 2: Contains(...)100%1010100%
File 2: Contains(...)100%11100%
File 2: Contains(...)100%88100%
File 2: Contains(...)100%66100%
File 2: Intersects(...)100%11100%
File 2: Intersects(...)100%11100%
File 2: IntersectsStrict(...)100%11100%
File 2: IntersectsStrict(...)100%11100%
File 2: Intersects(...)100%11100%
File 2: ProjectPoint(...)100%11100%
File 2: ClampPoint(...)100%11100%
File 2: ContainsBoxLike(...)100%66100%
File 2: IntersectsBoxLike(...)100%1010100%
File 2: IntersectsSphere(...)100%11100%
File 2: IntersectsSphereStrict(...)100%44100%
File 2: HasPositiveVolume()100%44100%
File 2: HasStrictAxisOverlap(...)100%1818100%
File 2: DistanceToSurface(...)100%22100%
File 2: GetPointOnSurfaceTowardsObject(...)100%11100%
File 2: ClosestPointOnSurface(...)100%1414100%
File 2: GetCorner(...)100%99100%
File 2: CopyCorners(...)100%22100%
File 2: GetVolumeExpansionCost(...)100%11100%
File 2: Union(...)100%11100%
File 2: FindClosestPointsBetweenBoxes(...)100%44100%
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: SetCenterAndHalfSize(...)100%44100%
File 2: SetCenterAndHalfSizeClippedToDomain(...)100%11100%
File 2: GetHalfSize(...)100%11100%
File 2: GetScopeMagnitude(...)100%11100%
File 3: FromFiniteConeClippedToDomain(...)100%44100%
File 3: FromCenteredCapsuleClippedToDomain(...)100%11100%
File 3: FromCenteredFiniteCylinderClippedToDomain(...)100%11100%
File 3: FromCenteredFiniteConeClippedToDomain(...)100%66100%
File 3: GetFiniteAxisLengthSquared(...)100%11100%
File 3: GetFiniteConeDiskExtent(...)100%22100%
File 3: GetFiniteDiskFloorExtent(...)100%88100%
File 3: GetFiniteDiskRepresentedSquare(...)100%11100%
File 3: GetFiniteDiskTarget(...)100%11100%
File 3: GetCenteredFiniteCylinderOutwardExtentRaw(...)100%22100%
File 3: GetPositiveCeilingRawRatio(...)100%22100%
File 3: IsCenteredFiniteCylinderExtentSufficient(...)100%11100%
File 3: FromCenteredFiniteCylinderExtentsClippedToDomain(...)100%11100%
File 3: GetCenteredFiniteCylinderBound(...)100%66100%
File 3: GetCenteredFiniteConeBound(...)100%1010100%
File 3: FromCenteredFiniteAxisClippedToDomain(...)100%11100%
File 3: ValidateCenteredFiniteAxis(...)100%88100%
File 4: FromCenteredCapsuleClippedToDomain(...)100%11100%
File 4: FromCenteredFiniteCylinderClippedToDomain(...)100%11100%
File 4: FromCenteredFiniteConeClippedToDomain(...)100%11100%
File 4: GetRigidAxis(...)100%11100%
File 4: GetRigidCapsuleBound(...)100%22100%
File 4: GetRigidCylinderOutwardExtentRaw(...)100%22100%
File 4: GetRigidPositiveCeilingRawRatio(...)100%22100%
File 4: GetRigidDiskFloorExtent(...)100%88100%
File 4: GetRigidDiskRepresentedSquare(...)100%11100%
File 4: IsRigidCylinderExtentSufficient(...)100%11100%
File 4: GetRigidConeBound(...)100%66100%
File 4: GetRigidConeBaseRimBound(...)100%66100%
File 4: GetRigidSignedRawRatio(...)100%88100%
File 4: IsRigidConeBaseRimBoundSufficient(...)100%22100%
File 4: GetRigidRationalBoundClippedToDomain(...)100%1212100%
File 4: GetMagnitude(...)100%22100%
File 4: ValidateRigidFiniteAxis(...)100%1010100%

File(s)

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Bounds/FixedBoundBox.BoundingBoxState.cs

#LineLine coverage
 1//=======================================================================
 2// FixedBoundBox.BoundingBoxState.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;
 10using System.Text.Json.Serialization;
 11using MemoryPack;
 12
 13namespace FixedMathSharp.Geometry;
 14
 15/// <content>BoundingBoxState declaration for FixedBoundBox.</content>
 16public partial struct FixedBoundBox
 17{
 18    /// <summary>
 19    /// Represents the state of a three-dimensional axis-aligned bounding box using its minimum and maximum coordinates.
 20    /// </summary>
 21    /// <remarks>
 22    /// The bounding box is defined by two points: the minimum and maximum corners in 3D space. This
 23    /// structure is immutable and can be used to describe spatial boundaries for geometric computations, collision
 24    /// detection, or spatial queries.
 25    /// </remarks>
 26    [Serializable]
 27    [MemoryPackable]
 28    public readonly partial struct BoundingBoxState
 29    {
 30        /// <inheritdoc cref="FixedBoundBox.Min"/>
 31        [JsonInclude]
 32        [MemoryPackInclude]
 33        public readonly Vector3d Min;
 34
 35        /// <inheritdoc cref="FixedBoundBox.Max"/>
 36        [JsonInclude]
 37        [MemoryPackInclude]
 38        public readonly Vector3d Max;
 39
 40        /// <summary>
 41        /// Initializes a new instance of the BoundingBoxState class with the specified minimum and maximum coordinates.
 42        /// </summary>
 43        [JsonConstructor]
 44        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 45        public BoundingBoxState(Vector3d min, Vector3d max)
 46        {
 747            Min = Vector3d.Min(min, max);
 748            Max = Vector3d.Max(min, max);
 749        }
 50    }
 51}

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

#LineLine coverage
 1//=======================================================================
 2// FixedBoundBox.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;
 10using System.Text.Json.Serialization;
 11using MemoryPack;
 12
 13namespace FixedMathSharp.Geometry;
 14
 15/// <summary>
 16/// Represents a normalized three-dimensional axis-aligned bounding box.
 17/// </summary>
 18/// <remarks>
 19/// Use <see cref="FromMinMax"/>, <see cref="FromCenterAndSize"/>, or
 20/// <see cref="FromCenterAndScope"/> so construction intent is explicit at the call site.
 21/// </remarks>
 22[Serializable]
 23[MemoryPackable]
 24public partial struct FixedBoundBox : IEquatable<FixedBoundBox>
 25{
 26    /// <summary>
 27    /// The number of stable corners exposed by <see cref="GetCorner"/> and <see cref="CopyCorners"/>.
 28    /// </summary>
 29    public const int CornerCount = 8;
 30
 31    #region Constructors
 32
 33    /// <summary>
 34    /// Initializes a new instance of the FixedBoundBox class with the specified bounding box state.
 35    /// </summary>
 36    /// <param name="state">The state that defines the position, size, and orientation of the bounding box.</param>
 37    [JsonConstructor]
 38    public FixedBoundBox(BoundingBoxState state)
 39    {
 440        State = state;
 441    }
 42
 43    #endregion
 44
 45    #region Properties
 46
 47    /// <summary>
 48    /// The minimum corner of the bounding box.
 49    /// </summary>
 50    [JsonIgnore]
 51    [MemoryPackIgnore]
 52    public Vector3d Min { get; private set; }
 53
 54    /// <summary>
 55    /// The maximum corner of the bounding box.
 56    /// </summary>
 57    [JsonIgnore]
 58    [MemoryPackIgnore]
 59    public Vector3d Max { get; private set; }
 60
 61    /// <summary>
 62    /// The center of the bounding box, rounded to the nearest-even Q32.32 lattice point.
 63    /// </summary>
 64    /// <remarks>
 65    /// Assigning a different center preserves a conservative half-extent. An
 66    /// odd raw-unit span can therefore expand by one raw unit so the assigned
 67    /// center remains exact and the previous box is not under-represented.
 68    /// </remarks>
 69    /// <exception cref="OverflowException">
 70    /// An assigned center would place an endpoint outside the scalar domain.
 71    /// </exception>
 72    [JsonIgnore]
 73    [MemoryPackIgnore]
 74    public Vector3d Center
 75    {
 76        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2777        get => Vector3d.Midpoint(Min, Max);
 78
 79        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 80        set
 81        {
 682            if (value != Center)
 583                SetCenterAndHalfSize(value, Scope);
 484        }
 85    }
 86
 87    /// <summary>
 88    /// The exact total size of the box.
 89    /// </summary>
 90    /// <remarks>
 91    /// Assigned values are normalized by absolute component value and divided
 92    /// outward. An odd raw-unit size therefore expands by one raw unit. Reading
 93    /// this property throws rather than returning a saturated value when an
 94    /// exact component span is not representable by <see cref="Fixed64"/>.
 95    /// </remarks>
 96    /// <exception cref="OverflowException">
 97    /// A component span is not representable, or an assigned size would place
 98    /// an endpoint outside the scalar domain.
 99    /// </exception>
 100    [JsonIgnore]
 101    [MemoryPackIgnore]
 102    public Vector3d Proportions
 103    {
 104        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 16105        get => new(
 16106            WideGeometry.GetIntervalSize(Min.X, Max.X),
 16107            WideGeometry.GetIntervalSize(Min.Y, Max.Y),
 16108            WideGeometry.GetIntervalSize(Min.Z, Max.Z));
 109
 110        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 111        set
 112        {
 1113            SetCenterAndHalfSize(Center, GetHalfSize(value));
 1114        }
 115    }
 116
 117    /// <summary>
 118    /// The smallest representable half-extent that conservatively contains the
 119    /// box around <see cref="Center"/>.
 120    /// </summary>
 121    /// <exception cref="OverflowException">
 122    /// A conservative half-extent is outside the representable scalar domain.
 123    /// </exception>
 124    [JsonIgnore]
 125    [MemoryPackIgnore]
 126    public Vector3d Scope
 127    {
 128        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 11129        get => new(
 11130            WideGeometry.GetIntervalScope(Min.X, Max.X),
 11131            WideGeometry.GetIntervalScope(Min.Y, Max.Y),
 11132            WideGeometry.GetIntervalScope(Min.Z, Max.Z));
 133    }
 134
 135    /// <summary>
 136    /// Gets or sets the current normalized bounding box state, including its minimum and maximum coordinates.
 137    /// </summary>
 138    [JsonInclude]
 139    [MemoryPackInclude]
 140    public BoundingBoxState State
 141    {
 3142        get => new(Min, Max);
 143
 144        internal set
 145        {
 4146            SetMinMax(value.Min, value.Max);
 4147        }
 148    }
 149
 150    #endregion
 151
 152    #region Factories
 153
 154    /// <summary>
 155    /// Creates a normalized bounding box from minimum and maximum corners.
 156    /// </summary>
 157    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 158    public static FixedBoundBox FromMinMax(Vector3d min, Vector3d max)
 159    {
 1003160        var box = default(FixedBoundBox);
 1003161        box.SetMinMax(min, max);
 1003162        return box;
 163    }
 164
 165    /// <summary>
 166    /// Creates a bounding box from a center point and total size.
 167    /// </summary>
 168    /// <remarks>
 169    /// Negative size components are normalized by absolute value. Odd raw-unit
 170    /// sizes are divided outward and therefore expand by one raw unit.
 171    /// </remarks>
 172    /// <exception cref="OverflowException">
 173    /// The centered box would place an endpoint outside the scalar domain.
 174    /// </exception>
 175    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 176    public static FixedBoundBox FromCenterAndSize(Vector3d center, Vector3d size)
 177    {
 128178        var box = default(FixedBoundBox);
 128179        box.SetCenterAndHalfSize(center, GetHalfSize(size));
 126180        return box;
 181    }
 182
 183    /// <summary>
 184    /// Creates a bounding box from a center point and half-size scope.
 185    /// </summary>
 186    /// <remarks>
 187    /// Negative scope components are normalized by absolute value.
 188    /// </remarks>
 189    /// <exception cref="OverflowException">
 190    /// A scope magnitude is not representable, or the centered box would place
 191    /// an endpoint outside the scalar domain.
 192    /// </exception>
 193    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 194    public static FixedBoundBox FromCenterAndScope(Vector3d center, Vector3d scope)
 195    {
 8196        var box = default(FixedBoundBox);
 8197        box.SetCenterAndHalfSize(center, GetScopeMagnitude(scope));
 7198        return box;
 199    }
 200
 201    /// <summary>
 202    /// Creates the representable-domain intersection of a box described by a
 203    /// center point and total size.
 204    /// </summary>
 205    /// <remarks>
 206    /// Negative size components are normalized by absolute value and odd raw-
 207    /// unit sizes divide outward. Endpoints outside the scalar domain are
 208    /// explicitly clipped to <see cref="Fixed64.MinValue"/> or
 209    /// <see cref="Fixed64.MaxValue"/>.
 210    /// </remarks>
 211    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 212    public static FixedBoundBox FromCenterAndSizeClippedToDomain(Vector3d center, Vector3d size)
 213    {
 1214        var box = default(FixedBoundBox);
 1215        box.SetCenterAndHalfSizeClippedToDomain(center, GetHalfSize(size));
 1216        return box;
 217    }
 218
 219    /// <summary>
 220    /// Creates the representable-domain intersection of a box described by a
 221    /// center point and half-size scope.
 222    /// </summary>
 223    /// <remarks>
 224    /// Negative scope components are normalized by absolute value. Endpoints
 225    /// outside the scalar domain are explicitly clipped to
 226    /// <see cref="Fixed64.MinValue"/> or <see cref="Fixed64.MaxValue"/>.
 227    /// </remarks>
 228    /// <exception cref="OverflowException">
 229    /// A scope magnitude is not representable.
 230    /// </exception>
 231    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 232    public static FixedBoundBox FromCenterAndScopeClippedToDomain(Vector3d center, Vector3d scope)
 233    {
 1234        var box = default(FixedBoundBox);
 1235        box.SetCenterAndHalfSizeClippedToDomain(center, GetScopeMagnitude(scope));
 1236        return box;
 237    }
 238
 239    /// <summary>
 240    /// Creates conservative target-frame bounds for a rotated source-frame
 241    /// box, clipping only final target-frame endpoints to the scalar domain.
 242    /// </summary>
 243    /// <remarks>
 244    /// Conceptually transforms every source local point as
 245    /// <c>targetRotation^-1 * (sourceOrigin + sourceRotation * point - targetOrigin)</c>.
 246    /// Source endpoints are normalized component-wise before the exact
 247    /// projection. Intermediate world points are never materialized.
 248    /// </remarks>
 249    public static FixedBoundBox FromRelativeRotatedBoundsClippedToDomain(
 250        Vector3d sourceOrigin,
 251        FixedQuaternion sourceRotation,
 252        Vector3d sourceLocalMin,
 253        Vector3d sourceLocalMax,
 254        Vector3d targetOrigin,
 255        FixedQuaternion targetRotation)
 256    {
 80257        if (!sourceRotation.IsNormalized())
 258        {
 1259            throw new ArgumentException(
 1260                "Source rotation must be normalized.",
 1261                nameof(sourceRotation));
 262        }
 79263        if (!targetRotation.IsNormalized())
 264        {
 1265            throw new ArgumentException(
 1266                "Target rotation must be normalized.",
 1267                nameof(targetRotation));
 268        }
 269
 78270        return WideOrientedBox.GetRelativeRotatedBoundsClippedToDomain(
 78271            sourceOrigin,
 78272            sourceRotation,
 78273            sourceLocalMin,
 78274            sourceLocalMax,
 78275            targetOrigin,
 78276            targetRotation);
 277    }
 278
 279    #endregion
 280
 281    #region Mutators
 282
 283    /// <summary>
 284    /// Orients the bounding box with the given center and size.
 285    /// </summary>
 286    /// <exception cref="OverflowException">
 287    /// The requested bounds would place an endpoint outside the scalar domain.
 288    /// </exception>
 289    public void Orient(Vector3d center, Vector3d? size)
 290    {
 3291        if (size.HasValue)
 292        {
 1293            SetCenterAndHalfSize(center, GetHalfSize(size.Value));
 1294            return;
 295        }
 296
 2297        Center = center;
 1298    }
 299
 300    /// <summary>
 301    /// Resizes the bounding box to the specified size, keeping the same center.
 302    /// </summary>
 303    /// <exception cref="OverflowException">
 304    /// The requested size would place an endpoint outside the scalar domain.
 305    /// </exception>
 306    public void Resize(Vector3d size)
 307    {
 4308        SetCenterAndHalfSize(Center, GetHalfSize(size));
 3309    }
 310
 311    /// <summary>
 312    /// Sets the normalized bounds of the bounding box by specifying its minimum and maximum points.
 313    /// </summary>
 314    public void SetMinMax(Vector3d min, Vector3d max)
 315    {
 1009316        Min = Vector3d.Min(min, max);
 1009317        Max = Vector3d.Max(min, max);
 1009318    }
 319
 320    /// <summary>
 321    /// Configures the bounding box with the specified center and scope (half-size).
 322    /// </summary>
 323    /// <remarks>
 324    /// Negative scope components are normalized by absolute value.
 325    /// </remarks>
 326    /// <exception cref="OverflowException">
 327    /// A scope magnitude is not representable, or the requested bounds would
 328    /// place an endpoint outside the scalar domain.
 329    /// </exception>
 330    public void SetBoundingBox(Vector3d center, Vector3d scope)
 331    {
 3332        SetCenterAndHalfSize(center, GetScopeMagnitude(scope));
 3333    }
 334
 335    /// <summary>
 336    /// Determines if a point is inside the bounding box (including boundaries).
 337    /// </summary>
 338    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 339    public bool Contains(Vector3d point)
 340    {
 58341        return point.X >= Min.X && point.X <= Max.X
 58342            && point.Y >= Min.Y && point.Y <= Max.Y
 58343            && point.Z >= Min.Z && point.Z <= Max.Z;
 344    }
 345
 346    /// <summary>
 347    /// Tests another bounding box against this bounding box.
 348    /// </summary>
 349    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 6350    public FixedEnclosureType Contains(FixedBoundBox box) => ContainsBoxLike(box.Min, box.Max);
 351
 352    /// <summary>
 353    /// Tests a bounding sphere against this bounding box.
 354    /// </summary>
 355    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 356    public FixedEnclosureType Contains(FixedBoundSphere sphere)
 357    {
 4358        if (WideGeometry.ContainsCenteredExtent(Min.X, Max.X, sphere.Center.X, sphere.Radius)
 4359            && WideGeometry.ContainsCenteredExtent(Min.Y, Max.Y, sphere.Center.Y, sphere.Radius)
 4360            && WideGeometry.ContainsCenteredExtent(Min.Z, Max.Z, sphere.Center.Z, sphere.Radius))
 1361            return FixedEnclosureType.Contains;
 362
 3363        return Intersects(sphere) ? FixedEnclosureType.Intersects : FixedEnclosureType.Disjoint;
 364    }
 365
 366    /// <summary>
 367    /// Tests a bounding frustum against this bounding box.
 368    /// </summary>
 369    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 370    public FixedEnclosureType Contains(FixedBoundFrustum frustum)
 371    {
 3372        if (Contains(frustum.Min) && Contains(frustum.Max))
 1373            return FixedEnclosureType.Contains;
 374
 2375        return Intersects(frustum) ? FixedEnclosureType.Intersects : FixedEnclosureType.Disjoint;
 376    }
 377
 378    /// <summary>
 379    /// Checks whether another bounding box intersects this bounding box, including boundary-only contact.
 380    /// </summary>
 381    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 19382    public bool Intersects(FixedBoundBox box) => IntersectsBoxLike(box.Min, box.Max);
 383
 384    /// <summary>
 385    /// Checks whether a bounding sphere intersects this bounding box, including boundary-only contact.
 386    /// </summary>
 387    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 21388    public bool Intersects(FixedBoundSphere sphere) => IntersectsSphere(sphere);
 389
 390    /// <summary>
 391    /// Checks whether another bounding box overlaps this bounding box with positive volume on every axis.
 392    /// </summary>
 393    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 12394    public bool IntersectsStrict(FixedBoundBox box) => HasStrictAxisOverlap(box.Min, box.Max);
 395
 396    /// <summary>
 397    /// Checks whether a bounding sphere overlaps this bounding box with positive volume.
 398    /// </summary>
 399    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 6400    public bool IntersectsStrict(FixedBoundSphere sphere) => IntersectsSphereStrict(sphere);
 401
 402    /// <summary>
 403    /// Checks whether a bounding frustum intersects this bounding box.
 404    /// </summary>
 405    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 406    public bool Intersects(FixedBoundFrustum frustum)
 407    {
 4408        return frustum.Intersects(this);
 409    }
 410
 411    /// <summary>
 412    /// Projects a point into the bounding box by clamping it to the box extents.
 413    /// </summary>
 414    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 415    public Vector3d ProjectPoint(Vector3d point)
 2416        => ClampPoint(point);
 417
 418    /// <summary>
 419    /// Clamps a point to this bounding box, returning the point unchanged when it is already inside.
 420    /// </summary>
 421    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 422    public Vector3d ClampPoint(Vector3d point)
 423    {
 28424        return new Vector3d(
 28425            FixedMath.Clamp(point.X, Min.X, Max.X),
 28426            FixedMath.Clamp(point.Y, Min.Y, Max.Y),
 28427            FixedMath.Clamp(point.Z, Min.Z, Max.Z));
 428    }
 429
 430    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 431    private FixedEnclosureType ContainsBoxLike(Vector3d otherMin, Vector3d otherMax)
 432    {
 6433        if (Contains(otherMin) && Contains(otherMax))
 2434            return FixedEnclosureType.Contains;
 435
 4436        return IntersectsBoxLike(otherMin, otherMax)
 4437            ? FixedEnclosureType.Intersects
 4438            : FixedEnclosureType.Disjoint;
 439    }
 440
 441    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 442    private bool IntersectsBoxLike(Vector3d otherMin, Vector3d otherMax)
 443    {
 23444        return Min.X <= otherMax.X && Max.X >= otherMin.X
 23445            && Min.Y <= otherMax.Y && Max.Y >= otherMin.Y
 23446            && Min.Z <= otherMax.Z && Max.Z >= otherMin.Z;
 447    }
 448
 449    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 450    private bool IntersectsSphere(FixedBoundSphere sphere)
 451    {
 21452        return WideGeometry.CompareDistanceToRadiusSum(
 21453            sphere.Center,
 21454            ClampPoint(sphere.Center),
 21455            sphere.Radius,
 21456            Fixed64.Zero) <= 0;
 457    }
 458
 459    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 460    private bool IntersectsSphereStrict(FixedBoundSphere sphere)
 461    {
 6462        return HasPositiveVolume()
 6463            && sphere.Radius > Fixed64.Zero
 6464            && WideGeometry.CompareDistanceToRadiusSum(
 6465                sphere.Center,
 6466                ClampPoint(sphere.Center),
 6467                sphere.Radius,
 6468                Fixed64.Zero) < 0;
 469    }
 470
 471    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 472    private bool HasPositiveVolume()
 473    {
 18474        return Min.X < Max.X && Min.Y < Max.Y && Min.Z < Max.Z;
 475    }
 476
 477    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 478    private bool HasStrictAxisOverlap(Vector3d otherMin, Vector3d otherMax)
 479    {
 12480        return HasPositiveVolume()
 12481            && otherMin.X < otherMax.X
 12482            && otherMin.Y < otherMax.Y
 12483            && otherMin.Z < otherMax.Z
 12484            && Min.X < otherMax.X && Max.X > otherMin.X
 12485            && Min.Y < otherMax.Y && Max.Y > otherMin.Y
 12486            && Min.Z < otherMax.Z && Max.Z > otherMin.Z;
 487    }
 488
 489    /// <summary>
 490    /// Calculates the shortest distance from a given point to the surface of the bounding box.
 491    /// If the point lies inside the box, the distance is zero.
 492    /// </summary>
 493    /// <param name="point">The point from which to calculate the distance.</param>
 494    /// <returns>
 495    /// The shortest distance from the point to the surface of the bounding box.
 496    /// If the point is inside the box, the method returns zero.
 497    /// </returns>
 498    /// <remarks>
 499    /// The method finds the closest point on the box's surface by clamping the given point
 500    /// to the box's bounds and returns the Euclidean distance between them.
 501    /// This ensures accurate distance calculations, even near corners or edges.
 502    /// </remarks>
 503    public Fixed64 DistanceToSurface(Vector3d point)
 504    {
 505        // Clamp the point to the nearest point on the box's surface
 2506        Vector3d clampedPoint = new(
 2507            FixedMath.Clamp(point.X, Min.X, Max.X),
 2508            FixedMath.Clamp(point.Y, Min.Y, Max.Y),
 2509            FixedMath.Clamp(point.Z, Min.Z, Max.Z)
 2510        );
 511
 512        // If the point is inside the box, return 0
 2513        if (Contains(point))
 1514            return Fixed64.Zero;
 515
 516        // Otherwise, return the Euclidean distance to the clamped point
 1517        return Vector3d.Distance(point, clampedPoint);
 518    }
 519
 520    /// <summary>
 521    /// Finds the closest point on the surface of the bounding box towards a specified object position.
 522    /// </summary>
 523    public Vector3d GetPointOnSurfaceTowardsObject(Vector3d objectPosition)
 1524        => ClosestPointOnSurface(ProjectPoint(objectPosition));
 525
 526    /// <summary>
 527    /// Finds the closest point on the surface of the bounding box to the specified point.
 528    /// </summary>
 529    public Vector3d ClosestPointOnSurface(Vector3d point)
 530    {
 16531        if (Contains(point))
 532        {
 533            // Calculate distances to each face and return the closest face.
 7534            Fixed64 distToMinX = point.X - Min.X;
 7535            Fixed64 distToMaxX = Max.X - point.X;
 7536            Fixed64 distToMinY = point.Y - Min.Y;
 7537            Fixed64 distToMaxY = Max.Y - point.Y;
 7538            Fixed64 distToMinZ = point.Z - Min.Z;
 7539            Fixed64 distToMaxZ = Max.Z - point.Z;
 540
 7541            Fixed64 minDistToFace = FixedMath.Min(distToMinX,
 7542                FixedMath.Min(distToMaxX,
 7543                FixedMath.Min(distToMinY,
 7544                FixedMath.Min(distToMaxY,
 7545                FixedMath.Min(distToMinZ, distToMaxZ)))));
 546
 547            // Adjust the closest point based on the face.
 8548            if (minDistToFace == distToMinX) point.X = Min.X;
 8549            else if (minDistToFace == distToMaxX) point.X = Max.X;
 550
 8551            if (minDistToFace == distToMinY) point.Y = Min.Y;
 7552            else if (minDistToFace == distToMaxY) point.Y = Max.Y;
 553
 8554            if (minDistToFace == distToMinZ) point.Z = Min.Z;
 7555            else if (minDistToFace == distToMaxZ) point.Z = Max.Z;
 556
 7557            return point;
 558        }
 559
 560        // If the point is outside the box, clamp to the nearest surface.
 9561        return new Vector3d(
 9562            FixedMath.Clamp(point.X, Min.X, Max.X),
 9563            FixedMath.Clamp(point.Y, Min.Y, Max.Y),
 9564            FixedMath.Clamp(point.Z, Min.Z, Max.Z)
 9565        );
 566    }
 567
 568    /// <summary>
 569    /// Gets a stable corner without allocating.
 570    /// </summary>
 571    /// <remarks>
 572    /// Corner order is: min/min/min, max/min/min, min/max/min, max/max/min,
 573    /// min/min/max, max/min/max, min/max/max, max/max/max.
 574    /// </remarks>
 575    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 576    public Vector3d GetCorner(int index)
 577    {
 173578        return index switch
 173579        {
 24580            0 => new Vector3d(Min.X, Min.Y, Min.Z),
 21581            1 => new Vector3d(Max.X, Min.Y, Min.Z),
 21582            2 => new Vector3d(Min.X, Max.Y, Min.Z),
 21583            3 => new Vector3d(Max.X, Max.Y, Min.Z),
 21584            4 => new Vector3d(Min.X, Min.Y, Max.Z),
 21585            5 => new Vector3d(Max.X, Min.Y, Max.Z),
 21586            6 => new Vector3d(Min.X, Max.Y, Max.Z),
 21587            7 => new Vector3d(Max.X, Max.Y, Max.Z),
 2588            _ => throw new ArgumentOutOfRangeException(nameof(index), $"Corner index must be between 0 and {CornerCount 
 173589        };
 590    }
 591
 592    /// <summary>
 593    /// Copies this box's corners into the destination span in <see cref="GetCorner"/> order.
 594    /// </summary>
 595    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 596    public void CopyCorners(Span<Vector3d> destination)
 597    {
 131598        if (destination.Length < CornerCount)
 1599            throw new ArgumentException($"The destination span must contain at least {CornerCount} elements.", nameof(de
 600
 130601        destination[0] = new Vector3d(Min.X, Min.Y, Min.Z);
 130602        destination[1] = new Vector3d(Max.X, Min.Y, Min.Z);
 130603        destination[2] = new Vector3d(Min.X, Max.Y, Min.Z);
 130604        destination[3] = new Vector3d(Max.X, Max.Y, Min.Z);
 130605        destination[4] = new Vector3d(Min.X, Min.Y, Max.Z);
 130606        destination[5] = new Vector3d(Max.X, Min.Y, Max.Z);
 130607        destination[6] = new Vector3d(Min.X, Max.Y, Max.Z);
 130608        destination[7] = new Vector3d(Max.X, Max.Y, Max.Z);
 130609    }
 610
 611    /// <summary>
 612    /// Gets the exact additional volume required for this box's union with
 613    /// <paramref name="other"/>, floored to integer world units and clamped to
 614    /// <see cref="long.MaxValue"/>.
 615    /// </summary>
 616    /// <remarks>
 617    /// The three endpoint spans and both volumes remain in exact unsigned
 618    /// 192-bit arithmetic. This metric is suitable for spatial-index insertion
 619    /// heuristics even when either box has an unrepresentable
 620    /// <see cref="Proportions"/> component.
 621    /// </remarks>
 622    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 623    public long GetVolumeExpansionCost(FixedBoundBox other) =>
 5624        WideGeometry.GetVolumeExpansionCost(Min, Max, other.Min, other.Max);
 625
 626    #endregion
 627
 628    #region Static Ops
 629
 630    /// <summary>
 631    /// Creates a new bounding box that is the union of two bounding boxes.
 632    /// </summary>
 633    public static FixedBoundBox Union(FixedBoundBox a, FixedBoundBox b)
 634    {
 1635        return FromMinMax(Vector3d.Min(a.Min, b.Min), Vector3d.Max(a.Max, b.Max));
 636    }
 637
 638    /// <summary>
 639    /// Finds the closest points between two bounding boxes.
 640    /// </summary>
 641    public static Vector3d FindClosestPointsBetweenBoxes(FixedBoundBox a, FixedBoundBox b)
 642    {
 1643        Vector3d closestPoint = Vector3d.Zero;
 1644        Fixed64 minDistance = Fixed64.MaxValue;
 645
 18646        for (int i = 0; i < CornerCount; i++)
 647        {
 8648            Vector3d corner = b.GetCorner(i);
 8649            Vector3d point = a.ClosestPointOnSurface(corner);
 8650            Fixed64 distance = Vector3d.Distance(point, corner);
 8651            if (distance < minDistance)
 652            {
 1653                closestPoint = point;
 1654                minDistance = distance;
 655            }
 656        }
 657
 1658        return closestPoint;
 659    }
 660
 661    #endregion
 662
 663    #region Equality
 664
 665    /// <summary>
 666    /// Determines whether two FixedBoundBox instances are equal.
 667    /// </summary>
 2668    public static bool operator ==(FixedBoundBox left, FixedBoundBox right) => left.Equals(right);
 669
 670    /// <summary>
 671    /// Determines whether two FixedBoundBox instances are not equal.
 672    /// </summary>
 1673    public static bool operator !=(FixedBoundBox left, FixedBoundBox right) => !left.Equals(right);
 674
 675    #endregion
 676
 677    #region Equality and HashCode Overrides
 678
 679    /// <inheritdoc/>
 2680    public override bool Equals(object? obj) => obj is FixedBoundBox other && Equals(other);
 681
 682    /// <inheritdoc/>
 683    public bool Equals(FixedBoundBox other)
 44684        => Min.Equals(other.Min) && Max.Equals(other.Max);
 685
 686    /// <inheritdoc/>
 687    public override int GetHashCode()
 688    {
 689        unchecked
 690        {
 513691            int hash = 17;
 513692            hash = (hash * 31) + Min.StateHash;
 513693            hash = (hash * 31) + Max.StateHash;
 513694            return hash;
 695        }
 696    }
 697
 698    #endregion
 699
 700    #region Helpers
 701
 702    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 703    private void SetCenterAndHalfSize(Vector3d center, Vector3d halfSize)
 704    {
 149705        if (!Vector3d.TrySubtract(center, halfSize, out Vector3d min)
 149706            || !Vector3d.TryAdd(center, halfSize, out Vector3d max))
 707        {
 5708            throw new OverflowException("The centered box places at least one endpoint outside the representable Fixed64
 709        }
 710
 144711        Min = min;
 144712        Max = max;
 144713    }
 714
 715    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 716    private void SetCenterAndHalfSizeClippedToDomain(Vector3d center, Vector3d halfSize)
 717    {
 2718        Vector3d min = center - halfSize;
 2719        Vector3d max = center + halfSize;
 2720        Min = min;
 2721        Max = max;
 2722    }
 723
 724    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 135725    private static Vector3d GetHalfSize(Vector3d size) => new(
 135726        WideGeometry.GetHalfSizeMagnitude(size.X),
 135727        WideGeometry.GetHalfSizeMagnitude(size.Y),
 135728        WideGeometry.GetHalfSizeMagnitude(size.Z));
 729
 730    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 12731    private static Vector3d GetScopeMagnitude(Vector3d scope) => new(
 12732        WideGeometry.GetExtentMagnitude(scope.X),
 12733        WideGeometry.GetExtentMagnitude(scope.Y),
 12734        WideGeometry.GetExtentMagnitude(scope.Z));
 735
 736    #endregion
 737}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Bounds/FixedBoundBox.FiniteAxis.cs

#LineLine coverage
 1//=======================================================================
 2// FixedBoundBox.FiniteAxis.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>
 13/// Contains methods for creating a representable-domain intersection of the tight axis-aligned bounds of finite
 14/// cones, cylinders, and capsules from their center-axis lengths and directions.
 15/// </content>
 16public partial struct FixedBoundBox
 17{
 18    /// <summary>
 19    /// Creates the representable-domain intersection of the tight axis-aligned
 20    /// bounds of a finite cone with a flat circular base.
 21    /// </summary>
 22    /// <param name="apex">The cone apex.</param>
 23    /// <param name="baseCenter">The center of the cone's flat base.</param>
 24    /// <param name="axisDirection">The normalized direction from apex to base.</param>
 25    /// <param name="baseRadius">The nonnegative base radius.</param>
 26    /// <remarks>
 27    /// Base-disk extents account for the exact squared length of a representably
 28    /// normalized axis. They are rounded outward so broad-phase bounds cannot
 29    /// omit a boundary point. Coordinates beyond the scalar domain are clipped.
 30    /// </remarks>
 31    /// <exception cref="ArgumentException">
 32    /// <paramref name="axisDirection"/> is zero or not normalized.
 33    /// </exception>
 34    /// <exception cref="ArgumentOutOfRangeException">
 35    /// <paramref name="baseRadius"/> is negative.
 36    /// </exception>
 37    public static FixedBoundBox FromFiniteConeClippedToDomain(
 38        Vector3d apex,
 39        Vector3d baseCenter,
 40        Vector3d axisDirection,
 41        Fixed64 baseRadius)
 42    {
 743        if (!axisDirection.IsNormalized())
 144            throw new ArgumentException("Finite cone axis direction must be normalized.", nameof(axisDirection));
 645        if (baseRadius < Fixed64.Zero)
 146            throw new ArgumentOutOfRangeException(nameof(baseRadius));
 47
 548        Signed192 axisLengthSquared = GetFiniteAxisLengthSquared(axisDirection);
 549        Vector3d baseExtents = new(
 550            GetFiniteConeDiskExtent(axisLengthSquared, axisDirection.X, baseRadius),
 551            GetFiniteConeDiskExtent(axisLengthSquared, axisDirection.Y, baseRadius),
 552            GetFiniteConeDiskExtent(axisLengthSquared, axisDirection.Z, baseRadius));
 553        return FromMinMax(
 554            Vector3d.Min(apex, baseCenter - baseExtents),
 555            Vector3d.Max(apex, baseCenter + baseExtents));
 56    }
 57
 58    /// <summary>
 59    /// Creates the representable-domain intersection of a centered capsule's
 60    /// tight axis-aligned bounds from its full center-axis length.
 61    /// </summary>
 62    public static FixedBoundBox FromCenteredCapsuleClippedToDomain(
 63        Vector3d center,
 64        Vector3d axisDirection,
 65        Fixed64 axisLength,
 66        Fixed64 radius)
 67    {
 14268        ValidateCenteredFiniteAxis(axisDirection, axisLength, radius, requirePositiveLength: false);
 14069        return FromCenteredFiniteAxisClippedToDomain(
 14070            center,
 14071            axisDirection,
 14072            axisLength,
 14073            new Vector3d(radius, radius, radius));
 74    }
 75
 76    /// <summary>
 77    /// Creates the representable-domain intersection of a centered finite
 78    /// cylinder's tight axis-aligned bounds from its full axis length.
 79    /// </summary>
 80    public static FixedBoundBox FromCenteredFiniteCylinderClippedToDomain(
 81        Vector3d center,
 82        Vector3d axisDirection,
 83        Fixed64 axisLength,
 84        Fixed64 radius)
 85    {
 13686        ValidateCenteredFiniteAxis(axisDirection, axisLength, radius, requirePositiveLength: true);
 13487        Signed192 axisLengthSquared = GetFiniteAxisLengthSquared(axisDirection);
 13488        return FromCenteredFiniteCylinderExtentsClippedToDomain(
 13489            center,
 13490            GetCenteredFiniteCylinderOutwardExtentRaw(
 13491                axisLengthSquared, axisDirection.X, axisLength, radius),
 13492            GetCenteredFiniteCylinderOutwardExtentRaw(
 13493                axisLengthSquared, axisDirection.Y, axisLength, radius),
 13494            GetCenteredFiniteCylinderOutwardExtentRaw(
 13495                axisLengthSquared, axisDirection.Z, axisLength, radius));
 96    }
 97
 98    /// <summary>
 99    /// Creates the representable-domain intersection of a centered finite
 100    /// cone's tight axis-aligned bounds from its full height.
 101    /// </summary>
 102    /// <param name="center">The midpoint between the base center and apex.</param>
 103    /// <param name="axisDirection">
 104    /// The normalized direction from the base center toward the apex.
 105    /// </param>
 106    /// <param name="height">The positive full base-to-apex height.</param>
 107    /// <param name="radius">The nonnegative base radius.</param>
 108    public static FixedBoundBox FromCenteredFiniteConeClippedToDomain(
 109        Vector3d center,
 110        Vector3d axisDirection,
 111        Fixed64 height,
 112        Fixed64 radius)
 113    {
 7114        if (!axisDirection.IsNormalized())
 1115            throw new ArgumentException("Finite cone axis direction must be normalized.", nameof(axisDirection));
 6116        if (height <= Fixed64.Zero)
 1117            throw new ArgumentOutOfRangeException(nameof(height));
 5118        if (radius < Fixed64.Zero)
 1119            throw new ArgumentOutOfRangeException(nameof(radius));
 120
 4121        Signed192 axisLengthSquared = GetFiniteAxisLengthSquared(axisDirection);
 4122        return FromMinMax(
 4123            new Vector3d(
 4124                GetCenteredFiniteConeBound(
 4125                    center.X,
 4126                    axisDirection.X,
 4127                    height,
 4128                    GetFiniteConeDiskExtent(axisLengthSquared, axisDirection.X, radius),
 4129                    minimum: true),
 4130                GetCenteredFiniteConeBound(
 4131                    center.Y,
 4132                    axisDirection.Y,
 4133                    height,
 4134                    GetFiniteConeDiskExtent(axisLengthSquared, axisDirection.Y, radius),
 4135                    minimum: true),
 4136                GetCenteredFiniteConeBound(
 4137                    center.Z,
 4138                    axisDirection.Z,
 4139                    height,
 4140                    GetFiniteConeDiskExtent(axisLengthSquared, axisDirection.Z, radius),
 4141                    minimum: true)),
 4142            new Vector3d(
 4143                GetCenteredFiniteConeBound(
 4144                    center.X,
 4145                    axisDirection.X,
 4146                    height,
 4147                    GetFiniteConeDiskExtent(axisLengthSquared, axisDirection.X, radius),
 4148                    minimum: false),
 4149                GetCenteredFiniteConeBound(
 4150                    center.Y,
 4151                    axisDirection.Y,
 4152                    height,
 4153                    GetFiniteConeDiskExtent(axisLengthSquared, axisDirection.Y, radius),
 4154                    minimum: false),
 4155                GetCenteredFiniteConeBound(
 4156                    center.Z,
 4157                    axisDirection.Z,
 4158                    height,
 4159                    GetFiniteConeDiskExtent(axisLengthSquared, axisDirection.Z, radius),
 4160                    minimum: false)));
 161    }
 162
 163    private static Signed192 GetFiniteAxisLengthSquared(Vector3d axisDirection) =>
 143164        WideGeometry.GetDifferenceDotProduct3D(
 143165            axisDirection.X, Fixed64.Zero,
 143166            axisDirection.Y, Fixed64.Zero,
 143167            axisDirection.Z, Fixed64.Zero,
 143168            axisDirection.X, Fixed64.Zero,
 143169            axisDirection.Y, Fixed64.Zero,
 143170            axisDirection.Z, Fixed64.Zero);
 171
 172    private static Fixed64 GetFiniteConeDiskExtent(
 173        Signed192 axisLengthSquared,
 174        Fixed64 axisComponent,
 175        Fixed64 radius)
 176    {
 39177        Fixed64 floor = GetFiniteDiskFloorExtent(
 39178            axisLengthSquared,
 39179            axisComponent,
 39180            radius,
 39181            out Signed192 capacity,
 39182            out Signed320 radiusSquared);
 39183        Signed576 represented = GetFiniteDiskRepresentedSquare(
 39184            floor,
 39185            axisLengthSquared);
 39186        Signed576 target = GetFiniteDiskTarget(radiusSquared, capacity);
 39187        return WideArithmetic.CompareNonNegative(represented, target) == 0
 39188            ? floor
 39189            : Fixed64.FromRaw(floor.m_rawValue + 1L);
 190    }
 191
 192    private static Fixed64 GetFiniteDiskFloorExtent(
 193        Signed192 axisLengthSquared,
 194        Fixed64 axisComponent,
 195        Fixed64 radius,
 196        out Signed192 capacity,
 197        out Signed320 radiusSquared)
 198    {
 441199        Signed192 componentSquared = WideGeometry.GetDifferenceDotProduct3D(
 441200            axisComponent, Fixed64.Zero,
 441201            Fixed64.Zero, Fixed64.Zero,
 441202            Fixed64.Zero, Fixed64.Zero,
 441203            axisComponent, Fixed64.Zero,
 441204            Fixed64.Zero, Fixed64.Zero,
 441205            Fixed64.Zero, Fixed64.Zero);
 441206        capacity = WideArithmetic.SubtractSigned192(
 441207            axisLengthSquared,
 441208            componentSquared);
 441209        Signed192 radiusRaw = Signed192.Signed(radius.m_rawValue);
 441210        radiusSquared = WideArithmetic.MultiplySigned192(radiusRaw, radiusRaw);
 441211        if (capacity.IsZero || radius == Fixed64.Zero)
 31212            return Fixed64.Zero;
 410213        if (WideArithmetic.CompareMagnitude(capacity, axisLengthSquared) == 0)
 142214            return radius;
 215
 268216        Signed320 capacityTimesAxisLengthSquared = WideArithmetic.MultiplySigned192(
 268217            capacity,
 268218            axisLengthSquared);
 268219        Signed576 radicand = WideArithmetic.MultiplySigned320(
 268220            radiusSquared,
 268221            capacityTimesAxisLengthSquared);
 268222        Signed320 scaledRoot = WideArithmetic.GetFloorSquareRootScaledByFixed64(radicand);
 268223        Signed320 scaledAxisLengthSquared = WideArithmetic.MultiplySigned192(
 268224            axisLengthSquared,
 268225            Signed192.Signed(FixedMath.ONE_L));
 268226        _ = Fixed64.TryGetSignedRawRatio(
 268227            Signed576.ExtendValue(scaledRoot),
 268228            Signed576.ExtendValue(scaledAxisLengthSquared),
 268229            out Fixed64 candidate);
 230
 268231        Signed576 represented = GetFiniteDiskRepresentedSquare(
 268232            candidate,
 268233            axisLengthSquared);
 268234        Signed576 target = GetFiniteDiskTarget(radiusSquared, capacity);
 268235        return WideArithmetic.CompareNonNegative(represented, target) <= 0
 268236            ? candidate
 268237            : Fixed64.FromRaw(candidate.m_rawValue - 1L);
 238    }
 239
 240    private static Signed576 GetFiniteDiskRepresentedSquare(
 241        Fixed64 extent,
 242        Signed192 axisLengthSquared)
 243    {
 307244        Signed192 extentRaw = Signed192.Signed(extent.m_rawValue);
 307245        return WideArithmetic.MultiplySigned576(
 307246            Signed576.ExtendValue(
 307247                WideArithmetic.MultiplySigned192(extentRaw, extentRaw)),
 307248            axisLengthSquared);
 249    }
 250
 251    private static Signed576 GetFiniteDiskTarget(
 252        Signed320 radiusSquared,
 253        Signed192 capacity) =>
 307254        WideArithmetic.MultiplySigned576(
 307255            Signed576.ExtendValue(radiusSquared),
 307256            capacity);
 257
 258    private static Signed192 GetCenteredFiniteCylinderOutwardExtentRaw(
 259        Signed192 axisLengthSquared,
 260        Fixed64 axisComponent,
 261        Fixed64 axisLength,
 262        Fixed64 radius)
 263    {
 402264        Signed320 axialNumerator = WideArithmetic.MultiplySigned192(
 402265            Signed192.Signed(axisComponent.Abs().m_rawValue),
 402266            Signed192.Signed(axisLength.m_rawValue));
 402267        Fixed64 axialCeiling = GetPositiveCeilingRawRatio(
 402268            axialNumerator,
 402269            WideGeometry.CenteredAxisDenominator);
 402270        Fixed64 diskFloor = GetFiniteDiskFloorExtent(
 402271            axisLengthSquared,
 402272            axisComponent,
 402273            radius,
 402274            out Signed192 capacity,
 402275            out Signed320 radiusSquared);
 402276        Signed192 candidate = WideArithmetic.AddSigned192(
 402277            Signed192.Signed(axialCeiling.m_rawValue),
 402278            Signed192.Signed(diskFloor.m_rawValue));
 279
 280        // floor(disk) + ceil(axis) is at most one raw unit below the
 281        // combined ceiling. Decide that final carry from the exact radical.
 402282        if (!IsCenteredFiniteCylinderExtentSufficient(
 402283                candidate,
 402284                axialNumerator,
 402285                axisLengthSquared,
 402286                capacity,
 402287                radiusSquared))
 288        {
 2289            candidate = WideArithmetic.AddSigned192(
 2290                candidate,
 2291                Signed192.Signed(1L));
 292        }
 293
 402294        return candidate;
 295    }
 296
 297    private static Fixed64 GetPositiveCeilingRawRatio(
 298        Signed320 numerator,
 299        Signed192 denominator)
 300    {
 402301        Fixed64 candidate = Fixed64.GetSignedRawRatio(numerator, denominator);
 402302        Signed320 represented = WideArithmetic.MultiplySigned192(
 402303            Signed192.Signed(candidate.m_rawValue),
 402304            denominator);
 402305        return WideArithmetic.SubtractSigned320(represented, numerator).Sign >= 0
 402306            ? candidate
 402307            : Fixed64.FromRaw(candidate.m_rawValue + 1L);
 308    }
 309
 310    private static bool IsCenteredFiniteCylinderExtentSufficient(
 311        Signed192 candidate,
 312        Signed320 axialNumerator,
 313        Signed192 axisLengthSquared,
 314        Signed192 diskCapacity,
 315        Signed320 radiusSquared)
 316    {
 402317        Signed320 remainder = WideArithmetic.SubtractSigned320(
 402318            WideArithmetic.MultiplySigned192(
 402319                candidate,
 402320                WideGeometry.CenteredAxisDenominator),
 402321            axialNumerator);
 402322        Signed576 left = WideArithmetic.MultiplySigned576(
 402323            WideArithmetic.MultiplySigned320(remainder, remainder),
 402324            axisLengthSquared);
 402325        Signed320 denominatorSquared = WideArithmetic.MultiplySigned192(
 402326            WideGeometry.CenteredAxisDenominator,
 402327            WideGeometry.CenteredAxisDenominator);
 402328        Signed576 right = WideArithmetic.MultiplySigned576(
 402329            WideArithmetic.MultiplySigned320(denominatorSquared, radiusSquared),
 402330            diskCapacity);
 402331        return WideArithmetic.CompareNonNegative(left, right) >= 0;
 332    }
 333
 334    private static FixedBoundBox FromCenteredFiniteCylinderExtentsClippedToDomain(
 335        Vector3d center,
 336        Signed192 xExtent,
 337        Signed192 yExtent,
 338        Signed192 zExtent) =>
 137339        FromMinMax(
 137340            new Vector3d(
 137341                GetCenteredFiniteCylinderBound(center.X, xExtent, minimum: true),
 137342                GetCenteredFiniteCylinderBound(center.Y, yExtent, minimum: true),
 137343                GetCenteredFiniteCylinderBound(center.Z, zExtent, minimum: true)),
 137344            new Vector3d(
 137345                GetCenteredFiniteCylinderBound(center.X, xExtent, minimum: false),
 137346                GetCenteredFiniteCylinderBound(center.Y, yExtent, minimum: false),
 137347                GetCenteredFiniteCylinderBound(center.Z, zExtent, minimum: false)));
 348
 349    private static Fixed64 GetCenteredFiniteCylinderBound(
 350        Fixed64 center,
 351        Signed192 outwardExtent,
 352        bool minimum)
 353    {
 822354        Signed192 centerRaw = Signed192.Signed(center.m_rawValue);
 822355        Signed192 boundRaw = minimum
 822356            ? WideArithmetic.SubtractSigned192(centerRaw, outwardExtent)
 822357            : WideArithmetic.AddSigned192(centerRaw, outwardExtent);
 822358        if (Fixed64.TryGetSignedRawRatio(
 822359                Signed576.ExtendValue(
 822360                    Signed320.ExtendValue(boundRaw)),
 822361                Signed576.ExtendValue(
 822362                    Signed320.ExtendValue(
 822363                        Signed192.Signed(1L))),
 822364                out Fixed64 bound))
 365        {
 818366            return bound;
 367        }
 368
 4369        return boundRaw.Sign < 0 ? Fixed64.MinValue : Fixed64.MaxValue;
 370    }
 371
 372    private static Fixed64 GetCenteredFiniteConeBound(
 373        Fixed64 center,
 374        Fixed64 axisComponent,
 375        Fixed64 height,
 376        Fixed64 diskExtent,
 377        bool minimum)
 378    {
 24379        Signed320 centerNumerator = WideArithmetic.MultiplySigned192(
 24380            Signed192.Signed(center.m_rawValue),
 24381            WideGeometry.CenteredAxisDenominator);
 24382        Signed320 axialNumerator = WideArithmetic.MultiplySigned192(
 24383            Signed192.Signed(axisComponent.m_rawValue),
 24384            Signed192.Signed(height.m_rawValue));
 24385        Signed320 apexNumerator = WideArithmetic.AddSigned320(
 24386            centerNumerator,
 24387            axialNumerator);
 24388        Signed320 baseNumerator = WideArithmetic.SubtractSigned320(
 24389            centerNumerator,
 24390            axialNumerator);
 24391        Signed320 radialNumerator = WideArithmetic.MultiplySigned192(
 24392            Signed192.Signed(diskExtent.m_rawValue),
 24393            WideGeometry.CenteredAxisDenominator);
 24394        Signed320 baseRimNumerator = minimum
 24395            ? WideArithmetic.SubtractSigned320(baseNumerator, radialNumerator)
 24396            : WideArithmetic.AddSigned320(baseNumerator, radialNumerator);
 24397        Signed320 boundNumerator =
 24398            WideArithmetic.SubtractSigned320(apexNumerator, baseRimNumerator).Sign switch
 24399            {
 5400                < 0 when minimum => apexNumerator,
 11401                > 0 when !minimum => apexNumerator,
 20402                _ => baseRimNumerator,
 24403            };
 24404        return WideGeometry.GetRationalBoundClippedToDomain(
 24405            boundNumerator,
 24406            WideGeometry.CenteredAxisDenominator,
 24407            minimum);
 408    }
 409
 410    private static FixedBoundBox FromCenteredFiniteAxisClippedToDomain(
 411        Vector3d center,
 412        Vector3d axisDirection,
 413        Fixed64 axisLength,
 414        Vector3d radialExtents) =>
 140415        FromMinMax(
 140416            new Vector3d(
 140417                WideGeometry.GetCenteredFiniteAxisBoundClippedToDomain(
 140418                    center.X, axisDirection.X, axisLength, radialExtents.X, minimum: true),
 140419                WideGeometry.GetCenteredFiniteAxisBoundClippedToDomain(
 140420                    center.Y, axisDirection.Y, axisLength, radialExtents.Y, minimum: true),
 140421                WideGeometry.GetCenteredFiniteAxisBoundClippedToDomain(
 140422                    center.Z, axisDirection.Z, axisLength, radialExtents.Z, minimum: true)),
 140423            new Vector3d(
 140424                WideGeometry.GetCenteredFiniteAxisBoundClippedToDomain(
 140425                    center.X, axisDirection.X, axisLength, radialExtents.X, minimum: false),
 140426                WideGeometry.GetCenteredFiniteAxisBoundClippedToDomain(
 140427                    center.Y, axisDirection.Y, axisLength, radialExtents.Y, minimum: false),
 140428                WideGeometry.GetCenteredFiniteAxisBoundClippedToDomain(
 140429                    center.Z, axisDirection.Z, axisLength, radialExtents.Z, minimum: false)));
 430
 431    private static void ValidateCenteredFiniteAxis(
 432        Vector3d axisDirection,
 433        Fixed64 axisLength,
 434        Fixed64 radius,
 435        bool requirePositiveLength)
 436    {
 278437        if (!axisDirection.IsNormalized())
 1438            throw new ArgumentException("Finite-axis direction must be normalized.", nameof(axisDirection));
 277439        if (requirePositiveLength ? axisLength <= Fixed64.Zero : axisLength < Fixed64.Zero)
 2440            throw new ArgumentOutOfRangeException(nameof(axisLength));
 275441        if (radius < Fixed64.Zero)
 1442            throw new ArgumentOutOfRangeException(nameof(radius));
 274443    }
 444}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Bounds/FixedBoundBox.RigidFiniteAxis.cs

#LineLine coverage
 1//=======================================================================
 2// FixedBoundBox.RigidFiniteAxis.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>
 13/// Provides high-precision, allocation-free bounding box computations for
 14/// rigidly-rotated finite-axis shapes (e.g. capsules) by working directly
 15/// with extended-precision axis components instead of materializing the
 16/// rotated axis vector.
 17/// </content>
 18public partial struct FixedBoundBox
 19{
 20    /// <summary>
 21    /// Creates the representable-domain intersection of a centered capsule's
 22    /// tight axis-aligned bounds without materializing its rotated axis.
 23    /// </summary>
 24    public static FixedBoundBox FromCenteredCapsuleClippedToDomain(
 25        Vector3d center,
 26        FixedQuaternion rotation,
 27        Vector3d localAxisDirection,
 28        Fixed64 axisLength,
 29        Fixed64 radius)
 30    {
 731        ValidateRigidFiniteAxis(
 732            rotation,
 733            localAxisDirection,
 734            axisLength,
 735            radius,
 736            requirePositiveLength: false);
 337        GetRigidAxis(
 338            rotation,
 339            localAxisDirection,
 340            out Signed192 axisX,
 341            out Signed192 axisY,
 342            out Signed192 axisZ,
 343            out _,
 344            out Signed192 centeredDenominator);
 345        return FromMinMax(
 346            new Vector3d(
 347                GetRigidCapsuleBound(
 348                    center.X,
 349                    axisX,
 350                    axisLength,
 351                    radius,
 352                    centeredDenominator,
 353                    minimum: true),
 354                GetRigidCapsuleBound(
 355                    center.Y,
 356                    axisY,
 357                    axisLength,
 358                    radius,
 359                    centeredDenominator,
 360                    minimum: true),
 361                GetRigidCapsuleBound(
 362                    center.Z,
 363                    axisZ,
 364                    axisLength,
 365                    radius,
 366                    centeredDenominator,
 367                    minimum: true)),
 368            new Vector3d(
 369                GetRigidCapsuleBound(
 370                    center.X,
 371                    axisX,
 372                    axisLength,
 373                    radius,
 374                    centeredDenominator,
 375                    minimum: false),
 376                GetRigidCapsuleBound(
 377                    center.Y,
 378                    axisY,
 379                    axisLength,
 380                    radius,
 381                    centeredDenominator,
 382                    minimum: false),
 383                GetRigidCapsuleBound(
 384                    center.Z,
 385                    axisZ,
 386                    axisLength,
 387                    radius,
 388                    centeredDenominator,
 389                    minimum: false)));
 90    }
 91
 92    /// <summary>
 93    /// Creates the representable-domain intersection of a centered finite
 94    /// cylinder's tight axis-aligned bounds without materializing its rotated
 95    /// axis.
 96    /// </summary>
 97    public static FixedBoundBox FromCenteredFiniteCylinderClippedToDomain(
 98        Vector3d center,
 99        FixedQuaternion rotation,
 100        Vector3d localAxisDirection,
 101        Fixed64 axisLength,
 102        Fixed64 radius)
 103    {
 4104        ValidateRigidFiniteAxis(
 4105            rotation,
 4106            localAxisDirection,
 4107            axisLength,
 4108            radius,
 4109            requirePositiveLength: true);
 3110        GetRigidAxis(
 3111            rotation,
 3112            localAxisDirection,
 3113            out Signed192 axisX,
 3114            out Signed192 axisY,
 3115            out Signed192 axisZ,
 3116            out Signed320 axisSquared,
 3117            out Signed192 centeredDenominator);
 3118        return FromCenteredFiniteCylinderExtentsClippedToDomain(
 3119            center,
 3120            GetRigidCylinderOutwardExtentRaw(
 3121                axisSquared,
 3122                axisX,
 3123                axisLength,
 3124                radius,
 3125                centeredDenominator),
 3126            GetRigidCylinderOutwardExtentRaw(
 3127                axisSquared,
 3128                axisY,
 3129                axisLength,
 3130                radius,
 3131                centeredDenominator),
 3132            GetRigidCylinderOutwardExtentRaw(
 3133                axisSquared,
 3134                axisZ,
 3135                axisLength,
 3136                radius,
 3137                centeredDenominator));
 138    }
 139
 140    /// <summary>
 141    /// Creates the representable-domain intersection of a centered finite
 142    /// cone's axis-aligned bounds without materializing its rotated axis.
 143    /// </summary>
 144    /// <param name="center">The midpoint between the base center and apex.</param>
 145    /// <param name="rotation">The cone rigid-frame rotation.</param>
 146    /// <param name="localAxisDirection">
 147    /// The normalized local direction from the base center toward the apex.
 148    /// </param>
 149    /// <param name="height">The positive full base-to-apex height.</param>
 150    /// <param name="radius">The nonnegative base radius.</param>
 151    public static FixedBoundBox FromCenteredFiniteConeClippedToDomain(
 152        Vector3d center,
 153        FixedQuaternion rotation,
 154        Vector3d localAxisDirection,
 155        Fixed64 height,
 156        Fixed64 radius)
 157    {
 5158        ValidateRigidFiniteAxis(
 5159            rotation,
 5160            localAxisDirection,
 5161            height,
 5162            radius,
 5163            requirePositiveLength: true);
 4164        GetRigidAxis(
 4165            rotation,
 4166            localAxisDirection,
 4167            out Signed192 axisX,
 4168            out Signed192 axisY,
 4169            out Signed192 axisZ,
 4170            out Signed320 axisSquared,
 4171            out Signed192 centeredDenominator);
 4172        return FromMinMax(
 4173            new Vector3d(
 4174                GetRigidConeBound(
 4175                    center.X,
 4176                    axisSquared,
 4177                    axisX,
 4178                    height,
 4179                    radius,
 4180                    centeredDenominator,
 4181                    minimum: true),
 4182                GetRigidConeBound(
 4183                    center.Y,
 4184                    axisSquared,
 4185                    axisY,
 4186                    height,
 4187                    radius,
 4188                    centeredDenominator,
 4189                    minimum: true),
 4190                GetRigidConeBound(
 4191                    center.Z,
 4192                    axisSquared,
 4193                    axisZ,
 4194                    height,
 4195                    radius,
 4196                    centeredDenominator,
 4197                    minimum: true)),
 4198            new Vector3d(
 4199                GetRigidConeBound(
 4200                    center.X,
 4201                    axisSquared,
 4202                    axisX,
 4203                    height,
 4204                    radius,
 4205                    centeredDenominator,
 4206                    minimum: false),
 4207                GetRigidConeBound(
 4208                    center.Y,
 4209                    axisSquared,
 4210                    axisY,
 4211                    height,
 4212                    radius,
 4213                    centeredDenominator,
 4214                    minimum: false),
 4215                GetRigidConeBound(
 4216                    center.Z,
 4217                    axisSquared,
 4218                    axisZ,
 4219                    height,
 4220                    radius,
 4221                    centeredDenominator,
 4222                    minimum: false)));
 223    }
 224
 225    private static void GetRigidAxis(
 226        FixedQuaternion rotation,
 227        Vector3d localAxisDirection,
 228        out Signed192 axisX,
 229        out Signed192 axisY,
 230        out Signed192 axisZ,
 231        out Signed320 axisSquared,
 232        out Signed192 centeredDenominator)
 233    {
 10234        WideOrientedBox.GetRotatedLocalAxisNumerators(
 10235            rotation,
 10236            localAxisDirection,
 10237            out axisX,
 10238            out axisY,
 10239            out axisZ,
 10240            out Signed192 rotationDenominator);
 10241        axisSquared = WideArithmetic.AddSigned320(
 10242            WideArithmetic.AddSigned320(
 10243                WideArithmetic.MultiplySigned192(axisX, axisX),
 10244                WideArithmetic.MultiplySigned192(axisY, axisY)),
 10245            WideArithmetic.MultiplySigned192(axisZ, axisZ));
 10246        Signed320 centeredDenominatorWide = WideArithmetic.MultiplySigned192(
 10247            rotationDenominator,
 10248            WideGeometry.CenteredAxisDenominator);
 249        // A normalized quaternion's squared-component denominator needs at
 250        // most 68 signed bits. The 34-bit centered Q32.32 factor therefore
 251        // keeps this exact product within Signed192.
 10252        _ = Signed192.TryNarrowSigned(
 10253            centeredDenominatorWide,
 10254            out centeredDenominator);
 10255    }
 256
 257    private static Fixed64 GetRigidCapsuleBound(
 258        Fixed64 center,
 259        Signed192 axisComponent,
 260        Fixed64 axisLength,
 261        Fixed64 radius,
 262        Signed192 centeredDenominator,
 263        bool minimum)
 264    {
 18265        Signed320 centerNumerator = WideArithmetic.MultiplySigned192(
 18266            Signed192.Signed(center.m_rawValue),
 18267            centeredDenominator);
 18268        Signed320 axialNumerator = WideArithmetic.MultiplySigned192(
 18269            GetMagnitude(axisComponent),
 18270            Signed192.Signed(axisLength.m_rawValue));
 18271        Signed320 radialNumerator = WideArithmetic.MultiplySigned192(
 18272            Signed192.Signed(radius.m_rawValue),
 18273            centeredDenominator);
 18274        Signed320 extentNumerator = WideArithmetic.AddSigned320(
 18275            axialNumerator,
 18276            radialNumerator);
 18277        Signed320 boundNumerator = minimum
 18278            ? WideArithmetic.SubtractSigned320(
 18279                centerNumerator,
 18280                extentNumerator)
 18281            : WideArithmetic.AddSigned320(
 18282                centerNumerator,
 18283                extentNumerator);
 18284        return GetRigidRationalBoundClippedToDomain(
 18285            boundNumerator,
 18286            centeredDenominator,
 18287            minimum);
 288    }
 289
 290    private static Signed192 GetRigidCylinderOutwardExtentRaw(
 291        Signed320 axisSquared,
 292        Signed192 axisComponent,
 293        Fixed64 axisLength,
 294        Fixed64 radius,
 295        Signed192 centeredDenominator)
 296    {
 9297        Signed320 axialNumerator = WideArithmetic.MultiplySigned192(
 9298            GetMagnitude(axisComponent),
 9299            Signed192.Signed(axisLength.m_rawValue));
 9300        Fixed64 axialCeiling = GetRigidPositiveCeilingRawRatio(
 9301            axialNumerator,
 9302            centeredDenominator);
 9303        Fixed64 diskFloor = GetRigidDiskFloorExtent(
 9304            axisSquared,
 9305            axisComponent,
 9306            radius,
 9307            out Signed320 capacity,
 9308            out Signed320 radiusSquared);
 9309        Signed192 candidate = WideArithmetic.AddSigned192(
 9310            Signed192.Signed(axialCeiling.m_rawValue),
 9311            Signed192.Signed(diskFloor.m_rawValue));
 9312        if (!IsRigidCylinderExtentSufficient(
 9313                candidate,
 9314                axialNumerator,
 9315                centeredDenominator,
 9316                axisSquared,
 9317                capacity,
 9318                radiusSquared))
 319        {
 2320            candidate = WideArithmetic.AddSigned192(
 2321                candidate,
 2322                Signed192.Signed(1L));
 323        }
 324
 9325        return candidate;
 326    }
 327
 328    private static Fixed64 GetRigidPositiveCeilingRawRatio(
 329        Signed320 numerator,
 330        Signed192 denominator)
 331    {
 9332        _ = Fixed64.TryGetSignedRawRatio(
 9333            Signed576.ExtendValue(numerator),
 9334            Signed576.ExtendValue(
 9335                Signed320.ExtendValue(denominator)),
 9336            out Fixed64 candidate);
 9337        Signed320 represented = WideArithmetic.MultiplySigned192(
 9338            Signed192.Signed(candidate.m_rawValue),
 9339            denominator);
 9340        return WideArithmetic.SubtractSigned320(
 9341                represented,
 9342                numerator).Sign >= 0
 9343            ? candidate
 9344            : Fixed64.FromRaw(candidate.m_rawValue + 1L);
 345    }
 346
 347    private static Fixed64 GetRigidDiskFloorExtent(
 348        Signed320 axisSquared,
 349        Signed192 axisComponent,
 350        Fixed64 radius,
 351        out Signed320 capacity,
 352        out Signed320 radiusSquared)
 353    {
 33354        capacity = WideArithmetic.SubtractSigned320(
 33355            axisSquared,
 33356            WideArithmetic.MultiplySigned192(
 33357                axisComponent,
 33358                axisComponent));
 33359        Signed192 radiusRaw =
 33360            Signed192.Signed(radius.m_rawValue);
 33361        radiusSquared = WideArithmetic.MultiplySigned192(
 33362            radiusRaw,
 33363            radiusRaw);
 33364        if (capacity.IsZero || radius == Fixed64.Zero)
 18365            return Fixed64.Zero;
 15366        if (WideArithmetic.SubtractSigned320(
 15367                capacity,
 15368                axisSquared).IsZero)
 369        {
 4370            return radius;
 371        }
 372
 11373        Signed704 radicandWide = WideArithmetic.MultiplySigned320(
 11374            radiusSquared,
 11375            capacity,
 11376            axisSquared);
 377        // Rotation preserves the normalized local-axis squared length:
 378        // axisSquared and capacity are each below 2^193. Combined with the
 379        // full-domain radius square, this radicand remains below 2^523.
 11380        Signed576 radicand = new(
 11381            radicandWide.Word8,
 11382            radicandWide.Word7,
 11383            radicandWide.Word6,
 11384            radicandWide.Word5,
 11385            radicandWide.Word4,
 11386            radicandWide.Word3,
 11387            radicandWide.Word2,
 11388            radicandWide.Word1,
 11389            radicandWide.Word0);
 11390        Signed320 scaledRoot =
 11391            WideArithmetic.GetFloorSquareRootScaledByFixed64(radicand);
 11392        Signed576 scaledAxisSquared = WideArithmetic.MultiplySigned320(
 11393            axisSquared,
 11394            Signed320.ExtendValue(
 11395                Signed192.Signed(FixedMath.ONE_L)));
 11396        _ = Fixed64.TryGetSignedRawRatio(
 11397            Signed576.ExtendValue(scaledRoot),
 11398            scaledAxisSquared,
 11399            out Fixed64 candidate);
 11400        Signed576 represented = GetRigidDiskRepresentedSquare(
 11401            candidate,
 11402            axisSquared);
 11403        Signed576 target = WideArithmetic.MultiplySigned320(
 11404            radiusSquared,
 11405            capacity);
 11406        return WideArithmetic.CompareNonNegative(represented, target) <= 0
 11407            ? candidate
 11408            : Fixed64.FromRaw(candidate.m_rawValue - 1L);
 409    }
 410
 411    private static Signed576 GetRigidDiskRepresentedSquare(
 412        Fixed64 extent,
 413        Signed320 axisSquared) =>
 11414        WideArithmetic.MultiplySigned320(
 11415            WideArithmetic.MultiplySigned192(
 11416                Signed192.Signed(extent.m_rawValue),
 11417                Signed192.Signed(extent.m_rawValue)),
 11418            axisSquared);
 419
 420    private static bool IsRigidCylinderExtentSufficient(
 421        Signed192 candidate,
 422        Signed320 axialNumerator,
 423        Signed192 centeredDenominator,
 424        Signed320 axisSquared,
 425        Signed320 diskCapacity,
 426        Signed320 radiusSquared)
 427    {
 9428        Signed320 remainder = WideArithmetic.SubtractSigned320(
 9429            WideArithmetic.MultiplySigned192(
 9430                candidate,
 9431                centeredDenominator),
 9432            axialNumerator);
 9433        Signed704 left = WideArithmetic.MultiplySigned320(
 9434            remainder,
 9435            remainder,
 9436            axisSquared);
 9437        Signed320 denominatorSquared = WideArithmetic.MultiplySigned192(
 9438            centeredDenominator,
 9439            centeredDenominator);
 9440        Signed704 right = WideArithmetic.MultiplySigned320(
 9441            denominatorSquared,
 9442            radiusSquared,
 9443            diskCapacity);
 9444        return WideArithmetic.CompareNonNegative(left, right) >= 0;
 445    }
 446
 447    private static Fixed64 GetRigidConeBound(
 448        Fixed64 center,
 449        Signed320 axisSquared,
 450        Signed192 axisComponent,
 451        Fixed64 height,
 452        Fixed64 radius,
 453        Signed192 centeredDenominator,
 454        bool minimum)
 455    {
 24456        Signed320 centerNumerator = WideArithmetic.MultiplySigned192(
 24457            Signed192.Signed(center.m_rawValue),
 24458            centeredDenominator);
 24459        Signed320 axialNumerator = WideArithmetic.MultiplySigned192(
 24460            axisComponent,
 24461            Signed192.Signed(height.m_rawValue));
 24462        Signed320 apexNumerator = WideArithmetic.AddSigned320(
 24463            centerNumerator,
 24464            axialNumerator);
 24465        Fixed64 apexBound = GetRigidRationalBoundClippedToDomain(
 24466            apexNumerator,
 24467            centeredDenominator,
 24468            minimum);
 24469        Fixed64 baseRimBound = GetRigidConeBaseRimBound(
 24470            center,
 24471            axisSquared,
 24472            axisComponent,
 24473            height,
 24474            radius,
 24475            centeredDenominator,
 24476            minimum);
 24477        return minimum
 24478            ? apexBound <= baseRimBound ? apexBound : baseRimBound
 24479            : apexBound >= baseRimBound ? apexBound : baseRimBound;
 480    }
 481
 482    private static Fixed64 GetRigidConeBaseRimBound(
 483        Fixed64 center,
 484        Signed320 axisSquared,
 485        Signed192 axisComponent,
 486        Fixed64 height,
 487        Fixed64 radius,
 488        Signed192 centeredDenominator,
 489        bool minimum)
 490    {
 24491        Signed320 baseOffsetNumerator = WideArithmetic.SubtractSigned320(
 24492            default,
 24493            WideArithmetic.MultiplySigned192(
 24494                axisComponent,
 24495                Signed192.Signed(height.m_rawValue)));
 24496        Fixed64 baseOffset = GetRigidSignedRawRatio(
 24497            baseOffsetNumerator,
 24498            centeredDenominator,
 24499            roundDown: minimum);
 24500        Fixed64 diskFloor = GetRigidDiskFloorExtent(
 24501            axisSquared,
 24502            axisComponent,
 24503            radius,
 24504            out Signed320 capacity,
 24505            out Signed320 radiusSquared);
 24506        Signed192 candidate = WideArithmetic.AddSigned192(
 24507            Signed192.Signed(center.m_rawValue),
 24508            Signed192.Signed(baseOffset.m_rawValue));
 24509        candidate = minimum
 24510            ? WideArithmetic.SubtractSigned192(
 24511                candidate,
 24512                Signed192.Signed(diskFloor.m_rawValue))
 24513            : WideArithmetic.AddSigned192(
 24514                candidate,
 24515                Signed192.Signed(diskFloor.m_rawValue));
 24516        if (!IsRigidConeBaseRimBoundSufficient(
 24517                candidate,
 24518                center,
 24519                baseOffsetNumerator,
 24520                centeredDenominator,
 24521                axisSquared,
 24522                capacity,
 24523                radiusSquared,
 24524                minimum))
 525        {
 2526            candidate = minimum
 2527                ? WideArithmetic.SubtractSigned192(
 2528                    candidate,
 2529                    Signed192.Signed(1L))
 2530                : WideArithmetic.AddSigned192(
 2531                    candidate,
 2532                    Signed192.Signed(1L));
 533        }
 534
 24535        return GetRigidRationalBoundClippedToDomain(
 24536            WideArithmetic.MultiplySigned192(
 24537                candidate,
 24538                centeredDenominator),
 24539            centeredDenominator,
 24540            minimum);
 541    }
 542
 543    private static Fixed64 GetRigidSignedRawRatio(
 544        Signed320 numerator,
 545        Signed192 denominator,
 546        bool roundDown)
 547    {
 24548        _ = Fixed64.TryGetSignedRawRatio(
 24549            Signed576.ExtendValue(numerator),
 24550            Signed576.ExtendValue(
 24551                Signed320.ExtendValue(denominator)),
 24552            out Fixed64 candidate);
 24553        Signed320 represented = WideArithmetic.MultiplySigned192(
 24554            Signed192.Signed(candidate.m_rawValue),
 24555            denominator);
 24556        int comparison = WideArithmetic.SubtractSigned320(
 24557            represented,
 24558            numerator).Sign;
 24559        if (roundDown && comparison > 0)
 2560            return Fixed64.FromRaw(candidate.m_rawValue - 1L);
 22561        if (!roundDown && comparison < 0)
 4562            return Fixed64.FromRaw(candidate.m_rawValue + 1L);
 18563        return candidate;
 564    }
 565
 566    private static bool IsRigidConeBaseRimBoundSufficient(
 567        Signed192 candidate,
 568        Fixed64 center,
 569        Signed320 baseOffsetNumerator,
 570        Signed192 centeredDenominator,
 571        Signed320 axisSquared,
 572        Signed320 diskCapacity,
 573        Signed320 radiusSquared,
 574        bool minimum)
 575    {
 24576        Signed192 candidateFromCenter = WideArithmetic.SubtractSigned192(
 24577            candidate,
 24578            Signed192.Signed(center.m_rawValue));
 24579        Signed320 representedOffset = WideArithmetic.MultiplySigned192(
 24580            candidateFromCenter,
 24581            centeredDenominator);
 24582        Signed320 radialGap = minimum
 24583            ? WideArithmetic.SubtractSigned320(
 24584                baseOffsetNumerator,
 24585                representedOffset)
 24586            : WideArithmetic.SubtractSigned320(
 24587                representedOffset,
 24588                baseOffsetNumerator);
 589
 24590        Signed704 left = WideArithmetic.MultiplySigned320(
 24591            radialGap,
 24592            radialGap,
 24593            axisSquared);
 24594        Signed320 denominatorSquared = WideArithmetic.MultiplySigned192(
 24595            centeredDenominator,
 24596            centeredDenominator);
 24597        Signed704 right = WideArithmetic.MultiplySigned320(
 24598            denominatorSquared,
 24599            radiusSquared,
 24600            diskCapacity);
 24601        return WideArithmetic.CompareNonNegative(left, right) >= 0;
 602    }
 603
 604    private static Fixed64 GetRigidRationalBoundClippedToDomain(
 605        Signed320 numerator,
 606        Signed192 denominator,
 607        bool minimum)
 608    {
 66609        Signed320 minimumNumerator = WideArithmetic.MultiplySigned192(
 66610            Signed192.Signed(Fixed64.MinValue.m_rawValue),
 66611            denominator);
 66612        Signed320 maximumNumerator = WideArithmetic.MultiplySigned192(
 66613            Signed192.Signed(Fixed64.MaxValue.m_rawValue),
 66614            denominator);
 66615        if (WideArithmetic.SubtractSigned320(
 66616                numerator,
 66617                minimumNumerator).Sign < 0)
 618        {
 3619            return Fixed64.MinValue;
 620        }
 63621        if (WideArithmetic.SubtractSigned320(
 63622                numerator,
 63623                maximumNumerator).Sign > 0)
 624        {
 3625            return Fixed64.MaxValue;
 626        }
 627
 60628        _ = Fixed64.TryGetSignedRawRatio(
 60629            Signed576.ExtendValue(numerator),
 60630            Signed576.ExtendValue(
 60631                Signed320.ExtendValue(denominator)),
 60632            out Fixed64 bound);
 60633        Signed320 represented = WideArithmetic.MultiplySigned192(
 60634            Signed192.Signed(bound.m_rawValue),
 60635            denominator);
 60636        int comparison = WideArithmetic.SubtractSigned320(
 60637            represented,
 60638            numerator).Sign;
 60639        if (minimum && comparison > 0)
 7640            return Fixed64.FromRaw(bound.m_rawValue - 1L);
 53641        if (!minimum && comparison < 0)
 4642            return Fixed64.FromRaw(bound.m_rawValue + 1L);
 49643        return bound;
 644    }
 645
 646    private static Signed192 GetMagnitude(Signed192 value)
 647    {
 27648        if (value.Sign >= 0)
 21649            return value;
 6650        return WideArithmetic.SubtractSigned192(default, value);
 651    }
 652
 653    private static void ValidateRigidFiniteAxis(
 654        FixedQuaternion rotation,
 655        Vector3d localAxisDirection,
 656        Fixed64 axisLength,
 657        Fixed64 radius,
 658        bool requirePositiveLength)
 659    {
 16660        if (!rotation.IsNormalized())
 661        {
 1662            throw new ArgumentException(
 1663                "Finite-axis rotation must be normalized.",
 1664                nameof(rotation));
 665        }
 15666        if (!localAxisDirection.IsNormalized())
 667        {
 1668            throw new ArgumentException(
 1669                "Finite-axis local direction must be normalized.",
 1670                nameof(localAxisDirection));
 671        }
 14672        if (requirePositiveLength
 14673            ? axisLength <= Fixed64.Zero
 14674            : axisLength < Fixed64.Zero)
 675        {
 3676            throw new ArgumentOutOfRangeException(nameof(axisLength));
 677        }
 11678        if (radius < Fixed64.Zero)
 1679            throw new ArgumentOutOfRangeException(nameof(radius));
 10680    }
 681}

Methods/Properties

.ctor(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
.ctor(FixedMathSharp.Geometry.FixedBoundBox/BoundingBoxState)
get_Center()
set_Center(FixedMathSharp.Vector3d)
get_Proportions()
set_Proportions(FixedMathSharp.Vector3d)
get_Scope()
get_State()
set_State(FixedMathSharp.Geometry.FixedBoundBox/BoundingBoxState)
FromMinMax(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
FromCenterAndSize(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
FromCenterAndScope(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
FromCenterAndSizeClippedToDomain(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
FromCenterAndScopeClippedToDomain(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
FromRelativeRotatedBoundsClippedToDomain(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion)
Orient(FixedMathSharp.Vector3d,System.Nullable`1<FixedMathSharp.Vector3d>)
Resize(FixedMathSharp.Vector3d)
SetMinMax(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
SetBoundingBox(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
Contains(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)
ProjectPoint(FixedMathSharp.Vector3d)
ClampPoint(FixedMathSharp.Vector3d)
ContainsBoxLike(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
IntersectsBoxLike(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
IntersectsSphere(FixedMathSharp.Geometry.FixedBoundSphere)
IntersectsSphereStrict(FixedMathSharp.Geometry.FixedBoundSphere)
HasPositiveVolume()
HasStrictAxisOverlap(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
DistanceToSurface(FixedMathSharp.Vector3d)
GetPointOnSurfaceTowardsObject(FixedMathSharp.Vector3d)
ClosestPointOnSurface(FixedMathSharp.Vector3d)
GetCorner(System.Int32)
CopyCorners(System.Span`1<FixedMathSharp.Vector3d>)
GetVolumeExpansionCost(FixedMathSharp.Geometry.FixedBoundBox)
Union(FixedMathSharp.Geometry.FixedBoundBox,FixedMathSharp.Geometry.FixedBoundBox)
FindClosestPointsBetweenBoxes(FixedMathSharp.Geometry.FixedBoundBox,FixedMathSharp.Geometry.FixedBoundBox)
op_Equality(FixedMathSharp.Geometry.FixedBoundBox,FixedMathSharp.Geometry.FixedBoundBox)
op_Inequality(FixedMathSharp.Geometry.FixedBoundBox,FixedMathSharp.Geometry.FixedBoundBox)
Equals(System.Object)
Equals(FixedMathSharp.Geometry.FixedBoundBox)
GetHashCode()
SetCenterAndHalfSize(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
SetCenterAndHalfSizeClippedToDomain(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
GetHalfSize(FixedMathSharp.Vector3d)
GetScopeMagnitude(FixedMathSharp.Vector3d)
FromFiniteConeClippedToDomain(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64)
FromCenteredCapsuleClippedToDomain(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
FromCenteredFiniteCylinderClippedToDomain(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
FromCenteredFiniteConeClippedToDomain(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
GetFiniteAxisLengthSquared(FixedMathSharp.Vector3d)
GetFiniteConeDiskExtent(FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
GetFiniteDiskFloorExtent(FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Signed192&,FixedMathSharp.Signed320&)
GetFiniteDiskRepresentedSquare(FixedMathSharp.Fixed64,FixedMathSharp.Signed192)
GetFiniteDiskTarget(FixedMathSharp.Signed320,FixedMathSharp.Signed192)
GetCenteredFiniteCylinderOutwardExtentRaw(FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
GetPositiveCeilingRawRatio(FixedMathSharp.Signed320,FixedMathSharp.Signed192)
IsCenteredFiniteCylinderExtentSufficient(FixedMathSharp.Signed192,FixedMathSharp.Signed320,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed320)
FromCenteredFiniteCylinderExtentsClippedToDomain(FixedMathSharp.Vector3d,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192)
GetCenteredFiniteCylinderBound(FixedMathSharp.Fixed64,FixedMathSharp.Signed192,System.Boolean)
GetCenteredFiniteConeBound(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Boolean)
FromCenteredFiniteAxisClippedToDomain(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d)
ValidateCenteredFiniteAxis(FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Boolean)
FromCenteredCapsuleClippedToDomain(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
FromCenteredFiniteCylinderClippedToDomain(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
FromCenteredFiniteConeClippedToDomain(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
GetRigidAxis(FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Signed192&,FixedMathSharp.Signed192&,FixedMathSharp.Signed192&,FixedMathSharp.Signed320&,FixedMathSharp.Signed192&)
GetRigidCapsuleBound(FixedMathSharp.Fixed64,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Signed192,System.Boolean)
GetRigidCylinderOutwardExtentRaw(FixedMathSharp.Signed320,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Signed192)
GetRigidPositiveCeilingRawRatio(FixedMathSharp.Signed320,FixedMathSharp.Signed192)
GetRigidDiskFloorExtent(FixedMathSharp.Signed320,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&)
GetRigidDiskRepresentedSquare(FixedMathSharp.Fixed64,FixedMathSharp.Signed320)
IsRigidCylinderExtentSufficient(FixedMathSharp.Signed192,FixedMathSharp.Signed320,FixedMathSharp.Signed192,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320)
GetRigidConeBound(FixedMathSharp.Fixed64,FixedMathSharp.Signed320,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Signed192,System.Boolean)
GetRigidConeBaseRimBound(FixedMathSharp.Fixed64,FixedMathSharp.Signed320,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Signed192,System.Boolean)
GetRigidSignedRawRatio(FixedMathSharp.Signed320,FixedMathSharp.Signed192,System.Boolean)
IsRigidConeBaseRimBoundSufficient(FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Signed320,FixedMathSharp.Signed192,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,System.Boolean)
GetRigidRationalBoundClippedToDomain(FixedMathSharp.Signed320,FixedMathSharp.Signed192,System.Boolean)
GetMagnitude(FixedMathSharp.Signed192)
ValidateRigidFiniteAxis(FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Boolean)