1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Nodes - Added maths node

Nodes - Added visual representation of easing types to easings node
This commit is contained in:
Robert 2022-03-27 15:29:58 +02:00
parent a664cfbcad
commit 28534cb57a
19 changed files with 190 additions and 63 deletions

View File

@ -1820,6 +1820,7 @@
"Ninject": "3.3.4",
"NoStringEvaluating": "2.2.2",
"ReactiveUI": "17.1.50",
"ReactiveUI.Validation": "2.2.1",
"SkiaSharp": "2.88.0-preview.178"
}
}

View File

@ -1820,6 +1820,7 @@
"Ninject": "3.3.4",
"NoStringEvaluating": "2.2.2",
"ReactiveUI": "17.1.50",
"ReactiveUI.Validation": "2.2.1",
"SkiaSharp": "2.88.0-preview.178"
}
}

View File

@ -3,13 +3,14 @@ using System.ComponentModel;
using System.Reactive.Disposables;
using Artemis.Core;
using ReactiveUI;
using ReactiveUI.Validation.Helpers;
namespace Artemis.UI.Shared.VisualScripting;
/// <summary>
/// Represents a custom view model for a node
/// </summary>
public abstract class CustomNodeViewModel : ActivatableViewModelBase, ICustomNodeViewModel
public abstract class CustomNodeViewModel : ReactiveValidationObject, IActivatableViewModel, ICustomNodeViewModel
{
/// <summary>
/// Creates a new instance of the <see cref="CustomNodeViewModel" /> class.
@ -38,6 +39,13 @@ public abstract class CustomNodeViewModel : ActivatableViewModelBase, ICustomNod
/// </summary>
public INodeScript Script { get; }
#region Implementation of IActivatableViewModel
/// <inheritdoc />
public ViewModelActivator Activator { get; } = new();
#endregion
/// <summary>
/// Invokes the <see cref="NodeModified" /> event
/// </summary>

View File

@ -1836,6 +1836,7 @@
"Ninject": "3.3.4",
"NoStringEvaluating": "2.2.2",
"ReactiveUI": "17.1.50",
"ReactiveUI.Validation": "2.2.1",
"SkiaSharp": "2.88.0-preview.178"
}
}

View File

@ -1807,6 +1807,7 @@
"Ninject": "3.3.4",
"NoStringEvaluating": "2.2.2",
"ReactiveUI": "17.1.50",
"ReactiveUI.Validation": "2.2.1",
"SkiaSharp": "2.88.0-preview.178"
}
}

View File

@ -14,6 +14,7 @@
<PackageReference Include="Ninject" Version="3.3.4" />
<PackageReference Include="NoStringEvaluating" Version="2.2.2" />
<PackageReference Include="ReactiveUI" Version="17.1.50" />
<PackageReference Include="ReactiveUI.Validation" Version="2.2.1" />
<PackageReference Include="SkiaSharp" Version="2.88.0-preview.178" />
</ItemGroup>

View File

@ -55,13 +55,13 @@ public class DataModelEventNodeCustomViewModel : CustomNodeViewModel
public ObservableCollection<Module>? Modules
{
get => _modules;
set => RaiseAndSetIfChanged(ref _modules, value);
set => this.RaiseAndSetIfChanged(ref _modules, value);
}
public DataModelPath? DataModelPath
{
get => _dataModelPath;
set => RaiseAndSetIfChanged(ref _dataModelPath, value);
set => this.RaiseAndSetIfChanged(ref _dataModelPath, value);
}
private void UpdateDataModelPath(DataModelPathEntity? entity)

View File

@ -54,13 +54,13 @@ public class DataModelNodeCustomViewModel : CustomNodeViewModel
public ObservableCollection<Module>? Modules
{
get => _modules;
set => RaiseAndSetIfChanged(ref _modules, value);
set => this.RaiseAndSetIfChanged(ref _modules, value);
}
public DataModelPath? DataModelPath
{
get => _dataModelPath;
set => RaiseAndSetIfChanged(ref _dataModelPath, value);
set => this.RaiseAndSetIfChanged(ref _dataModelPath, value);
}
private void UpdateDataModelPath(DataModelPathEntity? entity)

View File

@ -1,54 +1,35 @@
using System.Collections.ObjectModel;
using Artemis.Core;
using Artemis.UI.Shared.Services.NodeEditor;
using Artemis.UI.Shared.Services.NodeEditor.Commands;
using Artemis.UI.Shared.VisualScripting;
using ReactiveUI;
namespace Artemis.VisualScripting.Nodes.Easing.CustomViewModels;
public class EasingTypeNodeCustomViewModel : CustomNodeViewModel
{
private readonly EasingTypeNode _node;
private NodeEasingViewModel _selectedEasingViewModel;
private readonly INodeEditorService _nodeEditorService;
public EasingTypeNodeCustomViewModel(EasingTypeNode node, INodeScript script) : base(node, script)
public EasingTypeNodeCustomViewModel(EasingTypeNode node, INodeScript script, INodeEditorService nodeEditorService) : base(node, script)
{
_node = node;
_nodeEditorService = nodeEditorService;
NodeModified += (_, _) => this.RaisePropertyChanged(nameof(SelectedEasingViewModel));
EasingViewModels = new ObservableCollection<NodeEasingViewModel>(Enum.GetValues(typeof(Easings.Functions)).Cast<Easings.Functions>().Select(e => new NodeEasingViewModel(e)));
}
public ObservableCollection<NodeEasingViewModel> EasingViewModels { get; }
public NodeEasingViewModel SelectedEasingViewModel
public NodeEasingViewModel? SelectedEasingViewModel
{
get => _selectedEasingViewModel;
get => EasingViewModels.FirstOrDefault(e => e.EasingFunction == _node.Storage);
set
{
_selectedEasingViewModel = value;
_node.Storage = _selectedEasingViewModel.EasingFunction;
if (value != null && _node.Storage != value.EasingFunction)
_nodeEditorService.ExecuteCommand(Script, new UpdateStorage<Easings.Functions>(_node, value.EasingFunction));
}
}
// public override void OnActivate()
// {
// _node.PropertyChanged += NodeOnPropertyChanged;
// SelectedEasingViewModel = GetNodeEasingViewModel();
// }
//
// public override void OnDeactivate()
// {
// _node.PropertyChanged -= NodeOnPropertyChanged;
// }
//
// private void NodeOnPropertyChanged(object sender, PropertyChangedEventArgs e)
// {
// if (e.PropertyName == nameof(_node.Storage))
// {
// _selectedEasingViewModel = GetNodeEasingViewModel();
// NotifyOfPropertyChange(nameof(SelectedEasingViewModel));
// }
// }
private NodeEasingViewModel GetNodeEasingViewModel()
{
return EasingViewModels.FirstOrDefault(vm => vm.EasingFunction == _node.Storage);
}
}

View File

@ -15,9 +15,8 @@ public class NodeEasingViewModel : ViewModelBase
EasingPoints = new List<Point>();
for (int i = 1; i <= 10; i++)
{
int x = i;
double y = Easings.Interpolate(i / 10.0, EasingFunction) * 10;
EasingPoints.Add(new Point(x, y));
EasingPoints.Add(new Point(i, y));
}
}

View File

@ -2,8 +2,9 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared"
xmlns:customViewModels="clr-namespace:Artemis.VisualScripting.Nodes.Easing.CustomViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.VisualScripting.Nodes.Easing.CustomViews.EasingTypeNodeCustomView">
<controls:EnumComboBox Classes="condensed" MinWidth="75" Value="{Binding Node.Storage}"/>
</UserControl>
x:Class="Artemis.VisualScripting.Nodes.Easing.CustomViews.EasingTypeNodeCustomView"
x:DataType="customViewModels:EasingTypeNodeCustomViewModel">
<ComboBox Classes="condensed" MinWidth="75" Items="{CompiledBinding EasingViewModels}" SelectedItem="{CompiledBinding SelectedEasingViewModel}" />
</UserControl>

View File

@ -0,0 +1,19 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:customViewModels="clr-namespace:Artemis.VisualScripting.Nodes.Easing.CustomViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.VisualScripting.Nodes.Easing.CustomViews.NodeEasingView"
x:DataType="customViewModels:NodeEasingViewModel">
<StackPanel Orientation="Horizontal" Spacing="5">
<Polyline Stroke="{DynamicResource TextFillColorPrimaryBrush}"
StrokeThickness="1"
Points="{CompiledBinding EasingPoints}"
Stretch="Uniform"
Width="15"
Height="15"
VerticalAlignment="Center"/>
<TextBlock Text="{CompiledBinding Description}" />
</StackPanel>
</UserControl>

View File

@ -0,0 +1,19 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace Artemis.VisualScripting.Nodes.Easing.CustomViews
{
public partial class NodeEasingView : UserControl
{
public NodeEasingView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

View File

@ -1,11 +1,36 @@
using Artemis.Core;
using Artemis.UI.Shared.Services.NodeEditor;
using Artemis.UI.Shared.Services.NodeEditor.Commands;
using Artemis.UI.Shared.VisualScripting;
using ReactiveUI;
using ReactiveUI.Validation.Extensions;
namespace Artemis.VisualScripting.Nodes.Maths.CustomViewModels;
public class MathExpressionNodeCustomViewModel : CustomNodeViewModel
{
public MathExpressionNodeCustomViewModel(INode node, INodeScript script) : base(node, script)
private readonly MathExpressionNode _node;
private readonly INodeEditorService _nodeEditorService;
private string? _inputValue;
public MathExpressionNodeCustomViewModel(MathExpressionNode node, INodeScript script, INodeEditorService nodeEditorService) : base(node, script)
{
_node = node;
_nodeEditorService = nodeEditorService;
NodeModified += (_, _) => InputValue = _node.Storage;
this.ValidationRule(vm => vm.InputValue, value => _node.IsSyntaxValid(value), value => _node.GetSyntaxErrors(value));
}
public string? InputValue
{
get => _inputValue;
set => this.RaiseAndSetIfChanged(ref _inputValue, value);
}
public void UpdateInputValue()
{
if (!HasErrors && _node.Storage != InputValue)
_nodeEditorService.ExecuteCommand(Script, new UpdateStorage<string>(_node, InputValue));
}
}

View File

@ -0,0 +1,14 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:customViewModels="clr-namespace:Artemis.VisualScripting.Nodes.Maths.CustomViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.VisualScripting.Nodes.Maths.CustomViews.MathExpressionNodeCustomView"
x:DataType="customViewModels:MathExpressionNodeCustomViewModel">
<TextBox VerticalAlignment="Top"
MinWidth="75"
Classes="condensed"
Text="{CompiledBinding InputValue}"
LostFocus="InputElement_OnLostFocus" />
</UserControl>

View File

@ -0,0 +1,24 @@
using Artemis.VisualScripting.Nodes.Maths.CustomViewModels;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.VisualScripting.Nodes.Maths.CustomViews;
public class MathExpressionNodeCustomView : ReactiveUserControl<MathExpressionNodeCustomViewModel>
{
public MathExpressionNodeCustomView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private void InputElement_OnLostFocus(object? sender, RoutedEventArgs e)
{
ViewModel?.UpdateInputValue();
}
}

View File

@ -1,11 +0,0 @@
<UserControl x:Class="Artemis.VisualScripting.Nodes.Maths.CustomViews.MathExpressionNodeCustomView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<TextBox VerticalAlignment="Bottom"
HorizontalAlignment="Stretch"
Text="{Binding Node.Storage}" />
</UserControl>

View File

@ -2,6 +2,7 @@
using Artemis.VisualScripting.Nodes.Maths.CustomViewModels;
using NoStringEvaluating.Contract;
using NoStringEvaluating.Contract.Variables;
using NoStringEvaluating.Models.FormulaChecker;
using NoStringEvaluating.Models.Values;
namespace Artemis.VisualScripting.Nodes.Maths;
@ -10,14 +11,16 @@ namespace Artemis.VisualScripting.Nodes.Maths;
public class MathExpressionNode : Node<string, MathExpressionNodeCustomViewModel>
{
private readonly INoStringEvaluator _evaluator;
private readonly IFormulaChecker _checker;
private readonly PinsVariablesContainer _variables;
#region Constructors
public MathExpressionNode(INoStringEvaluator evaluator)
public MathExpressionNode(INoStringEvaluator evaluator, IFormulaChecker checker)
: base("Math Expression", "Outputs the result of a math expression.")
{
_evaluator = evaluator;
_checker = checker;
Output = CreateOutputPin<Numeric>();
Values = CreateInputPinCollection<Numeric>("Values", 2);
Values.PinAdded += (_, _) => SetPinNames();
@ -70,6 +73,45 @@ public class MathExpressionNode : Node<string, MathExpressionNodeCustomViewModel
}
#endregion
public bool IsSyntaxValid(string? s)
{
if (s == null)
return true;
if (!_checker.CheckSyntax(s).Ok)
return false;
try
{
_evaluator.CalcNumber(s, _variables);
return true;
}
catch
{
return false;
}
}
public string GetSyntaxErrors(string? s)
{
if (s == null)
return "";
CheckFormulaResult? syntaxCheck = _checker.CheckSyntax(s);
if (!syntaxCheck.Ok)
return string.Join(",", syntaxCheck.Mistakes);
try
{
_evaluator.CalcNumber(s, _variables);
return "";
}
catch (Exception e)
{
return e.Message;
}
}
}
public class PinsVariablesContainer : IVariablesContainer

View File

@ -68,6 +68,15 @@
"Splat": "14.1.45"
}
},
"ReactiveUI.Validation": {
"type": "Direct",
"requested": "[2.2.1, )",
"resolved": "2.2.1",
"contentHash": "rhEphZ4ErbGfNtbBQ/tYMsLJYHyLVyqidU+sgZ3kXKbS7QrNoM4j6PPxCwLMKsJUuvVL8JN45xgmB9tSwm7+lg==",
"dependencies": {
"ReactiveUI": "16.2.6"
}
},
"SkiaSharp": {
"type": "Direct",
"requested": "[2.88.0-preview.178, )",
@ -538,14 +547,6 @@
"Ninject": "3.3.3"
}
},
"ReactiveUI.Validation": {
"type": "Transitive",
"resolved": "2.2.1",
"contentHash": "rhEphZ4ErbGfNtbBQ/tYMsLJYHyLVyqidU+sgZ3kXKbS7QrNoM4j6PPxCwLMKsJUuvVL8JN45xgmB9tSwm7+lg==",
"dependencies": {
"ReactiveUI": "16.2.6"
}
},
"RGB.NET.Core": {
"type": "Transitive",
"resolved": "1.0.0-prerelease7",