< Summary

Information
Class: FixedMathSharp.BoundingBox
Assembly: FixedMathSharp
File(s): /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Bounds/BoundingBox.cs
Line coverage
98%
Covered lines: 166
Uncovered lines: 2
Coverable lines: 168
Total lines: 471
Line coverage: 98.8%
Branch coverage
91%
Covered branches: 55
Total branches: 60
Branch coverage: 91.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
.ctor(...)100%11100%
.ctor(...)100%11100%
get_Min()100%11100%
get_Max()100%11100%
get_Center()100%11100%
set_Center(...)100%11100%
get_Proportions()100%11100%
set_Proportions(...)100%11100%
get_Scope()100%210%
get_Vertices()100%11100%
get_State()100%11100%
set_State(...)100%11100%
Orient(...)100%22100%
Resize(...)100%11100%
SetMinMax(...)100%11100%
SetBoundingBox(...)100%11100%
Contains(...)100%1010100%
Intersects(...)95%202090.9%
ProjectPoint(...)100%11100%
DistanceToSurface(...)100%22100%
GetPointOnSurfaceTowardsObject(...)100%11100%
ClosestPointOnSurface(...)85.71%1414100%
GetOrGenerateVertices()75%44100%
Union(...)100%11100%
FindClosestPointsBetweenBoxes(...)100%44100%
op_Equality(...)100%11100%
op_Inequality(...)100%11100%
Equals(...)50%22100%
Equals(...)100%22100%
GetHashCode()100%11100%

File(s)

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Bounds/BoundingBox.cs

#LineLine coverage
 1using MemoryPack;
 2using System;
 3using System.Runtime.CompilerServices;
 4using System.Text.Json.Serialization;
 5
 6namespace FixedMathSharp;
 7
 8/// <summary>
 9/// Represents an axis-aligned bounding box with fixed-point precision, capable of encapsulating 3D objects for more com
 10/// </summary>
 11/// <remarks>
 12/// The BoundingBox provides a more detailed representation of an object's spatial extent compared to BoundingArea.
 13/// It is useful in 3D scenarios requiring more precise volume fitting and supports a wider range of operations, includi
 14///
 15/// Use Cases:
 16/// - Encapsulating objects in 3D space for collision detection, ray intersection, or visibility testing.
 17/// - Used in physics engines, rendering pipelines, and 3D simulations to represent object boundaries.
 18/// - Supports more precise intersection tests than BoundingArea, making it ideal for detailed spatial queries.
 19/// </remarks>
 20[Serializable]
 21[MemoryPackable]
 22public partial struct BoundingBox : IBound, IEquatable<BoundingBox>
 23{
 24    #region Nested Types
 25
 26    [Serializable]
 27    [MemoryPackable]
 28    public readonly partial struct BoundingBoxState
 29    {
 30        [JsonInclude]
 31        [MemoryPackInclude]
 32        public readonly Vector3d Min;
 33
 34        [JsonInclude]
 35        [MemoryPackInclude]
 36        public readonly Vector3d Max;
 37
 38        [JsonConstructor]
 39        public BoundingBoxState(Vector3d min, Vector3d max)
 440        {
 441            Min = min;
 442            Max = max;
 443        }
 44    }
 45
 46    #endregion
 47
 48    #region Fields
 49
 50    /// <summary>
 51    /// Vertices of the bounding box.
 52    /// </summary>
 53    private Vector3d[] _vertices;
 54
 55    private bool _isDirty;
 56
 57    #endregion
 58
 59    #region Constructors
 60
 61    /// <summary>
 62    /// Initializes a new instance of the BoundingBox struct with the specified center and size.
 63    /// </summary>
 64
 65    public BoundingBox(Vector3d center, Vector3d size)
 5166    {
 5167        Vector3d half = Vector3d.Abs(size) * Fixed64.Half;
 68
 5169        Min = center - half;
 5170        Max = center + half;
 71
 5172        _vertices = new Vector3d[8];
 5173        _isDirty = true;
 5174    }
 75
 76    [JsonConstructor]
 77    [MemoryPackConstructor]
 78    public BoundingBox(BoundingBoxState state)
 379    {
 380        State = state;
 381        _vertices = new Vector3d[8];
 382    }
 83
 84    #endregion
 85
 86    #region Properties
 87
 88    /// <summary>
 89    /// The minimum corner of the bounding box.
 90    /// </summary>
 91    [JsonIgnore]
 92    [MemoryPackIgnore]
 25393    public Vector3d Min { get; private set; }
 94
 95    /// <summary>
 96    /// The maximum corner of the bounding box.
 97    /// </summary>
 98    [JsonIgnore]
 99    [MemoryPackIgnore]
 248100    public Vector3d Max { get; private set; }
 101
 102    /// <summary>
 103    /// The center of the bounding box.
 104    /// </summary>
 105    [JsonIgnore]
 106    [MemoryPackIgnore]
 107    public Vector3d Center
 108    {
 109        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 7110        get => (Min + Max) * Fixed64.Half;
 111
 112        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 113        set
 1114        {
 1115            Vector3d half = (Max - Min) * Fixed64.Half;
 1116            Min = value - half;
 1117            Max = value + half;
 1118            _isDirty = true;
 1119        }
 120    }
 121
 122    /// <summary>
 123    /// The total size of the box (Width, Height, Depth). This is always twice the scope.
 124    /// </summary>
 125    [JsonIgnore]
 126    [MemoryPackIgnore]
 127    public Vector3d Proportions
 128    {
 129        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 7130        get => Max - Min;
 131
 132        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 133        set
 1134        {
 1135            Vector3d half = value * Fixed64.Half;
 1136            Vector3d center = (Min + Max) * Fixed64.Half;
 1137            Min = center - half;
 1138            Max = center + half;
 1139            _isDirty = true;
 1140        }
 141    }
 142
 143    /// <summary>
 144    /// The range (half-size) of the bounding box in all directions. Always half of the total size.
 145    /// </summary>
 146    [JsonIgnore]
 147    [MemoryPackIgnore]
 148    public Vector3d Scope
 149    {
 150        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 0151        get => (Max - Min) * Fixed64.Half;
 152    }
 153
 154    /// <inheritdoc cref="_vertices" />
 155    [JsonIgnore]
 156    [MemoryPackIgnore]
 157    public Vector3d[] Vertices
 158    {
 159        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 27160        get => GetOrGenerateVertices();
 161    }
 162
 163    [JsonInclude]
 164    [MemoryPackInclude]
 165    public BoundingBoxState State
 166    {
 3167        get => new(Min, Max);
 168
 169
 170        internal set
 3171        {
 3172            Min = value.Min;
 3173            Max = value.Max;
 3174            _isDirty = true;
 3175        }
 176    }
 177
 178    #endregion
 179
 180    #region Mutators
 181
 182    /// <summary>
 183    /// Orients the bounding box with the given center and size.
 184    /// </summary>
 185    public void Orient(Vector3d center, Vector3d? size)
 2186    {
 2187        Vector3d half = size.HasValue
 2188            ? size.Value * Fixed64.Half
 2189            : (Max - Min) * Fixed64.Half;
 190
 2191        Min = center - half;
 2192        Max = center + half;
 2193        _isDirty = true;
 2194    }
 195
 196    /// <summary>
 197    /// Resizes the bounding box to the specified size, keeping the same center.
 198    /// </summary>
 199    public void Resize(Vector3d size)
 3200    {
 3201        Vector3d half = size * Fixed64.Half;
 3202        Vector3d center = (Min + Max) * Fixed64.Half;
 203
 3204        Min = center - half;
 3205        Max = center + half;
 3206        _isDirty = true;
 3207    }
 208
 209    /// <summary>
 210    /// Sets the bounds of the bounding box by specifying its minimum and maximum points.
 211    /// </summary>
 212    public void SetMinMax(Vector3d min, Vector3d max)
 1213    {
 1214        Min = min;
 1215        Max = max;
 1216        _isDirty = true;
 1217    }
 218
 219    /// <summary>
 220    /// Configures the bounding box with the specified center and scope (half-size).
 221    /// </summary>
 222    public void SetBoundingBox(Vector3d center, Vector3d scope)
 1223    {
 1224        Min = center - scope;
 1225        Max = center + scope;
 1226        _isDirty = true;
 1227    }
 228
 229    /// <summary>
 230    /// Determines if a point is inside the bounding box (including boundaries).
 231    /// </summary>
 232    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 233    public bool Contains(Vector3d point)
 31234    {
 31235        return point.x >= Min.x && point.x <= Max.x
 31236            && point.y >= Min.y && point.y <= Max.y
 31237            && point.z >= Min.z && point.z <= Max.z;
 31238    }
 239
 240    /// <summary>
 241    /// Checks if another IBound intersects with this bounding box.
 242    /// Ensures overlap, not just touching boundaries.
 243    /// </summary>
 244    /// <remarks>
 245    /// It checks for overlap on all axes. If there is no overlap on any axis, they do not intersect.
 246    /// </remarks>
 247    public bool Intersects(IBound other)
 8248    {
 8249        switch (other)
 250        {
 251            case BoundingBox or BoundingArea:
 6252                {
 6253                    if (Contains(other.Min) && Contains(other.Max))
 0254                        return true;  // Full containment
 255
 256                    // General intersection logic (allowing for overlap)
 6257                    return !(Max.x <= other.Min.x || Min.x >= other.Max.x ||
 6258                             Max.y <= other.Min.y || Min.y >= other.Max.y ||
 6259                             Max.z <= other.Min.z || Min.z >= other.Max.z);
 260                }
 261            case BoundingSphere sphere:
 262                // project the sphere’s center onto the 3D volume and checks the distance to the surface.
 263                // If the distance from the closest point to the center is less than or equal to the sphere’s radius, th
 1264                return Vector3d.SqrDistance(sphere.Center, this.ProjectPointWithinBounds(sphere.Center)) <= sphere.SqrRa
 265
 266            default:
 1267                return false; // Default case for unknown or unsupported types
 268        }
 8269    }
 270
 271    /// <summary>
 272    /// Projects a point onto the bounding box. If the point is outside the box, it returns the closest point on the sur
 273    /// </summary>
 274    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 275    public Vector3d ProjectPoint(Vector3d point)
 2276        => this.ProjectPointWithinBounds(point);
 277
 278    /// <summary>
 279    /// Calculates the shortest distance from a given point to the surface of the bounding box.
 280    /// If the point lies inside the box, the distance is zero.
 281    /// </summary>
 282    /// <param name="point">The point from which to calculate the distance.</param>
 283    /// <returns>
 284    /// The shortest distance from the point to the surface of the bounding box.
 285    /// If the point is inside the box, the method returns zero.
 286    /// </returns>
 287    /// <remarks>
 288    /// The method finds the closest point on the box's surface by clamping the given point
 289    /// to the box's bounds and returns the Euclidean distance between them.
 290    /// This ensures accurate distance calculations, even near corners or edges.
 291    /// </remarks>
 292    public Fixed64 DistanceToSurface(Vector3d point)
 2293    {
 294        // Clamp the point to the nearest point on the box's surface
 2295        Vector3d clampedPoint = new(
 2296            FixedMath.Clamp(point.x, Min.x, Max.x),
 2297            FixedMath.Clamp(point.y, Min.y, Max.y),
 2298            FixedMath.Clamp(point.z, Min.z, Max.z)
 2299        );
 300
 301        // If the point is inside the box, return 0
 2302        if (Contains(point))
 1303            return Fixed64.Zero;
 304
 305        // Otherwise, return the Euclidean distance to the clamped point
 1306        return Vector3d.Distance(point, clampedPoint);
 2307    }
 308
 309    /// <summary>
 310    /// Finds the closest point on the surface of the bounding box towards a specified object position.
 311    /// </summary>
 312    public Vector3d GetPointOnSurfaceTowardsObject(Vector3d objectPosition)
 1313        => ClosestPointOnSurface(ProjectPoint(objectPosition));
 314
 315    /// <summary>
 316    /// Finds the closest point on the surface of the bounding box to the specified point.
 317    /// </summary>
 318    public Vector3d ClosestPointOnSurface(Vector3d point)
 13319    {
 13320        if (Contains(point))
 4321        {
 322            // Calculate distances to each face and return the closest face.
 4323            Fixed64 distToMinX = point.x - Min.x;
 4324            Fixed64 distToMaxX = Max.x - point.x;
 4325            Fixed64 distToMinY = point.y - Min.y;
 4326            Fixed64 distToMaxY = Max.y - point.y;
 4327            Fixed64 distToMinZ = point.z - Min.z;
 4328            Fixed64 distToMaxZ = Max.z - point.z;
 329
 4330            Fixed64 minDistToFace = FixedMath.Min(distToMinX,
 4331                FixedMath.Min(distToMaxX,
 4332                FixedMath.Min(distToMinY,
 4333                FixedMath.Min(distToMaxY,
 4334                FixedMath.Min(distToMinZ, distToMaxZ)))));
 335
 336            // Adjust the closest point based on the face.
 5337            if (minDistToFace == distToMinX) point.x = Min.x;
 4338            else if (minDistToFace == distToMaxX) point.x = Max.x;
 339
 4340            if (minDistToFace == distToMinY) point.y = Min.y;
 5341            else if (minDistToFace == distToMaxY) point.y = Max.y;
 342
 5343            if (minDistToFace == distToMinZ) point.z = Min.z;
 3344            else if (minDistToFace == distToMaxZ) point.z = Max.z;
 345
 4346            return point;
 347        }
 348
 349        // If the point is outside the box, clamp to the nearest surface.
 9350        return new Vector3d(
 9351            FixedMath.Clamp(point.x, Min.x, Max.x),
 9352            FixedMath.Clamp(point.y, Min.y, Max.y),
 9353            FixedMath.Clamp(point.z, Min.z, Max.z)
 9354        );
 13355    }
 356
 357    /// <summary>
 358    /// Generates the vertices of the bounding box based on its center and scope.
 359    /// </summary>
 360    /// <remarks>
 361    ///  Vertices[0]  near Bot left
 362    ///  Vertices[1]  near Bot right
 363    ///  Vertices[2]  near Top left
 364    ///  Vertices[3]  near Top right
 365    ///  Vertices[4]  far bot left
 366    ///  Vertices[5]  far bot right
 367    ///  Vertices[6]  far top left
 368    ///  Vertices[7]  far top right
 369    ///  ----
 370    ///  near quad
 371    ///  0 - 1 Bot left near to bot right near
 372    ///  2 - 3 Top left near to top right near
 373    ///  0 - 2 Bot left near to top left near
 374    ///  1 - 3 Bot right near to top right near
 375    ///  far quad
 376    ///  4 - 5 Bot left far to bot right far
 377    ///  6 - 7 Top left far to top right far
 378    ///  4 - 6 Bot left far to top left far
 379    ///  5 - 7 Bot right far to top right far
 380    ///  lines connecting near and far quads
 381    ///  0 - 4 Bot left near to bot left far
 382    ///  1 - 5 Bot right near to bot right far
 383    ///  2 - 6 Top left near to top left far
 384    ///  3 - 7 Top right near to top right far
 385    /// </remarks>
 386    private Vector3d[] GetOrGenerateVertices()
 27387    {
 27388        _vertices ??= new Vector3d[8];
 389
 27390        if (_isDirty)
 3391        {
 3392            Vector3d min = Min;
 3393            Vector3d max = Max;
 394
 3395            _vertices[0] = new(min.x, min.y, min.z);
 3396            _vertices[1] = new(max.x, min.y, min.z);
 3397            _vertices[2] = new(min.x, max.y, min.z);
 3398            _vertices[3] = new(max.x, max.y, min.z);
 3399            _vertices[4] = new(min.x, min.y, max.z);
 3400            _vertices[5] = new(max.x, min.y, max.z);
 3401            _vertices[6] = new(min.x, max.y, max.z);
 3402            _vertices[7] = new(max.x, max.y, max.z);
 403
 3404            _isDirty = false;
 3405        }
 406
 27407        return _vertices;
 27408    }
 409
 410    #endregion
 411
 412    #region Static Ops
 413
 414    /// <summary>
 415    /// Creates a new bounding box that is the union of two bounding boxes.
 416    /// </summary>
 417    public static BoundingBox Union(BoundingBox a, BoundingBox b)
 1418    {
 1419        return new BoundingBox
 1420        {
 1421            Min = Vector3d.Min(a.Min, b.Min),
 1422            Max = Vector3d.Max(a.Max, b.Max),
 1423            _isDirty = true
 1424        };
 1425    }
 426
 427    /// <summary>
 428    /// Finds the closest points between two bounding boxes.
 429    /// </summary>
 430    public static Vector3d FindClosestPointsBetweenBoxes(BoundingBox a, BoundingBox b)
 1431    {
 1432        Vector3d closestPoint = Vector3d.Zero;
 1433        Fixed64 minDistance = Fixed64.MAX_VALUE;
 434
 18435        for (int i = 0; i < b.Vertices.Length; i++)
 8436        {
 8437            Vector3d point = a.ClosestPointOnSurface(b.Vertices[i]);
 8438            Fixed64 distance = Vector3d.Distance(point, b.Vertices[i]);
 8439            if (distance < minDistance)
 1440            {
 1441                closestPoint = point;
 1442                minDistance = distance;
 1443            }
 8444        }
 445
 1446        return closestPoint;
 1447    }
 448
 449    #endregion
 450
 451    #region Equality
 452
 2453    public static bool operator ==(BoundingBox left, BoundingBox right) => left.Equals(right);
 1454    public static bool operator !=(BoundingBox left, BoundingBox right) => !left.Equals(right);
 455
 456    #endregion
 457
 458    #region Equality and HashCode Overrides
 459
 1460    public override bool Equals(object? obj) => obj is BoundingBox other && Equals(other);
 461
 462    public bool Equals(BoundingBox other)
 5463        => Min.Equals(other.Min) && Max.Equals(other.Max);
 464
 465    public override int GetHashCode()
 2466    {
 2467        return HashCode.Combine(Min, Max);
 2468    }
 469
 470    #endregion
 471}