1
0
mirror of https://github.com/DarthAffe/RGB.NET.git synced 2025-12-12 17:48:31 +00:00

More basic stuff

This commit is contained in:
Darth Affe 2017-01-22 22:07:33 +01:00
parent d64f019d89
commit 5cbcbd9d9c
15 changed files with 425 additions and 141 deletions

View File

@ -19,12 +19,12 @@ namespace RGB.NET.Core
/// <summary>
/// Gets or sets the overall percentage brightness of the <see cref="IBrush"/>.
/// </summary>
float Brightness { get; set; }
double Brightness { get; set; }
/// <summary>
/// Gets or sets the overall percentage opacity of the <see cref="IBrush"/>.
/// </summary>
float Opacity { get; set; }
double Opacity { get; set; }
/// <summary>
/// Gets a list of <see cref="IColorCorrection"/> used to correct the colors of the <see cref="IBrush"/>.

View File

@ -38,7 +38,7 @@ namespace RGB.NET.Core
Led IRGBDevice.this[Point location] => LedMapping.Values.FirstOrDefault(x => x.LedRectangle.Contains(location));
/// <inheritdoc />
IEnumerable<Led> IRGBDevice.this[Rectangle referenceRect, float minOverlayPercentage]
IEnumerable<Led> IRGBDevice.this[Rectangle referenceRect, double minOverlayPercentage]
=> LedMapping.Values.Where(x => referenceRect.CalculateIntersectPercentage(x.LedRectangle) >= minOverlayPercentage)
;

View File

@ -43,7 +43,7 @@ namespace RGB.NET.Core
/// <param name="referenceRect">The <see cref="Rectangle"/> to check.</param>
/// <param name="minOverlayPercentage">The minimal percentage overlay a <see cref="Led"/> must have with the <see cref="Rectangle" /> to be taken into the list.</param>
/// <returns></returns>
IEnumerable<Led> this[Rectangle referenceRect, float minOverlayPercentage = 0.5f] { get; }
IEnumerable<Led> this[Rectangle referenceRect, double minOverlayPercentage = 0.5] { get; }
#endregion

View File

@ -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
{
/// <summary>
/// Represents an generic effect-target.
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class AbstractEffectTarget<T> : IEffectTarget<T>
where T : IEffectTarget<T>
{
#region Properties & Fields
/// <summary>
/// Gets a list of <see cref="EffectTimeContainer"/> storing the attached effects.
/// </summary>
protected IList<EffectTimeContainer> EffectTimes { get; } = new List<EffectTimeContainer>();
/// <summary>
/// Gets all <see cref="IEffect{T}" /> attached to this <see cref="IEffectTarget{T}"/>.
/// </summary>
protected IList<IEffect<T>> Effects => EffectTimes.Select(x => x.Effect).Cast<IEffect<T>>().ToList();
/// <summary>
/// Gets the strongly-typed target used for the <see cref="IEffect{T}"/>.
/// </summary>
protected abstract T EffectTarget { get; }
#endregion
#region Methods
/// <summary>
/// Updates all <see cref="IEffect"/> added to this <see cref="IEffectTarget{T}"/>.
/// </summary>
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);
}
}
}
/// <summary>
/// Adds an <see cref="IEffect{T}"/>.
/// </summary>
/// <param name="effect">The <see cref="IEffect{T}"/> to add.</param>
public virtual void AddEffect(IEffect<T> 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));
}
/// <summary>
/// Removes an <see cref="IEffect{T}"/>.
/// </summary>
/// <param name="effect">The <see cref="IEffect{T}"/> to remove.</param>
public virtual void RemoveEffect(IEffect<T> effect)
{
EffectTimeContainer effectTimeToRemove = EffectTimes.FirstOrDefault(x => x.Effect == effect);
if (effectTimeToRemove == null) return;
effect.OnDetach(EffectTarget);
EffectTimes.Remove(effectTimeToRemove);
}
#endregion
}
}

View File

@ -0,0 +1,40 @@
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
namespace RGB.NET.Core
{
/// <summary>
/// Represents a wrapped effect with additional time information.
/// </summary>
public class EffectTimeContainer
{
#region Properties & Fields
/// <summary>
/// Gets or sets the wrapped <see cref="IEffect"/>.
/// </summary>
public IEffect Effect { get; }
/// <summary>
/// Gets or sets the tick-count from the last time the <see cref="IEffect"/> was updated.
/// </summary>
public long TicksAtLastUpdate { get; set; }
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="EffectTimeContainer"/> class.
/// </summary>
/// <param name="effect">The wrapped <see cref="IEffect"/>.</param>
/// <param name="ticksAtLastUpdate">The tick-count from the last time the <see cref="IEffect"/> was updated.</param>
public EffectTimeContainer(IEffect effect, long ticksAtLastUpdate)
{
this.Effect = effect;
this.TicksAtLastUpdate = ticksAtLastUpdate;
}
#endregion
}
}

View File

@ -24,7 +24,7 @@ namespace RGB.NET.Core
/// Updates this <see cref="IEffect"/>.
/// </summary>
/// <param name="deltaTime">The elapsed time (in seconds) since the last update.</param>
void Update(float deltaTime);
void Update(double deltaTime);
#endregion
}

View File

@ -1,36 +0,0 @@
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable UnusedAutoPropertyAccessor.Global
using System;
using System.Collections.Generic;
namespace RGB.NET.Core
{
/// <summary>
/// Represents the information supplied with an <see cref="IRGBSurface.LedsUpdated"/>-event.
/// </summary>
public class LedsUpdatedEventArgs : EventArgs
{
#region Properties & Fields
/// <summary>
/// Gets a list of <see cref="Led"/> which got updated.
/// </summary>
public IEnumerable<Led> UpdatedLeds { get; }
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="LedsUpdatedEventArgs"/> class.
/// </summary>
/// <param name="updatedLeds">The updated <see cref="Led"/>.</param>
public LedsUpdatedEventArgs(IEnumerable<Led> updatedLeds)
{
this.UpdatedLeds = updatedLeds;
}
#endregion
}
}

View File

@ -1,36 +0,0 @@
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable UnusedAutoPropertyAccessor.Global
using System;
using System.Collections.Generic;
namespace RGB.NET.Core
{
/// <summary>
/// Represents the information supplied with an <see cref="IRGBSurface.LedsUpdating"/>-event.
/// </summary>
public class LedsUpdatingEventArgs : EventArgs
{
#region Properties & Fields
/// <summary>
/// Gets a list of <see cref="Led"/> which are about to get updated.
/// </summary>
public ICollection<Led> UpdatingLeds { get; }
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="LedsUpdatingEventArgs"/> class.
/// </summary>
/// <param name="updatingLeds">The updating <see cref="Led"/>.</param>
public LedsUpdatingEventArgs(ICollection<Led> updatingLeds)
{
this.UpdatingLeds = updatingLeds;
}
#endregion
}
}

View File

@ -0,0 +1,23 @@
using System;
namespace RGB.NET.Core.Exceptions
{
/// <summary>
/// Represents an exception thrown by an <see cref="IEffect"/>.
/// </summary>
public class EffectException : ApplicationException
{
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="EffectException"/> class.
/// </summary>
/// <param name="message">The message which describes the reason of throwing this exception.</param>
/// <param name="innerException">Optional inner exception, which lead to this exception.</param>
public EffectException(string message, Exception innerException = null)
: base(message, innerException)
{ }
#endregion
}
}

View File

@ -0,0 +1,32 @@
using System.Collections.Generic;
namespace RGB.NET.Core
{
/// <summary>
/// Represents a generic ledgroup.
/// </summary>
public abstract class AbstractLedGroup : AbstractEffectTarget<ILedGroup>, ILedGroup
{
#region Properties & Fields
/// <summary>
/// Gets the strongly-typed target used for the effect.
/// </summary>
protected override ILedGroup EffectTarget => this;
/// <inheritdoc />
public IBrush Brush { get; set; }
/// <inheritdoc />
public int ZIndex { get; set; } = 0;
#endregion
#region Methods
/// <inheritdoc />
public abstract IEnumerable<Led> GetLeds();
#endregion
}
}

View File

@ -0,0 +1,29 @@
// ReSharper disable UnusedMemberInSuper.Global
// ReSharper disable UnusedMember.Global
using System.Collections.Generic;
namespace RGB.NET.Core
{
/// <summary>
/// Represents a generic ledgroup.
/// </summary>
public interface ILedGroup : IEffectTarget<ILedGroup>
{
/// <summary>
/// Gets or sets the <see cref="IBrush"/> which should be drawn over this <see cref="ILedGroup"/>.
/// </summary>
IBrush Brush { get; set; }
/// <summary>
/// Gets or sets the z-index of this <see cref="ILedGroup"/> to allow ordering them before drawing. (lowest first) (default: 0)
/// </summary>
int ZIndex { get; set; }
/// <summary>
/// Gets a list containing all <see cref="Led"/> of this <see cref="ILedGroup"/>.
/// </summary>
/// <returns>The list containing all <see cref="Led"/> of this <see cref="ILedGroup"/>.</returns>
IEnumerable<Led> GetLeds();
}
}

View File

@ -50,14 +50,17 @@
<Compile Include="Devices\DeviceType.cs" />
<Compile Include="Devices\IDeviceProvider.cs" />
<Compile Include="Devices\IRGBDeviceInfo.cs" />
<Compile Include="Effects\AbstractEffectTarget.cs" />
<Compile Include="Effects\EffectTimeContainer.cs" />
<Compile Include="Effects\IEffect.cs" />
<Compile Include="Effects\IEffectTarget.cs" />
<Compile Include="Events\ExceptionEventArgs.cs" />
<Compile Include="Events\LedsUpdatedEventArgs.cs" />
<Compile Include="Events\LedsUpdatingEventArgs.cs" />
<Compile Include="Events\UpdatedEventArgs.cs" />
<Compile Include="Events\UpdatingEventArgs.cs" />
<Compile Include="Exceptions\EffectException.cs" />
<Compile Include="Exceptions\RGBDeviceException.cs" />
<Compile Include="Groups\AbstractLedGroup.cs" />
<Compile Include="Groups\ILedGroup.cs" />
<Compile Include="Leds\ILedId.cs" />
<Compile Include="Surfaces\IRGBSurface.cs" />
<Compile Include="MVVM\AbstractBindable.cs" />

View File

@ -5,6 +5,7 @@
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=effects/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=events/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=extensions/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=groups/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=leds/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=mvvm/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=positioning/@EntryIndexedValue">True</s:Boolean>

View File

@ -1,4 +1,6 @@
namespace RGB.NET.Core
using System.Collections.Generic;
namespace RGB.NET.Core
{
#region EventHandler
@ -24,27 +26,58 @@
/// <param name="args">The arguments provided by the event.</param>
public delegate void UpdatedEventHandler(object sender, UpdatedEventArgs args);
/// <summary>
/// Represents the event-handler of the <see cref="IRGBSurface.LedsUpdating"/>-event.
/// </summary>
/// <param name="sender">The sender of the event.</param>
/// <param name="args">The arguments provided by the event.</param>
public delegate void LedsUpdatingEventHandler(object sender, LedsUpdatingEventArgs args);
/// <summary>
/// Represents the event-handler of the <see cref="IRGBSurface.LedsUpdated"/>-event.
/// </summary>
/// <param name="sender">The sender of the event.</param>
/// <param name="args">The arguments provided by the event.</param>
public delegate void LedsUpdatedEventHandler(object sender, LedsUpdatedEventArgs args);
#endregion
/// <summary>
/// Represents a generic RGB-surface.
/// </summary>
public interface IRGBSurface
public interface IRGBSurface : ILedGroup
{
#region Properties & Fields
/// <summary>
/// Gets a dictionary containing the locations of all <see cref="IRGBDevice"/> positioned on this <see cref="IRGBSurface"/>.
/// </summary>
Dictionary<IRGBDevice, Point> Devices { get; }
/// <summary>
/// Gets a copy of the <see cref="Rectangle"/> representing this <see cref="IRGBSurface"/>.
/// </summary>
Rectangle SurfaceRectangle { get; }
#endregion
#region Methods
/// <summary>
/// Perform an update for all dirty <see cref="Led"/>, or all <see cref="Led"/>, if flushLeds is set to true.
/// </summary>
/// <param name="flushLeds">Specifies whether all <see cref="Led"/>, (including clean ones) should be updated.</param>
void Update(bool flushLeds = false);
/// <summary>
/// Sets the location of the given <see cref="IRGBDevice"/> to the given <see cref="Point"/>.
/// </summary>
/// <param name="device">The <see cref="IRGBDevice"/> to move.</param>
/// <param name="location">The target <see cref="PositionDevice"/>.</param>
void PositionDevice(IRGBDevice device, Point location);
/// <summary>
/// Attaches the given <see cref="ILedGroup"/>.
/// </summary>
/// <param name="ledGroup">The <see cref="ILedGroup"/> to attach.</param>
/// <returns><c>true</c> if the <see cref="ILedGroup"/> could be attached; otherwise, <c>false</c>.</returns>
bool AttachLedGroup(ILedGroup ledGroup);
/// <summary>
/// Detaches the given <see cref="ILedGroup"/>.
/// </summary>
/// <param name="ledGroup">The <see cref="ILedGroup"/> to detached.</param>
/// <returns><c>true</c> if the <see cref="ILedGroup"/> could be detached; otherwise, <c>false</c>.</returns>
bool DetachLedGroup(ILedGroup ledGroup);
#endregion
#region Events
// ReSharper disable EventNeverSubscribedTo.Global
@ -64,16 +97,6 @@
/// </summary>
event UpdatedEventHandler Updated;
/// <summary>
/// Occurs when the <see cref="IRGBSurface"/> starts to update the leds.
/// </summary>
event LedsUpdatingEventHandler LedsUpdating;
/// <summary>
/// Occurs when the <see cref="IRGBSurface"/> updated the leds.
/// </summary>
event LedsUpdatedEventHandler LedsUpdated;
// ReSharper restore EventNeverSubscribedTo.Global
#endregion

View File

@ -1,17 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace RGB.NET.Core
{
/// <summary>
/// Represents a generic RGB-surface.
/// </summary>
public class RGBSurface : IRGBSurface
public class RGBSurface : AbstractLedGroup, IRGBSurface
{
#region Properties & Fields
private DateTime _lastUpdate;
/// <inheritdoc />
public Dictionary<IRGBDevice, Point> Devices { get; } = new Dictionary<IRGBDevice, Point>();
private readonly LinkedList<ILedGroup> _ledGroups = new LinkedList<ILedGroup>();
/// <inheritdoc />
public Rectangle SurfaceRectangle => new Rectangle(Devices.Select(x => x.Key.DeviceRectangle));
#endregion
#region Events
@ -27,12 +36,6 @@ namespace RGB.NET.Core
/// <inheritdoc />
public event UpdatedEventHandler Updated;
/// <inheritdoc />
public event LedsUpdatingEventHandler LedsUpdating;
/// <inheritdoc />
public event LedsUpdatedEventHandler LedsUpdated;
// ReSharper restore EventNeverSubscribedTo.Global
#endregion
@ -49,6 +52,137 @@ namespace RGB.NET.Core
#endregion
#region Methods
/// <inheritdoc />
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();
}
/// <summary>
/// Renders a ledgroup.
/// </summary>
/// <param name="ledGroup">The led group to render.</param>
private void Render(ILedGroup ledGroup)
{
IList<Led> 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<BrushRenderTarget, Color> 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);
}
/// <inheritdoc />
public void PositionDevice(IRGBDevice device, Point location)
{
if (device == null) return;
lock (Devices)
Devices[device] = location ?? new Point();
}
/// <summary>
/// Attaches the given <see cref="ILedGroup"/>.
/// </summary>
/// <param name="ledGroup">The <see cref="ILedGroup"/> to attach.</param>
/// <returns><c>true</c> if the <see cref="ILedGroup"/> could be attached; otherwise, <c>false</c>.</returns>
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;
}
}
/// <summary>
/// Detaches the given <see cref="ILedGroup"/>.
/// </summary>
/// <param name="ledGroup">The <see cref="ILedGroup"/> to detached.</param>
/// <returns><c>true</c> if the <see cref="ILedGroup"/> could be detached; otherwise, <c>false</c>.</returns>
public bool DetachLedGroup(ILedGroup ledGroup)
{
if (ledGroup is IRGBSurface) return false;
if (ledGroup == null) return false;
lock (_ledGroups)
{
LinkedListNode<ILedGroup> node = _ledGroups.Find(ledGroup);
if (node == null) return false;
_ledGroups.Remove(node);
return true;
}
}
/// <inheritdoc />
public override IEnumerable<Led> GetLeds()
{
return Devices.SelectMany(d => d.Key);
}
#region EventCaller
/// <summary>
@ -99,35 +233,7 @@ namespace RGB.NET.Core
}
}
/// <summary>
/// Handles the needed event-calls before the <see cref="Led"/> are updated.
/// </summary>
protected virtual void OnLedsUpdating(ICollection<Led> updatingLeds)
{
try
{
LedsUpdating?.Invoke(this, new LedsUpdatingEventArgs(updatingLeds));
}
catch
{
// Well ... that's not my fault
}
}
/// <summary>
/// Handles the needed event-calls after the <see cref="Led"/> are updated.
/// </summary>
protected virtual void OnLedsUpdated(IEnumerable<Led> updatedLeds)
{
try
{
LedsUpdated?.Invoke(this, new LedsUpdatedEventArgs(updatedLeds));
}
catch
{
// Well ... that's not my fault
}
}
#endregion
#endregion
}