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

Nodes - Lock nodes while resetting

Nodes - Added more boolean operators
Nodes - Fix number comparisons
This commit is contained in:
Robert 2021-09-29 21:43:12 +02:00
parent 43b7b8345c
commit 0058322936
13 changed files with 218 additions and 253 deletions

View File

@ -97,8 +97,11 @@ namespace Artemis.Core
/// <inheritdoc />
public void Run()
{
foreach (INode node in Nodes)
node.Reset();
lock (_nodes)
{
foreach (INode node in _nodes)
node.Reset();
}
ExitNode.Evaluate();
}
@ -106,7 +109,10 @@ namespace Artemis.Core
/// <inheritdoc />
public void AddNode(INode node)
{
_nodes.Add(node);
lock (_nodes)
{
_nodes.Add(node);
}
NodeAdded?.Invoke(this, new SingleValueEventArgs<INode>(node));
}
@ -114,7 +120,10 @@ namespace Artemis.Core
/// <inheritdoc />
public void RemoveNode(INode node)
{
_nodes.Remove(node);
lock (_nodes)
{
_nodes.Remove(node);
}
if (node is IDisposable disposable)
disposable.Dispose();
@ -128,10 +137,13 @@ namespace Artemis.Core
NodeTypeStore.NodeTypeAdded -= NodeTypeStoreOnNodeTypeChanged;
NodeTypeStore.NodeTypeRemoved -= NodeTypeStoreOnNodeTypeChanged;
foreach (INode node in _nodes)
lock (_nodes)
{
if (node is IDisposable disposable)
disposable.Dispose();
foreach (INode node in _nodes)
{
if (node is IDisposable disposable)
disposable.Dispose();
}
}
}
@ -142,9 +154,12 @@ namespace Artemis.Core
/// <inheritdoc />
public void Load()
{
List<INode> removeNodes = _nodes.Where(n => !n.IsExitNode).ToList();
foreach (INode removeNode in removeNodes)
RemoveNode(removeNode);
lock (_nodes)
{
List<INode> removeNodes = _nodes.Where(n => !n.IsExitNode).ToList();
foreach (INode removeNode in removeNodes)
RemoveNode(removeNode);
}
// Create nodes
foreach (NodeEntity entityNode in Entity.Nodes)

View File

@ -51,7 +51,6 @@ namespace Artemis.VisualScripting.Editor.Controls
set => SetValue(SourcePinProperty, value);
}
public static readonly DependencyProperty CreateNodeCommandProperty = DependencyProperty.Register(
"CreateNodeCommand", typeof(ICommand), typeof(VisualScriptNodeCreationBox), new PropertyMetadata(default(ICommand)));
@ -61,6 +60,15 @@ namespace Artemis.VisualScripting.Editor.Controls
set => SetValue(CreateNodeCommandProperty, value);
}
public static readonly DependencyProperty SearchInputProperty = DependencyProperty.Register(
"SearchInput", typeof(string), typeof(VisualScriptNodeCreationBox), new PropertyMetadata(default(string), OnSearchInputChanged));
public string SearchInput
{
get => (string)GetValue(SearchInputProperty);
set => SetValue(SearchInputProperty, value);
}
#endregion
#region Methods
@ -70,7 +78,6 @@ namespace Artemis.VisualScripting.Editor.Controls
_searchBox = GetTemplateChild(PART_SEARCHBOX) as TextBox ?? throw new NullReferenceException($"The Element '{PART_SEARCHBOX}' is missing.");
_contentList = GetTemplateChild(PART_CONTENT) as ListBox ?? throw new NullReferenceException($"The Element '{PART_CONTENT}' is missing.");
_searchBox.TextChanged += OnSearchBoxTextChanged;
_contentList.IsSynchronizedWithCurrentItem = false;
_contentList.SelectionChanged += OnContentListSelectionChanged;
_contentList.SelectionMode = SelectionMode.Single;
@ -90,32 +97,29 @@ namespace Artemis.VisualScripting.Editor.Controls
_searchBox.SelectionStart = 0;
_searchBox.SelectionLength = _searchBox.Text.Length;
}
private void OnSearchBoxTextChanged(object sender, TextChangedEventArgs args)
{
_contentView?.Refresh();
}
private void OnContentListSelectionChanged(object sender, SelectionChangedEventArgs args)
{
if ((args == null) || (_contentList?.SelectedItem == null)) return;
CreateNodeCommand?.Execute(_contentList.SelectedItem);
_contentList.SelectedItem = null;
}
private bool Filter(object o)
{
if (_searchBox == null) return false;
if (o is not NodeData nodeData) return false;
if (string.IsNullOrWhiteSpace(SearchInput)) return true;
bool nameContains = nodeData.Name.Contains(_searchBox.Text, StringComparison.OrdinalIgnoreCase);
bool searchContains = nodeData.Name.Contains(SearchInput, StringComparison.OrdinalIgnoreCase) ||
nodeData.Description.Contains(SearchInput, StringComparison.OrdinalIgnoreCase);
if (SourcePin == null || SourcePin.Pin.Type == typeof(object))
return nameContains;
return searchContains;
if (SourcePin.Pin.Direction == PinDirection.Input)
return nodeData.OutputType != null && nameContains && (nodeData.OutputType == typeof(object) || nodeData.OutputType.IsAssignableTo(SourcePin.Pin.Type));
return nodeData.InputType != null && nameContains && (nodeData.InputType == typeof(object) || nodeData.InputType.IsAssignableFrom(SourcePin.Pin.Type));
return nodeData.OutputType != null && searchContains && (nodeData.OutputType == typeof(object) || nodeData.OutputType.IsAssignableTo(SourcePin.Pin.Type));
return nodeData.InputType != null && searchContains && (nodeData.InputType == typeof(object) || nodeData.InputType.IsAssignableFrom(SourcePin.Pin.Type));
}
private void ItemsSourceChanged()
@ -150,6 +154,8 @@ namespace Artemis.VisualScripting.Editor.Controls
private static void OnSourcePinChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) => (d as VisualScriptNodeCreationBox)?._contentView.Refresh();
private static void OnSearchInputChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) => (d as VisualScriptNodeCreationBox)?._contentView.Refresh();
#endregion
}
}

View File

@ -552,7 +552,7 @@ namespace Artemis.VisualScripting.Editor.Controls
if (_creationBoxParent.ContextMenu != null)
_creationBoxParent.ContextMenu.IsOpen = false;
Script.AddNode(node);
}

View File

@ -2,46 +2,50 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Artemis.VisualScripting.Editor.Controls"
xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core"
xmlns:behaviors="clr-namespace:Artemis.VisualScripting.Behaviors">
xmlns:behaviors="clr-namespace:Artemis.VisualScripting.Behaviors"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes">
<DataTemplate x:Key="TemplateNodeItem"
DataType="{x:Type core:NodeData}">
<TextBlock Margin="8 0 0 0"
Text="{Binding Name}"
ToolTip="{Binding Description}"
behaviors:HighlightTermBehavior.TermToBeHighlighted="{Binding Text, ElementName=PART_SearchBox}"
behaviors:HighlightTermBehavior.Text="{Binding Name}"
behaviors:HighlightTermBehavior.HighlightForeground="{StaticResource Primary600Foreground}"
behaviors:HighlightTermBehavior.HighlightBackground="{StaticResource Primary600}"/>
<DataTemplate x:Key="TemplateNodeItem" DataType="{x:Type core:NodeData}">
<StackPanel>
<TextBlock behaviors:HighlightTermBehavior.TermToBeHighlighted="{Binding SearchInput, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=controls:VisualScriptNodeCreationBox}}"
behaviors:HighlightTermBehavior.Text="{Binding Name}"
behaviors:HighlightTermBehavior.HighlightForeground="{StaticResource Primary600Foreground}"
behaviors:HighlightTermBehavior.HighlightBackground="{StaticResource Primary600}" />
<TextBlock Foreground="{DynamicResource MaterialDesignBodyLight}"
behaviors:HighlightTermBehavior.TermToBeHighlighted="{Binding SearchInput, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=controls:VisualScriptNodeCreationBox}}"
behaviors:HighlightTermBehavior.Text="{Binding Description}"
behaviors:HighlightTermBehavior.HighlightForeground="{StaticResource Primary600Foreground}"
behaviors:HighlightTermBehavior.HighlightBackground="{StaticResource Primary600}" />
</StackPanel>
</DataTemplate>
<ControlTemplate x:Key="TemplateVisualScriptNodeCreationBox"
TargetType="{x:Type controls:VisualScriptNodeCreationBox}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<ControlTemplate x:Key="TemplateVisualScriptNodeCreationBox" TargetType="{x:Type controls:VisualScriptNodeCreationBox}">
<materialDesign:Card Margin="5" Opacity="0.9">
<Grid Margin="8">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border Padding="2"
BorderThickness="1"
BorderBrush="#A0FFFFFF">
<TextBox x:Name="PART_SearchBox"
Background="Transparent"
Foreground="#FFFFFFFF"
CaretBrush="#FFFFFFFF"
BorderThickness="0" />
</Border>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<materialDesign:PackIcon Grid.Column="0" Kind="Search" Width="20" Height="20" VerticalAlignment="Center" Foreground="{Binding ElementName=PART_SearchBox, Path=BorderBrush}" />
<TextBox Grid.Column="1" x:Name="PART_SearchBox"
Text="{Binding SearchInput, UpdateSourceTrigger=PropertyChanged, Delay=300, RelativeSource={RelativeSource TemplatedParent}}"
materialDesign:HintAssist.Hint="Type to search..."
materialDesign:TextFieldAssist.HasClearButton="True"/>
</Grid>
<ListBox x:Name="PART_Content"
Grid.Row="1"
Background="Transparent"
Foreground="#FFFFFFFF"
BorderThickness="0"
ItemTemplate="{StaticResource TemplateNodeItem}">
<Separator Grid.Row="1" Style="{StaticResource MaterialDesignLightSeparator}" />
<ListBox x:Name="PART_Content" Grid.Row="2" BorderThickness="0" ItemTemplate="{StaticResource TemplateNodeItem}"
VirtualizingPanel.IsVirtualizingWhenGrouping="True"
VirtualizingPanel.IsContainerVirtualizable="True"
VirtualizingPanel.ScrollUnit="Pixel">
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
@ -57,7 +61,7 @@
</Expander.HeaderTemplate>
<Expander.Style>
<Style TargetType="Expander">
<Setter Property="Foreground" Value="AliceBlue"></Setter>
<Setter Property="Foreground" Value="AliceBlue" />
</Style>
</Expander.Style>
<ItemsPresenter />
@ -70,14 +74,11 @@
</GroupStyle>
</ListBox.GroupStyle>
</ListBox>
</Grid>
</Border>
</materialDesign:Card>
</ControlTemplate>
<Style x:Key="StyleVisualScriptNodeCreationBox"
TargetType="{x:Type controls:VisualScriptNodeCreationBox}">
<Style x:Key="StyleVisualScriptNodeCreationBox" TargetType="{x:Type controls:VisualScriptNodeCreationBox}">
<Setter Property="Background" Value="#DD333333" />
<Setter Property="BorderBrush" Value="#FF222222" />
<Setter Property="BorderThickness" Value="2" />

View File

@ -1,8 +1,6 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
xmlns:controls="clr-namespace:Artemis.VisualScripting.Editor.Controls"
xmlns:model="clr-namespace:Artemis.VisualScripting.Model;assembly=Artemis.Core"
xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core">
<ControlTemplate x:Key="TemplateVisualScriptPinPresenterInput"

View File

@ -1,4 +1,6 @@
using System.Collections;
using System;
using System.Collections;
using System.Linq;
using Artemis.Core;
namespace Artemis.VisualScripting.Nodes
@ -31,6 +33,12 @@ namespace Artemis.VisualScripting.Nodes
public override void Evaluate()
{
if (Input2.Value != null && Input1.Value != null && Input1.Value.IsNumber() && Input2.Value.IsNumber())
{
Result.Value = Convert.ToSingle(Input1.Value) > Convert.ToSingle(Input2.Value);
return;
}
try
{
Result.Value = Comparer.DefaultInvariant.Compare(Input1.Value, Input2.Value) == 1;
@ -72,6 +80,12 @@ namespace Artemis.VisualScripting.Nodes
public override void Evaluate()
{
if (Input2.Value != null && Input1.Value != null && Input1.Value.IsNumber() && Input2.Value.IsNumber())
{
Result.Value = Convert.ToSingle(Input1.Value) < Convert.ToSingle(Input2.Value);
return;
}
try
{
Result.Value = Comparer.DefaultInvariant.Compare(Input1.Value, Input2.Value) == -1;
@ -156,4 +170,97 @@ namespace Artemis.VisualScripting.Nodes
#endregion
}
}
[Node("And", "Checks if all inputs are true.", "Operators", InputType = typeof(bool), OutputType = typeof(bool))]
public class AndNode : Node
{
#region Properties & Fields
public InputPinCollection<bool> Input { get; set; }
public OutputPin<bool> Result { get; }
#endregion
#region Constructors
public AndNode()
: base("And", "Checks if all inputs are true.")
{
Input = CreateInputPinCollection<bool>();
Result = CreateOutputPin<bool>();
}
#endregion
#region Methods
public override void Evaluate()
{
Result.Value = Input.Values.All(v => v);
}
#endregion
}
[Node("Or", "Checks if any inputs are true.", "Operators", InputType = typeof(bool), OutputType = typeof(bool))]
public class OrNode : Node
{
#region Properties & Fields
public InputPinCollection<bool> Input { get; set; }
public OutputPin<bool> Result { get; }
#endregion
#region Constructors
public OrNode()
: base("Or", "Checks if any inputs are true.")
{
Input = CreateInputPinCollection<bool>();
Result = CreateOutputPin<bool>();
}
#endregion
#region Methods
public override void Evaluate()
{
Result.Value = Input.Values.Any(v => v);
}
#endregion
}
[Node("Exclusive Or", "Checks if one of the inputs is true.", "Operators", InputType = typeof(bool), OutputType = typeof(bool))]
public class XorNode : Node
{
#region Properties & Fields
public InputPinCollection<bool> Input { get; set; }
public OutputPin<bool> Result { get; }
#endregion
#region Constructors
public XorNode()
: base("Exclusive Or", "Checks if one of the inputs is true.")
{
Input = CreateInputPinCollection<bool>();
Result = CreateOutputPin<bool>();
}
#endregion
#region Methods
public override void Evaluate()
{
Result.Value = Input.Values.Count(v => v) == 1;
}
#endregion
}
}

View File

@ -79,53 +79,7 @@ namespace Artemis.VisualScripting.Nodes
#endregion
}
[Node("To Double", "Converts the input to a double.", "Conversion", InputType = typeof(object), OutputType = typeof(double))]
public class ConvertToDoubleNode : Node
{
#region Properties & Fields
public InputPin<object> Input { get; }
public OutputPin<double> Double { get; }
#endregion
#region Constructors
public ConvertToDoubleNode()
: base("To Double", "Converts the input to a double.")
{
Input = CreateInputPin<object>();
Double = CreateOutputPin<double>();
}
#endregion
#region Methods
public override void Evaluate()
{
Double.Value = Input.Value switch
{
int input => input,
double input => input,
float input => input,
_ => TryParse(Input.Value)
};
}
private double TryParse(object input)
{
if (!double.TryParse(input?.ToString(), out double value))
value = 0.0;
return value;
}
#endregion
}
[Node("To Float", "Converts the input to a float.", "Conversion", InputType = typeof(object), OutputType = typeof(float))]
public class ConvertToFloatNode : Node
{

View File

@ -8,7 +8,7 @@ using Artemis.VisualScripting.Nodes.DataModel.CustomViewModels;
namespace Artemis.VisualScripting.Nodes.DataModel
{
[Node("Data Model-Event", "Responds to a data model event trigger", "External", OutputType = typeof(bool))]
[Node("Data Model-Event", "Responds to a data model event trigger", "Data Model", OutputType = typeof(bool))]
public class DataModelEventNode : Node<DataModelPathEntity, DataModelEventNodeCustomViewModel>, IDisposable
{
private DataModelPath _dataModelPath;

View File

@ -5,7 +5,7 @@ using Artemis.VisualScripting.Nodes.DataModel.CustomViewModels;
namespace Artemis.VisualScripting.Nodes.DataModel
{
[Node("Data Model-Value", "Outputs a selectable data model value.", "External")]
[Node("Data Model-Value", "Outputs a selectable data model value.", "Data Model")]
public class DataModelNode : Node<DataModelPathEntity, DataModelNodeCustomViewModel>, IDisposable
{
private DataModelPath _dataModelPath;
@ -44,14 +44,29 @@ namespace Artemis.VisualScripting.Nodes.DataModel
UpdateOutputPin(false);
object pathValue = DataModelPath.GetValue();
if (pathValue != null)
Output.Value = pathValue;
if (pathValue == null)
{
if (!Output.Type.IsValueType)
Output.Value = null;
}
else
{
if (pathValue is double doublePathValue)
Output.Value = (float) doublePathValue;
else
Output.Value = pathValue;
}
}
}
public void UpdateOutputPin(bool loadConnections)
{
if (Output != null && Output.Type == DataModelPath?.GetPropertyType())
Type type = DataModelPath?.GetPropertyType();
if (type == typeof(double))
type = typeof(float);
if (Output != null && Output.Type == type)
return;
if (Output != null)
@ -60,8 +75,7 @@ namespace Artemis.VisualScripting.Nodes.DataModel
Output = null;
}
Type type = DataModelPath?.GetPropertyType();
if (type != null)
if (type != null)
Output = CreateOutputPin(type);
if (loadConnections && Script is NodeScript nodeScript)

View File

@ -1,68 +0,0 @@
using System;
using Artemis.Core;
namespace Artemis.VisualScripting.Nodes.Easing
{
[Node("Double Easing", "Outputs an eased double value", "Easing", InputType = typeof(double), OutputType = typeof(double))]
public class DoubleEasingNode : Node
{
private DateTime _lastEvaluate = DateTime.MinValue;
private float _progress;
private double _currentValue;
private double _sourceValue;
private double _targetValue;
public DoubleEasingNode() : base("Double Easing", "Outputs an eased double value")
{
Input = CreateInputPin<double>();
EasingTime = CreateInputPin<float>("delay");
EasingFunction = CreateInputPin<Easings.Functions>("function");
Output = CreateOutputPin<double>();
}
public InputPin<double> Input { get; set; }
public InputPin<float> EasingTime { get; set; }
public InputPin<Easings.Functions> EasingFunction { get; set; }
public OutputPin<double> Output { get; set; }
public override void Evaluate()
{
DateTime now = DateTime.Now;
// If the value changed reset progress
if (Math.Abs(_targetValue - Input.Value) > 0.001f)
{
_sourceValue = _currentValue;
_targetValue = Input.Value;
_progress = 0f;
}
// Update until finished
if (_progress < 1f)
{
Update();
Output.Value = _currentValue;
}
// Stop updating past 1 and use the target value
else
{
Output.Value = _targetValue;
}
_lastEvaluate = now;
}
private void Update()
{
TimeSpan delta = DateTime.Now - _lastEvaluate;
// In case of odd delta's, keep progress between 0f and 1f
_progress = Math.Clamp(_progress + (float)delta.TotalMilliseconds / EasingTime.Value, 0f, 1f);
double eased = _sourceValue + (_targetValue - _sourceValue) * Easings.Interpolate(_progress, EasingFunction.Value);
_currentValue = eased;
}
}
}

View File

@ -8,7 +8,7 @@ using NoStringEvaluating.Models.Values;
namespace Artemis.VisualScripting.Nodes.Maths
{
[Node("Math Expression", "Outputs the result of a math expression.", "Math", InputType = typeof(float), OutputType = typeof(float))]
[Node("Math Expression", "Outputs the result of a math expression.", "Mathematics", InputType = typeof(float), OutputType = typeof(float))]
public class MathExpressionNode : Node<string, MathExpressionNodeCustomViewModel>
{
private readonly INoStringEvaluator _evaluator;
@ -42,7 +42,6 @@ namespace Artemis.VisualScripting.Nodes.Maths
public override void Evaluate()
{
var test = _evaluator.ToString();
if (Storage != null)
Output.Value = (float) _evaluator.CalcNumber(Storage, _variables);
}

View File

@ -3,7 +3,7 @@ using Artemis.VisualScripting.Nodes.CustomViewModels;
namespace Artemis.VisualScripting.Nodes
{
[Node("Integer-Value", "Outputs an configurable integer value.", "Static", OutputType = typeof(int))]
[Node("Integer-Value", "Outputs a configurable static integer value.", "Static", OutputType = typeof(int))]
public class StaticIntegerValueNode : Node<int, StaticIntegerValueNodeCustomViewModel>
{
#region Properties & Fields
@ -32,36 +32,7 @@ namespace Artemis.VisualScripting.Nodes
#endregion
}
[Node("Double-Value", "Outputs a configurable double value.", "Static", OutputType = typeof(double))]
public class StaticDoubleValueNode : Node<double, StaticDoubleValueNodeCustomViewModel>
{
#region Properties & Fields
public OutputPin<double> Output { get; }
#endregion
#region Constructors
public StaticDoubleValueNode()
: base("Double", "Outputs a configurable double value.")
{
Output = CreateOutputPin<double>();
}
#endregion
#region Methods
public override void Evaluate()
{
Output.Value = Storage as double? ?? 0.0;
}
#endregion
}
[Node("Float-Value", "Outputs a configurable float value.", "Static", OutputType = typeof(float))]
[Node("Float-Value", "Outputs a configurable static float value.", "Static", OutputType = typeof(float))]
public class StaticFloatValueNode : Node<float, StaticFloatValueNodeCustomViewModel>
{
#region Properties & Fields
@ -90,7 +61,7 @@ namespace Artemis.VisualScripting.Nodes
#endregion
}
[Node("String-Value", "Outputs a configurable string value.", "Static", OutputType = typeof(string))]
[Node("String-Value", "Outputs a configurable static string value.", "Static", OutputType = typeof(string))]
public class StaticStringValueNode : Node<string, StaticStringValueNodeCustomViewModel>
{
#region Properties & Fields

View File

@ -66,36 +66,4 @@ namespace Artemis.VisualScripting.Nodes
#endregion
}
[Node("Sum (Double)", "Sums the connected double values.", "Mathematics", InputType = typeof(double), OutputType = typeof(double))]
public class SumDoublesNode : Node
{
#region Properties & Fields
public InputPinCollection<double> Values { get; }
public OutputPin<double> Sum { get; }
#endregion
#region Constructors
public SumDoublesNode()
: base("Sum", "Sums the connected double values.")
{
Values = CreateInputPinCollection<double>("Values", 2);
Sum = CreateOutputPin<double>("Sum");
}
#endregion
#region Methods
public override void Evaluate()
{
Sum.Value = Values.Values.Sum();
}
#endregion
}
}