diff --git a/RGB.NET.Core/Brushes/IBrush.cs b/RGB.NET.Core/Brushes/IBrush.cs
index db0b9e6..b4beac8 100644
--- a/RGB.NET.Core/Brushes/IBrush.cs
+++ b/RGB.NET.Core/Brushes/IBrush.cs
@@ -19,12 +19,12 @@ namespace RGB.NET.Core
///
/// Gets or sets the overall percentage brightness of the .
///
- float Brightness { get; set; }
+ double Brightness { get; set; }
///
/// Gets or sets the overall percentage opacity of the .
///
- float Opacity { get; set; }
+ double Opacity { get; set; }
///
/// Gets a list of used to correct the colors of the .
diff --git a/RGB.NET.Core/Devices/AbstractRGBDevice.cs b/RGB.NET.Core/Devices/AbstractRGBDevice.cs
index 125f0ef..1d8958e 100644
--- a/RGB.NET.Core/Devices/AbstractRGBDevice.cs
+++ b/RGB.NET.Core/Devices/AbstractRGBDevice.cs
@@ -38,7 +38,7 @@ namespace RGB.NET.Core
Led IRGBDevice.this[Point location] => LedMapping.Values.FirstOrDefault(x => x.LedRectangle.Contains(location));
///
- IEnumerable IRGBDevice.this[Rectangle referenceRect, float minOverlayPercentage]
+ IEnumerable IRGBDevice.this[Rectangle referenceRect, double minOverlayPercentage]
=> LedMapping.Values.Where(x => referenceRect.CalculateIntersectPercentage(x.LedRectangle) >= minOverlayPercentage)
;
diff --git a/RGB.NET.Core/Devices/IRGBDevice.cs b/RGB.NET.Core/Devices/IRGBDevice.cs
index 10030da..7422dc1 100644
--- a/RGB.NET.Core/Devices/IRGBDevice.cs
+++ b/RGB.NET.Core/Devices/IRGBDevice.cs
@@ -43,7 +43,7 @@ namespace RGB.NET.Core
/// The to check.
/// The minimal percentage overlay a must have with the to be taken into the list.
///
- IEnumerable this[Rectangle referenceRect, float minOverlayPercentage = 0.5f] { get; }
+ IEnumerable this[Rectangle referenceRect, double minOverlayPercentage = 0.5] { get; }
#endregion
diff --git a/RGB.NET.Core/Effects/AbstractEffectTarget.cs b/RGB.NET.Core/Effects/AbstractEffectTarget.cs
new file mode 100644
index 0000000..cc65cd7
--- /dev/null
+++ b/RGB.NET.Core/Effects/AbstractEffectTarget.cs
@@ -0,0 +1,99 @@
+// ReSharper disable MemberCanBePrivate.Global
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using RGB.NET.Core.Exceptions;
+
+namespace RGB.NET.Core
+{
+ ///
+ /// Represents an generic effect-target.
+ ///
+ ///
+ public abstract class AbstractEffectTarget : IEffectTarget
+ where T : IEffectTarget
+ {
+ #region Properties & Fields
+
+ ///
+ /// Gets a list of storing the attached effects.
+ ///
+ protected IList EffectTimes { get; } = new List();
+
+ ///
+ /// Gets all attached to this .
+ ///
+ protected IList> Effects => EffectTimes.Select(x => x.Effect).Cast>().ToList();
+
+ ///
+ /// Gets the strongly-typed target used for the .
+ ///
+ protected abstract T EffectTarget { get; }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Updates all added to this .
+ ///
+ public virtual void UpdateEffects()
+ {
+ lock (Effects)
+ {
+ for (int i = EffectTimes.Count - 1; i >= 0; i--)
+ {
+ EffectTimeContainer effectTime = EffectTimes[i];
+ long currentTicks = DateTime.Now.Ticks;
+
+ double deltaTime;
+ if (effectTime.TicksAtLastUpdate < 0)
+ {
+ effectTime.TicksAtLastUpdate = currentTicks;
+ deltaTime = 0;
+ }
+ else
+ deltaTime = (currentTicks - effectTime.TicksAtLastUpdate) / 10000000.0;
+
+ effectTime.TicksAtLastUpdate = currentTicks;
+ effectTime.Effect.Update(deltaTime);
+
+ if (effectTime.Effect.IsDone)
+ EffectTimes.RemoveAt(i);
+ }
+ }
+ }
+
+ ///
+ /// Adds an .
+ ///
+ /// The to add.
+ public virtual void AddEffect(IEffect effect)
+ {
+ if (EffectTimes.Any(x => x.Effect == effect)) return;
+
+ if (!effect.CanBeAppliedTo(EffectTarget))
+ throw new EffectException($"Failed to add effect.\r\n" +
+ $"The effect of type '{effect.GetType()}' can't be applied to the target of type '{EffectTarget.GetType()}'.");
+
+ effect.OnAttach(EffectTarget);
+ EffectTimes.Add(new EffectTimeContainer(effect, -1));
+ }
+
+ ///
+ /// Removes an .
+ ///
+ /// The to remove.
+ public virtual void RemoveEffect(IEffect effect)
+ {
+ EffectTimeContainer effectTimeToRemove = EffectTimes.FirstOrDefault(x => x.Effect == effect);
+ if (effectTimeToRemove == null) return;
+
+ effect.OnDetach(EffectTarget);
+ EffectTimes.Remove(effectTimeToRemove);
+ }
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Core/Effects/EffectTimeContainer.cs b/RGB.NET.Core/Effects/EffectTimeContainer.cs
new file mode 100644
index 0000000..35d2991
--- /dev/null
+++ b/RGB.NET.Core/Effects/EffectTimeContainer.cs
@@ -0,0 +1,40 @@
+// ReSharper disable MemberCanBePrivate.Global
+// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
+
+namespace RGB.NET.Core
+{
+ ///
+ /// Represents a wrapped effect with additional time information.
+ ///
+ public class EffectTimeContainer
+ {
+ #region Properties & Fields
+
+ ///
+ /// Gets or sets the wrapped .
+ ///
+ public IEffect Effect { get; }
+
+ ///
+ /// Gets or sets the tick-count from the last time the was updated.
+ ///
+ public long TicksAtLastUpdate { get; set; }
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The wrapped .
+ /// The tick-count from the last time the was updated.
+ public EffectTimeContainer(IEffect effect, long ticksAtLastUpdate)
+ {
+ this.Effect = effect;
+ this.TicksAtLastUpdate = ticksAtLastUpdate;
+ }
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Core/Effects/IEffect.cs b/RGB.NET.Core/Effects/IEffect.cs
index 17129fa..165f81f 100644
--- a/RGB.NET.Core/Effects/IEffect.cs
+++ b/RGB.NET.Core/Effects/IEffect.cs
@@ -24,7 +24,7 @@ namespace RGB.NET.Core
/// Updates this .
///
/// The elapsed time (in seconds) since the last update.
- void Update(float deltaTime);
+ void Update(double deltaTime);
#endregion
}
diff --git a/RGB.NET.Core/Events/LedsUpdatedEventArgs.cs b/RGB.NET.Core/Events/LedsUpdatedEventArgs.cs
deleted file mode 100644
index 53a8a26..0000000
--- a/RGB.NET.Core/Events/LedsUpdatedEventArgs.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-// ReSharper disable MemberCanBePrivate.Global
-// ReSharper disable UnusedAutoPropertyAccessor.Global
-
-using System;
-using System.Collections.Generic;
-
-namespace RGB.NET.Core
-{
- ///
- /// Represents the information supplied with an -event.
- ///
- public class LedsUpdatedEventArgs : EventArgs
- {
- #region Properties & Fields
-
- ///
- /// Gets a list of which got updated.
- ///
- public IEnumerable UpdatedLeds { get; }
-
- #endregion
-
- #region Constructors
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The updated .
- public LedsUpdatedEventArgs(IEnumerable updatedLeds)
- {
- this.UpdatedLeds = updatedLeds;
- }
-
- #endregion
- }
-}
diff --git a/RGB.NET.Core/Events/LedsUpdatingEventArgs.cs b/RGB.NET.Core/Events/LedsUpdatingEventArgs.cs
deleted file mode 100644
index 4ddbe93..0000000
--- a/RGB.NET.Core/Events/LedsUpdatingEventArgs.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-// ReSharper disable MemberCanBePrivate.Global
-// ReSharper disable UnusedAutoPropertyAccessor.Global
-
-using System;
-using System.Collections.Generic;
-
-namespace RGB.NET.Core
-{
- ///
- /// Represents the information supplied with an -event.
- ///
- public class LedsUpdatingEventArgs : EventArgs
- {
- #region Properties & Fields
-
- ///
- /// Gets a list of which are about to get updated.
- ///
- public ICollection UpdatingLeds { get; }
-
- #endregion
-
- #region Constructors
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The updating .
- public LedsUpdatingEventArgs(ICollection updatingLeds)
- {
- this.UpdatingLeds = updatingLeds;
- }
-
- #endregion
- }
-}
diff --git a/RGB.NET.Core/Exceptions/EffectException.cs b/RGB.NET.Core/Exceptions/EffectException.cs
new file mode 100644
index 0000000..46d3280
--- /dev/null
+++ b/RGB.NET.Core/Exceptions/EffectException.cs
@@ -0,0 +1,23 @@
+using System;
+
+namespace RGB.NET.Core.Exceptions
+{
+ ///
+ /// Represents an exception thrown by an .
+ ///
+ public class EffectException : ApplicationException
+ {
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The message which describes the reason of throwing this exception.
+ /// Optional inner exception, which lead to this exception.
+ public EffectException(string message, Exception innerException = null)
+ : base(message, innerException)
+ { }
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Core/Groups/AbstractLedGroup.cs b/RGB.NET.Core/Groups/AbstractLedGroup.cs
new file mode 100644
index 0000000..f00cc0a
--- /dev/null
+++ b/RGB.NET.Core/Groups/AbstractLedGroup.cs
@@ -0,0 +1,32 @@
+using System.Collections.Generic;
+
+namespace RGB.NET.Core
+{
+ ///
+ /// Represents a generic ledgroup.
+ ///
+ public abstract class AbstractLedGroup : AbstractEffectTarget, ILedGroup
+ {
+ #region Properties & Fields
+
+ ///
+ /// Gets the strongly-typed target used for the effect.
+ ///
+ protected override ILedGroup EffectTarget => this;
+
+ ///
+ public IBrush Brush { get; set; }
+
+ ///
+ public int ZIndex { get; set; } = 0;
+
+ #endregion
+
+ #region Methods
+
+ ///
+ public abstract IEnumerable GetLeds();
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Core/Groups/ILedGroup.cs b/RGB.NET.Core/Groups/ILedGroup.cs
new file mode 100644
index 0000000..70d1eaa
--- /dev/null
+++ b/RGB.NET.Core/Groups/ILedGroup.cs
@@ -0,0 +1,29 @@
+// ReSharper disable UnusedMemberInSuper.Global
+// ReSharper disable UnusedMember.Global
+
+using System.Collections.Generic;
+
+namespace RGB.NET.Core
+{
+ ///
+ /// Represents a generic ledgroup.
+ ///
+ public interface ILedGroup : IEffectTarget
+ {
+ ///
+ /// Gets or sets the which should be drawn over this .
+ ///
+ IBrush Brush { get; set; }
+
+ ///
+ /// Gets or sets the z-index of this to allow ordering them before drawing. (lowest first) (default: 0)
+ ///
+ int ZIndex { get; set; }
+
+ ///
+ /// Gets a list containing all of this .
+ ///
+ /// The list containing all of this .
+ IEnumerable GetLeds();
+ }
+}
diff --git a/RGB.NET.Core/RGB.NET.Core.csproj b/RGB.NET.Core/RGB.NET.Core.csproj
index 6f069ce..7bee7ae 100644
--- a/RGB.NET.Core/RGB.NET.Core.csproj
+++ b/RGB.NET.Core/RGB.NET.Core.csproj
@@ -50,14 +50,17 @@
+
+
-
-
+
+
+
diff --git a/RGB.NET.Core/RGB.NET.Core.csproj.DotSettings b/RGB.NET.Core/RGB.NET.Core.csproj.DotSettings
index 7ec5688..4c65387 100644
--- a/RGB.NET.Core/RGB.NET.Core.csproj.DotSettings
+++ b/RGB.NET.Core/RGB.NET.Core.csproj.DotSettings
@@ -5,6 +5,7 @@
True
True
True
+ True
True
True
True
diff --git a/RGB.NET.Core/Surfaces/IRGBSurface.cs b/RGB.NET.Core/Surfaces/IRGBSurface.cs
index 042f796..8810b10 100644
--- a/RGB.NET.Core/Surfaces/IRGBSurface.cs
+++ b/RGB.NET.Core/Surfaces/IRGBSurface.cs
@@ -1,4 +1,6 @@
-namespace RGB.NET.Core
+using System.Collections.Generic;
+
+namespace RGB.NET.Core
{
#region EventHandler
@@ -24,27 +26,58 @@
/// The arguments provided by the event.
public delegate void UpdatedEventHandler(object sender, UpdatedEventArgs args);
- ///
- /// Represents the event-handler of the -event.
- ///
- /// The sender of the event.
- /// The arguments provided by the event.
- public delegate void LedsUpdatingEventHandler(object sender, LedsUpdatingEventArgs args);
-
- ///
- /// Represents the event-handler of the -event.
- ///
- /// The sender of the event.
- /// The arguments provided by the event.
- public delegate void LedsUpdatedEventHandler(object sender, LedsUpdatedEventArgs args);
-
#endregion
///
/// Represents a generic RGB-surface.
///
- public interface IRGBSurface
+ public interface IRGBSurface : ILedGroup
{
+ #region Properties & Fields
+
+ ///
+ /// Gets a dictionary containing the locations of all positioned on this .
+ ///
+ Dictionary Devices { get; }
+
+ ///
+ /// Gets a copy of the representing this .
+ ///
+ Rectangle SurfaceRectangle { get; }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Perform an update for all dirty , or all , if flushLeds is set to true.
+ ///
+ /// Specifies whether all , (including clean ones) should be updated.
+ void Update(bool flushLeds = false);
+
+ ///
+ /// Sets the location of the given to the given .
+ ///
+ /// The to move.
+ /// The target .
+ void PositionDevice(IRGBDevice device, Point location);
+
+ ///
+ /// Attaches the given .
+ ///
+ /// The to attach.
+ /// true if the could be attached; otherwise, false.
+ bool AttachLedGroup(ILedGroup ledGroup);
+
+ ///
+ /// Detaches the given .
+ ///
+ /// The to detached.
+ /// true if the could be detached; otherwise, false.
+ bool DetachLedGroup(ILedGroup ledGroup);
+
+ #endregion
+
#region Events
// ReSharper disable EventNeverSubscribedTo.Global
@@ -64,16 +97,6 @@
///
event UpdatedEventHandler Updated;
- ///
- /// Occurs when the starts to update the leds.
- ///
- event LedsUpdatingEventHandler LedsUpdating;
-
- ///
- /// Occurs when the updated the leds.
- ///
- event LedsUpdatedEventHandler LedsUpdated;
-
// ReSharper restore EventNeverSubscribedTo.Global
#endregion
diff --git a/RGB.NET.Core/Surfaces/RGBSurface.cs b/RGB.NET.Core/Surfaces/RGBSurface.cs
index 0b182f5..fbbf712 100644
--- a/RGB.NET.Core/Surfaces/RGBSurface.cs
+++ b/RGB.NET.Core/Surfaces/RGBSurface.cs
@@ -1,17 +1,26 @@
using System;
using System.Collections.Generic;
+using System.Linq;
namespace RGB.NET.Core
{
///
/// Represents a generic RGB-surface.
///
- public class RGBSurface : IRGBSurface
+ 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
@@ -27,12 +36,6 @@ namespace RGB.NET.Core
///
public event UpdatedEventHandler Updated;
- ///
- public event LedsUpdatingEventHandler LedsUpdating;
-
- ///
- public event LedsUpdatedEventHandler LedsUpdated;
-
// ReSharper restore EventNeverSubscribedTo.Global
#endregion
@@ -49,6 +52,137 @@ namespace RGB.NET.Core
#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
///
@@ -99,35 +233,7 @@ namespace RGB.NET.Core
}
}
- ///
- /// Handles the needed event-calls before the are updated.
- ///
- protected virtual void OnLedsUpdating(ICollection updatingLeds)
- {
- try
- {
- LedsUpdating?.Invoke(this, new LedsUpdatingEventArgs(updatingLeds));
- }
- catch
- {
- // Well ... that's not my fault
- }
- }
-
- ///
- /// Handles the needed event-calls after the are updated.
- ///
- protected virtual void OnLedsUpdated(IEnumerable updatedLeds)
- {
- try
- {
- LedsUpdated?.Invoke(this, new LedsUpdatedEventArgs(updatedLeds));
- }
- catch
- {
- // Well ... that's not my fault
- }
- }
+ #endregion
#endregion
}