using System; using System.Collections.Generic; using System.Diagnostics; using Microsoft.Xna.Framework; namespace MonoGame.Extended { // Real-Time Collision Detection, Christer Ericson, 2005. Chapter 4.2; Bounding Volumes - Axis-aligned Bounding Boxes (AABBs). pg 77 /// /// An axis-aligned, four sided, two dimensional box defined by a centre and a radii /// . /// /// /// /// An is categorized by having its faces oriented in such a way that its /// face normals are at all times parallel with the axes of the given coordinate system. /// /// /// The of a rotated will be equivalent or larger /// in size /// than the original depending on the angle of rotation. /// /// /// /// [DebuggerDisplay("{" + nameof(DebugDisplayString) + ",nq}")] public struct BoundingRectangle : IEquatable, IEquatableByRef { /// /// The with and /// set to . /// public static readonly BoundingRectangle Empty = new BoundingRectangle(); /// /// The centre position of this . /// public Vector2 Center; /// /// The distance from the point along both axes to any point on the boundary of this /// . /// public Vector2 HalfExtents; /// /// Initializes a new instance of the structure from the specified centre /// and the radii . /// /// The centre . /// The radii . public BoundingRectangle(Vector2 center, SizeF halfExtents) { Center = center; HalfExtents = halfExtents; } /// /// Computes the from a minimum and maximum /// . /// /// The minimum point. /// The maximum point. /// The resulting bounding rectangle. public static void CreateFrom(Vector2 minimum, Vector2 maximum, out BoundingRectangle result) { result.Center = new Vector2((maximum.X + minimum.X) * 0.5f, (maximum.Y + minimum.Y) * 0.5f); result.HalfExtents = new Vector2((maximum.X - minimum.X) * 0.5f, (maximum.Y - minimum.Y) * 0.5f); } /// /// Computes the from a minimum and maximum /// . /// /// The minimum point. /// The maximum point. /// The resulting . public static BoundingRectangle CreateFrom(Vector2 minimum, Vector2 maximum) { BoundingRectangle result; CreateFrom(minimum, maximum, out result); return result; } /// /// Computes the from a list of structures. /// /// The points. /// The resulting bounding rectangle. public static void CreateFrom(IReadOnlyList points, out BoundingRectangle result) { Vector2 minimum; Vector2 maximum; PrimitivesHelper.CreateRectangleFromPoints(points, out minimum, out maximum); CreateFrom(minimum, maximum, out result); } /// /// Computes the from a list of structures. /// /// The points. /// The resulting . public static BoundingRectangle CreateFrom(IReadOnlyList points) { BoundingRectangle result; CreateFrom(points, out result); return result; } /// /// Computes the from the specified transformed by /// the /// specified . /// /// The bounding rectangle. /// The transform matrix. /// The resulting bounding rectangle. /// /// The from the transformed by the /// . /// /// /// /// If a transformed is used for then the /// resulting will have the compounded transformation, which most likely is /// not desired. /// /// public static void Transform(ref BoundingRectangle boundingRectangle, ref Matrix3x2 transformMatrix, out BoundingRectangle result) { PrimitivesHelper.TransformRectangle(ref boundingRectangle.Center, ref boundingRectangle.HalfExtents, ref transformMatrix); result.Center = boundingRectangle.Center; result.HalfExtents = boundingRectangle.HalfExtents; } /// /// Computes the from the specified transformed by /// the /// specified . /// /// The bounding rectangle. /// The transform matrix. /// /// The from the transformed by the /// . /// /// /// /// If a transformed is used for then the /// resulting will have the compounded transformation, which most likely is /// not desired. /// /// public static BoundingRectangle Transform(BoundingRectangle boundingRectangle, ref Matrix3x2 transformMatrix) { BoundingRectangle result; Transform(ref boundingRectangle, ref transformMatrix, out result); return result; } /// /// Computes the that contains the two specified /// structures. /// /// The first bounding rectangle. /// The second bounding rectangle. /// The resulting bounding rectangle that contains both the and the /// . public static void Union(ref BoundingRectangle first, ref BoundingRectangle second, out BoundingRectangle result) { // Real-Time Collision Detection, Christer Ericson, 2005. Chapter 6.5; Bounding Volume Hierarchies - Merging Bounding Volumes. pg 267 var firstMinimum = first.Center - first.HalfExtents; var firstMaximum = first.Center + first.HalfExtents; var secondMinimum = second.Center - second.HalfExtents; var secondMaximum = second.Center + second.HalfExtents; var minimum = MathExtended.CalculateMinimumVector2(firstMinimum, secondMinimum); var maximum = MathExtended.CalculateMaximumVector2(firstMaximum, secondMaximum); result = CreateFrom(minimum, maximum); } /// /// Computes the that contains the two specified /// structures. /// /// The first bounding rectangle. /// The second bounding rectangle. /// /// A that contains both the and the /// . /// public static BoundingRectangle Union(BoundingRectangle first, BoundingRectangle second) { BoundingRectangle result; Union(ref first, ref second, out result); return result; } /// /// Computes the that contains both the specified /// and this . /// /// The bounding rectangle. /// /// A that contains both the and /// this /// . /// public BoundingRectangle Union(BoundingRectangle boundingRectangle) { return Union(this, boundingRectangle); } /// /// Computes the that is in common between the two specified /// structures. /// /// The first bounding rectangle. /// The second bounding rectangle. /// The resulting bounding rectangle that is in common between both the and /// the , if they intersect; otherwise, . public static void Intersection(ref BoundingRectangle first, ref BoundingRectangle second, out BoundingRectangle result) { var firstMinimum = first.Center - first.HalfExtents; var firstMaximum = first.Center + first.HalfExtents; var secondMinimum = second.Center - second.HalfExtents; var secondMaximum = second.Center + second.HalfExtents; var minimum = MathExtended.CalculateMaximumVector2(firstMinimum, secondMinimum); var maximum = MathExtended.CalculateMinimumVector2(firstMaximum, secondMaximum); if ((maximum.X < minimum.X) || (maximum.Y < minimum.Y)) result = new BoundingRectangle(); else result = CreateFrom(minimum, maximum); } /// /// Computes the that is in common between the two specified /// structures. /// /// The first bounding rectangle. /// The second bounding rectangle. /// /// A that is in common between both the and /// the , if they intersect; otherwise, . /// public static BoundingRectangle Intersection(BoundingRectangle first, BoundingRectangle second) { BoundingRectangle result; Intersection(ref first, ref second, out result); return result; } /// /// Computes the that is in common between the specified /// and this . /// /// The bounding rectangle. /// /// A that is in common between both the and /// this , if they intersect; otherwise, . /// public BoundingRectangle Intersection(BoundingRectangle boundingRectangle) { BoundingRectangle result; Intersection(ref this, ref boundingRectangle, out result); return result; } /// /// Determines whether the two specified structures intersect. /// /// The first bounding rectangle. /// The second bounding rectangle. /// /// true if the intersects with the ; otherwise, false. /// public static bool Intersects(ref BoundingRectangle first, ref BoundingRectangle second) { // Real-Time Collision Detection, Christer Ericson, 2005. Chapter 4.2; Bounding Volumes - Axis-aligned Bounding Boxes (AABBs). pg 80 var distance = first.Center - second.Center; var radii = first.HalfExtents + second.HalfExtents; return Math.Abs(distance.X) <= radii.X && Math.Abs(distance.Y) <= radii.Y; } /// /// Determines whether the two specified structures intersect. /// /// The first bounding rectangle. /// The second bounding rectangle. /// /// true if the intersects with the ; otherwise, false. /// public static bool Intersects(BoundingRectangle first, BoundingRectangle second) { return Intersects(ref first, ref second); } /// /// Determines whether the specified intersects with this /// . /// /// The bounding rectangle. /// /// true if the intersects with this /// ; otherwise, /// false. /// public bool Intersects(ref BoundingRectangle boundingRectangle) { return Intersects(ref this, ref boundingRectangle); } /// /// Determines whether the specified intersects with this /// . /// /// The bounding rectangle. /// /// true if the intersects with this /// ; otherwise, /// false. /// public bool Intersects(BoundingRectangle boundingRectangle) { return Intersects(ref this, ref boundingRectangle); } /// /// Updates this from a list of structures. /// /// The points. public void UpdateFromPoints(IReadOnlyList points) { var boundingRectangle = CreateFrom(points); Center = boundingRectangle.Center; HalfExtents = boundingRectangle.HalfExtents; } /// /// Determines whether the specified contains the specified /// . /// /// The bounding rectangle. /// The point. /// /// true if the contains the ; otherwise, /// false. /// public static bool Contains(ref BoundingRectangle boundingRectangle, ref Vector2 point) { // Real-Time Collision Detection, Christer Ericson, 2005. Chapter 4.2; Bounding Volumes - Axis-aligned Bounding Boxes (AABBs). pg 78 var distance = boundingRectangle.Center - point; var radii = boundingRectangle.HalfExtents; return (Math.Abs(distance.X) <= radii.X) && (Math.Abs(distance.Y) <= radii.Y); } /// /// Determines whether the specified contains the specified /// . /// /// The bounding rectangle. /// The point. /// /// true if the contains the ; otherwise, /// false. /// public static bool Contains(BoundingRectangle boundingRectangle, Vector2 point) { return Contains(ref boundingRectangle, ref point); } /// /// Determines whether this contains the specified . /// /// The point. /// /// true if this contains the ; otherwise, /// false. /// public bool Contains(Vector2 point) { return Contains(this, point); } /// /// Computes the squared distance from this to a . /// /// The point. /// The squared distance from this to the . public float SquaredDistanceTo(Vector2 point) { return PrimitivesHelper.SquaredDistanceToPointFromRectangle(Center - HalfExtents, Center + HalfExtents, point); } /// /// Computes the closest on this to a specified /// . /// /// The point. /// The closest on this to the . public Vector2 ClosestPointTo(Vector2 point) { Vector2 result; PrimitivesHelper.ClosestPointToPointFromRectangle(Center - HalfExtents, Center + HalfExtents, point, out result); return result; } /// /// Compares two structures. The result specifies whether the values of the /// and fields of the two structures /// are equal. /// /// The first bounding rectangle. /// The second bounding rectangle. /// /// true if the and fields of the two /// structures are equal; otherwise, false. /// public static bool operator ==(BoundingRectangle first, BoundingRectangle second) { return first.Equals(ref second); } /// /// Compares two structures. The result specifies whether the values of the /// and fields of the two structures /// are unequal. /// /// The first bounding rectangle. /// The second bounding rectangle. /// /// true if the and fields of the two /// structures are unequal; otherwise, false. /// public static bool operator !=(BoundingRectangle first, BoundingRectangle second) { return !(first == second); } /// /// Indicates whether this is equal to another /// . /// /// The bounding rectangle. /// /// true if this is equal to the ; /// otherwise, false. /// public bool Equals(BoundingRectangle boundingRectangle) { return Equals(ref boundingRectangle); } /// /// Indicates whether this is equal to another . /// /// The bounding rectangle. /// /// true if this is equal to the ; /// otherwise, /// false. /// public bool Equals(ref BoundingRectangle boundingRectangle) { return (boundingRectangle.Center == Center) && (boundingRectangle.HalfExtents == HalfExtents); } /// /// Returns a value indicating whether this is equal to a specified object. /// /// The object to make the comparison with. /// /// true if this is equal to ; otherwise, false. /// public override bool Equals(object obj) { if (obj is BoundingRectangle) return Equals((BoundingRectangle)obj); return false; } /// /// Returns a hash code of this suitable for use in hashing algorithms and data /// structures like a hash table. /// /// /// A hash code of this . /// public override int GetHashCode() { unchecked { return (Center.GetHashCode() * 397) ^ HalfExtents.GetHashCode(); } } /// /// Performs an implicit conversion from a to a . /// /// The rectangle. /// /// The resulting . /// public static implicit operator BoundingRectangle(Rectangle rectangle) { var radii = new SizeF(rectangle.Width * 0.5f, rectangle.Height * 0.5f); var centre = new Vector2(rectangle.X + radii.Width, rectangle.Y + radii.Height); return new BoundingRectangle(centre, radii); } /// /// Performs an implicit conversion from a to a . /// /// The bounding rectangle. /// /// The resulting . /// public static implicit operator Rectangle(BoundingRectangle boundingRectangle) { var minimum = boundingRectangle.Center - boundingRectangle.HalfExtents; return new Rectangle((int)minimum.X, (int)minimum.Y, (int)boundingRectangle.HalfExtents.X * 2, (int)boundingRectangle.HalfExtents.Y * 2); } /// /// Performs an implicit conversion from a to a . /// /// The rectangle. /// /// The resulting . /// public static implicit operator BoundingRectangle(RectangleF rectangle) { var radii = new SizeF(rectangle.Width * 0.5f, rectangle.Height * 0.5f); var centre = new Vector2(rectangle.X + radii.Width, rectangle.Y + radii.Height); return new BoundingRectangle(centre, radii); } /// /// Performs an implicit conversion from a to a . /// /// The bounding rectangle. /// /// The resulting . /// public static implicit operator RectangleF(BoundingRectangle boundingRectangle) { var minimum = boundingRectangle.Center - boundingRectangle.HalfExtents; return new RectangleF(minimum.X, minimum.Y, boundingRectangle.HalfExtents.X * 2, boundingRectangle.HalfExtents.Y * 2); } /// /// Returns a that represents this . /// /// /// A that represents this . /// public override string ToString() { return $"Centre: {Center}, Radii: {HalfExtents}"; } internal string DebugDisplayString => ToString(); } }