using System; using System.Collections.Generic; using System.Linq; namespace RGB.NET.Core { /// /// Represents a generic RGB-surface. /// public class RGBSurface : AbstractLedGroup, IRGBSurface { #region Properties & Fields private DateTime _lastUpdate; /// public Dictionary Devices { get; } = new Dictionary(); private readonly LinkedList _ledGroups = new LinkedList(); /// public Rectangle SurfaceRectangle => new Rectangle(Devices.Select(x => x.Key.DeviceRectangle)); #endregion #region Events // ReSharper disable EventNeverSubscribedTo.Global /// public event ExceptionEventHandler Exception; /// public event UpdatingEventHandler Updating; /// public event UpdatedEventHandler Updated; // ReSharper restore EventNeverSubscribedTo.Global #endregion #region Constructors /// /// Initializes a new instance of the class. /// public RGBSurface() { _lastUpdate = DateTime.Now; } #endregion #region Methods /// public void Update(bool flushLeds = false) { OnUpdating(); lock (_ledGroups) { // Update effects foreach (ILedGroup ledGroup in _ledGroups) ledGroup.UpdateEffects(); // Render brushes Render(this); foreach (ILedGroup ledGroup in _ledGroups.OrderBy(x => x.ZIndex)) Render(ledGroup); } foreach (IRGBDevice device in Devices.Keys) device.Update(flushLeds); OnUpdated(); } /// /// Renders a ledgroup. /// /// The led group to render. private void Render(ILedGroup ledGroup) { IList leds = ledGroup.GetLeds().ToList(); IBrush brush = ledGroup.Brush; try { switch (brush.BrushCalculationMode) { case BrushCalculationMode.Relative: Rectangle brushRectangle = new Rectangle(leds.Select(x => GetDeviceLedLocation(x))); Point offset = new Point(-brushRectangle.Location.X, -brushRectangle.Location.Y); brushRectangle.Location.X = 0; brushRectangle.Location.Y = 0; brush.PerformRender(brushRectangle, leds.Select(x => new BrushRenderTarget(x, GetDeviceLedLocation(x, offset)))); break; case BrushCalculationMode.Absolute: brush.PerformRender(SurfaceRectangle, leds.Select(x => new BrushRenderTarget(x, GetDeviceLedLocation(x)))); break; default: throw new ArgumentException(); } brush.UpdateEffects(); brush.PerformFinalize(); foreach (KeyValuePair renders in brush.RenderedTargets) renders.Key.Led.Color = renders.Value; } // ReSharper disable once CatchAllClause catch (Exception ex) { OnException(ex); } } private Rectangle GetDeviceLedLocation(Led led, Point extraOffset = null) { Point deviceLocation; if (!Devices.TryGetValue(led.Device, out deviceLocation)) deviceLocation = new Point(); return extraOffset != null ? new Rectangle(led.LedRectangle.Location + deviceLocation + extraOffset, led.LedRectangle.Size) : new Rectangle(led.LedRectangle.Location + deviceLocation, led.LedRectangle.Size); } /// public void PositionDevice(IRGBDevice device, Point location) { if (device == null) return; lock (Devices) Devices[device] = location ?? new Point(); } /// /// Attaches the given . /// /// The to attach. /// true if the could be attached; otherwise, false. public bool AttachLedGroup(ILedGroup ledGroup) { if (ledGroup is IRGBSurface) return false; if (ledGroup == null) return false; lock (_ledGroups) { if (_ledGroups.Contains(ledGroup)) return false; _ledGroups.AddLast(ledGroup); return true; } } /// /// Detaches the given . /// /// The to detached. /// true if the could be detached; otherwise, false. public bool DetachLedGroup(ILedGroup ledGroup) { if (ledGroup is IRGBSurface) return false; if (ledGroup == null) return false; lock (_ledGroups) { LinkedListNode node = _ledGroups.Find(ledGroup); if (node == null) return false; _ledGroups.Remove(node); return true; } } /// public override IEnumerable GetLeds() { return Devices.SelectMany(d => d.Key); } #region EventCaller /// /// Handles the needed event-calls for an exception. /// /// The exception previously thrown. protected virtual void OnException(Exception ex) { try { Exception?.Invoke(this, new ExceptionEventArgs(ex)); } catch { // Well ... that's not my fault } } /// /// Handles the needed event-calls before updating. /// protected virtual void OnUpdating() { try { long lastUpdateTicks = _lastUpdate.Ticks; _lastUpdate = DateTime.Now; Updating?.Invoke(this, new UpdatingEventArgs((DateTime.Now.Ticks - lastUpdateTicks) / 10000000.0)); } catch { // Well ... that's not my fault } } /// /// Handles the needed event-calls after an update. /// protected virtual void OnUpdated() { try { Updated?.Invoke(this, new UpdatedEventArgs()); } catch { // Well ... that's not my fault } } #endregion #endregion } }