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);
}
}