diff --git a/RGB.NET.Core/Devices/AbstractRGBDevice.cs b/RGB.NET.Core/Devices/AbstractRGBDevice.cs
index c12c931..8a50ae9 100644
--- a/RGB.NET.Core/Devices/AbstractRGBDevice.cs
+++ b/RGB.NET.Core/Devices/AbstractRGBDevice.cs
@@ -27,6 +27,14 @@ namespace RGB.NET.Core
///
IRGBDeviceInfo IRGBDevice.DeviceInfo => DeviceInfo;
+ private Point _location = new Point(0, 0);
+ ///
+ public Point Location
+ {
+ get => _location;
+ set => SetProperty(ref _location, value);
+ }
+
private Size _size = Size.Invalid;
///
public Size Size
@@ -35,19 +43,22 @@ namespace RGB.NET.Core
protected set
{
if (SetProperty(ref _size, value))
+ {
OnPropertyChanged(nameof(ActualSize));
+ OnPropertyChanged(nameof(DeviceRectangle));
+ }
}
}
///
public Size ActualSize => Size * Scale;
- private Point _location = new Point(0, 0);
- ///
- public Point Location
+ public Rectangle DeviceRectangle
{
- get => _location;
- set => SetProperty(ref _location, value);
+ get
+ {
+ return new Rectangle(Location, new Rectangle(new Rectangle(Location, ActualSize).Rotate(Rotation)).Size);
+ }
}
private Scale _scale = new Scale(1);
@@ -58,7 +69,24 @@ namespace RGB.NET.Core
set
{
if (SetProperty(ref _scale, value))
+ {
OnPropertyChanged(nameof(ActualSize));
+ OnPropertyChanged(nameof(DeviceRectangle));
+ }
+ }
+ }
+
+ private Rotation _rotation = new Rotation(0);
+ ///
+ public Rotation Rotation
+ {
+ get => _rotation;
+ set
+ {
+ if (SetProperty(ref _rotation, value))
+ {
+ OnPropertyChanged(nameof(DeviceRectangle));
+ }
}
}
@@ -86,11 +114,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.ActualLedRectangle.Contains(location));
+ Led IRGBDevice.this[Point location] => LedMapping.Values.FirstOrDefault(x => x.LedRectangle.Contains(location));
///
IEnumerable IRGBDevice.this[Rectangle referenceRect, double minOverlayPercentage]
- => LedMapping.Values.Where(x => referenceRect.CalculateIntersectPercentage(x.ActualLedRectangle) >= minOverlayPercentage);
+ => LedMapping.Values.Where(x => referenceRect.CalculateIntersectPercentage(x.LedRectangle) >= minOverlayPercentage);
#endregion
@@ -143,11 +171,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;
}
@@ -190,11 +228,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 = new Rectangle(new Point(layoutLed.X, layoutLed.Y), 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 07d28ee..c64f2ef 100644
--- a/RGB.NET.Core/Devices/IRGBDevice.cs
+++ b/RGB.NET.Core/Devices/IRGBDevice.cs
@@ -32,12 +32,19 @@ namespace RGB.NET.Core
/// Gets the actual (scaled and rotated) of the .
///
Size ActualSize { get; }
+
+ 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/RectangleExtensions.cs b/RGB.NET.Core/Extensions/RectangleExtensions.cs
index 7d2f85a..f16842e 100644
--- a/RGB.NET.Core/Extensions/RectangleExtensions.cs
+++ b/RGB.NET.Core/Extensions/RectangleExtensions.cs
@@ -110,6 +110,44 @@ namespace RGB.NET.Core
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));
+ public static Point Translate(this Point point, double x = 0, double y = 0) => new Point(point.X + x, point.Y + y);
+
+ 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); ;
+ }
+
+ public static Rectangle Translate(this Rectangle rect, Point point) => rect.Translate(point.X, point.Y);
+ public static Rectangle Translate(this Rectangle rect, double x = 0, double y = 0) => new Rectangle(rect.Location.Translate(x, y), rect.Size);
+
+ 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 a52cc6d..d172269 100644
--- a/RGB.NET.Core/Leds/Led.cs
+++ b/RGB.NET.Core/Leds/Led.cs
@@ -45,29 +45,53 @@ namespace RGB.NET.Core
set => SetProperty(ref _shapeData, value);
}
- private Rectangle _ledRectangle;
- ///
- /// Gets a rectangle representing the physical location of the relative to the .
- ///
- public Rectangle LedRectangle
+ public Point Location { get; set; }
+
+ public Size Size { get; set; }
+
+ public Point ActualLocation
{
- get => _ledRectangle;
- set
+ get
{
- if (SetProperty(ref _ledRectangle, value))
+ Point point = (Location * Device.Scale);
+ if (!Device.Rotation.Radians.EqualsInTolerance(0))
{
- OnPropertyChanged(nameof(ActualLedRectangle));
- OnPropertyChanged(nameof(AbsoluteLedRectangle));
+ Point deviceCenter = new Rectangle(Device.ActualSize).Center;
+ Point actualDeviceCenter = Device.DeviceRectangle.Center;
+ Point centerOffset = new Point(actualDeviceCenter.X - deviceCenter.X, actualDeviceCenter.Y - deviceCenter.Y);
+ point = point.Rotate(Device.Rotation, new Rectangle(Device.ActualSize).Center) + centerOffset;
}
+
+ return point;
}
}
- public Rectangle ActualLedRectangle => new Rectangle(LedRectangle.Location * Device.Scale, LedRectangle.Size * Device.Scale);
+ public Size ActualSize => Size * Device.Scale;
///
- /// Gets a rectangle representing the physical location of the on the .
+ /// Gets a rectangle representing the logical location of the relative to the .
///
- public Rectangle AbsoluteLedRectangle => new Rectangle(ActualLedRectangle.Location + Device.Location, ActualLedRectangle.Size);
+ public Rectangle LedRectangle
+ {
+ get
+ {
+ Rectangle rect = new Rectangle(Location * Device.Scale, Size * Device.Scale);
+ if (!Device.Rotation.Radians.EqualsInTolerance(0))
+ {
+ Point deviceCenter = new Rectangle(Device.ActualSize).Center;
+ Point actualDeviceCenter = Device.DeviceRectangle.Center;
+ Point centerOffset = new Point(actualDeviceCenter.X - deviceCenter.X, actualDeviceCenter.Y - deviceCenter.Y);
+ rect = new Rectangle(rect.Rotate(Device.Rotation, new Rectangle(Device.ActualSize).Center)).Translate(centerOffset);
+ }
+
+ return rect;
+ }
+ }
+
+ ///
+ /// Gets a rectangle representing the logical location of the on the .
+ ///
+ public Rectangle AbsoluteLedRectangle => LedRectangle.Translate(Device.Location);
///
/// Indicates whether the is about to change it's color.
@@ -149,13 +173,15 @@ 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;
@@ -171,10 +197,12 @@ namespace RGB.NET.Core
{
OnPropertyChanged(nameof(AbsoluteLedRectangle));
}
- else if ((e.PropertyName == nameof(IRGBDevice.Scale)))
+ else if ((e.PropertyName == nameof(IRGBDevice.Scale)) || (e.PropertyName == nameof(IRGBDevice.Rotation)))
{
- OnPropertyChanged(nameof(ActualLedRectangle));
+ OnPropertyChanged(nameof(LedRectangle));
OnPropertyChanged(nameof(AbsoluteLedRectangle));
+ OnPropertyChanged(nameof(ActualLocation));
+ OnPropertyChanged(nameof(ActualSize));
}
}
@@ -185,7 +213,7 @@ namespace RGB.NET.Core
public override string ToString() => $"{Id} {Color}";
///
- /// Updates the to the requested .
+ /// Updates the to the requested .
///
internal void Update()
{
@@ -199,7 +227,7 @@ namespace RGB.NET.Core
}
///
- /// Resets the back to default.
+ /// Resets the back to default.
///
internal void Reset()
{
@@ -225,7 +253,7 @@ namespace RGB.NET.Core
/// Converts a to a .
///
/// The to convert.
- public static implicit operator Rectangle(Led led) => led?.ActualLedRectangle ?? new Rectangle();
+ public static implicit operator Rectangle(Led led) => led?.LedRectangle ?? new Rectangle();
#endregion
}
diff --git a/RGB.NET.Core/Positioning/Rectangle.cs b/RGB.NET.Core/Positioning/Rectangle.cs
index 7bf5330..d3b1618 100644
--- a/RGB.NET.Core/Positioning/Rectangle.cs
+++ b/RGB.NET.Core/Positioning/Rectangle.cs
@@ -53,11 +53,18 @@ namespace RGB.NET.Core
: 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;
diff --git a/RGB.NET.Core/Positioning/Rotation.cs b/RGB.NET.Core/Positioning/Rotation.cs
new file mode 100644
index 0000000..92e6c5b
--- /dev/null
+++ b/RGB.NET.Core/Positioning/Rotation.cs
@@ -0,0 +1,54 @@
+using System;
+
+namespace RGB.NET.Core
+{
+ 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
+
+ public double Degrees { get; }
+ public double Radians { get; }
+
+ #endregion
+
+ #region Constructors
+
+ 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
+
+ public static Rotation FromDegrees(double degrees) => new Rotation(degrees);
+ public static Rotation FromRadians(double radians) => new Rotation(radians * RADIANS_DEGREES_CONVERSION, radians);
+
+ public bool Equals(Rotation other) => Degrees.EqualsInTolerance(other.Degrees);
+ public override bool Equals(object obj) => obj is Rotation other && Equals(other);
+ public override int GetHashCode() => Degrees.GetHashCode();
+
+ #endregion
+
+ #region Operators
+
+ public static implicit operator Rotation(double rotation) => new Rotation(rotation);
+ public static implicit operator double(Rotation rotation) => rotation.Degrees;
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Core/RGBSurface.cs b/RGB.NET.Core/RGBSurface.cs
index 058eda5..7ba3183 100644
--- a/RGB.NET.Core/RGBSurface.cs
+++ b/RGB.NET.Core/RGBSurface.cs
@@ -161,10 +161,10 @@ namespace RGB.NET.Core
Rectangle brushRectangle = new Rectangle(leds.Select(led => led.AbsoluteLedRectangle));
Point offset = new Point(-brushRectangle.Location.X, -brushRectangle.Location.Y);
brushRectangle = brushRectangle.SetLocation(new Point(0, 0));
- brush.PerformRender(brushRectangle, leds.Select(x => new BrushRenderTarget(x, GetDeviceLedLocation(x, offset))));
+ 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();
@@ -177,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 .
///
@@ -226,7 +220,7 @@ namespace RGB.NET.Core
private void UpdateSurfaceRectangle()
{
- Rectangle devicesRectangle = new Rectangle(_devices.Select(d => new Rectangle(d.Location, d.ActualSize)));
+ 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.Groups/Groups/RectangleLedGroup.cs b/RGB.NET.Groups/Groups/RectangleLedGroup.cs
index 9958a0d..1b23c26 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.ActualLedRectangle, toLed.ActualLedRectangle), minOverlayPercentage, autoAttach)
+ : this(new Rectangle(fromLed.LedRectangle, toLed.LedRectangle), minOverlayPercentage, autoAttach)
{ }
///
@@ -105,7 +105,7 @@ namespace RGB.NET.Groups
/// Gets a list containing all of this .
///
/// The list containing all of this .
- public override IEnumerable GetLeds() => _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;