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;
}
[Obsolete("RectangleF.Intersection() will be removed in the next update. Use Intersect() instead.")]
public static void Intersection(ref RectangleF first,
ref RectangleF second, out RectangleF result)
{
Intersect(ref first, ref second, out result);
}
[Obsolete("RectangleF.Intersection() will be removed in the next update. Use Intersect() instead.")]
public static RectangleF Intersection(RectangleF first,
RectangleF second)
{
return Intersect(first, second);
}
[Obsolete("RectangleF.Intersection() will be removed in the next update. Use Intersect() instead.")]
public RectangleF Intersection(RectangleF rectangle)
{
return Intersect(rectangle);
}
///
/// Computes the that represents the intersection of two
/// structures.
///
/// The first rectangle to intersect.
/// The second rectangle to intersect.
///
/// A that represents the intersection of and
/// , if there is an intersection; otherwise, .
///
public static RectangleF Intersect(RectangleF value1, RectangleF value2)
{
RectangleF rectangle;
Intersect(ref value1, ref value2, out rectangle);
return rectangle;
}
///
/// Computes the that represents the intersection of two
/// structures.
///
/// The first rectangle to intersect.
/// The second rectangle to intersect.
///
/// When this method returns, contains the that represents the intersection of
/// and , if there is an intersection; otherwise,
///
///
public static void Intersect(ref RectangleF value1, ref RectangleF value2, out RectangleF result)
{
var firstMinimum = value1.TopLeft;
var firstMaximum = value1.BottomRight;
var secondMinimum = value2.TopLeft;
var secondMaximum = value2.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 represents the intersection of this and
/// another.
///
/// The other to intersect.
///
/// A that represents the intersection of this and
/// , if there is an intersection; otherwise, .
///
public RectangleF Intersect(RectangleF rectangle)
{
RectangleF result;
Intersect(ref this, ref rectangle, out result);
return 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);
}
///
/// Normalizes this so that the and are
/// positive without changing the location of the rectangle.
///
public void Normalize()
{
if (Width < 0)
{
X += Width;
Width = -Width;
}
if (Height < 0)
{
Y += Height;
Height = -Height;
}
}
///
/// Normalizes the specified so that the and
/// are positive without changing the location of the rectangle.
///
/// The to normalize.
/// A with positive width and height.
public static RectangleF Normalize(RectangleF rectangle)
{
if (rectangle.Width < 0)
{
rectangle.X += rectangle.Width;
rectangle.Width = -rectangle.Width;
}
if (rectangle.Height < 0)
{
rectangle.Y += rectangle.Height;
rectangle.Height = -rectangle.Height;
}
return rectangle;
}
///
/// Normalizes a so that the and are positive
/// without changing the location of the rectangle.
///
/// The source .
///
/// When this method returns, contains the a normalized with positive width and height.
///
public static void Normalize(ref RectangleF rectangle, out RectangleF result)
{
result.X = rectangle.X;
result.Width = rectangle.Width;
if (result.Width < 0)
{
result.X += result.Width;
result.Width = -result.Width;
}
result.Y = rectangle.Y;
result.Height = rectangle.Height;
if (result.Height < 0)
{
result.Y += result.Height;
result.Height = -result.Height;
}
}
///
/// 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);
}
}