using System.Diagnostics; using System.Diagnostics.CodeAnalysis; namespace RhythmBase.Components { /// /// Represents a rectangle defined by its left, top, right, and bottom coordinates. /// /// The left coordinate of the rectangle. /// The top coordinate of the rectangle. /// The right coordinate of the rectangle. /// The bottom coordinate of the rectangle. [DebuggerDisplay($"{{{nameof(GetDebuggerDisplay)}(),nq}}")] public struct RDRectNI(int left, int top, int right, int bottom) : IEquatable { /// /// Gets or sets the left coordinate of the rectangle. /// public int Left { get; set; } = left; /// /// Gets or sets the right coordinate of the rectangle. /// public int Right { get; set; } = right; /// /// Gets or sets the top coordinate of the rectangle. /// public int Top { get; set; } = top; /// /// Gets or sets the bottom coordinate of the rectangle. /// public int Bottom { get; set; } = bottom; /// /// Gets the bottom-left point of the rectangle. /// public readonly RDPointNI LeftBottom => new(Left, Bottom); /// /// Gets the bottom-right point of the rectangle. /// public readonly RDPointNI RightBottom => new(Right, Bottom); /// /// Gets the top-left point of the rectangle. /// public readonly RDPointNI LeftTop => new(Left, Top); /// /// Gets the top-right point of the rectangle. /// public readonly RDPointNI RightTop => new(Right, Top); /// /// Gets the width of the rectangle. /// public readonly int Width => Right - Left; /// /// Gets the height of the rectangle. /// public readonly int 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 RDRectNI(RDPointNI location, RDSizeNI 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 RDRectNI(RDSizeNI size) : this(0, size.Height, size.Width, 0) { } /// /// Initializes a new instance of the struct with the specified width and height. /// /// The width of the rectangle. /// The height of the rectangle. public RDRectNI(int width, int height) : this(0, height, width, 0) { } /// /// Gets the location of the rectangle. /// public readonly RDPointNI Location => new(Left, Bottom); /// /// Gets the size of the rectangle. /// public readonly RDSizeNI Size => new(Width, Height); /// /// Inflates the specified rectangle by the specified size. /// /// The rectangle to inflate. /// The size to inflate by. /// The inflated rectangle. public static RDRectNI Inflate(RDRectNI rect, RDSizeNI size) { RDRectNI result = new(rect.Left, rect.Top, rect.Right, rect.Bottom); result.Inflate(size); return result; } /// /// Inflates the specified rectangle by the specified amounts. /// /// The rectangle to inflate. /// The amount to inflate the width by. /// The amount to inflate the height by. /// The inflated rectangle. public static RDRectNI Inflate(RDRectNI rect, int x, int y) { RDRectNI result = new(rect.Left, rect.Top, rect.Right, rect.Bottom); result.Inflate(x, y); return result; } /// /// Returns a rectangle structure that represents the smallest possible rectangle that can contain the specified rectangle, with each value rounded up to the nearest integer. /// /// The rectangle to be rounded up. /// A new rectangle structure with each value rounded up to the nearest integer. public static RDRectNI Ceiling(RDRectN rect) => Ceiling(rect, false); /// /// Returns a rectangle structure that represents the smallest possible rectangle that can contain the specified rectangle, with each value rounded up or down to the nearest integer. /// /// The rectangle to be rounded. /// If true, values are rounded outwards; otherwise, they are rounded inwards. /// A new rectangle structure with each value rounded to the nearest integer. public static RDRectNI Ceiling(RDRectN rect, bool outwards) => new( (int)Math.Round((outwards && rect.Width > 0f) ? Math.Floor((double)rect.Left) : Math.Ceiling((double)rect.Left)), (int)Math.Round((outwards && rect.Height > 0f) ? Math.Floor((double)rect.Top) : Math.Ceiling((double)rect.Top)), (int)Math.Round((outwards && rect.Width < 0f) ? Math.Floor((double)rect.Right) : Math.Ceiling((double)rect.Right)), (int)Math.Round((outwards && rect.Height < 0f) ? Math.Floor((double)rect.Bottom) : Math.Ceiling((double)rect.Bottom))); /// /// Returns a rectangle structure that represents the largest possible rectangle that can fit within the specified rectangle, with each value rounded down to the nearest integer. /// /// The rectangle to be rounded down. /// A new rectangle structure with each value rounded down to the nearest integer. public static RDRectNI Floor(RDRectN rect) => Ceiling(rect, false); /// /// Returns a rectangle structure that represents the largest possible rectangle that can fit within the specified rectangle, with each value rounded up or down to the nearest integer. /// /// The rectangle to be rounded. /// If true, values are rounded inwards; otherwise, they are rounded outwards. /// A new rectangle structure with each value rounded to the nearest integer. public static RDRectNI Floor(RDRectN rect, bool inwards) => new( (int)Math.Round((inwards && rect.Width > 0f) ? Math.Ceiling((double)rect.Left) : Math.Floor((double)rect.Left)), (int)Math.Round((inwards && rect.Height > 0f) ? Math.Ceiling((double)rect.Top) : Math.Floor((double)rect.Top)), (int)Math.Round((inwards && rect.Width < 0f) ? Math.Ceiling((double)rect.Right) : Math.Floor((double)rect.Right)), (int)Math.Round((inwards && rect.Height < 0f) ? Math.Ceiling((double)rect.Bottom) : Math.Floor((double)rect.Bottom))); /// /// Returns a rectangle structure that represents the specified rectangle, with each value rounded to the nearest integer. /// /// The rectangle to be rounded. /// A new rectangle structure with each value rounded to the nearest integer. public static RDRectNI Round(RDRectN rect) => new( (int)Math.Round((double)rect.Left), (int)Math.Round((double)rect.Top), (int)Math.Round((double)rect.Right), (int)Math.Round((double)rect.Bottom)); /// /// Returns a rectangle structure that represents the union of two rectangles. The union is the smallest rectangle that contains both rectangles. /// /// The first rectangle. /// The second rectangle. /// A new rectangle structure that represents the union of the two rectangles. public static RDRectNI Union(RDRectNI rect1, RDRectNI 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 a rectangle structure that represents the intersection of two rectangles. The intersection is the largest rectangle that is contained within both rectangles. /// /// The first rectangle. /// The second rectangle. /// A new rectangle structure that represents the intersection of the two rectangles, or the default rectangle if there is no intersection. public static RDRectNI Intersect(RDRectNI rect1, RDRectNI rect2) => rect1.IntersectsWithInclusive(rect2) ? new RDRectNI( 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; /// /// Returns a rectangle structure that represents the specified rectangle, with each value truncated to the nearest integer. /// /// The rectangle to be truncated. /// A new rectangle structure with each value truncated to the nearest integer. public static RDRectNI Truncate(RDRectN rect) => new( (int)Math.Round((double)rect.Left), (int)Math.Round((double)rect.Top), (int)Math.Round((double)rect.Right), (int)Math.Round((double)rect.Bottom)); /// /// Moves the rectangle by the specified horizontal and vertical amounts. /// /// The amount to move the rectangle horizontally. /// The amount to move the rectangle vertically. public void Offset(int x, int y) { Left += x; Top += y; Right += x; Bottom += y; } /// /// Moves the rectangle by the specified point. /// /// The point to move the rectangle by. public void Offset(RDPointNI p) => Offset(p.X, p.Y); /// /// Inflates the rectangle by the specified size. /// /// The size to inflate the rectangle by. public void Inflate(RDSizeNI size) { Left -= size.Width; Top += size.Height; Right += size.Width; Bottom -= size.Height; } /// /// Inflates the rectangle by the specified width and height. /// /// The amount to inflate the rectangle's width by. /// The amount to inflate the rectangle's height by. public void Inflate(int width, int height) { Left -= width; Top += height; Right += width; Bottom -= height; } /// /// 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(int x, int 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) => (float)Left < p.X && p.X < (float)Right && (float)Bottom < p.Y && p.Y < (float)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(RDRectNI rect) => Left < rect.Left && rect.Right < Right && Bottom < rect.Bottom && rect.Top < Top; /// /// Returns a rectangle structure that represents the union of this rectangle and the specified rectangle. /// /// The rectangle to union with. /// A new rectangle structure that represents the union of the two rectangles. public readonly RDRectNI Union(RDRectNI rect) => Union(this, rect); /// /// Returns a rectangle structure that represents the intersection of this rectangle and the specified rectangle. /// /// The rectangle to intersect with. /// A new rectangle structure that represents the intersection of the two rectangles, or the default rectangle if there is no intersection. public readonly object Intersect(RDRectNI rect) => Intersect(this, rect); /// /// Determines whether this rectangle intersects with the specified rectangle. /// /// The rectangle to check for intersection. /// True if the rectangles intersect; otherwise, false. public readonly bool IntersectsWith(RDRectNI rect) => Left < rect.Right && Right > rect.Left && Top < rect.Bottom && Bottom > rect.Top; /// /// Determines whether this rectangle intersects with the specified rectangle, including the edges. /// /// The rectangle to check for intersection. /// True if the rectangles intersect, including the edges; otherwise, false. public readonly bool IntersectsWithInclusive(RDRectNI rect) => Left <= rect.Right && Right >= rect.Left && Top <= rect.Bottom && Bottom >= rect.Top; /// public static bool operator ==(RDRectNI rect1, RDRectNI rect2) => rect1.Equals(rect2); /// public static bool operator !=(RDRectNI rect1, RDRectNI rect2) => !rect1.Equals(rect2); /// public override readonly bool Equals([NotNullWhen(true)] object? obj) => obj is RDRectNI 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(RDRectNI other) => Left == other.Left && Top == other.Top && Right == other.Right && Bottom == other.Bottom; /// /// Implicitly converts an to an . /// /// The to convert. /// An that represents the same rectangle. public static implicit operator RDRectN(RDRectNI rect) => new(rect.Left, rect.Top, rect.Right, rect.Bottom); /// /// Implicitly converts an to an . /// /// The to convert. /// An that represents the same rectangle. public static implicit operator RDRectI(RDRectNI rect) => new(rect.Left, rect.Top, rect.Right, rect.Bottom); /// /// Implicitly converts an to an . /// /// The to convert. /// An that represents the same rectangle. public static implicit operator RDRectE(RDRectNI rect) => new(rect.Left, rect.Top, rect.Right, rect.Bottom); private readonly string GetDebuggerDisplay() => ToString(); } }