Nodes - Added node type registration system

Nodes - Moved models and node logic into core
Nodes - Added node service that leverages DI
This commit is contained in:
Robert committed 2021-08-07 21:05:15 +02:00
1 parent 51541b4e6e
commit 7eadc58bee
57 files changed
+481 -259

No files matched your search

@@ -1,5 +1,6 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=defaulttypes/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Cnodes/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Cprofileconfiguration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Cprofile_005Cadaption/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Cprofile_005Cadaptionhints/@EntryIndexedValue">True</s:Boolean>
@@ -49,6 +50,8 @@
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Cprofile_005Clayershapes/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Csurface/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Csurface_005Clayout/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Cvisualscripting/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Cvisualscripting_005Cinterfaces/@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/=placeholders/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins/@EntryIndexedValue">True</s:Boolean>
@@ -83,4 +86,6 @@
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cwebserver_005Cjson/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=stores/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=stores_005Cregistrations/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=utilities/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=utilities/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=visualscripting/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=visualscripting_005Cinterfaces/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
@@ -0,0 +1,12 @@
namespace Artemis.Core
{
internal class NodeTypeStoreEvent
{
public NodeTypeStoreEvent(NodeTypeRegistration typeRegistration)
{
TypeRegistration = typeRegistration;
}
public NodeTypeRegistration TypeRegistration { get; }
}
}
@@ -66,9 +66,9 @@ namespace Artemis.Core
internal void LoadRenderElement()
{
DisplayCondition = RenderElementEntity.DisplayCondition != null
? new DataModelConditionGroup(null, RenderElementEntity.DisplayCondition)
: new DataModelConditionGroup(null);
// DisplayCondition = RenderElementEntity.DisplayCondition != null
// ? new DataModelConditionGroup(null, RenderElementEntity.DisplayCondition)
// : new DataModelConditionGroup(null);
Timeline = RenderElementEntity.Timeline != null
? new Timeline(RenderElementEntity.Timeline)
@@ -97,8 +97,8 @@ namespace Artemis.Core
}
// Conditions
RenderElementEntity.DisplayCondition = DisplayCondition?.Entity;
DisplayCondition?.Save();
// RenderElementEntity.DisplayCondition = DisplayCondition?.Entity;
// DisplayCondition?.Save();
// Timeline
RenderElementEntity.Timeline = Timeline?.Entity;
@@ -125,8 +125,8 @@ namespace Artemis.Core
{
// The play mode dictates whether to stick to the main segment unless the display conditions contains events
bool stickToMainSegment = (Timeline.PlayMode == TimelinePlayMode.Repeat || Timeline.EventOverlapMode == TimeLineEventOverlapMode.Toggle) && DisplayConditionMet;
if (DisplayCondition != null && DisplayCondition.ContainsEvents && Timeline.EventOverlapMode != TimeLineEventOverlapMode.Toggle)
stickToMainSegment = false;
// if (DisplayCondition != null && DisplayCondition.ContainsEvents && Timeline.EventOverlapMode != TimeLineEventOverlapMode.Toggle)
// stickToMainSegment = false;
Timeline.Update(TimeSpan.FromSeconds(deltaTime), stickToMainSegment);
}
@@ -354,14 +354,14 @@ namespace Artemis.Core
protected set => SetAndNotify(ref _displayConditionMet, value);
}
private DataModelConditionGroup? _displayCondition;
private NodeScript<bool>? _displayCondition;
private bool _displayConditionMet;
private bool _toggledOnByEvent = false;
/// <summary>
/// Gets or sets the root display condition group
/// </summary>
public DataModelConditionGroup? DisplayCondition
public NodeScript<bool>? DisplayCondition
{
get => _displayCondition;
set => SetAndNotify(ref _displayCondition, value);
@@ -387,20 +387,22 @@ namespace Artemis.Core
if (Timeline.EventOverlapMode != TimeLineEventOverlapMode.Toggle)
_toggledOnByEvent = false;
bool conditionMet = DisplayCondition.Evaluate();
DisplayCondition.Run();
bool conditionMet = DisplayCondition.Result;
if (Parent is RenderProfileElement parent && !parent.DisplayConditionMet)
conditionMet = false;
if (!DisplayCondition.ContainsEvents)
{
// Regular conditions reset the timeline whenever their condition is met and was not met before that
if (conditionMet && !DisplayConditionMet && Timeline.IsFinished)
Timeline.JumpToStart();
// If regular conditions are no longer met, jump to the end segment if stop mode requires it
if (!conditionMet && Timeline.StopMode == TimelineStopMode.SkipToEnd)
Timeline.JumpToEndSegment();
}
else if (conditionMet)
// if (!DisplayCondition.ContainsEvents)
// {
// // Regular conditions reset the timeline whenever their condition is met and was not met before that
// if (conditionMet && !DisplayConditionMet && Timeline.IsFinished)
// Timeline.JumpToStart();
// // If regular conditions are no longer met, jump to the end segment if stop mode requires it
// if (!conditionMet && Timeline.StopMode == TimelineStopMode.SkipToEnd)
// Timeline.JumpToEndSegment();
// }
// else if (conditionMet)
if (conditionMet)
{
if (Timeline.EventOverlapMode == TimeLineEventOverlapMode.Toggle)
{
+2 -2
View File
@@ -107,7 +107,7 @@ namespace Artemis.Core
GetRegionData(skRectI.Left, skRectI.Top, skRectI.Width, skRectI.Height, buffer);
Span<byte> pixelData = stackalloc byte[DATA_PER_PIXEL];
Sampler.SampleColor(new SamplerInfo<byte>(skRectI.Width, skRectI.Height, buffer), pixelData);
Sampler.Sample(new SamplerInfo<byte>(skRectI.Width, skRectI.Height, buffer), pixelData);
return GetColor(pixelData);
}
@@ -119,7 +119,7 @@ namespace Artemis.Core
GetRegionData(skRectI.Left, skRectI.Top, skRectI.Width, skRectI.Height, buffer);
Span<byte> pixelData = stackalloc byte[DATA_PER_PIXEL];
Sampler.SampleColor(new SamplerInfo<byte>(skRectI.Width, skRectI.Height, buffer), pixelData);
Sampler.Sample(new SamplerInfo<byte>(skRectI.Width, skRectI.Height, buffer), pixelData);
ArrayPool<byte>.Shared.Return(rent);
@@ -4,7 +4,7 @@ using Artemis.Core.ScriptingProviders;
namespace Artemis.Core.Services
{
/// <summary>
/// A service that allows you to manage various types of <see cref="Script" /> instances
/// A service that allows you to manage various types of <see cref="NodeScript" /> instances
/// </summary>
public interface IScriptingService : IArtemisService
{
+79
View File
@@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Ninject;
namespace Artemis.Core.Services
{
internal class NodeService : INodeService
{
private readonly IKernel _kernel;
#region Constants
private static readonly Type TYPE_NODE = typeof(INode);
#endregion
#region Properties & Fields
public IEnumerable<NodeData> AvailableNodes => NodeTypeStore.GetAll();
#endregion
#region Constructors
public NodeService(IKernel kernel)
{
_kernel = kernel;
}
#endregion
#region Methods
public NodeTypeRegistration RegisterNodeType(Plugin plugin, Type nodeType)
{
if (plugin == null) throw new ArgumentNullException(nameof(plugin));
if (nodeType == null) throw new ArgumentNullException(nameof(nodeType));
if (!TYPE_NODE.IsAssignableFrom(nodeType)) throw new ArgumentException("Node has to be a base type of the Node-Type.", nameof(nodeType));
NodeAttribute? nodeAttribute = nodeType.GetCustomAttribute<NodeAttribute>();
string name = nodeAttribute?.Name ?? nodeType.Name;
string description = nodeAttribute?.Description ?? string.Empty;
string category = nodeAttribute?.Category ?? string.Empty;
NodeData nodeData = new(plugin, nodeType, name, description, category, () => CreateNode(nodeType));
return NodeTypeStore.Add(nodeData);
}
private INode CreateNode(Type nodeType)
{
INode node = _kernel.Get(nodeType) as INode ?? throw new InvalidOperationException($"Node {nodeType} is not an INode");
if (node.CustomViewModelType != null)
node.CustomViewModel = _kernel.Get(node.CustomViewModelType);
return node;
}
#endregion
}
/// <summary>
/// A service that provides access to the node system
/// </summary>
public interface INodeService : IArtemisService
{
/// <summary>
/// Gets all available nodes
/// </summary>
IEnumerable<NodeData> AvailableNodes { get; }
/// <summary>
/// Initializes a node of the provided <paramref name="nodeType"/>
/// </summary>
/// <param name="plugin">The plugin the node belongs to</param>
/// <param name="nodeType">The type of node to initialize</param>
NodeTypeRegistration RegisterNodeType(Plugin plugin, Type nodeType);
}
}
+77
View File
@@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Artemis.Core
{
internal class NodeTypeStore
{
private static readonly List<NodeTypeRegistration> Registrations = new();
public static NodeTypeRegistration Add(NodeData nodeData)
{
if (nodeData.Plugin == null)
throw new ArtemisCoreException("Cannot add a data binding modifier type that is not associated with a plugin");
NodeTypeRegistration typeRegistration;
lock (Registrations)
{
if (Registrations.Any(r => r.NodeData == nodeData))
throw new ArtemisCoreException($"Data binding modifier type store already contains modifier '{nodeData.Name}'");
typeRegistration = new NodeTypeRegistration(nodeData, nodeData.Plugin) { IsInStore = true };
Registrations.Add(typeRegistration);
}
OnDataBindingModifierAdded(new NodeTypeStoreEvent(typeRegistration));
return typeRegistration;
}
public static void Remove(NodeTypeRegistration typeRegistration)
{
lock (Registrations)
{
if (!Registrations.Contains(typeRegistration))
throw new ArtemisCoreException($"Data binding modifier type store does not contain modifier type '{typeRegistration.NodeData.Name}'");
Registrations.Remove(typeRegistration);
typeRegistration.IsInStore = false;
}
OnDataBindingModifierRemoved(new NodeTypeStoreEvent(typeRegistration));
}
public static IEnumerable<NodeData> GetAll()
{
lock (Registrations)
{
return Registrations.Select(r => r.NodeData).ToList();
}
}
public static NodeTypeRegistration? Get(Guid pluginGuid, string type)
{
lock (Registrations)
{
return Registrations.FirstOrDefault(r => r.Plugin.Guid == pluginGuid && r.NodeData.Type.Name == type);
}
}
#region Events
public static event EventHandler<NodeTypeStoreEvent>? DataBindingModifierAdded;
public static event EventHandler<NodeTypeStoreEvent>? DataBindingModifierRemoved;
private static void OnDataBindingModifierAdded(NodeTypeStoreEvent e)
{
DataBindingModifierAdded?.Invoke(null, e);
}
private static void OnDataBindingModifierRemoved(NodeTypeStoreEvent e)
{
DataBindingModifierRemoved?.Invoke(null, e);
}
#endregion
}
}
@@ -0,0 +1,37 @@
using System;
namespace Artemis.Core
{
/// <summary>
/// Represents a registration for a type of <see cref="INode"/>
/// </summary>
public class NodeTypeRegistration
{
internal NodeTypeRegistration(NodeData nodeData, Plugin plugin)
{
NodeData = nodeData;
Plugin = plugin;
Plugin.Disabled += OnDisabled;
}
public NodeData NodeData { get; }
/// <summary>
/// Gets the plugin the node is associated with
/// </summary>
public Plugin Plugin { get; }
/// <summary>
/// Gets a boolean indicating whether the registration is in the internal Core store
/// </summary>
public bool IsInStore { get; internal set; }
private void OnDisabled(object? sender, EventArgs e)
{
Plugin.Disabled -= OnDisabled;
if (IsInStore)
NodeTypeStore.Remove(this);
}
}
}
@@ -0,0 +1,100 @@
using System;
namespace Artemis.Core
{
public sealed class InputPin<T> : Pin
{
#region Properties & Fields
public override Type Type { get; } = typeof(T);
public override object PinValue => Value;
public override PinDirection Direction => PinDirection.Input;
private T _value;
public T Value
{
get
{
if (!IsEvaluated)
Evaluate();
return _value;
}
private set
{
_value = value;
IsEvaluated = true;
}
}
#endregion
#region Constructors
internal InputPin(INode node, string name)
: base(node, name)
{ }
#endregion
#region Methods
private void Evaluate()
{
Value = ConnectedTo.Count > 0 ? (T)ConnectedTo[0].PinValue : default;
}
#endregion
}
public sealed class InputPin : Pin
{
#region Properties & Fields
public override Type Type { get; }
public override object PinValue => Value;
public override PinDirection Direction => PinDirection.Input;
private object _value;
public object Value
{
get
{
if (!IsEvaluated)
Evaluate();
return _value;
}
private set
{
if (!Type.IsInstanceOfType(value)) throw new ArgumentException($"Value of type '{value?.GetType().Name ?? "null"}' can't be assigned to a pin of type {Type.Name}.");
_value = value;
IsEvaluated = true;
}
}
#endregion
#region Constructors
internal InputPin(INode node, Type type, string name)
: base(node, name)
{
this.Type = type;
}
#endregion
#region Methods
private void Evaluate()
{
Value = ConnectedTo.Count > 0 ? ConnectedTo[0].PinValue : default;
}
#endregion
}
}
@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Artemis.Core
{
public sealed class InputPinCollection<T> : PinCollection
{
#region Properties & Fields
public override PinDirection Direction => PinDirection.Input;
public override Type Type => typeof(T);
public new IEnumerable<InputPin<T>> Pins => base.Pins.Cast<InputPin<T>>();
public IEnumerable<T> Values => Pins.Select(p => p.Value);
#endregion
#region Constructors
internal InputPinCollection(INode node, string name, int initialCount)
: base(node, name, initialCount)
{ }
#endregion
#region Methods
protected override IPin CreatePin() => new InputPin<T>(Node, string.Empty);
#endregion
}
}
@@ -2,12 +2,13 @@
using System.Collections.Generic;
using System.ComponentModel;
namespace Artemis.Core.VisualScripting
namespace Artemis.Core
{
public interface INode : INotifyPropertyChanged
{
string Name { get; }
string Description { get; }
bool IsExitNode { get; }
public double X { get; set; }
public double Y { get; set; }
@@ -15,8 +16,8 @@ namespace Artemis.Core.VisualScripting
public IReadOnlyCollection<IPin> Pins { get; }
public IReadOnlyCollection<IPinCollection> PinCollections { get; }
public object CustomView { get; }
public object CustomViewModel { get; }
public Type? CustomViewModelType { get; }
public object? CustomViewModel { get; set; }
event EventHandler Resetting;
@@ -2,9 +2,9 @@
using System.Collections.Generic;
using System.ComponentModel;
namespace Artemis.Core.VisualScripting
namespace Artemis.Core
{
public interface IScript : INotifyPropertyChanged, IDisposable
public interface INodeScript : INotifyPropertyChanged, IDisposable
{
string Name { get; }
string Description { get; }
@@ -18,7 +18,7 @@ namespace Artemis.Core.VisualScripting
void RemoveNode(INode node);
}
public interface IScript<out T> : IScript
public interface INodeScript<out T> : INodeScript
{
T Result { get; }
}
@@ -1,8 +1,7 @@
using System;
using System.Collections.Generic;
using Artemis.VisualScripting.Model;
namespace Artemis.Core.VisualScripting
namespace Artemis.Core
{
public interface IPin
{
@@ -1,8 +1,7 @@
using System;
using System.Collections.Generic;
using Artemis.VisualScripting.Model;
namespace Artemis.Core.VisualScripting
namespace Artemis.Core
{
public interface IPinCollection : IEnumerable<IPin>
{
@@ -0,0 +1,39 @@
namespace Artemis.Core.Internal
{
internal interface IExitNode : INode
{ }
internal class ExitNode<T> : Node, IExitNode
{
#region Properties & Fields
public InputPin<T> Input { get; }
public T Value { get; private set; }
public override bool IsExitNode => true;
#endregion
#region Constructors
public ExitNode(string name, string description = "")
{
this.Name = name;
this.Description = description;
Input = CreateInputPin<T>();
}
#endregion
#region Methods
public override void Evaluate()
{
Value = Input.Value;
}
#endregion
}
}
+152
View File
@@ -0,0 +1,152 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace Artemis.Core
{
public abstract class Node : CorePropertyChanged, INode
{
#region Properties & Fields
private string _name;
public string Name
{
get => _name;
protected set => SetAndNotify(ref _name, value);
}
private string _description;
public string Description
{
get => _description;
protected set => SetAndNotify(ref _description, value);
}
private double _x;
public double X
{
get => _x;
set => SetAndNotify(ref _x, value);
}
private double _y;
public double Y
{
get => _y;
set => SetAndNotify(ref _y, value);
}
public virtual bool IsExitNode => false;
private readonly List<IPin> _pins = new();
public IReadOnlyCollection<IPin> Pins => new ReadOnlyCollection<IPin>(_pins);
private readonly List<IPinCollection> _pinCollections = new();
public IReadOnlyCollection<IPinCollection> PinCollections => new ReadOnlyCollection<IPinCollection>(_pinCollections);
public Type? CustomViewModelType { get; private set; }
public object? CustomViewModel { get; set; }
#endregion
#region Events
public event EventHandler Resetting;
#endregion
#region Construtors
protected Node()
{
}
protected Node(string name, string description)
{
this.Name = name;
this.Description = description;
}
#endregion
#region Methods
protected InputPin<T> CreateInputPin<T>(string name = "")
{
InputPin<T> pin = new(this, name);
_pins.Add(pin);
OnPropertyChanged(nameof(Pins));
return pin;
}
protected InputPin CreateInputPin(Type type, string name = "")
{
InputPin pin = new(this, type, name);
_pins.Add(pin);
OnPropertyChanged(nameof(Pins));
return pin;
}
protected OutputPin<T> CreateOutputPin<T>(string name = "")
{
OutputPin<T> pin = new(this, name);
_pins.Add(pin);
OnPropertyChanged(nameof(Pins));
return pin;
}
protected OutputPin CreateOutputPin(Type type, string name = "")
{
OutputPin pin = new(this, type, name);
_pins.Add(pin);
OnPropertyChanged(nameof(Pins));
return pin;
}
protected bool RemovePin(Pin pin)
{
bool isRemoved = _pins.Remove(pin);
if (isRemoved)
{
pin.DisconnectAll();
OnPropertyChanged(nameof(Pins));
}
return isRemoved;
}
protected InputPinCollection<T> CreateInputPinCollection<T>(string name = "", int initialCount = 1)
{
InputPinCollection<T> pin = new(this, name, initialCount);
_pinCollections.Add(pin);
OnPropertyChanged(nameof(PinCollections));
return pin;
}
protected OutputPinCollection<T> CreateOutputPinCollection<T>(string name = "", int initialCount = 1)
{
OutputPinCollection<T> pin = new(this, name, initialCount);
_pinCollections.Add(pin);
OnPropertyChanged(nameof(PinCollections));
return pin;
}
protected void RegisterCustomViewModel<T>()
{
CustomViewModelType = typeof(T);
}
public abstract void Evaluate();
public virtual void Reset()
{
Resetting?.Invoke(this, new EventArgs());
}
#endregion
}
}
@@ -0,0 +1,37 @@
using System;
namespace Artemis.Core
{
public class NodeAttribute : Attribute
{
#region Properties & Fields
public string Name { get; }
public string Description { get; set; }
public string Category { get; set; }
#endregion
#region Constructors
public NodeAttribute(string name)
{
this.Name = name;
}
public NodeAttribute(string name, string description)
{
this.Name = name;
this.Description = description;
}
public NodeAttribute(string name, string description, string category)
{
this.Name = name;
this.Description = description;
this.Category = category;
}
#endregion
}
}
@@ -0,0 +1,40 @@
using System;
namespace Artemis.Core
{
public class NodeData
{
#region Properties & Fields
public Plugin Plugin { get; }
public Type Type { get; }
public string Name { get; }
public string Description { get; }
public string Category { get; }
private Func<INode> _create;
#endregion
#region Constructors
internal NodeData(Plugin plugin, Type type, string name, string description, string category, Func<INode> create)
{
this.Plugin = plugin;
this.Type = type;
this.Name = name;
this.Description = description;
this.Category = category;
this._create = create;
}
#endregion
#region Methods
public INode CreateNode() => _create();
#endregion
}
}
@@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Artemis.Core.Internal;
using Artemis.Core.Properties;
namespace Artemis.Core
{
public abstract class NodeScript : CorePropertyChanged, INodeScript
{
#region Properties & Fields
public string Name { get; }
public string Description { get; }
private readonly List<INode> _nodes = new();
public IEnumerable<INode> Nodes => new ReadOnlyCollection<INode>(_nodes);
protected INode ExitNode { get; set; }
public abstract Type ResultType { get; }
#endregion
#region Constructors
public NodeScript(string name, string description)
{
this.Name = name;
this.Description = description;
}
#endregion
#region Methods
public void Run()
{
foreach (INode node in Nodes)
node.Reset();
ExitNode.Evaluate();
}
public void AddNode(INode node)
{
_nodes.Add(node);
}
public void RemoveNode(INode node)
{
_nodes.Remove(node);
}
public void Dispose()
{ }
#endregion
}
public class NodeScript<T> : NodeScript, INodeScript<T>
{
#region Properties & Fields
public T Result => ((ExitNode<T>)ExitNode).Value;
public override Type ResultType => typeof(T);
#endregion
#region Constructors
public NodeScript(string name, string description)
: base(name, description)
{
ExitNode = new ExitNode<T>(name, description);
AddNode(ExitNode);
}
[UsedImplicitly(ImplicitUseKindFlags.InstantiatedWithFixedConstructorSignature)]
private NodeScript(string name, string description, INode exitNode)
: base(name, description)
{
ExitNode = exitNode;
AddNode(ExitNode);
}
#endregion
}
}
@@ -0,0 +1,80 @@
using System;
namespace Artemis.Core
{
public sealed class OutputPin<T> : Pin
{
#region Properties & Fields
public override Type Type { get; } = typeof(T);
public override object PinValue => Value;
public override PinDirection Direction => PinDirection.Output;
private T _value;
public T Value
{
get
{
if (!IsEvaluated)
Node?.Evaluate();
return _value;
}
set
{
_value = value;
IsEvaluated = true;
}
}
#endregion
#region Constructors
internal OutputPin(INode node, string name)
: base(node, name)
{ }
#endregion
}
public sealed class OutputPin : Pin
{
#region Properties & Fields
public override Type Type { get; }
public override object PinValue => Value;
public override PinDirection Direction => PinDirection.Output;
private object _value;
public object Value
{
get
{
if (!IsEvaluated)
Node?.Evaluate();
return _value;
}
set
{
if (!Type.IsInstanceOfType(value)) throw new ArgumentException($"Value of type '{value?.GetType().Name ?? "null"}' can't be assigned to a pin of type {Type.Name}.");
_value = value;
IsEvaluated = true;
}
}
#endregion
#region Constructors
internal OutputPin(INode node, Type type, string name)
: base(node, name)
{
this.Type = type;
}
#endregion
}
}
@@ -0,0 +1,28 @@
using System;
namespace Artemis.Core
{
public sealed class OutputPinCollection<T> : PinCollection
{
#region Properties & Fields
public override PinDirection Direction => PinDirection.Output;
public override Type Type => typeof(T);
#endregion
#region Constructors
internal OutputPinCollection(INode node, string name, int initialCount)
: base(node, name, initialCount)
{ }
#endregion
#region Methods
protected override IPin CreatePin() => new OutputPin<T>(Node, string.Empty);
#endregion
}
}
+70
View File
@@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace Artemis.Core
{
public abstract class Pin : CorePropertyChanged, IPin
{
#region Properties & Fields
public INode Node { get; }
public string Name { get; }
private bool _isEvaluated;
public bool IsEvaluated
{
get => _isEvaluated;
set => SetAndNotify(ref _isEvaluated, value);
}
private readonly List<IPin> _connectedTo = new();
public IReadOnlyList<IPin> ConnectedTo => new ReadOnlyCollection<IPin>(_connectedTo);
public abstract PinDirection Direction { get; }
public abstract Type Type { get; }
public abstract object PinValue { get; }
#endregion
#region Constructors
protected Pin(INode node, string name = "")
{
this.Node = node;
this.Name = name;
if (Node != null)
Node.Resetting += OnNodeResetting;
}
#endregion
#region Methods
public void ConnectTo(IPin pin)
{
_connectedTo.Add(pin);
OnPropertyChanged(nameof(ConnectedTo));
}
public void DisconnectFrom(IPin pin)
{
_connectedTo.Remove(pin);
OnPropertyChanged(nameof(ConnectedTo));
}
public void DisconnectAll()
{
_connectedTo.Clear();
OnPropertyChanged(nameof(ConnectedTo));
}
private void OnNodeResetting(object sender, EventArgs e)
{
IsEvaluated = false;
}
#endregion
}
}
@@ -0,0 +1,54 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace Artemis.Core
{
public abstract class PinCollection : IPinCollection
{
#region Properties & Fields
public INode Node { get; }
public string Name { get; }
public abstract PinDirection Direction { get; }
public abstract Type Type { get; }
private readonly ObservableCollection<IPin> _pins = new();
public ReadOnlyObservableCollection<IPin> Pins => new(_pins);
#endregion
#region Constructors
protected PinCollection(INode node, string name, int initialCount)
{
this.Node = node;
this.Name = name;
for (int i = 0; i < initialCount; i++)
AddPin();
}
#endregion
#region Methods
public IPin AddPin()
{
IPin pin = CreatePin();
_pins.Add(pin);
return pin;
}
public bool Remove(IPin pin) => _pins.Remove(pin);
protected abstract IPin CreatePin();
public IEnumerator<IPin> GetEnumerator() => Pins.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
#endregion
}
}
@@ -1,4 +1,4 @@
namespace Artemis.VisualScripting.Model
namespace Artemis.Core
{
public enum PinDirection
{