// ReSharper disable MemberCanBePrivate.Global // ReSharper disable UnusedMember.Global using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; namespace RGB.NET.Core; /// /// Represents a rectangle defined by it's position and it's size. /// [DebuggerDisplay("[Location: {Location}, Size: {Size}]")] public readonly struct Rectangle { #region Properties & Fields /// /// Gets the representing the top-left corner of the . /// public Point Location { get; } /// /// Gets the of the . /// public Size Size { get; } /// /// Gets a new representing the center-point of the . /// public Point Center { get; } /// /// Gets a bool indicating if both, the width and the height of the rectangle is greater than zero. /// True if the rectangle has a width or a height of zero; otherwise, false. /// public bool IsEmpty => (Size.Width <= FloatExtensions.TOLERANCE) || (Size.Height <= FloatExtensions.TOLERANCE); #endregion #region Constructors /// /// /// Initializes a new instance of the class using the provided values for ans . /// /// The x-value of the of this . /// The y-value of the -position of this . /// The width of the of this . /// The height of the of this . public Rectangle(float x, float y, float width, float height) : this(new Point(x, y), new Size(width, height)) { } /// /// Initializes a new instance of the class using the (0,0) and the specified . /// /// The size of of this . public Rectangle(Size size) : this(new Point(), size) { } /// /// Initializes a new instance of the class using the specified and . /// /// The location of this of this . /// The size of of this . public Rectangle(Point location, Size size) { this.Location = location; this.Size = size; Center = new Point(Location.X + (Size.Width / 2.0f), Location.Y + (Size.Height / 2.0f)); } /// /// /// Initializes a new instance of the class using the specified array of . /// The and is calculated to completely contain all rectangles provided as parameters. /// /// The array of used to calculate the and . public Rectangle(params Rectangle[] rectangles) : this(rectangles.AsEnumerable()) { } /// /// Initializes a new instance of the class using the specified list of . /// The and is calculated to completely contain all rectangles provided as parameters. /// /// The list of used to calculate the and . public Rectangle(IEnumerable rectangles) { bool hasPoint = false; float posX = float.MaxValue; float posY = float.MaxValue; float posX2 = float.MinValue; float posY2 = float.MinValue; foreach (Rectangle rectangle in rectangles) { hasPoint = true; posX = Math.Min(posX, rectangle.Location.X); posY = Math.Min(posY, rectangle.Location.Y); posX2 = Math.Max(posX2, rectangle.Location.X + rectangle.Size.Width); posY2 = Math.Max(posY2, rectangle.Location.Y + rectangle.Size.Height); } (Point location, Size size) = hasPoint ? InitializeFromPoints(new Point(posX, posY), new Point(posX2, posY2)) : InitializeFromPoints(new Point(0, 0), new Point(0, 0)); Location = location; Size = size; Center = new Point(Location.X + (Size.Width / 2.0f), Location.Y + (Size.Height / 2.0f)); } /// /// /// Initializes a new instance of the class using the specified array of . /// The and is calculated to contain all points provided as parameters. /// /// The array of used to calculate the and . public Rectangle(params Point[] points) : this(points.AsEnumerable()) { } /// /// /// Initializes a new instance of the class using the specified list of . /// The and is calculated to contain all points provided as parameters. /// /// The list of used to calculate the and . public Rectangle(IEnumerable points) : this() { bool hasPoint = false; float posX = float.MaxValue; float posY = float.MaxValue; float posX2 = float.MinValue; float posY2 = float.MinValue; foreach (Point point in points) { hasPoint = true; posX = Math.Min(posX, point.X); posY = Math.Min(posY, point.Y); posX2 = Math.Max(posX2, point.X); posY2 = Math.Max(posY2, point.Y); } (Point location, Size size) = hasPoint ? InitializeFromPoints(new Point(posX, posY), new Point(posX2, posY2)) : InitializeFromPoints(new Point(0, 0), new Point(0, 0)); Location = location; Size = size; Center = new Point(Location.X + (Size.Width / 2.0f), Location.Y + (Size.Height / 2.0f)); } #endregion #region Methods private static (Point location, Size size) InitializeFromPoints(in Point point1, in Point point2) { float posX = Math.Min(point1.X, point2.X); float posY = Math.Min(point1.Y, point2.Y); float width = Math.Max(point1.X, point2.X) - posX; float height = Math.Max(point1.Y, point2.Y) - posY; return (new Point(posX, posY), new Size(width, height)); } /// /// Converts the - and -position of this to a human-readable string. /// /// A string that contains the and of this . For example "[Location: [X: 100, Y: 10], Size: [Width: 20, Height: [40]]". public override string ToString() => $"[Location: {Location}, Size: {Size}]"; /// /// Tests whether the specified object is a and is equivalent to this . /// /// The object to test. /// true if is a equivalent to this ; otherwise, false. public override bool Equals(object? obj) { if (obj is not Rectangle compareRect) return false; if (GetType() != compareRect.GetType()) return false; return (Location == compareRect.Location) && (Size == compareRect.Size); } /// /// Returns a hash code for this . /// /// An integer value that specifies the hash code for this . public override int GetHashCode() { unchecked { int hashCode = Location.GetHashCode(); hashCode = (hashCode * 397) ^ Size.GetHashCode(); return hashCode; } } #endregion #region Operators /// /// Returns a value that indicates whether two specified are equal. /// /// The first to compare. /// The second to compare. /// true if and are equal; otherwise, false. public static bool operator ==(in Rectangle rectangle1, in Rectangle rectangle2) => rectangle1.Equals(rectangle2); /// /// Returns a value that indicates whether two specified are equal. /// /// The first to compare. /// The second to compare. /// true if and are not equal; otherwise, false. public static bool operator !=(in Rectangle rectangle1, in Rectangle rectangle2) => !(rectangle1 == rectangle2); // DarthAffe 20.02.2021: Used for normalization /// /// Returns a normalized to the specified reference. /// /// The rectangle to nromalize. /// The reference used for normalization. /// A normalized rectangle. public static Rectangle operator /(in Rectangle rectangle1, in Rectangle rectangle2) { float x = rectangle1.Location.X / (rectangle2.Size.Width - rectangle2.Location.X); float y = rectangle1.Location.Y / (rectangle2.Size.Height - rectangle2.Location.Y); Size size = rectangle1.Size / rectangle2.Size; return new Rectangle(new Point(x, y), size); } #endregion }