mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-11 11:26:27 +00:00
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:
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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -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;
|
||||
|
||||
+3
-3
@@ -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
-2
@@ -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
-2
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user