using System.Numerics; using System.Runtime.CompilerServices; namespace Ghost.Graphics.Data; public struct Bounds : IEquatable { /// /// The minimum point contained by the AABB. /// /// /// If any component of is greater than then this AABB is invalid. /// /// public Vector3 Min { get; set; } /// /// The maximum point contained by the AABB. /// /// /// If any component of is less than then this AABB is invalid. /// /// public Vector3 Max { get; set; } /// /// Constructs the AABB with the given minimum and maximum. /// /// /// If you have a center and extents, you can call or /// to create the AABB. /// /// Minimum point inside AABB. /// Maximum point inside AABB. [MethodImpl(MethodImplOptions.AggressiveInlining)] public Bounds(Vector3 min, Vector3 max) { Min = min; Max = max; } /// /// Creates the AABB from a center and extents. /// /// /// This function takes full extents. It is the distance between and . /// If you have half extents, you can call . /// /// Center of AABB. /// Full extents of AABB. /// AABB created from inputs. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Bounds CreateFromCenterAndExtents(Vector3 center, Vector3 extents) { return CreateFromCenterAndHalfExtents(center, extents * 0.5f); } /// /// Creates the AABB from a center and half extents. /// /// /// This function takes half extents. It is half the distance between and . /// If you have full extents, you can call . /// /// Center of AABB. /// Half extents of AABB. /// AABB created from inputs. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Bounds CreateFromCenterAndHalfExtents(Vector3 center, Vector3 halfExtents) { return new Bounds(center - halfExtents, center + halfExtents); } /// /// Creates a new AABB with zero extents, centered at the origin. /// public static Bounds Zero { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return new Bounds(Vector3.Zero, Vector3.Zero); } } /// /// Computes the extents of the AABB. /// /// /// Extents is the componentwise distance between min and max. /// public readonly Vector3 Extents => Max - Min; /// /// Computes the half extents of the AABB. /// /// /// HalfExtents is half of the componentwise distance between min and max. Subtracting HalfExtents from Center /// gives Min and adding HalfExtents to Center gives Max. /// public readonly Vector3 HalfExtents => (Max - Min) * 0.5f; /// /// Computes the center of the AABB. /// public readonly Vector3 Center => (Max + Min) * 0.5f; /// /// Check if the AABB is valid. /// /// /// An AABB is considered valid if is componentwise less than or equal to . /// /// True if is componentwise less than or equal to . public readonly bool IsValid => Vector3.Dot(Min, Min) <= Vector3.Dot(Max, Max); /// /// Computes the surface area for this axis aligned bounding box. /// public readonly float SurfaceArea { get { var diff = Max - Min; return 2 * Vector3.Dot(diff, new Vector3(diff.Y, diff.Z, diff.X)); } } /// /// Tests if the input point is contained by the AABB. /// /// Point to test. /// True if AABB contains the input point. [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Contains(Vector3 point) => Vector3.Dot(point, point) >= Vector3.Dot(Min, Min) && Vector3.Dot(point, point) <= Vector3.Dot(Max, Max); /// /// Tests if the input AABB is contained entirely by this AABB. /// /// AABB to test. /// True if input AABB is contained entirely by this AABB. [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Contains(Bounds aabb) => Vector3.Dot(Min, Min) <= Vector3.Dot(aabb.Min, aabb.Min) && Vector3.Dot(Max, Max) >= Vector3.Dot(aabb.Max, aabb.Max); /// /// Tests if the input AABB overlaps this AABB. /// /// AABB to test. /// True if input AABB overlaps with this AABB. [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Overlaps(Bounds aabb) { return Vector3.Dot(Max, Max) >= Vector3.Dot(aabb.Min, aabb.Min) && Vector3.Dot(Min, Min) <= Vector3.Dot(aabb.Max, aabb.Max); } /// /// Expands the AABB by the given signed distance. /// /// /// Positive distance expands the AABB while negative distance shrinks the AABB. /// /// Signed distance to expand the AABB with. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Expand(float signedDistance) { Min -= new Vector3(signedDistance); Max += new Vector3(signedDistance); } /// /// Encapsulates the given AABB. /// /// /// Modifies this AABB so that it contains the given AABB. If the given AABB is already contained by this AABB, /// then this AABB doesn't change. /// /// /// AABB to encapsulate. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Encapsulate(Bounds aabb) { Min = Vector3.Min(Min, aabb.Min); Max = Vector3.Max(Max, aabb.Max); } /// /// Encapsulate the given point. /// /// /// Modifies this AABB so that it contains the given point. If the given point is already contained by this AABB, /// then this AABB doesn't change. /// /// /// Point to encapsulate. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Encapsulate(Vector3 point) { Min = Vector3.Min(Min, point); Max = Vector3.Max(Max, point); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Bounds other) { return Min.Equals(other.Min) && Max.Equals(other.Max); } public override bool Equals(object? obj) { if (obj is Bounds bounds) { return Equals(bounds); } return false; } public static bool operator ==(Bounds left, Bounds right) { return left.Equals(right); } public static bool operator !=(Bounds left, Bounds right) { return !(left == right); } public override int GetHashCode() { unchecked { var hash = 17; hash = hash * 31 + Min.GetHashCode(); hash = hash * 31 + Max.GetHashCode(); return hash; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly override string ToString() { return string.Format("Bounds({0}, {1})", Min, Max); } }