using System.Diagnostics; using System.Diagnostics.CodeAnalysis; namespace RhythmBase.Components { /// /// Represents a rectangle defined by its left, top, right, and bottom edges. /// /// The left edge of the rectangle. /// The top edge of the rectangle. /// The right edge of the rectangle. /// The bottom edge of the rectangle. [DebuggerDisplay($"{{{nameof(GetDebuggerDisplay)}(),nq}}")] public struct RDRectN(float left, float top, float right, float bottom) { /// /// Gets or sets the left edge of the rectangle. /// public float Left { get; set; } = left; /// /// Gets or sets the right edge of the rectangle. /// public float Right { get; set; } = top; /// /// Gets or sets the top edge of the rectangle. /// public float Top { get; set; } = right; /// /// Gets or sets the bottom edge of the rectangle. /// public float Bottom { get; set; } = bottom; /// /// Gets the point at the left-bottom corner of the rectangle. /// public readonly RDPointN LeftBottom => new(Left, Bottom); /// /// Gets the point at the right-bottom corner of the rectangle. /// public readonly RDPointN RightBottom => new(Right, Bottom); /// /// Gets the point at the left-top corner of the rectangle. /// public readonly RDPointN LeftTop => new(Left, Top); /// /// Gets the point at the right-top corner of the rectangle. /// public readonly RDPointN RightTop => new(Right, Top); /// /// Gets the width of the rectangle. /// public readonly float Width => Right - Left; /// /// Gets the height of the rectangle. /// public readonly float Height => Top - Bottom; /// /// Initializes a new instance of the struct with the specified location and size. /// /// The location of the rectangle. /// The size of the rectangle. public RDRectN(RDPointN location, RDSizeN size) : this(location.X, location.Y + size.Height, location.X + size.Width, location.Y) { } /// /// Initializes a new instance of the struct with the specified size. /// /// The size of the rectangle. public RDRectN(RDSizeN size) : this(0f, size.Height, size.Width, 0f) { } /// /// Initializes a new instance of the struct with the specified width and height. /// /// The width of the rectangle. /// The height of the rectangle. public RDRectN(float width, float height) : this(0f, height, width, 0f) { } /// /// Gets the location of the rectangle as an . /// public readonly RDPointNI Location => new((int)Math.Round((double)Left), (int)Math.Round((double)Bottom)); /// /// Gets the size of the rectangle as an . /// public readonly RDSizeNI Size => (new RDSizeNI((int)Math.Round((double)Width), (int)Math.Round((double)Height))); /// /// Inflates the specified rectangle by the specified size. /// /// The rectangle to inflate. /// The size to inflate by. /// The inflated rectangle. public static RDRectN Inflate(RDRectN rect, RDSizeNI size) { RDRectN result = new(rect.Left, rect.Top, rect.Right, rect.Bottom); result.Inflate(size); return result; } /// /// Inflates the specified rectangle by the specified width and height. /// /// The rectangle to inflate. /// The width to inflate by. /// The height to inflate by. /// The inflated rectangle. public static RDRectN Inflate(RDRectN rect, float x, float y) { RDRectN result = new(rect.Left, rect.Top, rect.Right, rect.Bottom); result.Inflate(x, y); return result; } /// /// Determines whether the rectangle contains the specified point. /// /// The x-coordinate of the point. /// The y-coordinate of the point. /// true if the rectangle contains the point; otherwise, false. public readonly bool Contains(float x, float y) => Left < x && x < Right && Bottom < y && y < Top; /// /// Determines whether the rectangle contains the specified point. /// /// The point to check. /// true if the rectangle contains the point; otherwise, false. public readonly bool Contains(RDPointN p) => Left < p.X && p.X < Right && Bottom < p.Y && p.Y < Top; /// /// Determines whether the rectangle contains the specified rectangle. /// /// The rectangle to check. /// true if the rectangle contains the specified rectangle; otherwise, false. public readonly bool Contains(RDRectN rect) => Left < rect.Left && rect.Right < Right && Bottom < rect.Bottom && rect.Top < Top; /// /// Returns the union of two rectangles. /// /// The first rectangle. /// The second rectangle. /// The union of the two rectangles. public static RDRectN Union(RDRectN rect1, RDRectN rect2) => new(Math.Min(rect1.Left, rect2.Left), Math.Max(rect1.Top, rect2.Top), Math.Max(rect1.Right, rect2.Right), Math.Min(rect1.Bottom, rect2.Bottom)); /// /// Returns the intersection of two rectangles. /// /// The first rectangle. /// The second rectangle. /// The intersection of the two rectangles, or the default rectangle if they do not intersect. public static RDRectN Intersect(RDRectN rect1, RDRectN rect2) => rect1.IntersectsWithInclusive(rect2) ? new RDRectN( Math.Max(rect1.Left, rect2.Left), Math.Max(rect1.Top, rect2.Top), Math.Min(rect1.Right, rect2.Right), Math.Min(rect1.Bottom, rect2.Bottom)) : default; /// /// Truncates the specified rectangle. /// /// The rectangle to truncate. /// The truncated rectangle. public static RDRectN Truncate(RDRectN rect) => new(rect.Left, rect.Top, rect.Right, rect.Bottom); /// /// Offsets the rectangle by the specified amount. /// /// The amount to offset by along the x-axis. /// The amount to offset by along the y-axis. public void Offset(float x, float y) { Left += x; Top += y; Right += x; Bottom += y; } /// /// Offsets the rectangle by the specified point. /// /// The point to offset by. public void Offset(RDPointN p) => Offset(p.X, p.Y); /// /// Inflates the rectangle by the specified size. /// /// The size to inflate by. public void Inflate(RDSizeN size) { Left -= size.Width; Top += size.Height; Right += size.Width; Bottom -= size.Height; } /// /// Inflates the rectangle by the specified width and height. /// /// The width to inflate by. /// The height to inflate by. public void Inflate(float width, float height) { Left -= width; Top += height; Right += width; Bottom -= height; } /// /// Returns the union of this rectangle with the specified rectangle. /// /// The rectangle to union with. /// The union of the two rectangles. public readonly RDRectN Union(RDRectN rect) => Union(this, rect); /// /// Determines whether this rectangle intersects with the specified rectangle, including the edges. /// /// The rectangle to check for intersection. /// true if the rectangles intersect; otherwise, false. public readonly bool IntersectsWithInclusive(RDRectN rect) => Left <= rect.Right && Right >= rect.Left && Top <= rect.Bottom && Bottom >= rect.Top; /// public static bool operator ==(RDRectN rect1, RDRectN rect2) => rect1.Equals(rect2); /// public static bool operator !=(RDRectN rect1, RDRectN rect2) => !rect1.Equals(rect2); /// public override readonly bool Equals([NotNullWhen(true)] object? obj) => obj is RDRectN e && Equals(e); /// public override readonly int GetHashCode() => HashCode.Combine(Left, Top, Right, Bottom); /// public override readonly string ToString() => $"{{Location=[{Left},{Bottom}],Size=[{Width},{Height}]}}"; /// public readonly bool Equals(RDRectN other) => Left == other.Left && Top == other.Top && Right == other.Right && Bottom == other.Bottom; /// /// Converts an to an . /// /// The to convert. /// An that represents the same rectangle. public static implicit operator RDRect(RDRectN rect) => new(rect.Left, rect.Top, rect.Right, rect.Bottom); /// /// Converts an to an . /// /// The to convert. /// An that represents the same rectangle. public static implicit operator RDRectE(RDRectN rect) => new(rect.Left, rect.Top, rect.Right, rect.Bottom); private readonly string GetDebuggerDisplay() => ToString(); } }