// 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 double _x;
///
/// Gets or sets the X-position of this .
///
public double X
{
get => _x;
set
{
if (SetProperty(ref _x, value))
{
OnPropertyChanged(nameof(Location));
OnPropertyChanged(nameof(Center));
}
}
}
private double _y;
///
/// Gets or sets the Y-position of this .
///
public double Y
{
get => _y;
set
{
if (SetProperty(ref _y, value))
{
OnPropertyChanged(nameof(Location));
OnPropertyChanged(nameof(Center));
}
}
}
private double _width;
///
/// Gets or sets the width of this .
///
public double Width
{
get => _width;
set
{
if (SetProperty(ref _width, Math.Max(0, value)))
{
OnPropertyChanged(nameof(Size));
OnPropertyChanged(nameof(Center));
OnPropertyChanged(nameof(IsEmpty));
}
}
}
private double _height;
///
/// Gets or sets the height of this .
///
public double Height
{
get => _height;
set
{
if (SetProperty(ref _height, Math.Max(0, value)))
{
OnPropertyChanged(nameof(Size));
OnPropertyChanged(nameof(Center));
OnPropertyChanged(nameof(IsEmpty));
}
}
}
///
/// Gets or sets the representing the top-left corner of the .
///
public Point Location
{
get => new Point(X, Y);
set
{
if (Location != value)
{
_x = value.X;
_y = value.Y;
OnPropertyChanged(nameof(Location));
OnPropertyChanged(nameof(Center));
}
}
}
///
/// Gets or sets the of the .
///
public Size Size
{
get => new Size(Width, Height);
set
{
if (Size != value)
{
_width = value.Width;
_height = value.Height;
OnPropertyChanged(nameof(Size));
OnPropertyChanged(nameof(Center));
OnPropertyChanged(nameof(IsEmpty));
}
}
}
///
/// Gets a new representing the center-point of the .
///
public Point Center => new Point(X + (Width / 2.0), Y + (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 => (Width <= DoubleExtensions.TOLERANCE) || (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) => 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) => (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) => (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() => $"[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 Rectangle compareRect))
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) => rectangle1?.Equals(rectangle2) ?? ReferenceEquals(rectangle2, null);
///
/// 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) => !(rectangle1 == rectangle2);
#endregion
}
}