using System; using System.Runtime.Serialization; using Microsoft.Xna.Framework; using MLEM.Extensions; namespace MLEM.Misc { /// /// Represents a float-based version of /// [DataContract] public struct RectangleF : IEquatable { /// /// The empty rectangle, with an x, y, width and height of 0. /// public static RectangleF Empty => default; /// /// The x position of the top left corner of this rectangle. /// [DataMember] public float X; /// /// The y position of the top left corner of this rectangle. /// [DataMember] public float Y; /// /// The width of this rectangle. /// [DataMember] public float Width; /// /// The height of this rectangle. /// [DataMember] public float Height; /// public float Left => this.X; /// /// The x position of the bottom right corner of this rectangle. /// public float Right => this.X + this.Width; /// public float Top => this.Y; /// /// The y position of the bottom right corner of this rectangle. /// public float Bottom => this.Y + this.Height; /// /// A boolean that is true if this rectangle is empty. /// A rectangle is considered empty if the width or height is 0. /// public bool IsEmpty => this.Width <= 0 || this.Height <= 0; /// /// The top left corner of this rectangle /// public Vector2 Location { get => new Vector2(this.X, this.Y); set { this.X = value.X; this.Y = value.Y; } } /// /// The size, that is, the width and height of this rectangle. /// public Vector2 Size { get => new Vector2(this.Width, this.Height); set { this.Width = value.X; this.Height = value.Y; } } /// /// The center of this rectangle, based on the top left corner and the size. /// public Vector2 Center => new Vector2(this.X + this.Width / 2, this.Y + this.Height / 2); /// /// Creates a new rectangle with the specified location and size /// /// The x coordinate of the top left corner of the rectangle /// The y coordinate of the top left corner of the rectangle /// The width of the rectangle /// The height of the rectangle public RectangleF(float x, float y, float width, float height) { this.X = x; this.Y = y; this.Width = width; this.Height = height; } /// /// Creates a new rectangle with the specified location and size vectors /// /// The top left corner of the rectangle /// The size of the rectangle, where x represents width and the y represents height public RectangleF(Vector2 location, Vector2 size) { this.X = location.X; this.Y = location.Y; this.Width = size.X; this.Height = size.Y; } /// /// Creates a new rectangle based on two corners that form a bounding box. /// The resulting rectangle will encompass both corners as well as all of the space between them. /// /// The first corner to use /// The second corner to use /// public static RectangleF FromCorners(Vector2 corner1, Vector2 corner2) { var minX = Math.Min(corner1.X, corner2.X); var minY = Math.Min(corner1.Y, corner2.Y); var maxX = Math.Max(corner1.X, corner2.X); var maxY = Math.Max(corner1.Y, corner2.Y); return new RectangleF(minX, minY, maxX - minX, maxY - minY); } /// /// Converts a float-based rectangle to an int-based rectangle, flooring each value in the process. /// /// The rectangle to convert /// The resulting rectangle public static explicit operator Rectangle(RectangleF rect) { return new Rectangle(rect.X.Floor(), rect.Y.Floor(), rect.Width.Floor(), rect.Height.Floor()); } /// /// Converts an int-based rectangle to a float-based rectangle. /// /// The rectangle to convert /// The resulting rectangle public static explicit operator RectangleF(Rectangle rect) { return new RectangleF(rect.X, rect.Y, rect.Width, rect.Height); } /// public static bool operator ==(RectangleF a, RectangleF b) { return a.X == b.X && a.Y == b.Y && a.Width == b.Width && a.Height == b.Height; } /// public static bool operator !=(RectangleF a, RectangleF b) { return !(a == b); } /// public bool Contains(float x, float y) { return this.X <= x && x < this.X + this.Width && this.Y <= y && y < this.Y + this.Height; } /// public bool Contains(Vector2 value) { return this.Contains(value.X, value.Y); } /// public bool Contains(RectangleF value) { return this.X <= value.X && value.X + value.Width <= this.X + this.Width && this.Y <= value.Y && value.Y + value.Height <= this.Y + this.Height; } /// Indicates whether this instance and a specified object are equal. /// The object to compare with the current instance. /// if and this instance are the same type and represent the same value; otherwise, . public override bool Equals(object obj) { return obj is RectangleF f && this == f; } /// Indicates whether the current object is equal to another object of the same type. /// An object to compare with this object. /// if the current object is equal to the parameter; otherwise, . public bool Equals(RectangleF other) { return this == other; } /// Returns the hash code for this instance. /// A 32-bit signed integer that is the hash code for this instance. public override int GetHashCode() { return (((17 * 23 + this.X.GetHashCode()) * 23 + this.Y.GetHashCode()) * 23 + this.Width.GetHashCode()) * 23 + this.Height.GetHashCode(); } /// public void Inflate(float horizontalAmount, float verticalAmount) { this.X -= horizontalAmount; this.Y -= verticalAmount; this.Width += horizontalAmount * 2; this.Height += verticalAmount * 2; } /// public bool Intersects(RectangleF value) { return value.Left < this.Right && this.Left < value.Right && value.Top < this.Bottom && this.Top < value.Bottom; } /// public static RectangleF Intersect(RectangleF value1, RectangleF value2) { if (value1.Intersects(value2)) { var num1 = Math.Min(value1.X + value1.Width, value2.X + value2.Width); var x = Math.Max(value1.X, value2.X); var y = Math.Max(value1.Y, value2.Y); var num2 = Math.Min(value1.Y + value1.Height, value2.Y + value2.Height); return new RectangleF(x, y, num1 - x, num2 - y); } else { return Empty; } } /// public void Offset(float offsetX, float offsetY) { this.X += offsetX; this.Y += offsetY; } /// public void Offset(Vector2 amount) { this.X += amount.X; this.Y += amount.Y; } /// Returns a string that represents the current object. /// A string that represents the current object. public override string ToString() { return "{X:" + this.X + " Y:" + this.Y + " Width:" + this.Width + " Height:" + this.Height + "}"; } /// public static RectangleF Union(RectangleF value1, RectangleF value2) { var x = Math.Min(value1.X, value2.X); var y = Math.Min(value1.Y, value2.Y); return new RectangleF(x, y, Math.Max(value1.Right, value2.Right) - x, Math.Max(value1.Bottom, value2.Bottom) - y); } /// public void Deconstruct(out float x, out float y, out float width, out float height) { x = this.X; y = this.Y; width = this.Width; height = this.Height; } } }