using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.Serialization; 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 top-left position ( and /// ) and a size ( and ). /// /// /// /// 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 bounding of a rotated will be equivalent or larger /// in size than the original depending on the angle of rotation. /// /// /// /// [DataContract] [DebuggerDisplay("{DebugDisplayString,nq}")] public struct RectangleF : IEquatable, IEquatableByRef, IShapeF { /// /// The with , , and /// all set to 0.0f. /// public static readonly RectangleF Empty = new RectangleF(); /// /// The x-coordinate of the top-left corner position of this . /// [DataMember] public float X; /// /// The y-coordinate of the top-left corner position of this . /// [DataMember] public float Y; /// /// The width of this . /// [DataMember] public float Width; /// /// The height of this . /// [DataMember] public float Height; /// /// Gets the x-coordinate of the left edge of this . /// public float Left => X; /// /// Gets the x-coordinate of the right edge of this . /// public float Right => X + Width; /// /// Gets the y-coordinate of the top edge of this . /// public float Top => Y; /// /// Gets the y-coordinate of the bottom edge of this . /// public float Bottom => Y + Height; /// /// Gets a value indicating whether this has a , , /// , /// all equal to 0.0f. /// /// /// true if this instance is empty; otherwise, false. /// public bool IsEmpty => Width.Equals(0) && Height.Equals(0) && X.Equals(0) && Y.Equals(0); /// /// Gets the representing the the top-left of this . /// public Vector2 Position { get { return new Vector2(X, Y); } set { X = value.X; Y = value.Y; } } public RectangleF BoundingRectangle => this; /// /// Gets the representing the extents of this . /// public SizeF Size { get { return new SizeF(Width, Height); } set { Width = value.Width; Height = value.Height; } } /// /// Gets the representing the center of this . /// public Vector2 Center => new Vector2(X + Width * 0.5f, Y + Height * 0.5f); /// /// Gets the representing the top-left of this . /// public Vector2 TopLeft => new Vector2(X, Y); /// /// Gets the representing the top-right of this . /// public Vector2 TopRight => new Vector2(X + Width, Y); /// /// Gets the representing the bottom-left of this . /// public Vector2 BottomLeft => new Vector2(X, Y + Height); /// /// Gets the representing the bottom-right of this . /// public Vector2 BottomRight => new Vector2(X + Width, Y + Height); /// /// Initializes a new instance of the structure from the specified top-left xy-coordinate /// s, width and height . /// /// The x-coordinate. /// The y-coordinate. /// The width. /// The height. public RectangleF(float x, float y, float width, float height) { X = x; Y = y; Width = width; Height = height; } /// /// Initializes a new instance of the structure from the specified top-left /// and the extents . /// /// The top-left point. /// The extents. public RectangleF(Vector2 position, SizeF size) { X = position.X; Y = position.Y; Width = size.Width; Height = size.Height; } /// /// Computes the from a minimum and maximum /// . /// /// The minimum point. /// The maximum point. /// The resulting rectangle. public static void CreateFrom(Vector2 minimum, Vector2 maximum, out RectangleF result) { result.X = minimum.X; result.Y = minimum.Y; result.Width = maximum.X - minimum.X; result.Height = maximum.Y - minimum.Y; } /// /// Computes the from a minimum and maximum /// . /// /// The minimum point. /// The maximum point. /// The resulting . public static RectangleF CreateFrom(Vector2 minimum, Vector2 maximum) { RectangleF result; CreateFrom(minimum, maximum, out result); return result; } /// /// Computes the from a list of structures. /// /// The points. /// The resulting rectangle. public static void CreateFrom(IReadOnlyList points, out RectangleF 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 RectangleF CreateFrom(IReadOnlyList points) { RectangleF result; CreateFrom(points, out result); return result; } /// /// Computes the from the specified transformed by /// the specified . /// /// The rectangle to be transformed. /// The transform matrix. /// The resulting transformed 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 RectangleF rectangle, ref Matrix3x2 transformMatrix, out RectangleF result) { var center = rectangle.Center; var halfExtents = (Vector2)rectangle.Size * 0.5f; PrimitivesHelper.TransformRectangle(ref center, ref halfExtents, ref transformMatrix); result.X = center.X - halfExtents.X; result.Y = center.Y - halfExtents.Y; result.Width = halfExtents.X * 2; result.Height = halfExtents.Y * 2; } /// /// 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 RectangleF Transform(RectangleF rectangle, ref Matrix3x2 transformMatrix) { RectangleF result; Transform(ref rectangle, ref transformMatrix, out result); return result; } /// /// Computes the that contains the two specified /// structures. /// /// The first rectangle. /// The second rectangle. /// The resulting rectangle that contains both the and the /// . public static void Union(ref RectangleF first, ref RectangleF second, out RectangleF result) { result.X = Math.Min(first.X, second.X); result.Y = Math.Min(first.Y, second.Y); result.Width = Math.Max(first.Right, second.Right) - result.X; result.Height = Math.Max(first.Bottom, second.Bottom) - result.Y; } /// /// Computes the that contains the two specified /// structures. /// /// The first rectangle. /// The second rectangle. /// /// An that contains both the and the /// . /// public static RectangleF Union(RectangleF first, RectangleF second) { RectangleF result; Union(ref first, ref second, out result); return result; } /// /// Computes the that contains both the specified and this . /// /// The rectangle. /// /// An that contains both the and /// this . /// public RectangleF Union(RectangleF rectangle) { RectangleF result; Union(ref this, ref rectangle, out result); return result; } /// /// Computes the that is in common between the two specified /// structures. /// /// The first rectangle. /// The second rectangle. /// The resulting rectangle that is in common between both the and /// the , if they intersect; otherwise, . public static void Intersection(ref RectangleF first, ref RectangleF second, out RectangleF result) { var firstMinimum = first.TopLeft; var firstMaximum = first.BottomRight; var secondMinimum = second.TopLeft; var secondMaximum = second.BottomRight; var minimum = MathExtended.CalculateMaximumVector2(firstMinimum, secondMinimum); var maximum = MathExtended.CalculateMinimumVector2(firstMaximum, secondMaximum); if ((maximum.X < minimum.X) || (maximum.Y < minimum.Y)) result = new RectangleF(); else result = CreateFrom(minimum, maximum); } /// /// Computes the that is in common between the two specified /// structures. /// /// The first rectangle. /// The second rectangle. /// /// A that is in common between both the and /// the , if they intersect; otherwise, . /// public static RectangleF Intersection(RectangleF first, RectangleF second) { RectangleF result; Intersection(ref first, ref second, out result); return result; } /// /// Computes the that is in common between the specified /// and this . /// /// The rectangle. /// /// A that is in common between both the and /// this , if they intersect; otherwise, . /// public RectangleF Intersection(RectangleF rectangle) { RectangleF result; Intersection(ref this, ref rectangle, out result); return result; } [Obsolete("RectangleF.Intersect() may be removed in the future. Use Intersection() instead.")] public static RectangleF Intersect(RectangleF value1, RectangleF value2) { RectangleF rectangle; Intersection(ref value1, ref value2, out rectangle); return rectangle; } [Obsolete("RectangleF.Intersect() may be removed in the future. Use Intersection() instead.")] public static void Intersect(ref RectangleF value1, ref RectangleF value2, out RectangleF result) { Intersection(ref value1, ref value2, out result); } /// /// Determines whether the two specified structures intersect. /// /// The first rectangle. /// The second rectangle. /// /// true if the intersects with the ; otherwise, false. /// public static bool Intersects(ref RectangleF first, ref RectangleF second) { return first.X < second.X + second.Width && first.X + first.Width > second.X && first.Y < second.Y + second.Height && first.Y + first.Height > second.Y; } /// /// Determines whether the two specified structures intersect. /// /// The first rectangle. /// The second rectangle. /// /// true if the intersects with the ; otherwise, false. /// public static bool Intersects(RectangleF first, RectangleF 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(RectangleF rectangle) { return Intersects(ref this, ref rectangle); } /// /// Determines whether the specified contains the specified /// . /// /// The rectangle. /// The point. /// /// true if the contains the ; otherwise, /// false. /// public static bool Contains(ref RectangleF rectangle, ref Vector2 point) { return rectangle.X <= point.X && point.X < rectangle.X + rectangle.Width && rectangle.Y <= point.Y && point.Y < rectangle.Y + rectangle.Height; } /// /// Determines whether the specified contains the specified /// . /// /// The rectangle. /// The point. /// /// true if the contains the ; otherwise, /// false. /// public static bool Contains(RectangleF rectangle, Vector2 point) { return Contains(ref rectangle, ref point); } /// /// Determines whether this contains the specified /// . /// /// The point. /// /// true if the this contains the ; otherwise, /// false. /// public bool Contains(Vector2 point) { return Contains(ref this, ref point); } /// /// Updates this from a list of structures. /// /// The points. public void UpdateFromPoints(IReadOnlyList points) { var rectangle = CreateFrom(points); X = rectangle.X; Y = rectangle.Y; Width = rectangle.Width; Height = rectangle.Height; } /// /// 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(TopLeft, BottomRight, point); } /// /// Computes the distance from this to a . /// /// The point. /// The distance from this to the . public float DistanceTo(Vector2 point) { return (float)Math.Sqrt(SquaredDistanceTo(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(TopLeft, BottomRight, point, out result); return result; } //TODO: Document this. public void Inflate(float horizontalAmount, float verticalAmount) { X -= horizontalAmount; Y -= verticalAmount; Width += horizontalAmount * 2; Height += verticalAmount * 2; } //TODO: Document this. public void Offset(float offsetX, float offsetY) { X += offsetX; Y += offsetY; } //TODO: Document this. public void Offset(Vector2 amount) { X += amount.X; Y += amount.Y; } /// /// Compares two structures. The result specifies whether the values of the /// , , and fields of the two structures /// are equal. /// /// The first rectangle. /// The second rectangle. /// /// true if the values of the /// , , and fields of the two structures /// are equal; otherwise, false. /// public static bool operator ==(RectangleF first, RectangleF 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 rectangle. /// The second rectangle. /// /// true if the values of the /// , , and fields of the two structures /// are unequal; otherwise, false. /// public static bool operator !=(RectangleF first, RectangleF second) { return !(first == second); } /// /// Indicates whether this is equal to another . /// /// The rectangle. /// /// true if this is equal to the ; otherwise, false. /// public bool Equals(RectangleF rectangle) { return Equals(ref rectangle); } /// /// Indicates whether this is equal to another . /// /// The rectangle. /// /// true if this is equal to the ; otherwise, false. /// public bool Equals(ref RectangleF rectangle) { // ReSharper disable CompareOfFloatsByEqualityOperator return X == rectangle.X && Y == rectangle.Y && Width == rectangle.Width && Height == rectangle.Height; // ReSharper restore CompareOfFloatsByEqualityOperator } /// /// 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) { return obj is RectangleF && Equals((RectangleF)obj); } /// /// 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 { var hashCode = X.GetHashCode(); hashCode = (hashCode * 397) ^ Y.GetHashCode(); hashCode = (hashCode * 397) ^ Width.GetHashCode(); hashCode = (hashCode * 397) ^ Height.GetHashCode(); return hashCode; } } /// /// Performs an implicit conversion from a to a . /// /// The rectangle. /// /// The resulting . /// public static implicit operator RectangleF(Rectangle rectangle) { return new RectangleF { X = rectangle.X, Y = rectangle.Y, Width = rectangle.Width, Height = rectangle.Height }; } /// /// Performs an explicit conversion from a to a . /// /// The rectangle. /// /// The resulting . /// /// /// A loss of precision may occur due to the truncation from to . /// public static explicit operator Rectangle(RectangleF rectangle) { return new Rectangle((int)rectangle.X, (int)rectangle.Y, (int)rectangle.Width, (int)rectangle.Height); } /// /// Returns a that represents this . /// /// /// A that represents this . /// public override string ToString() { return $"X: {X}, Y: {Y}, Width: {Width}, Height: {Height}"; } internal string DebugDisplayString => string.Concat(X, " ", Y, " ", Width, " ", Height); } }