diff --git a/RGB.NET.Core/Devices/AbstractRGBDevice.cs b/RGB.NET.Core/Devices/AbstractRGBDevice.cs index 59d0eae..3a418f3 100644 --- a/RGB.NET.Core/Devices/AbstractRGBDevice.cs +++ b/RGB.NET.Core/Devices/AbstractRGBDevice.cs @@ -31,8 +31,8 @@ namespace RGB.NET.Core /// public Size Size { - get => _size; - set => SetProperty(ref _size, value); + get => _size * Scale; + protected set => SetProperty(ref _size, value); } private Point _location = new Point(0, 0); @@ -43,6 +43,18 @@ namespace RGB.NET.Core set => SetProperty(ref _location, value); } + private Scale _scale = new Scale(1); + /// + public Scale Scale + { + get => _scale; + set + { + if (SetProperty(ref _scale, value)) + OnPropertyChanged(nameof(Size)); + } + } + /// /// Gets or sets if the device needs to be flushed on every update. /// @@ -67,11 +79,11 @@ namespace RGB.NET.Core Led IRGBDevice.this[LedId ledId] => LedMapping.TryGetValue(ledId, out Led led) ? led : null; /// - Led IRGBDevice.this[Point location] => LedMapping.Values.FirstOrDefault(x => x.LedRectangle.Contains(location)); + Led IRGBDevice.this[Point location] => LedMapping.Values.FirstOrDefault(x => x.ActualLedRectangle.Contains(location)); /// IEnumerable IRGBDevice.this[Rectangle referenceRect, double minOverlayPercentage] - => LedMapping.Values.Where(x => referenceRect.CalculateIntersectPercentage(x.LedRectangle) >= minOverlayPercentage); + => LedMapping.Values.Where(x => referenceRect.CalculateIntersectPercentage(x.ActualLedRectangle) >= minOverlayPercentage); #endregion diff --git a/RGB.NET.Core/Devices/IRGBDevice.cs b/RGB.NET.Core/Devices/IRGBDevice.cs index 9ba17c4..9321850 100644 --- a/RGB.NET.Core/Devices/IRGBDevice.cs +++ b/RGB.NET.Core/Devices/IRGBDevice.cs @@ -27,6 +27,8 @@ namespace RGB.NET.Core /// Gets a copy of the of the whole . /// Size Size { get; } + + Scale Scale { get; set; } /// /// Gets or sets the of the . diff --git a/RGB.NET.Core/Leds/Led.cs b/RGB.NET.Core/Leds/Led.cs index 27bf4ed..a52cc6d 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 @@ -54,14 +55,19 @@ namespace RGB.NET.Core set { if (SetProperty(ref _ledRectangle, value)) + { + OnPropertyChanged(nameof(ActualLedRectangle)); OnPropertyChanged(nameof(AbsoluteLedRectangle)); + } } } + public Rectangle ActualLedRectangle => new Rectangle(LedRectangle.Location * Device.Scale, LedRectangle.Size * Device.Scale); + /// /// Gets a rectangle representing the physical location of the on the . /// - public Rectangle AbsoluteLedRectangle => (LedRectangle.Location + Device.Location) + new Size(LedRectangle.Size.Width, LedRectangle.Size.Height); + public Rectangle AbsoluteLedRectangle => new Rectangle(ActualLedRectangle.Location + Device.Location, ActualLedRectangle.Size); /// /// Indicates whether the is about to change it's color. @@ -152,17 +158,26 @@ namespace RGB.NET.Core this.LedRectangle = ledRectangle; this.CustomData = customData; - device.PropertyChanged += (sender, args) => - { - OnPropertyChanged(nameof(LedRectangle)); - OnPropertyChanged(nameof(AbsoluteLedRectangle)); - }; + device.PropertyChanged += DevicePropertyChanged; } #endregion #region Methods + private void DevicePropertyChanged(object sender, PropertyChangedEventArgs e) + { + if ((e.PropertyName == nameof(IRGBDevice.Location))) + { + OnPropertyChanged(nameof(AbsoluteLedRectangle)); + } + else if ((e.PropertyName == nameof(IRGBDevice.Scale))) + { + OnPropertyChanged(nameof(ActualLedRectangle)); + OnPropertyChanged(nameof(AbsoluteLedRectangle)); + } + } + /// /// Converts the and the of this to a human-readable string. /// @@ -210,7 +225,7 @@ namespace RGB.NET.Core /// Converts a to a . /// /// The to convert. - public static implicit operator Rectangle(Led led) => led?.LedRectangle ?? new Rectangle(); + public static implicit operator Rectangle(Led led) => led?.ActualLedRectangle ?? new Rectangle(); #endregion } diff --git a/RGB.NET.Core/Positioning/Point.cs b/RGB.NET.Core/Positioning/Point.cs index 58eb2d9..38be165 100644 --- a/RGB.NET.Core/Positioning/Point.cs +++ b/RGB.NET.Core/Positioning/Point.cs @@ -149,6 +149,8 @@ namespace RGB.NET.Core return new Point(point1.X / point2.X, point1.Y / point2.Y); } + 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/Scale.cs b/RGB.NET.Core/Positioning/Scale.cs new file mode 100644 index 0000000..42558e5 --- /dev/null +++ b/RGB.NET.Core/Positioning/Scale.cs @@ -0,0 +1,46 @@ +namespace RGB.NET.Core +{ + public struct Scale + { + #region Properties & Fields + + public double Horizontal { get; } + public double Vertical { get; } + + #endregion + + #region Constructors + + public Scale(double scale = 1.0) : this(scale, scale) + { } + + public Scale(double horizontal, double vertical) + { + this.Horizontal = horizontal; + this.Vertical = vertical; + } + + #endregion + + #region Methods + + public bool Equals(Scale other) => Horizontal.EqualsInTolerance(other.Horizontal) && Vertical.EqualsInTolerance(other.Vertical); + public override bool Equals(object obj) => obj is Scale other && Equals(other); + public override int GetHashCode() { unchecked { return (Horizontal.GetHashCode() * 397) ^ Vertical.GetHashCode(); } } + + public void Deconstruct(out double horizontalScale, out double verticalScale) + { + horizontalScale = Horizontal; + verticalScale = Vertical; + } + + #endregion + + #region Operators + + public static implicit operator Scale(double scale) => new Scale(scale); + public static implicit operator Scale((double horizontal, double vertical) scale) => new Scale(scale.horizontal, scale.vertical); + + #endregion + } +} diff --git a/RGB.NET.Core/Positioning/Size.cs b/RGB.NET.Core/Positioning/Size.cs index d6b7c6f..8342c4f 100644 --- a/RGB.NET.Core/Positioning/Size.cs +++ b/RGB.NET.Core/Positioning/Size.cs @@ -94,6 +94,12 @@ namespace RGB.NET.Core } } + public void Deconstruct(out double width, out double height) + { + width = Width; + height = Height; + } + #endregion #region Operators @@ -172,6 +178,8 @@ 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); + public static Size operator *(Size size, Scale scale) => new Size(size.Width * scale.Horizontal, size.Height * scale.Vertical); + #endregion } } diff --git a/RGB.NET.Groups/Groups/RectangleLedGroup.cs b/RGB.NET.Groups/Groups/RectangleLedGroup.cs index 18ffb8c..9958a0d 100644 --- a/RGB.NET.Groups/Groups/RectangleLedGroup.cs +++ b/RGB.NET.Groups/Groups/RectangleLedGroup.cs @@ -59,7 +59,7 @@ namespace RGB.NET.Groups /// (optional) The minimal percentage overlay a must have with the to be taken into the . (default: 0.5) /// (optional) Specifies whether this should be automatically attached or not. (default: true) public RectangleLedGroup(Led fromLed, Led toLed, double minOverlayPercentage = 0.5, bool autoAttach = true) - : this(new Rectangle(fromLed.LedRectangle, toLed.LedRectangle), minOverlayPercentage, autoAttach) + : this(new Rectangle(fromLed.ActualLedRectangle, toLed.ActualLedRectangle), minOverlayPercentage, autoAttach) { } ///