// 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 : IEquatable
{
#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)
{
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));
}
internal Rectangle(IList leds)
{
float posX = float.MaxValue;
float posY = float.MaxValue;
float posX2 = float.MinValue;
float posY2 = float.MinValue;
// ReSharper disable once ForCanBeConvertedToForeach
for (int i = 0; i < leds.Count; i++)
{
Rectangle rectangle = leds[i].AbsoluteBoundary;
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) = leds.Count > 0 ? 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(Point point1, 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 is equivalent to this .
///
/// The rectangle to test.
/// true if is equivalent to this ; otherwise, false.
public bool Equals(Rectangle other) => (Location == other.Location) && (Size == other.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) => obj is Rectangle other && Equals(other);
///
/// Returns a hash code for this .
///
/// An integer value that specifies the hash code for this .
public override int GetHashCode() => HashCode.Combine(Location, Size);
#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);
///
/// 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);
// 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 /(Rectangle rectangle1, 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
}