diff --git a/RGB.NET.Core/Devices/AbstractRGBDevice.cs b/RGB.NET.Core/Devices/AbstractRGBDevice.cs index 90c97e1..cd76493 100644 --- a/RGB.NET.Core/Devices/AbstractRGBDevice.cs +++ b/RGB.NET.Core/Devices/AbstractRGBDevice.cs @@ -27,14 +27,6 @@ namespace RGB.NET.Core /// IRGBDeviceInfo IRGBDevice.DeviceInfo => DeviceInfo; - private Size _size = Size.Invalid; - /// - public Size Size - { - get => _size; - set => SetProperty(ref _size, value); - } - private Point _location = new Point(0, 0); /// public Point Location @@ -43,6 +35,58 @@ namespace RGB.NET.Core set => SetProperty(ref _location, value); } + private Size _size = Size.Invalid; + /// + public Size Size + { + get => _size; + protected set + { + if (SetProperty(ref _size, value)) + UpdateActualData(); + } + } + + private Size _actualSize; + /// + public Size ActualSize + { + get => _actualSize; + private set => SetProperty(ref _actualSize, value); + } + + private Rectangle _deviceRectangle; + /// + public Rectangle DeviceRectangle + { + get => _deviceRectangle; + private set => SetProperty(ref _deviceRectangle, value); + } + + private Scale _scale = new Scale(1); + /// + public Scale Scale + { + get => _scale; + set + { + if (SetProperty(ref _scale, value)) + UpdateActualData(); + } + } + + private Rotation _rotation = new Rotation(0); + /// + public Rotation Rotation + { + get => _rotation; + set + { + if (SetProperty(ref _rotation, value)) + UpdateActualData(); + } + } + /// /// Gets or sets if the device needs to be flushed on every update. /// @@ -79,6 +123,12 @@ namespace RGB.NET.Core #region Methods + private void UpdateActualData() + { + ActualSize = Size * Scale; + DeviceRectangle = new Rectangle(Location, new Rectangle(new Rectangle(Location, ActualSize).Rotate(Rotation)).Size); + } + /// public virtual void Update(bool flushLeds = false) { @@ -124,11 +174,21 @@ namespace RGB.NET.Core /// The to initialize. /// The representing the position of the to initialize. /// - protected virtual Led InitializeLed(LedId ledId, Rectangle ledRectangle) + [Obsolete("Use InitializeLed(LedId ledId, Point location, Size size) instead.")] + protected virtual Led InitializeLed(LedId ledId, Rectangle rectangle) => InitializeLed(ledId, rectangle.Location, rectangle.Size); + + /// + /// Initializes the with the specified id. + /// + /// The to initialize. + /// The location of the to initialize. + /// The size of the to initialize. + /// The initialized led. + protected virtual Led InitializeLed(LedId ledId, Point location, Size size) { if ((ledId == LedId.Invalid) || LedMapping.ContainsKey(ledId)) return null; - Led led = new Led(this, ledId, ledRectangle, CreateLedCustomData(ledId)); + Led led = new Led(this, ledId, location, size, CreateLedCustomData(ledId)); LedMapping.Add(ledId, led); return led; } @@ -171,13 +231,12 @@ namespace RGB.NET.Core if (Enum.TryParse(layoutLed.Id, true, out LedId ledId)) { if (!LedMapping.TryGetValue(ledId, out Led led) && createMissingLeds) - led = InitializeLed(ledId, new Rectangle()); + led = InitializeLed(ledId, new Point(), new Size()); if (led != null) { - led.LedRectangle.Location = new Point(layoutLed.X, layoutLed.Y); - led.LedRectangle.Size = new Size(layoutLed.Width, layoutLed.Height); - + led.Location = new Point(layoutLed.X, layoutLed.Y); + led.Size = new Size(layoutLed.Width, layoutLed.Height); led.Shape = layoutLed.Shape; led.ShapeData = layoutLed.ShapeData; diff --git a/RGB.NET.Core/Devices/IRGBDevice.cs b/RGB.NET.Core/Devices/IRGBDevice.cs index 9ba17c4..23278ba 100644 --- a/RGB.NET.Core/Devices/IRGBDevice.cs +++ b/RGB.NET.Core/Devices/IRGBDevice.cs @@ -24,10 +24,31 @@ namespace RGB.NET.Core Point Location { get; set; } /// - /// Gets a copy of the of the whole . + /// Gets the of the . /// Size Size { get; } + /// + /// Gets the actual of the . + /// This includes the . + /// + Size ActualSize { get; } + + /// + /// Gets a representing the logical location of the relative to the . + /// + Rectangle DeviceRectangle { get; } + + /// + /// Gets or sets the scale of the . + /// + Scale Scale { get; set; } + + /// + /// Gets or sets the rotation of the . + /// + Rotation Rotation { get; set; } + /// /// Gets or sets the of the . /// diff --git a/RGB.NET.Core/Extensions/PointExtensions.cs b/RGB.NET.Core/Extensions/PointExtensions.cs new file mode 100644 index 0000000..7c413e9 --- /dev/null +++ b/RGB.NET.Core/Extensions/PointExtensions.cs @@ -0,0 +1,37 @@ +using System; + +namespace RGB.NET.Core +{ + public static class PointExtensions + { + #region Methods + + /// + /// Moves the specified by the given amount. + /// + /// The to move. + /// The x-ammount to move. + /// The y-ammount to move. + /// The new location of the point. + public static Point Translate(this Point point, double x = 0, double y = 0) => new Point(point.X + x, point.Y + y); + + /// + /// Rotates the specified by the given amuont around the given origin. + /// + /// The to rotate. + /// The rotation. + /// The origin to rotate around. [0,0] if not set. + /// The new location of the point. + public static Point Rotate(this Point point, Rotation rotation, Point origin = new Point()) + { + double sin = Math.Sin(rotation.Radians); + double cos = Math.Cos(rotation.Radians); + + point = new Point(point.X - origin.X, point.Y - origin.Y); + point = new Point((point.X * cos) - (point.Y * sin), (point.X * sin) + (point.Y * cos)); + return new Point(point.X + origin.X, point.Y + origin.Y); ; + } + + #endregion + } +} diff --git a/RGB.NET.Core/Extensions/RectangleExtensions.cs b/RGB.NET.Core/Extensions/RectangleExtensions.cs new file mode 100644 index 0000000..aae9b04 --- /dev/null +++ b/RGB.NET.Core/Extensions/RectangleExtensions.cs @@ -0,0 +1,169 @@ +using System; + +namespace RGB.NET.Core +{ + public static class RectangleExtensions + { + #region Methods + + /// + /// Sets the of the given rectangle. + /// + /// The rectangle to modify. + /// The new location of the rectangle. + /// The modified . + public static Rectangle SetLocation(this Rectangle rect, Point location) => new Rectangle(location, rect.Size); + + /// + /// Sets the of the of the given rectangle. + /// + /// The rectangle to modify. + /// The new x-location of the rectangle. + /// The modified . + public static Rectangle SetX(this Rectangle rect, double x) => new Rectangle(new Point(x, rect.Location.Y), rect.Size); + + /// + /// Sets the of the of the given rectangle. + /// + /// The rectangle to modify. + /// The new y-location of the rectangle. + /// The modified . + public static Rectangle SetY(this Rectangle rect, double y) => new Rectangle(new Point(rect.Location.X, y), rect.Size); + + /// + /// Sets the of the given rectangle. + /// + /// The rectangle to modify. + /// The new size of the rectangle. + /// The modified . + public static Rectangle SetSize(this Rectangle rect, Size size) => new Rectangle(rect.Location, size); + + /// + /// Sets the of the of the given rectangle. + /// + /// The rectangle to modify. + /// The new width of the rectangle. + /// The modified . + public static Rectangle SetWidth(this Rectangle rect, double width) => new Rectangle(rect.Location, new Size(width, rect.Size.Height)); + + /// + /// Sets the of the of the given rectangle. + /// + /// The rectangle to modify. + /// The new height of the rectangle. + /// The modified . + public static Rectangle SetHeight(this Rectangle rect, double height) => new Rectangle(rect.Location, new Size(rect.Size.Width, height)); + + /// + /// Calculates the percentage of intersection of a rectangle. + /// + /// The intersecting rectangle. + /// The percentage of intersection. + public static double CalculateIntersectPercentage(this Rectangle rect, Rectangle intersectingRect) + { + if (rect.IsEmpty || intersectingRect.IsEmpty) return 0; + + Rectangle intersection = rect.CalculateIntersection(intersectingRect); + return (intersection.Size.Width * intersection.Size.Height) / (rect.Size.Width * rect.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 static Rectangle CalculateIntersection(this Rectangle rect, Rectangle intersectingRectangle) + { + double x1 = Math.Max(rect.Location.X, intersectingRectangle.Location.X); + double x2 = Math.Min(rect.Location.X + rect.Size.Width, intersectingRectangle.Location.X + intersectingRectangle.Size.Width); + + double y1 = Math.Max(rect.Location.Y, intersectingRectangle.Location.Y); + double y2 = Math.Min(rect.Location.Y + rect.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. + /// true if the rectangle contains the given point; otherwise false. + public static bool Contains(this Rectangle rect, Point point) => rect.Contains(point.X, point.Y); + + /// + /// Determines if the specified location is contained within this . + /// + /// The X-location to test. + /// The Y-location to test. + /// true if the rectangle contains the given coordinates; otherwise false. + public static bool Contains(this Rectangle rect, double x, double y) => (rect.Location.X <= x) && (x < (rect.Location.X + rect.Size.Width)) + && (rect.Location.Y <= y) && (y < (rect.Location.Y + rect.Size.Height)); + + /// + /// Determines if the specified is contained within this . + /// + /// The to test. + /// true if the rectangle contains the given rect; otherwise false. + public static bool Contains(this Rectangle rect, Rectangle rect2) => (rect.Location.X <= rect2.Location.X) && ((rect2.Location.X + rect2.Size.Width) <= (rect.Location.X + rect.Size.Width)) + && (rect.Location.Y <= rect2.Location.Y) && ((rect2.Location.Y + rect2.Size.Height) <= (rect.Location.Y + rect.Size.Height)); + + /// + /// Moves the specified by the given amount. + /// + /// The to move. + /// The amount to move. + /// The moved rectangle. + public static Rectangle Translate(this Rectangle rect, Point point) => rect.Translate(point.X, point.Y); + + /// + /// Moves the specified by the given amount. + /// + /// The to move. + /// The x-ammount to move. + /// The y-ammount to move. + /// The moved rectangle. + public static Rectangle Translate(this Rectangle rect, double x = 0, double y = 0) => new Rectangle(rect.Location.Translate(x, y), rect.Size); + + /// + /// Rotates the specified by the given amuont around the given origin. + /// + /// + /// The returned array of is filled with the new locations of the rectangle clockwise starting from the top left: + /// [0] = top left + /// [1] = top right + /// [2] = bottom right + /// [3] = bottom left + /// + /// The to rotate. + /// The rotation. + /// The origin to rotate around. [0,0] if not set. + /// A array of containing the new locations of the corners of the original rectangle. + public static Point[] Rotate(this Rectangle rect, Rotation rotation, Point origin = new Point()) + { + Point[] points = { + rect.Location, // top left + new Point(rect.Location.X + rect.Size.Width, rect.Location.Y), // top right + new Point(rect.Location.X + rect.Size.Width, rect.Location.Y + rect.Size.Height), // bottom right + new Point(rect.Location.X, rect.Location.Y + rect.Size.Height), // bottom right + }; + + double sin = Math.Sin(rotation.Radians); + double cos = Math.Cos(rotation.Radians); + + for (int i = 0; i < points.Length; i++) + { + Point point = points[i]; + point = new Point(point.X - origin.X, point.Y - origin.Y); + point = new Point((point.X * cos) - (point.Y * sin), (point.X * sin) + (point.Y * cos)); + points[i] = new Point(point.X + origin.X, point.Y + origin.Y); + } + + return points; + } + + #endregion + } +} diff --git a/RGB.NET.Core/Leds/Led.cs b/RGB.NET.Core/Leds/Led.cs index b300065..67cea33 100644 --- a/RGB.NET.Core/Leds/Led.cs +++ b/RGB.NET.Core/Leds/Led.cs @@ -1,6 +1,7 @@ // ReSharper disable MemberCanBePrivate.Global using System; +using System.ComponentModel; using System.Diagnostics; namespace RGB.NET.Core @@ -44,15 +45,81 @@ namespace RGB.NET.Core set => SetProperty(ref _shapeData, value); } + private Point _location; /// - /// Gets a rectangle representing the physical location of the relative to the . + /// Gets or sets the relative location of the . /// - public Rectangle LedRectangle { get; } + public Point Location + { + get => _location; + set + { + if (SetProperty(ref _location, value)) + { + UpdateActualData(); + UpdateAbsoluteData(); + } + } + } + private Size _size; /// - /// Gets a rectangle representing the physical location of the on the . + /// Gets or sets the size of the . /// - public Rectangle AbsoluteLedRectangle => (LedRectangle.Location + Device.Location) + new Size(LedRectangle.Size.Width, LedRectangle.Size.Height); + public Size Size + { + get => _size; + set + { + if (SetProperty(ref _size, value)) + { + UpdateActualData(); + UpdateAbsoluteData(); + } + } + } + + private Point _actualLocation; + /// + /// Gets the actual location of the . + /// This includes device-scaling and rotation. + /// + public Point ActualLocation + { + get => _actualLocation; + private set => SetProperty(ref _actualLocation, value); + } + + private Size _actualSize; + /// + /// Gets the actual size of the . + /// This includes device-scaling. + /// + public Size ActualSize + { + get => _actualSize; + private set => SetProperty(ref _actualSize, value); + } + + private Rectangle _ledRectangle; + /// + /// Gets a rectangle representing the logical location of the relative to the . + /// + public Rectangle LedRectangle + { + get => _ledRectangle; + private set => SetProperty(ref _ledRectangle, value); + } + + private Rectangle _absoluteLedRectangle; + /// + /// Gets a rectangle representing the logical location of the on the . + /// + public Rectangle AbsoluteLedRectangle + { + get => _absoluteLedRectangle; + private set => SetProperty(ref _absoluteLedRectangle, value); + } /// /// Indicates whether the is about to change it's color. @@ -134,20 +201,61 @@ namespace RGB.NET.Core /// /// The the is associated with. /// The of the . - /// The representing the physical location of the relative to the . + /// The physical location of the relative to the . + /// The size of the . /// The provider-specific data associated with this led. - internal Led(IRGBDevice device, LedId id, Rectangle ledRectangle, object customData = null) + internal Led(IRGBDevice device, LedId id, Point location, Size size, object customData = null) { this.Device = device; this.Id = id; - this.LedRectangle = ledRectangle; + this.Location = location; + this.Size = size; this.CustomData = customData; + + device.PropertyChanged += DevicePropertyChanged; } #endregion #region Methods + private void DevicePropertyChanged(object sender, PropertyChangedEventArgs e) + { + if ((e.PropertyName == nameof(IRGBDevice.Location))) + UpdateAbsoluteData(); + else if (e.PropertyName == nameof(IRGBDevice.DeviceRectangle)) + { + UpdateActualData(); + UpdateAbsoluteData(); + } + } + + private void UpdateActualData() + { + ActualSize = Size * Device.Scale; + + Point actualLocation = (Location * Device.Scale); + Rectangle ledRectangle = new Rectangle(Location * Device.Scale, Size * Device.Scale); + + if (Device.Rotation.IsRotated) + { + Point deviceCenter = new Rectangle(Device.ActualSize).Center; + Point actualDeviceCenter = new Rectangle(Device.DeviceRectangle.Size).Center; + Point centerOffset = new Point(actualDeviceCenter.X - deviceCenter.X, actualDeviceCenter.Y - deviceCenter.Y); + + actualLocation = actualLocation.Rotate(Device.Rotation, new Rectangle(Device.ActualSize).Center) + centerOffset; + ledRectangle = new Rectangle(ledRectangle.Rotate(Device.Rotation, new Rectangle(Device.ActualSize).Center)).Translate(centerOffset); + } + + ActualLocation = actualLocation; + LedRectangle = ledRectangle; + } + + private void UpdateAbsoluteData() + { + AbsoluteLedRectangle = LedRectangle.Translate(Device.Location); + } + /// /// Converts the and the of this to a human-readable string. /// @@ -155,7 +263,7 @@ namespace RGB.NET.Core public override string ToString() => $"{Id} {Color}"; /// - /// Updates the to the requested . + /// Updates the to the requested . /// internal void Update() { @@ -169,7 +277,7 @@ namespace RGB.NET.Core } /// - /// Resets the back to default. + /// Resets the back to default. /// internal void Reset() { @@ -195,7 +303,7 @@ namespace RGB.NET.Core /// Converts a to a . /// /// The to convert. - public static implicit operator Rectangle(Led led) => led?.LedRectangle; + public static implicit operator Rectangle(Led led) => led?.LedRectangle ?? new Rectangle(); #endregion } diff --git a/RGB.NET.Core/Positioning/Point.cs b/RGB.NET.Core/Positioning/Point.cs index 58eb2d9..9583863 100644 --- a/RGB.NET.Core/Positioning/Point.cs +++ b/RGB.NET.Core/Positioning/Point.cs @@ -149,6 +149,14 @@ namespace RGB.NET.Core return new Point(point1.X / point2.X, point1.Y / point2.Y); } + /// + /// Returns a new representing the multiplication of the and the provided . + /// + /// The . + /// The . + /// A new representing the multiplication of the and the provided . + public static Point operator *(Point point, Scale scale) => new Point(point.X * scale.Horizontal, point.Y * scale.Vertical); + #endregion } } diff --git a/RGB.NET.Core/Positioning/Rectangle.cs b/RGB.NET.Core/Positioning/Rectangle.cs index ede55c3..d3b1618 100644 --- a/RGB.NET.Core/Positioning/Rectangle.cs +++ b/RGB.NET.Core/Positioning/Rectangle.cs @@ -8,168 +8,68 @@ 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 + public struct Rectangle { #region Properties & Fields - private double _x; /// - /// Gets or sets the X-position of this . + /// Gets the representing the top-left corner of the . /// - 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)); - } - } - } + public Point Location { get; } /// - /// Gets or sets the representing the top-left corner of the . + /// Gets the 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)); - } - } - } + public Size Size { get; } /// /// Gets a new representing the center-point of the . /// - public Point Center => new Point(X + (Width / 2.0), Y + (Height / 2.0)); + 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 => (Width <= DoubleExtensions.TOLERANCE) || (Height <= DoubleExtensions.TOLERANCE); + 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 . + /// 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(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 (0,0) and the given . + /// + /// The size of of this . + public Rectangle(Size size) : this(new Point(), size) + { } + /// /// Initializes a new instance of the class using the given 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.0), Location.Y + (Size.Height / 2.0)); } /// @@ -205,10 +105,10 @@ namespace RGB.NET.Core 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)); + (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.0), Location.Y + (Size.Height / 2.0)); } /// @@ -246,82 +146,27 @@ namespace RGB.NET.Core 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)); + (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.0), Location.Y + (Size.Height / 2.0)); } #endregion #region Methods - private void InitializeFromPoints(Point point1, Point point2) + private static (Point location, Size size) 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); + return (new Point(posX, posY), 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. /// @@ -338,9 +183,6 @@ namespace RGB.NET.Core if (!(obj is Rectangle compareRect)) return false; - if (ReferenceEquals(this, compareRect)) - return true; - if (GetType() != compareRect.GetType()) return false; @@ -371,7 +213,7 @@ namespace RGB.NET.Core /// 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); + public static bool operator ==(Rectangle rectangle1, Rectangle rectangle2) => rectangle1.Equals(rectangle2); /// /// Returns a value that indicates whether two specified are equal. diff --git a/RGB.NET.Core/Positioning/Rotation.cs b/RGB.NET.Core/Positioning/Rotation.cs new file mode 100644 index 0000000..0f638ec --- /dev/null +++ b/RGB.NET.Core/Positioning/Rotation.cs @@ -0,0 +1,162 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System; +using System.Diagnostics; + +namespace RGB.NET.Core +{ + /// + /// Represents an angular rotation. + /// + [DebuggerDisplay("[{Degrees}°]")] + public struct Rotation + { + #region Constants + + private const double TWO_PI = Math.PI * 2.0; + private const double RADIANS_DEGREES_CONVERSION = 180.0 / Math.PI; + private const double DEGREES_RADIANS_CONVERSION = Math.PI / 180.0; + + #endregion + + #region Properties & Fields + + /// + /// Gets the angle in degrees. + /// + public double Degrees { get; } + + /// + /// Gets the angle in radians. + /// + public double Radians { get; } + + /// + /// Gets a bool indicating if the rotation is > 0. + /// + public bool IsRotated => !Degrees.EqualsInTolerance(0); + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class using the provided values. + /// + /// The rotation in degrees. + public Rotation(double degrees) + : this(degrees, degrees * DEGREES_RADIANS_CONVERSION) + { } + + private Rotation(double degrees, double radians) + { + this.Degrees = degrees % 360.0; + this.Radians = radians % TWO_PI; + } + + #endregion + + #region Methods + + /// + /// Creates a new Rotation out of the given degree-angle. + /// + /// The angle in degrees. + /// The new rotation. + public static Rotation FromDegrees(double degrees) => new Rotation(degrees); + + /// + /// Creates a new Rotation out of the given radian-angle. + /// + /// The angle in radians. + /// The new rotation. + public static Rotation FromRadians(double radians) => new Rotation(radians * RADIANS_DEGREES_CONVERSION, radians); + + /// + /// Tests whether the specified is equivalent to this . + /// + /// The rotation to test. + /// true if is equivalent to this ; otherwise, false. + public bool Equals(Rotation other) => Degrees.EqualsInTolerance(other.Degrees); + + /// + /// 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 Rotation other && Equals(other); + + /// + /// Returns a hash code for this . + /// + /// An integer value that specifies the hash code for this . + public override int GetHashCode() => Degrees.GetHashCode(); + + #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 ==(Rotation rotation1, Rotation rotation2) => rotation1.Equals(rotation2); + + /// + /// 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 !=(Rotation rotation1, Rotation rotation2) => !(rotation1 == rotation2); + + /// + /// Returns a new representing the addition of the and the provided value. + /// + /// The . + /// The value to add. + /// A new representing the addition of the and the provided value. + public static Rotation operator +(Rotation rotation, double value) => new Rotation(rotation.Degrees + value); + + /// + /// Returns a new representing the subtraction of the and the provided value. + /// + /// The . + /// The value to substract. + /// A new representing the subtraction of the and the provided value. + public static Rotation operator -(Rotation rotation, double value) => new Rotation(rotation.Degrees - value); + + /// + /// Returns a new representing the multiplication of the and the provided value. + /// + /// The . + /// The value to multiply with. + /// A new representing the multiplication of the and the provided value. + public static Rotation operator *(Rotation rotation, double value) => new Rotation(rotation.Degrees * value); + + /// + /// Returns a new representing the division of the and the provided value. + /// + /// The . + /// The value to device with. + /// A new representing the division of the and the provided value. + public static Rotation operator /(Rotation rotation, double value) => value.EqualsInTolerance(0) ? new Rotation(0) : new Rotation(rotation.Degrees / value); + + /// + /// Converts a double to a . + /// + /// The rotation in degrees to convert. + public static implicit operator Rotation(double rotation) => new Rotation(rotation); + + /// + /// Converts to a double representing the rotation in degrees. + /// + /// The rotatio to convert. + public static implicit operator double(Rotation rotation) => rotation.Degrees; + + #endregion + } +} diff --git a/RGB.NET.Core/Positioning/Scale.cs b/RGB.NET.Core/Positioning/Scale.cs new file mode 100644 index 0000000..04b8f09 --- /dev/null +++ b/RGB.NET.Core/Positioning/Scale.cs @@ -0,0 +1,144 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System.Diagnostics; + +namespace RGB.NET.Core +{ + /// + /// Represents a scaling. + /// + [DebuggerDisplay("[Horizontal: {Horizontal}, Vertical: {Vertical}]")] + public struct Scale + { + #region Properties & Fields + + /// + /// Gets the horizontal scaling value. + /// + public double Horizontal { get; } + + /// + /// Gets the vertical scaling value. + /// + public double Vertical { get; } + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class using the provided values. + /// + /// The value used for horizontal and vertical scaling. 0 if not set. + public Scale(double scale = 1.0) : this(scale, scale) + { } + + /// + /// Initializes a new instance of the class using the provided values. + /// + /// The value used for horizontal scaling. + /// The value used for vertical scaling. + public Scale(double horizontal, double vertical) + { + this.Horizontal = horizontal; + this.Vertical = vertical; + } + + #endregion + + #region Methods + + /// + /// Tests whether the specified is equivalent to this . + /// + /// The scale to test. + /// true if is equivalent to this ; otherwise, false. + public bool Equals(Scale other) => Horizontal.EqualsInTolerance(other.Horizontal) && Vertical.EqualsInTolerance(other.Vertical); + + /// + /// 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 Scale other && Equals(other); + + /// + /// Returns a hash code for this . + /// + /// An integer value that specifies the hash code for this . + public override int GetHashCode() { unchecked { return (Horizontal.GetHashCode() * 397) ^ Vertical.GetHashCode(); } } + + /// + /// Deconstructs the scale into the horizontal and vertical value. + /// + /// The horizontal scaling value. + /// The vertical scaling value. + public void Deconstruct(out double horizontalScale, out double verticalScale) + { + horizontalScale = Horizontal; + verticalScale = Vertical; + } + + #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 ==(Scale scale1, Scale scale2) => scale1.Equals(scale2); + + /// + /// 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 !=(Scale scale1, Scale scale2) => !(scale1 == scale2); + + /// + /// Returns a new representing the addition of the and the provided value. + /// + /// The . + /// The value to add. + /// A new representing the addition of the and the provided value. + public static Scale operator +(Scale scale, double value) => new Scale(scale.Horizontal + value, scale.Vertical + value); + + /// + /// Returns a new representing the subtraction of the and the provided value. + /// + /// The . + /// The value to substract. + /// A new representing the subtraction of the and the provided value. + public static Scale operator -(Scale scale, double value) => new Scale(scale.Horizontal - value, scale.Vertical - value); + + /// + /// Returns a new representing the multiplication of the and the provided value. + /// + /// The . + /// The value to multiply with. + /// A new representing the multiplication of the and the provided value. + public static Scale operator *(Scale scale, double value) => new Scale(scale.Horizontal * value, scale.Vertical * value); + + /// + /// Returns a new representing the division of the and the provided value. + /// + /// The . + /// The value to device with. + /// A new representing the division of the and the provided value. + public static Scale operator /(Scale scale, double value) => value.EqualsInTolerance(0) ? new Scale(0) : new Scale(scale.Horizontal / value, scale.Vertical / value); + + + /// + /// Converts a double to a . + /// + /// The scale value to convert. + public static implicit operator Scale(double scale) => new Scale(scale); + + #endregion + } +} diff --git a/RGB.NET.Core/Positioning/Size.cs b/RGB.NET.Core/Positioning/Size.cs index d6b7c6f..74f9153 100644 --- a/RGB.NET.Core/Positioning/Size.cs +++ b/RGB.NET.Core/Positioning/Size.cs @@ -94,6 +94,17 @@ namespace RGB.NET.Core } } + /// + /// Deconstructs the size into the width and height value. + /// + /// The width. + /// The height. + public void Deconstruct(out double width, out double height) + { + width = Width; + height = Height; + } + #endregion #region Operators @@ -172,6 +183,14 @@ namespace RGB.NET.Core /// A new representing the division of the and the provided factor. public static Size operator /(Size size, double factor) => factor.EqualsInTolerance(0) ? Invalid : new Size(size.Width / factor, size.Height / factor); + /// + /// Returns a new representing the multiplication of the and the given . + /// + /// The to scale. + /// The scaling factor. + /// A new representing the multiplication of the and the given . + public static Size operator *(Size size, Scale scale) => new Size(size.Width * scale.Horizontal, size.Height * scale.Vertical); + #endregion } } diff --git a/RGB.NET.Core/RGBSurface.cs b/RGB.NET.Core/RGBSurface.cs index d2933c7..7ba3183 100644 --- a/RGB.NET.Core/RGBSurface.cs +++ b/RGB.NET.Core/RGBSurface.cs @@ -33,8 +33,6 @@ namespace RGB.NET.Core private readonly LinkedList _ledGroups = new LinkedList(); - private readonly Rectangle _surfaceRectangle = new Rectangle(); - // ReSharper restore InconsistentNaming /// @@ -50,7 +48,7 @@ namespace RGB.NET.Core /// /// Gets a copy of the representing this . /// - public Rectangle SurfaceRectangle => new Rectangle(_surfaceRectangle); + public Rectangle SurfaceRectangle { get; private set; } /// /// Gets a list of all on this . @@ -162,12 +160,11 @@ namespace RGB.NET.Core case BrushCalculationMode.Relative: Rectangle brushRectangle = new Rectangle(leds.Select(led => led.AbsoluteLedRectangle)); Point offset = new Point(-brushRectangle.Location.X, -brushRectangle.Location.Y); - brushRectangle.Location = new Point(0, 0); - brush.PerformRender(brushRectangle, - leds.Select(x => new BrushRenderTarget(x, GetDeviceLedLocation(x, offset)))); + brushRectangle = brushRectangle.SetLocation(new Point(0, 0)); + brush.PerformRender(brushRectangle, leds.Select(led => new BrushRenderTarget(led, led.AbsoluteLedRectangle.Translate(offset)))); break; case BrushCalculationMode.Absolute: - brush.PerformRender(SurfaceRectangle, leds.Select(x => new BrushRenderTarget(x, x.AbsoluteLedRectangle))); + brush.PerformRender(SurfaceRectangle, leds.Select(led => new BrushRenderTarget(led, led.AbsoluteLedRectangle))); break; default: throw new ArgumentException(); @@ -180,12 +177,6 @@ namespace RGB.NET.Core renders.Key.Led.Color = renders.Value; } - private Rectangle GetDeviceLedLocation(Led led, Point extraOffset) - { - Rectangle absoluteRectangle = led.AbsoluteLedRectangle; - return (absoluteRectangle.Location + extraOffset) + absoluteRectangle.Size; - } - /// /// Attaches the given . /// @@ -229,10 +220,8 @@ namespace RGB.NET.Core private void UpdateSurfaceRectangle() { - Rectangle devicesRectangle = new Rectangle(_devices.Select(d => new Rectangle(d.Location, d.Size))); - - _surfaceRectangle.Width = devicesRectangle.Location.X + devicesRectangle.Size.Width; - _surfaceRectangle.Height = devicesRectangle.Location.Y + devicesRectangle.Size.Height; + Rectangle devicesRectangle = new Rectangle(_devices.Select(d => d.DeviceRectangle)); + SurfaceRectangle = SurfaceRectangle.SetSize(new Size(devicesRectangle.Location.X + devicesRectangle.Size.Width, devicesRectangle.Location.Y + devicesRectangle.Size.Height)); } /// diff --git a/RGB.NET.Core/RGBSurfaceDeviceLoader.cs b/RGB.NET.Core/RGBSurfaceDeviceLoader.cs index b3fe9e6..f233282 100644 --- a/RGB.NET.Core/RGBSurfaceDeviceLoader.cs +++ b/RGB.NET.Core/RGBSurfaceDeviceLoader.cs @@ -63,7 +63,7 @@ namespace RGB.NET.Core foreach (IRGBDevice device in Devices) { device.Location += new Point(posX, 0); - posX += device.Size.Width + 1; + posX += device.ActualSize.Width + 1; } } diff --git a/RGB.NET.Groups/Groups/RectangleLedGroup.cs b/RGB.NET.Groups/Groups/RectangleLedGroup.cs index 63b5616..1b23c26 100644 --- a/RGB.NET.Groups/Groups/RectangleLedGroup.cs +++ b/RGB.NET.Groups/Groups/RectangleLedGroup.cs @@ -2,7 +2,6 @@ // ReSharper disable AutoPropertyCanBeMadeGetOnly.Global // ReSharper disable UnusedMember.Global -using System; using System.Collections.Generic; using System.Linq; using RGB.NET.Core; @@ -28,17 +27,8 @@ namespace RGB.NET.Groups get => _rectangle; set { - Rectangle oldValue = _rectangle; if (SetProperty(ref _rectangle, value)) - { - if (oldValue != null) - oldValue.PropertyChanged -= RectangleChanged; - - if (_rectangle != null) - _rectangle.PropertyChanged += RectangleChanged; - InvalidateCache(); - } } } @@ -110,14 +100,12 @@ namespace RGB.NET.Groups private void RGBSurfaceOnSurfaceLayoutChanged(SurfaceLayoutChangedEventArgs args) => InvalidateCache(); - private void RectangleChanged(object sender, EventArgs eventArgs) => InvalidateCache(); - /// /// /// Gets a list containing all of this . /// /// The list containing all of this . - public override IEnumerable GetLeds() => _ledCache ?? (_ledCache = RGBSurface.Instance.Leds.Where(x => x.AbsoluteLedRectangle.CalculateIntersectPercentage(Rectangle) >= MinOverlayPercentage).ToList()); + public override IEnumerable GetLeds() => _ledCache ??= RGBSurface.Instance.Leds.Where(led => led.AbsoluteLedRectangle.CalculateIntersectPercentage(Rectangle) >= MinOverlayPercentage).ToList(); private void InvalidateCache() => _ledCache = null;