diff --git a/src/Artemis.Core/Models/Profile/Colors/ColorGradient.cs b/src/Artemis.Core/Models/Profile/Colors/ColorGradient.cs index eeb5c3938..060c2b0e6 100644 --- a/src/Artemis.Core/Models/Profile/Colors/ColorGradient.cs +++ b/src/Artemis.Core/Models/Profile/Colors/ColorGradient.cs @@ -13,7 +13,7 @@ namespace Artemis.Core; /// public class ColorGradient : IList, IList, INotifyCollectionChanged { - private static readonly SKColor[] FastLedRainbow = + private static readonly SKColor[] FAST_LED_RAINBOW = { new(0xFFFF0000), // Red new(0xFFFF9900), // Orange @@ -27,8 +27,25 @@ public class ColorGradient : IList, IList, INotifyCollectionC }; private readonly List _stops; + private SKColor[] _colors = Array.Empty(); + private float[] _positions = Array.Empty(); + private bool _dirty = true; private bool _updating; + /// + /// Gets an array containing the colors of the color gradient. + /// + /// Note: Making changes to this array will not be reflected on the gradient, is is essentially read-only. + /// + public SKColor[] Colors => GetColors(); + + /// + /// Gets an array containing the positions of colors of the color gradient. + /// + /// Note: Making changes to this array will not be reflected on the gradient, is is essentially read-only. + /// + public float[] Positions => GetPositions(); + /// /// Creates a new instance of the class /// @@ -81,8 +98,12 @@ public class ColorGradient : IList, IList, INotifyCollectionC /// last color /// /// An array containing each color in the gradient + [Obsolete("Use the Colors property instead", true)] public SKColor[] GetColorsArray(int timesToRepeat = 0, bool seamless = false) { + if (timesToRepeat == 0 && !seamless) + return Colors; + List result = new(); if (timesToRepeat == 0) result = this.Select(c => c.Color).ToList(); @@ -105,8 +126,12 @@ public class ColorGradient : IList, IList, INotifyCollectionC /// last color /// /// An array containing a position for each color between 0.0 and 1.0 + [Obsolete("Use the Positions property instead", true)] public float[] GetPositionsArray(int timesToRepeat = 0, bool seamless = false) { + if (timesToRepeat == 0 && seamless) + return Positions; + List result = new(); if (timesToRepeat == 0) { @@ -147,8 +172,12 @@ public class ColorGradient : IList, IList, INotifyCollectionC /// A boolean indicating whether to make the gradient seamless by adding the first color behind the /// last color /// + [Obsolete("Use GetColor(float position) instead.", true)] public SKColor GetColor(float position, int timesToRepeat = 0, bool seamless = false) { + if (timesToRepeat == 0 && !seamless) + return GetColor(position); + if (!this.Any()) return new SKColor(255, 255, 255); @@ -194,30 +223,38 @@ public class ColorGradient : IList, IList, INotifyCollectionC } /// - /// Gets a new ColorGradient with colors looping through the HSV-spectrum + /// Gets a color at any position between 0.0 and 1.0 using interpolation /// - public static ColorGradient GetUnicornBarf() + /// A position between 0.0 and 1.0 + public SKColor GetColor(float position) { - ColorGradient gradient = new(); - for (int index = 0; index < FastLedRainbow.Length; index++) - { - SKColor skColor = FastLedRainbow[index]; - float position = 1f / (FastLedRainbow.Length - 1f) * index; - gradient.Add(new ColorGradientStop(skColor, position)); - } + if (_stops.Count == 0) + return SKColors.Transparent; - return gradient; - } + if (_stops.Count == 1) + return _stops[0].Color; - /// - /// Gets a new ColorGradient with random colors from the HSV-spectrum - /// - /// The amount of stops to add - public ColorGradient GetRandom(int stops) - { - ColorGradient gradient = new(); - gradient.Randomize(stops); - return gradient; + if (position <= 0) + return _stops[0].Color; + + if (position >= 1) + return _stops[^1].Color; + + //find the first stop after the position + int stop2Index = _stops.FindIndex(s => s.Position >= position); + //if the position is before the first stop, return that color + if (stop2Index == 0) + return _stops[0].Color; + + //interpolate between that one and the one before + int stop1Index = stop2Index - 1; + + ColorGradientStop stop1 = _stops[stop1Index]; + ColorGradientStop stop2 = _stops[stop2Index]; + + //calculate how far between the 2 stops we want to interpolate + float positionBetween = (position - stop1.Position) / (stop2.Position - stop1.Position); + return stop1.Color.Interpolate(stop2.Color, positionBetween); } /// @@ -243,7 +280,7 @@ public class ColorGradient : IList, IList, INotifyCollectionC finally { _updating = false; - Sort(); + Update(); } } @@ -289,7 +326,7 @@ public class ColorGradient : IList, IList, INotifyCollectionC finally { _updating = false; - Sort(); + Update(); } } @@ -307,7 +344,7 @@ public class ColorGradient : IList, IList, INotifyCollectionC finally { _updating = false; - Sort(); + Update(); } } @@ -332,7 +369,7 @@ public class ColorGradient : IList, IList, INotifyCollectionC finally { _updating = false; - Sort(); + Update(); } } @@ -358,17 +395,96 @@ public class ColorGradient : IList, IList, INotifyCollectionC finally { _updating = false; - Sort(); + Update(); } } + + /// + /// Interpolates a color gradient between the this gradient and the provided . + /// + /// The second color gradient. + /// A value between 0 and 1. + /// The interpolated color gradient. + public ColorGradient Interpolate(ColorGradient targetValue, float progress) + { + ColorGradient interpolated = new(this); + + // Add new stops + if (targetValue.Count > interpolated.Count) + { + // Prefer the stops on a vacant position + foreach (ColorGradientStop stop in targetValue.Take(targetValue.Count - interpolated.Count)) + interpolated.Add(new ColorGradientStop(GetColor(stop.Position), stop.Position)); + } + + // Interpolate stops + int index = 0; + foreach (ColorGradientStop stop in interpolated.ToList()) + { + if (index < targetValue.Count) + { + ColorGradientStop targetStop = targetValue[index]; + stop.Interpolate(targetStop, progress); + } + // Interpolate stops not on the target gradient + else + { + stop.Color = stop.Color.Interpolate(targetValue.GetColor(stop.Position), progress); + } + + index++; + } + + return interpolated; + } + + /// + /// Gets a new ColorGradient with colors looping through the HSV-spectrum + /// + public static ColorGradient GetUnicornBarf() + { + ColorGradient gradient = new(); + for (int index = 0; index < FAST_LED_RAINBOW.Length; index++) + { + SKColor skColor = FAST_LED_RAINBOW[index]; + float position = 1f / (FAST_LED_RAINBOW.Length - 1f) * index; + gradient.Add(new ColorGradientStop(skColor, position)); + } + + return gradient; + } + + /// + /// Gets a new ColorGradient with random colors from the HSV-spectrum + /// + /// The amount of stops to add + public static ColorGradient GetRandom(int stops) + { + ColorGradient gradient = new(); + gradient.Randomize(stops); + return gradient; + } /// /// Occurs when any of the stops has changed in some way /// public event EventHandler? StopChanged; - internal void Sort() + private void ItemOnPropertyChanged(object? sender, PropertyChangedEventArgs e) { + Update(); + OnStopChanged(); + } + + private void OnStopChanged() + { + StopChanged?.Invoke(this, EventArgs.Empty); + } + + private void Update() + { + _dirty = true; + if (_updating) return; @@ -387,15 +503,24 @@ public class ColorGradient : IList, IList, INotifyCollectionC } } - private void ItemOnPropertyChanged(object? sender, PropertyChangedEventArgs e) + private float[] GetPositions() { - Sort(); - OnStopChanged(); + if (!_dirty) + return _positions; + + _colors = this.Select(s => s.Color).ToArray(); + _positions = this.Select(s => s.Position).ToArray(); + return _positions; } - private void OnStopChanged() + private SKColor[] GetColors() { - StopChanged?.Invoke(this, EventArgs.Empty); + if (!_dirty) + return _colors; + + _colors = this.Select(s => s.Color).ToArray(); + _positions = this.Select(s => s.Position).ToArray(); + return _colors; } #region Equality members @@ -473,7 +598,7 @@ public class ColorGradient : IList, IList, INotifyCollectionC item.PropertyChanged += ItemOnPropertyChanged; OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, _stops.IndexOf(item))); - Sort(); + Update(); } /// @@ -583,7 +708,7 @@ public class ColorGradient : IList, IList, INotifyCollectionC { _stops.Insert(index, item); item.PropertyChanged += ItemOnPropertyChanged; - Sort(); + Update(); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, _stops.IndexOf(item))); } @@ -608,7 +733,7 @@ public class ColorGradient : IList, IList, INotifyCollectionC oldValue.PropertyChanged -= ItemOnPropertyChanged; _stops[index] = value; _stops[index].PropertyChanged += ItemOnPropertyChanged; - Sort(); + Update(); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, oldValue)); } } @@ -626,36 +751,4 @@ public class ColorGradient : IList, IList, INotifyCollectionC } #endregion - - public ColorGradient Interpolate(ColorGradient targetValue, float progress) - { - ColorGradient interpolated = new(this); - - // Add new stops - if (targetValue.Count > interpolated.Count) - { - // Prefer the stops on a vacant position - foreach (ColorGradientStop stop in targetValue.Take(targetValue.Count - interpolated.Count)) - interpolated.Add(new ColorGradientStop(GetColor(stop.Position), stop.Position)); - } - // Interpolate stops - int index = 0; - foreach (ColorGradientStop stop in interpolated.ToList()) - { - if (index < targetValue.Count) - { - ColorGradientStop targetStop = targetValue[index]; - stop.Interpolate(targetStop, progress); - } - // Interpolate stops not on the target gradient - else - { - stop.Color = stop.Color.Interpolate(targetValue.GetColor(stop.Position), progress); - } - - index++; - } - - return interpolated; - } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/Colors/ColorGradientStop.cs b/src/Artemis.Core/Models/Profile/Colors/ColorGradientStop.cs index d38fe4598..ff692afc0 100644 --- a/src/Artemis.Core/Models/Profile/Colors/ColorGradientStop.cs +++ b/src/Artemis.Core/Models/Profile/Colors/ColorGradientStop.cs @@ -94,11 +94,17 @@ public class ColorGradientStop : CorePropertyChanged return stopPosition; } - #endregion - + /// + /// Interpolates a color gradient stop between the this stop and the provided . + /// + /// The second stop. + /// A value between 0 and 1. + /// The interpolated color gradient stop. public void Interpolate(ColorGradientStop targetValue, float progress) { Color = Color.Interpolate(targetValue.Color, progress); - Position = Position + ((targetValue.Position - Position) * progress); + Position += (targetValue.Position - Position) * progress; } + + #endregion } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs index ad2528865..02b75ce9b 100644 --- a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs +++ b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs @@ -273,7 +273,7 @@ public abstract class RenderProfileElement : ProfileElement // If no descriptor was found and there was no existing placeholder, create a placeholder else { - descriptor = PlaceholderLayerEffectDescriptor.Create(layerEffectEntity.ProviderId); + descriptor = PlaceholderLayerEffectDescriptor.Create(); layerEffect = descriptor.CreateInstance(this, layerEffectEntity); } @@ -286,7 +286,7 @@ public abstract class RenderProfileElement : ProfileElement if (index == -1) return; - LayerEffectDescriptor descriptor = PlaceholderLayerEffectDescriptor.Create(layerEffect.ProviderId); + LayerEffectDescriptor descriptor = PlaceholderLayerEffectDescriptor.Create(); BaseLayerEffect placeholder = descriptor.CreateInstance(this, layerEffect.LayerEffectEntity); _layerEffects[index] = placeholder; layerEffect.Dispose(); @@ -299,7 +299,7 @@ public abstract class RenderProfileElement : ProfileElement if (index == -1) return; - LayerEffectDescriptor? descriptor = LayerEffectStore.Get(placeholder.OriginalEntity.ProviderId, placeholder.PlaceholderFor)?.LayerEffectDescriptor; + LayerEffectDescriptor? descriptor = LayerEffectStore.Get(placeholder.OriginalEntity.ProviderId, placeholder.OriginalEntity.EffectType)?.LayerEffectDescriptor; if (descriptor == null) throw new ArtemisCoreException("Can't replace a placeholder effect because the real effect isn't available."); @@ -324,7 +324,7 @@ public abstract class RenderProfileElement : ProfileElement private void LayerEffectStoreOnLayerEffectRemoved(object? sender, LayerEffectStoreEvent e) { // Find effects that just got disabled and replace them with placeholders - List affectedLayerEffects = _layerEffects.Where(ef => ef.ProviderId == e.Registration.PluginFeature.Id).ToList(); + List affectedLayerEffects = _layerEffects.Where(e.Registration.Matches).ToList(); if (!affectedLayerEffects.Any()) return; @@ -338,7 +338,7 @@ public abstract class RenderProfileElement : ProfileElement { // Find placeholders that just got enabled and replace them with real effects List affectedPlaceholders = LayerEffects - .Where(l => l is PlaceholderLayerEffect ph && ph.OriginalEntity.ProviderId == e.Registration.PluginFeature.Id) + .Where(l => l is PlaceholderLayerEffect ph && e.Registration.Matches(ph)) .Cast() .ToList(); diff --git a/src/Artemis.Core/Plugins/LayerEffects/Internal/BaseLayerEffect.cs b/src/Artemis.Core/Plugins/LayerEffects/Internal/BaseLayerEffect.cs index d574c2e08..94cc830ba 100644 --- a/src/Artemis.Core/Plugins/LayerEffects/Internal/BaseLayerEffect.cs +++ b/src/Artemis.Core/Plugins/LayerEffects/Internal/BaseLayerEffect.cs @@ -164,12 +164,7 @@ public abstract class BaseLayerEffect : BreakableModel, IDisposable, IStorageMod // Not only is this needed to initialize properties on the layer effects, it also prevents implementing anything // but LayerEffect outside the core internal abstract void Initialize(); - - internal virtual string GetEffectTypeName() - { - return GetType().Name; - } - + internal void InternalUpdate(Timeline timeline) { BaseProperties?.Update(timeline); @@ -220,20 +215,23 @@ public abstract class BaseLayerEffect : BreakableModel, IDisposable, IStorageMod /// public void Load() { - HasBeenRenamed = LayerEffectEntity.HasBeenRenamed; Name = HasBeenRenamed ? LayerEffectEntity.Name : Descriptor.DisplayName; + HasBeenRenamed = LayerEffectEntity.HasBeenRenamed; Order = LayerEffectEntity.Order; } /// public void Save() { - LayerEffectEntity.ProviderId = Descriptor.Provider.Id; - LayerEffectEntity.EffectType = GetType().FullName; LayerEffectEntity.Name = Name; LayerEffectEntity.HasBeenRenamed = HasBeenRenamed; LayerEffectEntity.Order = Order; + if (Descriptor.IsPlaceholder) + return; + + LayerEffectEntity.ProviderId = Descriptor.Provider.Id; + LayerEffectEntity.EffectType = GetType().FullName; BaseProperties?.ApplyToEntity(); LayerEffectEntity.PropertyGroup = BaseProperties?.PropertyGroupEntity; } diff --git a/src/Artemis.Core/Plugins/LayerEffects/LayerEffectDescriptor.cs b/src/Artemis.Core/Plugins/LayerEffects/LayerEffectDescriptor.cs index 5c605de8d..196461c45 100644 --- a/src/Artemis.Core/Plugins/LayerEffects/LayerEffectDescriptor.cs +++ b/src/Artemis.Core/Plugins/LayerEffects/LayerEffectDescriptor.cs @@ -19,9 +19,9 @@ public class LayerEffectDescriptor Provider = provider ?? throw new ArgumentNullException(nameof(provider)); } - internal LayerEffectDescriptor(string placeholderFor, LayerEffectProvider provider) + private LayerEffectDescriptor(LayerEffectProvider provider) { - PlaceholderFor = placeholderFor ?? throw new ArgumentNullException(nameof(placeholderFor)); + IsPlaceholder = true; Provider = provider ?? throw new ArgumentNullException(nameof(provider)); DisplayName = "Missing effect"; Description = "This effect could not be loaded"; @@ -55,16 +55,21 @@ public class LayerEffectDescriptor public LayerEffectProvider Provider { get; } /// - /// Gets the GUID this descriptor is acting as a placeholder for. If null, this descriptor is not a placeholder + /// Gets a boolean indicating whether this descriptor is a placeholder descriptor. /// - public string? PlaceholderFor { get; } + public bool IsPlaceholder { get; } + + internal static LayerEffectDescriptor CreatePlaceholder(LayerEffectProvider provider) + { + return new LayerEffectDescriptor(provider); + } /// /// Creates an instance of the described effect and applies it to the render element /// public BaseLayerEffect CreateInstance(RenderProfileElement renderElement, LayerEffectEntity? entity) { - if (PlaceholderFor != null) + if (IsPlaceholder) { if (entity == null) throw new ArtemisCoreException("Cannot create a placeholder for a layer effect that wasn't loaded from an entity"); @@ -97,10 +102,10 @@ public class LayerEffectDescriptor private BaseLayerEffect CreatePlaceHolderInstance(RenderProfileElement renderElement, LayerEffectEntity entity) { - if (PlaceholderFor == null) + if (!IsPlaceholder) throw new ArtemisCoreException("Cannot create a placeholder instance using a layer effect descriptor that is not a placeholder for anything"); - PlaceholderLayerEffect effect = new(entity, PlaceholderFor) + PlaceholderLayerEffect effect = new(entity) { ProfileElement = renderElement, Descriptor = this diff --git a/src/Artemis.Core/Plugins/LayerEffects/Placeholder/PlaceholderLayerEffect.cs b/src/Artemis.Core/Plugins/LayerEffects/Placeholder/PlaceholderLayerEffect.cs index 5ccbdd05b..1f5752bc9 100644 --- a/src/Artemis.Core/Plugins/LayerEffects/Placeholder/PlaceholderLayerEffect.cs +++ b/src/Artemis.Core/Plugins/LayerEffects/Placeholder/PlaceholderLayerEffect.cs @@ -8,10 +8,9 @@ namespace Artemis.Core.LayerEffects.Placeholder; /// internal class PlaceholderLayerEffect : LayerEffect { - internal PlaceholderLayerEffect(LayerEffectEntity originalEntity, string placeholderFor) + internal PlaceholderLayerEffect(LayerEffectEntity originalEntity) { OriginalEntity = originalEntity; - PlaceholderFor = placeholderFor; LayerEffectEntity = originalEntity; Order = OriginalEntity.Order; @@ -19,8 +18,6 @@ internal class PlaceholderLayerEffect : LayerEffect HasBeenRenamed = OriginalEntity.HasBeenRenamed; } - public string PlaceholderFor { get; } - internal LayerEffectEntity OriginalEntity { get; } /// @@ -47,11 +44,6 @@ internal class PlaceholderLayerEffect : LayerEffect public override void PostProcess(SKCanvas canvas, SKRect bounds, SKPaint paint) { } - - internal override string GetEffectTypeName() - { - return OriginalEntity.EffectType; - } } /// diff --git a/src/Artemis.Core/Plugins/LayerEffects/Placeholder/PlaceholderLayerEffectDescriptor.cs b/src/Artemis.Core/Plugins/LayerEffects/Placeholder/PlaceholderLayerEffectDescriptor.cs index 27628a2d2..4f71dd81c 100644 --- a/src/Artemis.Core/Plugins/LayerEffects/Placeholder/PlaceholderLayerEffectDescriptor.cs +++ b/src/Artemis.Core/Plugins/LayerEffects/Placeholder/PlaceholderLayerEffectDescriptor.cs @@ -2,9 +2,9 @@ internal static class PlaceholderLayerEffectDescriptor { - public static LayerEffectDescriptor Create(string missingProviderId) + public static LayerEffectDescriptor Create() { - LayerEffectDescriptor descriptor = new(missingProviderId, Constants.EffectPlaceholderPlugin); + LayerEffectDescriptor descriptor = LayerEffectDescriptor.CreatePlaceholder(Constants.EffectPlaceholderPlugin); return descriptor; } } \ No newline at end of file diff --git a/src/Artemis.Core/Services/RgbService.cs b/src/Artemis.Core/Services/RgbService.cs index 7b8ec6d69..a3471e729 100644 --- a/src/Artemis.Core/Services/RgbService.cs +++ b/src/Artemis.Core/Services/RgbService.cs @@ -170,11 +170,13 @@ internal class RgbService : IRgbService public void AddDeviceProvider(IRGBDeviceProvider deviceProvider) { + _logger.Verbose("[AddDeviceProvider] Pausing rendering to add {DeviceProvider}", deviceProvider.GetType().Name); bool changedRenderPaused = SetRenderPaused(true); try { List toRemove = _devices.Where(a => deviceProvider.Devices.Any(d => a.RgbDevice == d)).ToList(); + _logger.Verbose("[AddDeviceProvider] Removing {Count} old device(s)", toRemove.Count); Surface.Detach(toRemove.Select(d => d.RgbDevice)); foreach (ArtemisDevice device in toRemove) RemoveDevice(device); @@ -189,8 +191,10 @@ internal class RgbService : IRgbService _logger.Warning(e.Exception, "Device provider {deviceProvider} threw non-critical exception", deviceProvider.GetType().Name); } + _logger.Verbose("[AddDeviceProvider] Initializing device provider"); deviceProvider.Exception += DeviceProviderOnException; deviceProvider.Initialize(); + _logger.Verbose("[AddDeviceProvider] Attaching devices of device provider"); Surface.Attach(deviceProvider.Devices); deviceProvider.Exception -= DeviceProviderOnException; if (providerExceptions.Count == 1) @@ -220,7 +224,10 @@ internal class RgbService : IRgbService } finally { + _logger.Verbose("[AddDeviceProvider] Updating the LED group"); UpdateLedGroup(); + + _logger.Verbose("[AddDeviceProvider] Resuming rendering after adding {DeviceProvider}", deviceProvider.GetType().Name); if (changedRenderPaused) SetRenderPaused(false); } @@ -228,11 +235,13 @@ internal class RgbService : IRgbService public void RemoveDeviceProvider(IRGBDeviceProvider deviceProvider) { + _logger.Verbose("[RemoveDeviceProvider] Pausing rendering to remove {DeviceProvider}", deviceProvider.GetType().Name); bool changedRenderPaused = SetRenderPaused(true); try { List toRemove = _devices.Where(a => deviceProvider.Devices.Any(d => a.RgbDevice == d)).ToList(); + _logger.Verbose("[RemoveDeviceProvider] Removing {Count} old device(s)", toRemove.Count); Surface.Detach(toRemove.Select(d => d.RgbDevice)); foreach (ArtemisDevice device in toRemove) RemoveDevice(device); @@ -246,7 +255,10 @@ internal class RgbService : IRgbService } finally { + _logger.Verbose("[RemoveDeviceProvider] Updating the LED group"); UpdateLedGroup(); + + _logger.Verbose("[RemoveDeviceProvider] Resuming rendering after adding {DeviceProvider}", deviceProvider.GetType().Name); if (changedRenderPaused) SetRenderPaused(false); } diff --git a/src/Artemis.Core/Stores/Registrations/LayerEffectRegistration.cs b/src/Artemis.Core/Stores/Registrations/LayerEffectRegistration.cs index e9b931524..e09526d35 100644 --- a/src/Artemis.Core/Stores/Registrations/LayerEffectRegistration.cs +++ b/src/Artemis.Core/Stores/Registrations/LayerEffectRegistration.cs @@ -1,5 +1,6 @@ using System; using Artemis.Core.LayerEffects; +using Artemis.Core.LayerEffects.Placeholder; namespace Artemis.Core; @@ -37,4 +38,25 @@ public class LayerEffectRegistration if (IsInStore) LayerEffectStore.Remove(this); } + + /// + /// Determines whether the provided placeholder matches this event. + /// + /// The placeholder to check + /// if the placeholder is for the provided layer effect registration, otherwise . + internal bool Matches(PlaceholderLayerEffect placeholder) + { + return placeholder.OriginalEntity.ProviderId == PluginFeature.Id && + placeholder.OriginalEntity.EffectType == LayerEffectDescriptor.LayerEffectType?.FullName; + } + + /// + /// Determines whether the provided layer effect matches this event. + /// + /// The layer effect to check + /// if the placeholder is for the provided layer effect registration, otherwise . + internal bool Matches(BaseLayerEffect layerEffect) + { + return layerEffect.Descriptor == LayerEffectDescriptor; + } } \ No newline at end of file diff --git a/src/Artemis.Core/Utilities/Numeric.cs b/src/Artemis.Core/Utilities/Numeric.cs index 8fda4c66b..06c2ca7fd 100644 --- a/src/Artemis.Core/Utilities/Numeric.cs +++ b/src/Artemis.Core/Utilities/Numeric.cs @@ -432,5 +432,30 @@ public static class NumericExtensions return new Numeric(sum); } + /// + /// Subtracts the numerics in the provided collection + /// + /// The remainder of all numerics subtracted from one another in the collection + /// + public static Numeric Subtract(this IEnumerable source) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + + float subtraction = 0f; + bool first = true; + foreach (float v in source) + { + if (first) + { + subtraction = v; + first = false; + } + else + subtraction -= v; + } + + return new Numeric(subtraction); + } + #endregion } \ No newline at end of file diff --git a/src/Artemis.Core/VisualScripting/NodeData.cs b/src/Artemis.Core/VisualScripting/NodeData.cs index d01dd9ab4..1769cb821 100644 --- a/src/Artemis.Core/VisualScripting/NodeData.cs +++ b/src/Artemis.Core/VisualScripting/NodeData.cs @@ -78,10 +78,17 @@ public class NodeData /// public bool MatchesSearch(string text) { - text = text.Trim(); - return Name.Contains(text, StringComparison.InvariantCultureIgnoreCase) || - Description.Contains(text, StringComparison.InvariantCultureIgnoreCase) || - Category.Contains(text, StringComparison.InvariantCultureIgnoreCase); + string rawText = text.Trim(); + text = text.Trim().TrimStart('!'); + + if (rawText.StartsWith("!!")) + return Name.Equals(text, StringComparison.InvariantCultureIgnoreCase); + else if (rawText.StartsWith("!")) + return Name.StartsWith(text, StringComparison.InvariantCultureIgnoreCase); + else + return Name.Contains(text, StringComparison.InvariantCultureIgnoreCase) || + Description.Contains(text, StringComparison.InvariantCultureIgnoreCase) || + Category.Contains(text, StringComparison.InvariantCultureIgnoreCase); } #region Properties & Fields diff --git a/src/Artemis.Core/VisualScripting/Pins/InputPin.cs b/src/Artemis.Core/VisualScripting/Pins/InputPin.cs index 90933f31b..8addc4627 100644 --- a/src/Artemis.Core/VisualScripting/Pins/InputPin.cs +++ b/src/Artemis.Core/VisualScripting/Pins/InputPin.cs @@ -26,6 +26,8 @@ public sealed class InputPin : Pin { if (ConnectedTo.Count > 0 && ConnectedTo[0].PinValue is T value) Value = value; + else + Value = default; } #endregion diff --git a/src/Artemis.Storage/Migrations/M0021GradientNodes.cs b/src/Artemis.Storage/Migrations/M0021GradientNodes.cs index 4d701ef24..c89801fab 100644 --- a/src/Artemis.Storage/Migrations/M0021GradientNodes.cs +++ b/src/Artemis.Storage/Migrations/M0021GradientNodes.cs @@ -67,7 +67,7 @@ public class M0021GradientNodes : IStorageMigration List profiles = repository.Query().ToList(); foreach (ProfileEntity profileEntity in profiles) { - foreach (LayerEntity layer in profileEntity.Layers) + foreach (LayerEntity layer in profileEntity.Layers.Where(le => le.LayerBrush != null)) MigrateDataBinding(layer.LayerBrush.PropertyGroup); repository.Update(profileEntity); diff --git a/src/Artemis.VisualScripting/Nodes/Mathematics/SubtractNode.cs b/src/Artemis.VisualScripting/Nodes/Mathematics/SubtractNode.cs new file mode 100644 index 000000000..baaf4147a --- /dev/null +++ b/src/Artemis.VisualScripting/Nodes/Mathematics/SubtractNode.cs @@ -0,0 +1,34 @@ +using Artemis.Core; + +namespace Artemis.VisualScripting.Nodes.Mathematics; + +[Node("Subtract", "Subtracts the connected numeric values.", "Mathematics", InputType = typeof(Numeric), OutputType = typeof(Numeric))] +public class SubtractNumericsNode : Node +{ + #region Constructors + + public SubtractNumericsNode() + { + Values = CreateInputPinCollection("Values", 2); + Remainder = CreateOutputPin("Remainder"); + } + + #endregion + + #region Methods + + public override void Evaluate() + { + Remainder.Value = Values.Values.Subtract(); + } + + #endregion + + #region Properties & Fields + + public InputPinCollection Values { get; } + + public OutputPin Remainder { get; } + + #endregion +} \ No newline at end of file