diff --git a/src/Artemis.Core/MVVM/CorePropertyChanged.cs b/src/Artemis.Core/MVVM/CorePropertyChanged.cs index 8ab50083a..5e7b6b417 100644 --- a/src/Artemis.Core/MVVM/CorePropertyChanged.cs +++ b/src/Artemis.Core/MVVM/CorePropertyChanged.cs @@ -1,4 +1,5 @@ -using System.ComponentModel; +using System.Collections.Generic; +using System.ComponentModel; using System.Runtime.CompilerServices; using Artemis.Core.Properties; @@ -26,7 +27,7 @@ public abstract class CorePropertyChanged : INotifyPropertyChanged [MethodImpl(MethodImplOptions.AggressiveInlining)] protected bool RequiresUpdate(ref T storage, T value) { - return !Equals(storage, value); + return !EqualityComparer.Default.Equals(storage, value); } /// diff --git a/src/Artemis.Core/Plugins/LayerBrushes/PerLedLayerBrush.cs b/src/Artemis.Core/Plugins/LayerBrushes/PerLedLayerBrush.cs index e1623b3fc..f0ace6a43 100644 --- a/src/Artemis.Core/Plugins/LayerBrushes/PerLedLayerBrush.cs +++ b/src/Artemis.Core/Plugins/LayerBrushes/PerLedLayerBrush.cs @@ -28,6 +28,8 @@ public abstract class PerLedLayerBrush : PropertiesLayerBrush where T : La /// The color the LED will receive public abstract SKColor GetColor(ArtemisLed led, SKPoint renderPoint); + private readonly SKPoint[] _points = new SKPoint[2]; + internal override void InternalRender(SKCanvas canvas, SKRect bounds, SKPaint paint) { // We don't want rotation on this canvas because that'll displace the LEDs, translations are applied to the points of each LED instead @@ -37,25 +39,21 @@ public abstract class PerLedLayerBrush : PropertiesLayerBrush where T : La using SKPath pointsPath = new(); foreach (ArtemisLed artemisLed in Layer.Leds) { - pointsPath.AddPoly(new[] - { - new SKPoint(0, 0), - new SKPoint(artemisLed.AbsoluteRectangle.Left - Layer.Bounds.Left, artemisLed.AbsoluteRectangle.Top - Layer.Bounds.Top) - }); + _points[0] = new SKPoint(0, 0); + _points[1] = new SKPoint(artemisLed.AbsoluteRectangle.Left - Layer.Bounds.Left, artemisLed.AbsoluteRectangle.Top - Layer.Bounds.Top); + pointsPath.AddPoly(_points); } // Apply the translation to the points of each LED instead if (Layer.General.TransformMode.CurrentValue == LayerTransformMode.Normal && SupportsTransformation) pointsPath.Transform(Layer.GetTransformMatrix(true, true, true, true).Invert()); - SKPoint[] points = pointsPath.Points; - TryOrBreak(() => { for (int index = 0; index < Layer.Leds.Count; index++) { ArtemisLed artemisLed = Layer.Leds[index]; - SKPoint renderPoint = points[index * 2 + 1]; + SKPoint renderPoint = pointsPath.GetPoint(index * 2 + 1); if (!float.IsFinite(renderPoint.X) || !float.IsFinite(renderPoint.Y)) continue; diff --git a/src/Artemis.Core/Stores/LogStore.cs b/src/Artemis.Core/Stores/LogStore.cs index de459357d..a4a3e66ed 100644 --- a/src/Artemis.Core/Stores/LogStore.cs +++ b/src/Artemis.Core/Stores/LogStore.cs @@ -10,12 +10,25 @@ namespace Artemis.Core; /// public static class LogStore { + private static readonly object _lock = new(); + private static readonly LinkedList LinkedList = new(); /// /// Gets a list containing the last 500 log events. /// - public static List Events => LinkedList.ToList(); + public static List Events + { + get + { + List events; + + lock (_lock) + events = LinkedList.ToList(); + + return events; + } + } /// /// Occurs when a new was received. @@ -24,9 +37,13 @@ public static class LogStore internal static void Emit(LogEvent logEvent) { - LinkedList.AddLast(logEvent); - while (LinkedList.Count > 500) - LinkedList.RemoveFirst(); + lock (_lock) + { + LinkedList.AddLast(logEvent); + while (LinkedList.Count > 500) + LinkedList.RemoveFirst(); + } + OnEventAdded(new LogEventEventArgs(logEvent)); } diff --git a/src/Artemis.UI.Shared/Services/NodeEditor/Commands/DuplicateNode.cs b/src/Artemis.UI.Shared/Services/NodeEditor/Commands/DuplicateNode.cs index 8c60677b6..62340d3a6 100644 --- a/src/Artemis.UI.Shared/Services/NodeEditor/Commands/DuplicateNode.cs +++ b/src/Artemis.UI.Shared/Services/NodeEditor/Commands/DuplicateNode.cs @@ -56,7 +56,7 @@ public class DuplicateNode : INodeEditorCommand, IDisposable if (targetCollection == null) continue; while (targetCollection.Count() < sourceCollection.Count()) - targetCollection.CreatePin(); + targetCollection.Add(targetCollection.CreatePin()); } // Copy the storage diff --git a/src/Artemis.UI/Screens/Debugger/Tabs/Logs/LogsDebugView.axaml.cs b/src/Artemis.UI/Screens/Debugger/Tabs/Logs/LogsDebugView.axaml.cs index 20bb00ff2..132d4606a 100644 --- a/src/Artemis.UI/Screens/Debugger/Tabs/Logs/LogsDebugView.axaml.cs +++ b/src/Artemis.UI/Screens/Debugger/Tabs/Logs/LogsDebugView.axaml.cs @@ -14,7 +14,7 @@ namespace Artemis.UI.Screens.Debugger.Logs; public class LogsDebugView : ReactiveUserControl { private int _lineCount; - private TextEditor _textEditor; + private TextEditor? _textEditor; public LogsDebugView() { @@ -31,7 +31,7 @@ public class LogsDebugView : ReactiveUserControl protected override void OnInitialized() { base.OnInitialized(); - Dispatcher.UIThread.Post(() => _textEditor.ScrollToEnd(), DispatcherPriority.ApplicationIdle); + Dispatcher.UIThread.Post(() => _textEditor?.ScrollToEnd(), DispatcherPriority.ApplicationIdle); } private void OnTextChanged(object? sender, EventArgs e) diff --git a/src/Artemis.UI/Screens/Debugger/Tabs/Logs/LogsDebugViewModel.cs b/src/Artemis.UI/Screens/Debugger/Tabs/Logs/LogsDebugViewModel.cs index ca067a392..c1860b01a 100644 --- a/src/Artemis.UI/Screens/Debugger/Tabs/Logs/LogsDebugViewModel.cs +++ b/src/Artemis.UI/Screens/Debugger/Tabs/Logs/LogsDebugViewModel.cs @@ -48,8 +48,11 @@ public class LogsDebugViewModel : ActivatableViewModelBase }); } - private void AddLogEvent(LogEvent logEvent) + private void AddLogEvent(LogEvent? logEvent) { + if (logEvent is null) + return; + using StringWriter writer = new(); _formatter.Format(logEvent, writer); string line = writer.ToString(); @@ -60,7 +63,7 @@ public class LogsDebugViewModel : ActivatableViewModelBase private void RemoveOldestLine() { - int firstNewLine = Document.Text.IndexOf('\n'); + int firstNewLine = Document.IndexOf('\n', 0, Document.TextLength); if (firstNewLine == -1) { //this should never happen. diff --git a/src/Artemis.UI/Screens/StartupWizard/StartupWizardViewModel.cs b/src/Artemis.UI/Screens/StartupWizard/StartupWizardViewModel.cs index e2a3e516a..72d296643 100644 --- a/src/Artemis.UI/Screens/StartupWizard/StartupWizardViewModel.cs +++ b/src/Artemis.UI/Screens/StartupWizard/StartupWizardViewModel.cs @@ -52,7 +52,7 @@ public class StartupWizardViewModel : DialogViewModelBase DeviceProviders = new ObservableCollection(pluginManagementService.GetAllPlugins() .Where(p => p.Info.IsCompatible && p.Features.Any(f => f.AlwaysEnabled && f.FeatureType.IsAssignableTo(typeof(DeviceProvider)))) .OrderBy(p => p.Info.Name) - .Select(p => settingsVmFactory.PluginViewModel(p, null))); + .Select(p => settingsVmFactory.PluginViewModel(p, ReactiveCommand.Create(() => new Unit())))); CurrentStep = 1; SetupButtons(); diff --git a/src/Artemis.VisualScripting/Nodes/Color/HslSKColorNode.cs b/src/Artemis.VisualScripting/Nodes/Color/HslSKColorNode.cs index cba686c38..15ce66d84 100644 --- a/src/Artemis.VisualScripting/Nodes/Color/HslSKColorNode.cs +++ b/src/Artemis.VisualScripting/Nodes/Color/HslSKColorNode.cs @@ -3,7 +3,7 @@ using SkiaSharp; namespace Artemis.VisualScripting.Nodes.Color; -[Node("HSL Color", "Creates a color from hue, saturation and lightness values", "Color", InputType = typeof(Numeric), OutputType = typeof(SKColor))] +[Node("HSL Color", "Creates a color from hue, saturation and lightness numbers", "Color", InputType = typeof(Numeric), OutputType = typeof(SKColor))] public class HslSKColorNode : Node { public HslSKColorNode() diff --git a/src/Artemis.VisualScripting/Nodes/Color/HsvSKColorNode.cs b/src/Artemis.VisualScripting/Nodes/Color/HsvSKColorNode.cs new file mode 100644 index 000000000..2544ccec2 --- /dev/null +++ b/src/Artemis.VisualScripting/Nodes/Color/HsvSKColorNode.cs @@ -0,0 +1,31 @@ +using Artemis.Core; +using SkiaSharp; + +namespace Artemis.VisualScripting.Nodes.Color; + +[Node("HSV Color", "Creates a color from hue, saturation and value numbers", "Color", InputType = typeof(Numeric), OutputType = typeof(SKColor))] +public class HsvSKColorNode : Node +{ + public HsvSKColorNode() + { + H = CreateInputPin("H"); + S = CreateInputPin("S"); + V = CreateInputPin("V"); + Output = CreateOutputPin(); + } + + public InputPin H { get; set; } + public InputPin S { get; set; } + public InputPin V { get; set; } + public OutputPin Output { get; } + + #region Overrides of Node + + /// + public override void Evaluate() + { + Output.Value = SKColor.FromHsv(H.Value, S.Value, V.Value); + } + + #endregion +} \ No newline at end of file diff --git a/src/Artemis.VisualScripting/Nodes/Color/SkColorHsl.cs b/src/Artemis.VisualScripting/Nodes/Color/SkColorHsl.cs new file mode 100644 index 000000000..aaa1c6e26 --- /dev/null +++ b/src/Artemis.VisualScripting/Nodes/Color/SkColorHsl.cs @@ -0,0 +1,36 @@ +using Artemis.Core; +using SkiaSharp; + +namespace Artemis.VisualScripting.Nodes.Color; + +[Node("Color to HSL", "Outputs H, S and L values from a color", "Color", InputType = typeof(SKColor), OutputType = typeof(Numeric))] +public class SkColorHsl : Node +{ + + public SkColorHsl() + { + Input = CreateInputPin(); + H = CreateOutputPin("H"); + S = CreateOutputPin("S"); + L = CreateOutputPin("L"); + } + + public InputPin Input { get; } + public OutputPin H { get; } + public OutputPin S { get; } + public OutputPin L { get; } + + #region Overrides of Node + + /// + public override void Evaluate() + { + Input.Value.ToHsl(out float h, out float s, out float l); + + H.Value = h; + S.Value = s; + L.Value = l; + } + + #endregion +} \ No newline at end of file diff --git a/src/Artemis.VisualScripting/Nodes/Color/SkColorHsv.cs b/src/Artemis.VisualScripting/Nodes/Color/SkColorHsv.cs new file mode 100644 index 000000000..a0e448cf4 --- /dev/null +++ b/src/Artemis.VisualScripting/Nodes/Color/SkColorHsv.cs @@ -0,0 +1,36 @@ +using Artemis.Core; +using SkiaSharp; + +namespace Artemis.VisualScripting.Nodes.Color; + +[Node("Color to HSV", "Outputs H, S and L values from a color", "Color", InputType = typeof(SKColor), OutputType = typeof(Numeric))] +public class SkColorHsv : Node +{ + + public SkColorHsv() + { + Input = CreateInputPin(); + H = CreateOutputPin("H"); + S = CreateOutputPin("S"); + V = CreateOutputPin("V"); + } + + public InputPin Input { get; } + public OutputPin H { get; } + public OutputPin S { get; } + public OutputPin V { get; } + + #region Overrides of Node + + /// + public override void Evaluate() + { + Input.Value.ToHsv(out float h, out float s, out float v); + + H.Value = h; + S.Value = s; + V.Value = v; + } + + #endregion +} \ No newline at end of file diff --git a/src/Artemis.VisualScripting/Nodes/Conversion/ConvertToNumericNode.cs b/src/Artemis.VisualScripting/Nodes/Conversion/ConvertToNumericNode.cs index 8a4190098..0dcfb6e1b 100644 --- a/src/Artemis.VisualScripting/Nodes/Conversion/ConvertToNumericNode.cs +++ b/src/Artemis.VisualScripting/Nodes/Conversion/ConvertToNumericNode.cs @@ -1,4 +1,4 @@ -using Artemis.Core; +using Artemis.Core; namespace Artemis.VisualScripting.Nodes.Conversion; @@ -33,6 +33,7 @@ public class ConvertToNumericNode : Node double input => new Numeric(input), float input => new Numeric(input), byte input => new Numeric(input), + bool input => new Numeric(input ? 1 : 0), _ => TryParse(Input.Value) }; } @@ -44,4 +45,4 @@ public class ConvertToNumericNode : Node } #endregion -} \ No newline at end of file +}