// ReSharper disable MemberCanBePrivate.Global using System; using System.ComponentModel; using System.Diagnostics; namespace RGB.NET.Core { /// /// /// Represents a single LED of a RGB-device. /// [DebuggerDisplay("{Id} {Color}")] public class Led : AbstractBindable { #region Properties & Fields /// /// Gets the this is associated with. /// public IRGBDevice Device { get; } /// /// Gets the of the . /// public LedId Id { get; } private Shape _shape = Shape.Rectangle; /// /// Gets or sets the of the . /// public Shape Shape { get => _shape; set => SetProperty(ref _shape, value); } private string _shapeData; /// /// Gets or sets the data used for by the -. /// public string ShapeData { get => _shapeData; set => SetProperty(ref _shapeData, value); } public Point Location { get; set; } public Size Size { get; set; } public Point ActualLocation { get { Point point = (Location * 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); point = point.Rotate(Device.Rotation, new Rectangle(Device.ActualSize).Center) + centerOffset; } return point; } } public Size ActualSize => Size * Device.Scale; /// /// Gets a rectangle representing the logical location of the relative to the . /// 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. /// public bool IsDirty => RequestedColor.HasValue && (RequestedColor != InternalColor); private Color? _requestedColor; /// /// Gets a copy of the the LED should be set to on the next update. /// Null if there is no update-request for the next update. /// public Color? RequestedColor { get => _requestedColor; private set { SetProperty(ref _requestedColor, value); // ReSharper disable once ExplicitCallerInfoArgument OnPropertyChanged(nameof(IsDirty)); } } private Color _color = Color.Transparent; /// /// Gets the current of the . Sets the for the next update. /// public Color Color { get => _color; set { if (!IsLocked) { if (RequestedColor.HasValue) RequestedColor += value; else RequestedColor = value; } } } /// /// Gets or set the ignoring all workflows regarding locks and update-requests. /> /// internal Color InternalColor { get => _color; set => SetProperty(ref _color, value); } private bool _isLocked; /// /// Gets or sets if the color of this LED can be changed. /// public bool IsLocked { get => _isLocked; set => SetProperty(ref _isLocked, value); } /// /// Gets the URI of an image of the or null if there is no image. /// public Uri Image { get; set; } /// /// Gets the provider-specific data associated with this led. /// public object CustomData { get; } #endregion #region Constructors /// /// Initializes a new instance of the class. /// /// The the is associated with. /// The of 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, Point location, Size size, object customData = null) { this.Device = device; this.Id = id; 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))) { OnPropertyChanged(nameof(AbsoluteLedRectangle)); } else if ((e.PropertyName == nameof(IRGBDevice.Scale)) || (e.PropertyName == nameof(IRGBDevice.Rotation))) { OnPropertyChanged(nameof(LedRectangle)); OnPropertyChanged(nameof(AbsoluteLedRectangle)); OnPropertyChanged(nameof(ActualLocation)); OnPropertyChanged(nameof(ActualSize)); } } /// /// Converts the and the of this to a human-readable string. /// /// A string that contains the and the of this . For example "Enter [A: 255, R: 255, G: 0, B: 0]". public override string ToString() => $"{Id} {Color}"; /// /// Updates the to the requested . /// internal void Update() { if (!RequestedColor.HasValue) return; _color = RequestedColor.Value; RequestedColor = null; // ReSharper disable once ExplicitCallerInfoArgument OnPropertyChanged(nameof(Color)); } /// /// Resets the back to default. /// internal void Reset() { _color = Color.Transparent; RequestedColor = null; IsLocked = false; // ReSharper disable once ExplicitCallerInfoArgument OnPropertyChanged(nameof(Color)); } #endregion #region Operators /// /// Converts a to a . /// /// The to convert. public static implicit operator Color(Led led) => led?.Color ?? Color.Transparent; /// /// Converts a to a . /// /// The to convert. public static implicit operator Rectangle(Led led) => led?.LedRectangle ?? new Rectangle(); #endregion } }