// 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 class Rectangle : AbstractBindable { #region Properties & Fields private Point _location; /// /// Gets or sets the representing the top-left corner of the . /// public Point Location { get => _location; set { if (SetProperty(ref _location, value)) // ReSharper disable once ExplicitCallerInfoArgument OnPropertyChanged(nameof(Center)); } } private Size _size; /// /// Gets or sets the of the . /// public Size Size { get => _size; set { if (SetProperty(ref _size, value)) // ReSharper disable once ExplicitCallerInfoArgument OnPropertyChanged(nameof(Center)); } } /// /// Gets a new representing the center-point of the . /// public Point Center => new Point(Location.X + (Size.Width / 2.0), Location.Y + (Size.Height / 2.0)); /// /// 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 <= DoubleExtensions.TOLERANCE) || (Size.Height <= DoubleExtensions.TOLERANCE); #endregion #region Constructors /// /// Initializes a new instance of the class. /// public Rectangle() : this(new Point(), new Size()) { } /// /// Initializes a new instance of the class using the provided values for ans . /// /// The -position of this . /// The -position of this . /// The of this . /// The of this . public Rectangle(double x, double y, double width, double height) : this(new Point(x, y), new Size(width, height)) { } /// /// Initializes a new instance of the class using the given and . /// /// /// public Rectangle(Point location, Size size) { this.Location = location; this.Size = size; } /// /// Initializes a new instance of the class using the given 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 given 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; double posX = double.MaxValue; double posY = double.MaxValue; double posX2 = double.MinValue; double posY2 = double.MinValue; if (rectangles != null) 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); } if (hasPoint) InitializeFromPoints(new Point(posX, posY), new Point(posX2, posY2)); else InitializeFromPoints(new Point(0, 0), new Point(0, 0)); } /// /// Initializes a new instance of the class using the given 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 given 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; double posX = double.MaxValue; double posY = double.MaxValue; double posX2 = double.MinValue; double posY2 = double.MinValue; if (points != null) 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); } if (hasPoint) InitializeFromPoints(new Point(posX, posY), new Point(posX2, posY2)); else InitializeFromPoints(new Point(0, 0), new Point(0, 0)); } #endregion #region Methods private void InitializeFromPoints(Point point1, Point point2) { double posX = Math.Min(point1.X, point2.X); double posY = Math.Min(point1.Y, point2.Y); double width = Math.Max(point1.X, point2.X) - posX; double height = Math.Max(point1.Y, point2.Y) - posY; Location = new Point(posX, posY); Size = new Size(width, height); } /// /// Calculates the percentage of intersection of a rectangle. /// /// The intersecting rectangle. /// The percentage of intersection. public double CalculateIntersectPercentage(Rectangle intersectingRect) { if (IsEmpty || intersectingRect.IsEmpty) return 0; Rectangle intersection = CalculateIntersection(intersectingRect); return (intersection.Size.Width * intersection.Size.Height) / (Size.Width * Size.Height); } /// /// Calculates the representing the intersection of this and the one provided as parameter. /// /// The intersecting /// A new representing the intersection this and the one provided as parameter. public Rectangle CalculateIntersection(Rectangle intersectingRectangle) { double x1 = Math.Max(Location.X, intersectingRectangle.Location.X); double x2 = Math.Min(Location.X + Size.Width, intersectingRectangle.Location.X + intersectingRectangle.Size.Width); double y1 = Math.Max(Location.Y, intersectingRectangle.Location.Y); double y2 = Math.Min(Location.Y + Size.Height, intersectingRectangle.Location.Y + intersectingRectangle.Size.Height); if ((x2 >= x1) && (y2 >= y1)) return new Rectangle(x1, y1, x2 - x1, y2 - y1); return new Rectangle(); } /// /// Determines if the specified is contained within this . /// /// The to test. /// public bool Contains(Point point) { return Contains(point.X, point.Y); } /// /// Determines if the specified location is contained within this . /// /// The X-location to test. /// The Y-location to test. /// public bool Contains(double x, double y) { return (Location.X <= x) && (x < (Location.X + Size.Width)) && (Location.Y <= y) && (y < (Location.Y + Size.Height)); } /// /// Determines if the specified is contained within this . /// /// The to test. /// public bool Contains(Rectangle rect) { return (Location.X <= rect.Location.X) && ((rect.Location.X + rect.Size.Width) <= (Location.X + Size.Width)) && (Location.Y <= rect.Location.Y) && ((rect.Location.Y + rect.Size.Height) <= (Location.Y + Size.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() { return $"[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) { Rectangle compareRect = obj as Rectangle; if (ReferenceEquals(compareRect, null)) return false; if (ReferenceEquals(this, compareRect)) return true; 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 ==(Rectangle rectangle1, Rectangle rectangle2) { return ReferenceEquals(rectangle1, null) ? ReferenceEquals(rectangle2, null) : 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 !=(Rectangle rectangle1, Rectangle rectangle2) { return !(rectangle1 == rectangle2); } #endregion } }