using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using Artemis.Core.DeviceProviders; using Artemis.Storage.Entities.Surface; using RGB.NET.Core; using SkiaSharp; namespace Artemis.Core { /// /// Represents an RGB device usable by Artemis, provided by a /// public class ArtemisDevice : CorePropertyChanged { private ReadOnlyCollection _leds; private SKPath? _renderPath; private SKRect _renderRectangle; internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider, ArtemisSurface surface) { RgbDevice = rgbDevice; DeviceProvider = deviceProvider; Surface = surface; InputIdentifiers = new List(); DeviceEntity = new DeviceEntity(); _leds = rgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly(); Rotation = 0; Scale = 1; ZIndex = 1; ApplyToEntity(); CalculateRenderProperties(); } internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider, ArtemisSurface surface, DeviceEntity deviceEntity) { RgbDevice = rgbDevice; DeviceProvider = deviceProvider; Surface = surface; InputIdentifiers = new List(); DeviceEntity = deviceEntity; _leds = rgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly(); } /// /// Gets the rectangle covering the device, sized to match the render scale /// public SKRect RenderRectangle { get => _renderRectangle; private set => SetAndNotify(ref _renderRectangle, value); } /// /// Gets the path surrounding the device, sized to match the render scale /// public SKPath? RenderPath { get => _renderPath; private set => SetAndNotify(ref _renderPath, value); } /// /// Gets the RGB.NET device backing this Artemis device /// public IRGBDevice RgbDevice { get; } /// /// Gets the device provider that provided this device /// public DeviceProvider DeviceProvider { get; } /// /// Gets the surface containing this device /// public ArtemisSurface Surface { get; } /// /// Gets a read only collection containing the LEDs of this device /// public ReadOnlyCollection Leds { get => _leds; private set => SetAndNotify(ref _leds, value); } /// /// Gets a list of input identifiers associated with this device /// public List InputIdentifiers { get; } /// /// Gets or sets the X-position of the device /// public double X { get => DeviceEntity.X; set { DeviceEntity.X = value; OnPropertyChanged(nameof(X)); } } /// /// Gets or sets the Y-position of the device /// public double Y { get => DeviceEntity.Y; set { DeviceEntity.Y = value; OnPropertyChanged(nameof(Y)); } } /// /// Gets or sets the rotation of the device /// public double Rotation { get => DeviceEntity.Rotation; set { DeviceEntity.Rotation = value; OnPropertyChanged(nameof(Rotation)); } } /// /// Gets or sets the scale of the device /// public double Scale { get => DeviceEntity.Scale; set { DeviceEntity.Scale = value; OnPropertyChanged(nameof(Scale)); } } /// /// Gets or sets the Z-index of the device /// public int ZIndex { get => DeviceEntity.ZIndex; set { DeviceEntity.ZIndex = value; OnPropertyChanged(nameof(ZIndex)); } } internal DeviceEntity DeviceEntity { get; } /// public override string ToString() { return $"[{RgbDevice.DeviceInfo.DeviceType}] {RgbDevice.DeviceInfo.DeviceName} - {X}.{Y}.{ZIndex}"; } /// /// Occurs when the underlying RGB.NET device was updated /// public event EventHandler? DeviceUpdated; /// /// Invokes the event /// protected virtual void OnDeviceUpdated() { DeviceUpdated?.Invoke(this, EventArgs.Empty); } internal void ApplyToEntity() { // Other properties are computed DeviceEntity.DeviceIdentifier = RgbDevice.GetDeviceIdentifier(); DeviceEntity.InputIdentifier.Clear(); foreach (ArtemisDeviceInputIdentifier identifier in InputIdentifiers) { DeviceEntity.InputIdentifier.Add(new DeviceInputIdentifierEntity { InputProvider = identifier.InputProvider, Identifier = identifier.Identifier }); } } internal void ApplyToRgbDevice() { RgbDevice.Rotation = DeviceEntity.Rotation; RgbDevice.Scale = DeviceEntity.Scale; // Workaround for device rotation not applying if (DeviceEntity.X == 0 && DeviceEntity.Y == 0) RgbDevice.Location = new Point(1, 1); RgbDevice.Location = new Point(DeviceEntity.X, DeviceEntity.Y); InputIdentifiers.Clear(); foreach (DeviceInputIdentifierEntity identifierEntity in DeviceEntity.InputIdentifier) InputIdentifiers.Add(new ArtemisDeviceInputIdentifier(identifierEntity.InputProvider, identifierEntity.Identifier)); CalculateRenderProperties(); OnDeviceUpdated(); } internal void CalculateRenderProperties() { RenderRectangle = SKRect.Create( (RgbDevice.Location.X * Surface.Scale).RoundToInt(), (RgbDevice.Location.Y * Surface.Scale).RoundToInt(), (RgbDevice.DeviceRectangle.Size.Width * Surface.Scale).RoundToInt(), (RgbDevice.DeviceRectangle.Size.Height * Surface.Scale).RoundToInt() ); if (!Leds.Any()) return; foreach (ArtemisLed led in Leds) led.CalculateRenderRectangle(); SKPath path = new SKPath {FillType = SKPathFillType.Winding}; foreach (ArtemisLed artemisLed in Leds) path.AddRect(artemisLed.AbsoluteRenderRectangle); RenderPath = path; } } }