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