mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-11 11:26:27 +00:00
Nodes - Implemented saving/loading
Nodes - Added data model node
This commit is contained in:
1 parent
61d7f1e1fb
commit
afc4bee7ac
29 files changed
+730
-157
No files matched your search
@@ -92,5 +92,17 @@ namespace Artemis.Core
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int IndexOf<T>(this IReadOnlyCollection<T> self, T elementToFind)
|
||||
{
|
||||
int i = 0;
|
||||
foreach (T element in self)
|
||||
{
|
||||
if (Equals(element, elementToFind))
|
||||
return i;
|
||||
i++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,7 +72,12 @@ namespace Artemis.Core
|
||||
SubscribeToDataModelStore();
|
||||
}
|
||||
|
||||
internal DataModelPath(DataModel? target, DataModelPathEntity entity)
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="DataModelPath" /> class based on a <see cref="DataModelPathEntity" />
|
||||
/// </summary>
|
||||
/// <param name="target"></param>
|
||||
/// <param name="entity"></param>
|
||||
public DataModelPath(DataModel? target, DataModelPathEntity entity)
|
||||
{
|
||||
Target = target;
|
||||
Path = entity.Path;
|
||||
@@ -110,7 +115,10 @@ namespace Artemis.Core
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<DataModelPathSegment> Segments => _segments.ToList().AsReadOnly();
|
||||
|
||||
internal DataModelPathEntity Entity { get; }
|
||||
/// <summary>
|
||||
/// Gets the entity used for persistent storage
|
||||
/// </summary>
|
||||
public DataModelPathEntity Entity { get; }
|
||||
|
||||
internal Func<object, object>? Accessor { get; private set; }
|
||||
|
||||
@@ -173,6 +181,52 @@ namespace Artemis.Core
|
||||
return string.IsNullOrWhiteSpace(Path) ? "this" : Path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs whenever the path becomes invalid
|
||||
/// </summary>
|
||||
public event EventHandler? PathInvalidated;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs whenever the path becomes valid
|
||||
/// </summary>
|
||||
public event EventHandler? PathValidated;
|
||||
|
||||
/// <summary>
|
||||
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing">
|
||||
/// <see langword="true" /> to release both managed and unmanaged resources;
|
||||
/// <see langword="false" /> to release only unmanaged resources.
|
||||
/// </param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_disposed = true;
|
||||
|
||||
DataModelStore.DataModelAdded -= DataModelStoreOnDataModelAdded;
|
||||
DataModelStore.DataModelRemoved -= DataModelStoreOnDataModelRemoved;
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="PathInvalidated" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnPathValidated()
|
||||
{
|
||||
PathValidated?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="PathValidated" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnPathInvalidated()
|
||||
{
|
||||
PathInvalidated?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
internal void Invalidate()
|
||||
{
|
||||
Target?.RemoveDataModelPath(this);
|
||||
@@ -262,27 +316,24 @@ namespace Artemis.Core
|
||||
DataModelStore.DataModelAdded += DataModelStoreOnDataModelAdded;
|
||||
DataModelStore.DataModelRemoved += DataModelStoreOnDataModelRemoved;
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
/// <summary>
|
||||
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing">
|
||||
/// <see langword="true" /> to release both managed and unmanaged resources;
|
||||
/// <see langword="false" /> to release only unmanaged resources.
|
||||
/// </param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
private void DataModelStoreOnDataModelAdded(object? sender, DataModelStoreEvent e)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_disposed = true;
|
||||
if (e.Registration.DataModel.Module.Id != Entity.DataModelId)
|
||||
return;
|
||||
|
||||
DataModelStore.DataModelAdded -= DataModelStoreOnDataModelAdded;
|
||||
DataModelStore.DataModelRemoved -= DataModelStoreOnDataModelRemoved;
|
||||
Invalidate();
|
||||
Target = e.Registration.DataModel;
|
||||
Initialize();
|
||||
}
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
private void DataModelStoreOnDataModelRemoved(object? sender, DataModelStoreEvent e)
|
||||
{
|
||||
if (e.Registration.DataModel.Module.Id != Entity.DataModelId)
|
||||
return;
|
||||
|
||||
Invalidate();
|
||||
Target = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -292,8 +343,6 @@ namespace Artemis.Core
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Storage
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -324,58 +373,5 @@ namespace Artemis.Core
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event handlers
|
||||
|
||||
private void DataModelStoreOnDataModelAdded(object? sender, DataModelStoreEvent e)
|
||||
{
|
||||
if (e.Registration.DataModel.Module.Id != Entity.DataModelId)
|
||||
return;
|
||||
|
||||
Invalidate();
|
||||
Target = e.Registration.DataModel;
|
||||
Initialize();
|
||||
}
|
||||
|
||||
private void DataModelStoreOnDataModelRemoved(object? sender, DataModelStoreEvent e)
|
||||
{
|
||||
if (e.Registration.DataModel.Module.Id != Entity.DataModelId)
|
||||
return;
|
||||
|
||||
Invalidate();
|
||||
Target = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Occurs whenever the path becomes invalid
|
||||
/// </summary>
|
||||
public event EventHandler? PathInvalidated;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs whenever the path becomes valid
|
||||
/// </summary>
|
||||
public event EventHandler? PathValidated;
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="PathInvalidated" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnPathValidated()
|
||||
{
|
||||
PathValidated?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="PathValidated" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnPathInvalidated()
|
||||
{
|
||||
PathInvalidated?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ using Artemis.Core.LayerEffects.Placeholder;
|
||||
using Artemis.Core.Properties;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
using Artemis.Storage.Entities.Profile.Abstract;
|
||||
using Newtonsoft.Json;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.Core
|
||||
@@ -66,6 +67,8 @@ namespace Artemis.Core
|
||||
|
||||
internal void LoadRenderElement()
|
||||
{
|
||||
DisplayCondition = RenderElementEntity.NodeScript != null ? new NodeScript<bool>(RenderElementEntity.NodeScript) : null;
|
||||
|
||||
// DisplayCondition = RenderElementEntity.DisplayCondition != null
|
||||
// ? new DataModelConditionGroup(null, RenderElementEntity.DisplayCondition)
|
||||
// : new DataModelConditionGroup(null);
|
||||
@@ -97,6 +100,8 @@ namespace Artemis.Core
|
||||
}
|
||||
|
||||
// Conditions
|
||||
DisplayCondition?.Save();
|
||||
RenderElementEntity.NodeScript = DisplayCondition?.Entity;
|
||||
// RenderElementEntity.DisplayCondition = DisplayCondition?.Entity;
|
||||
// DisplayCondition?.Save();
|
||||
|
||||
@@ -126,7 +131,7 @@ 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;
|
||||
// stickToMainSegment = false;
|
||||
|
||||
Timeline.Update(TimeSpan.FromSeconds(deltaTime), stickToMainSegment);
|
||||
}
|
||||
@@ -386,50 +391,51 @@ namespace Artemis.Core
|
||||
|
||||
if (Timeline.EventOverlapMode != TimeLineEventOverlapMode.Toggle)
|
||||
_toggledOnByEvent = false;
|
||||
|
||||
|
||||
DisplayCondition.Run();
|
||||
bool conditionMet = DisplayCondition.Result;
|
||||
|
||||
// TODO: Handle this nicely, right now when there's only an exit node we assume true
|
||||
bool conditionMet = DisplayCondition.Nodes.Count() == 1 || 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 (conditionMet)
|
||||
{
|
||||
if (Timeline.EventOverlapMode == TimeLineEventOverlapMode.Toggle)
|
||||
{
|
||||
_toggledOnByEvent = !_toggledOnByEvent;
|
||||
if (_toggledOnByEvent)
|
||||
Timeline.JumpToStart();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Event conditions reset if the timeline finished
|
||||
if (Timeline.IsFinished)
|
||||
{
|
||||
Timeline.JumpToStart();
|
||||
}
|
||||
// and otherwise apply their overlap mode
|
||||
else
|
||||
{
|
||||
if (Timeline.EventOverlapMode == TimeLineEventOverlapMode.Restart)
|
||||
Timeline.JumpToStart();
|
||||
else if (Timeline.EventOverlapMode == TimeLineEventOverlapMode.Copy)
|
||||
Timeline.AddExtraTimeline();
|
||||
// The third option is ignore which is handled below:
|
||||
|
||||
// done
|
||||
}
|
||||
}
|
||||
// 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 (Timeline.EventOverlapMode == TimeLineEventOverlapMode.Toggle)
|
||||
// {
|
||||
// _toggledOnByEvent = !_toggledOnByEvent;
|
||||
// if (_toggledOnByEvent)
|
||||
// Timeline.JumpToStart();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// // Event conditions reset if the timeline finished
|
||||
// if (Timeline.IsFinished)
|
||||
// {
|
||||
// Timeline.JumpToStart();
|
||||
// }
|
||||
// // and otherwise apply their overlap mode
|
||||
// else
|
||||
// {
|
||||
// if (Timeline.EventOverlapMode == TimeLineEventOverlapMode.Restart)
|
||||
// Timeline.JumpToStart();
|
||||
// else if (Timeline.EventOverlapMode == TimeLineEventOverlapMode.Copy)
|
||||
// Timeline.AddExtraTimeline();
|
||||
// // The third option is ignore which is handled below:
|
||||
//
|
||||
// // done
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
DisplayConditionMet = Timeline.EventOverlapMode == TimeLineEventOverlapMode.Toggle
|
||||
? _toggledOnByEvent
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Artemis.Storage.Entities.Profile.Nodes;
|
||||
using Ninject;
|
||||
using Ninject.Parameters;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
{
|
||||
@@ -44,15 +46,25 @@ namespace Artemis.Core.Services
|
||||
string description = nodeAttribute?.Description ?? string.Empty;
|
||||
string category = nodeAttribute?.Category ?? string.Empty;
|
||||
|
||||
NodeData nodeData = new(plugin, nodeType, name, description, category, () => CreateNode(nodeType));
|
||||
NodeData nodeData = new(plugin, nodeType, name, description, category, (e) => CreateNode(e, nodeType));
|
||||
return NodeTypeStore.Add(nodeData);
|
||||
}
|
||||
|
||||
private INode CreateNode(Type nodeType)
|
||||
private INode CreateNode(NodeEntity? entity, Type nodeType)
|
||||
{
|
||||
INode node = _kernel.Get(nodeType) as INode ?? throw new InvalidOperationException($"Node {nodeType} is not an INode");
|
||||
|
||||
if (entity != null)
|
||||
{
|
||||
node.X = entity.X;
|
||||
node.Y = entity.Y;
|
||||
node.Storage = entity.Storage;
|
||||
}
|
||||
|
||||
if (node is CustomViewModelNode customViewModelNode)
|
||||
customViewModelNode.BaseCustomViewModel = _kernel.Get(customViewModelNode.CustomViewModelType);
|
||||
customViewModelNode.BaseCustomViewModel = _kernel.Get(customViewModelNode.CustomViewModelType, new ConstructorArgument("node", node));
|
||||
|
||||
node.Initialize();
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Artemis.Core
|
||||
Registrations.Add(typeRegistration);
|
||||
}
|
||||
|
||||
OnDataBindingModifierAdded(new NodeTypeStoreEvent(typeRegistration));
|
||||
OnNodeTypeAdded(new NodeTypeStoreEvent(typeRegistration));
|
||||
return typeRegistration;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace Artemis.Core
|
||||
typeRegistration.IsInStore = false;
|
||||
}
|
||||
|
||||
OnDataBindingModifierRemoved(new NodeTypeStoreEvent(typeRegistration));
|
||||
OnNodeTypeRemoved(new NodeTypeStoreEvent(typeRegistration));
|
||||
}
|
||||
|
||||
public static IEnumerable<NodeData> GetAll()
|
||||
@@ -59,19 +59,27 @@ namespace Artemis.Core
|
||||
|
||||
#region Events
|
||||
|
||||
public static event EventHandler<NodeTypeStoreEvent>? DataBindingModifierAdded;
|
||||
public static event EventHandler<NodeTypeStoreEvent>? DataBindingModifierRemoved;
|
||||
public static event EventHandler<NodeTypeStoreEvent>? NodeTypeAdded;
|
||||
public static event EventHandler<NodeTypeStoreEvent>? NodeTypeRemoved;
|
||||
|
||||
private static void OnDataBindingModifierAdded(NodeTypeStoreEvent e)
|
||||
private static void OnNodeTypeAdded(NodeTypeStoreEvent e)
|
||||
{
|
||||
DataBindingModifierAdded?.Invoke(null, e);
|
||||
NodeTypeAdded?.Invoke(null, e);
|
||||
}
|
||||
|
||||
private static void OnDataBindingModifierRemoved(NodeTypeStoreEvent e)
|
||||
private static void OnNodeTypeRemoved(NodeTypeStoreEvent e)
|
||||
{
|
||||
DataBindingModifierRemoved?.Invoke(null, e);
|
||||
NodeTypeRemoved?.Invoke(null, e);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public static Plugin? GetPlugin(INode node)
|
||||
{
|
||||
lock (Registrations)
|
||||
{
|
||||
return Registrations.FirstOrDefault(r => r.Plugin.GetType().Assembly == node.GetType().Assembly)?.Plugin;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
public class CustomNodeViewModel : CorePropertyChanged
|
||||
{
|
||||
[JsonIgnore]
|
||||
public INode Node { get; }
|
||||
|
||||
public CustomNodeViewModel(INode node)
|
||||
{
|
||||
Node = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
@@ -32,6 +33,7 @@ namespace Artemis.Core
|
||||
|
||||
#region Constructors
|
||||
|
||||
[JsonConstructor]
|
||||
internal InputPin(INode node, string name)
|
||||
: base(node, name)
|
||||
{ }
|
||||
|
||||
@@ -12,13 +12,15 @@ namespace Artemis.Core
|
||||
|
||||
public double X { get; set; }
|
||||
public double Y { get; set; }
|
||||
public object Storage { get; set; }
|
||||
|
||||
public IReadOnlyCollection<IPin> Pins { get; }
|
||||
public IReadOnlyCollection<IPinCollection> PinCollections { get; }
|
||||
|
||||
event EventHandler Resetting;
|
||||
|
||||
void Initialize();
|
||||
void Evaluate();
|
||||
void Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,14 @@ namespace Artemis.Core
|
||||
set => SetAndNotify(ref _y, value);
|
||||
}
|
||||
|
||||
private object? _storage;
|
||||
|
||||
public object? Storage
|
||||
{
|
||||
get => _storage;
|
||||
set => SetAndNotify(ref _storage, value);
|
||||
}
|
||||
|
||||
public virtual bool IsExitNode => false;
|
||||
|
||||
private readonly List<IPin> _pins = new();
|
||||
@@ -128,6 +136,8 @@ namespace Artemis.Core
|
||||
return pin;
|
||||
}
|
||||
|
||||
public virtual void Initialize() { }
|
||||
|
||||
public abstract void Evaluate();
|
||||
|
||||
public virtual void Reset()
|
||||
@@ -138,7 +148,7 @@ namespace Artemis.Core
|
||||
#endregion
|
||||
}
|
||||
|
||||
public abstract class Node<T> : CustomViewModelNode
|
||||
public abstract class Node<T> : CustomViewModelNode where T : CustomNodeViewModel
|
||||
{
|
||||
protected Node()
|
||||
{
|
||||
@@ -149,7 +159,7 @@ namespace Artemis.Core
|
||||
}
|
||||
|
||||
public override Type CustomViewModelType => typeof(T);
|
||||
public T? CustomViewModel => (T?) BaseCustomViewModel;
|
||||
public T CustomViewModel => (T) BaseCustomViewModel!;
|
||||
}
|
||||
|
||||
public abstract class CustomViewModelNode : Node
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Artemis.Storage.Entities.Profile.Nodes;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
@@ -13,13 +14,13 @@ namespace Artemis.Core
|
||||
public string Description { get; }
|
||||
public string Category { get; }
|
||||
|
||||
private Func<INode> _create;
|
||||
private Func<NodeEntity?, INode> _create;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
internal NodeData(Plugin plugin, Type type, string name, string description, string category, Func<INode> create)
|
||||
internal NodeData(Plugin plugin, Type type, string name, string description, string category, Func<NodeEntity?, INode>? create)
|
||||
{
|
||||
this.Plugin = plugin;
|
||||
this.Type = type;
|
||||
@@ -33,7 +34,7 @@ namespace Artemis.Core
|
||||
|
||||
#region Methods
|
||||
|
||||
public INode CreateNode() => _create();
|
||||
public INode CreateNode(NodeEntity? entity) => _create(entity);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Artemis.Core.Internal;
|
||||
using Artemis.Core.Properties;
|
||||
using Artemis.Storage.Entities.Profile.Nodes;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
public abstract class NodeScript : CorePropertyChanged, INodeScript
|
||||
public abstract class NodeScript : CorePropertyChanged, INodeScript, IStorageModel
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
internal NodeScriptEntity Entity { get; }
|
||||
|
||||
public string Name { get; }
|
||||
public string Description { get; }
|
||||
|
||||
@@ -18,6 +22,7 @@ namespace Artemis.Core
|
||||
|
||||
protected INode ExitNode { get; set; }
|
||||
public abstract Type ResultType { get; }
|
||||
public abstract void CreateExitNode(string name, string description = "");
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -27,6 +32,22 @@ namespace Artemis.Core
|
||||
{
|
||||
this.Name = name;
|
||||
this.Description = description;
|
||||
this.Entity = new NodeScriptEntity();
|
||||
|
||||
NodeTypeStore.NodeTypeAdded += NodeTypeStoreOnNodeTypeChanged;
|
||||
NodeTypeStore.NodeTypeRemoved += NodeTypeStoreOnNodeTypeChanged;
|
||||
}
|
||||
|
||||
internal NodeScript(NodeScriptEntity entity)
|
||||
{
|
||||
this.Name = entity.Name;
|
||||
this.Description = entity.Description;
|
||||
this.Entity = entity;
|
||||
|
||||
Load();
|
||||
|
||||
NodeTypeStore.NodeTypeAdded += NodeTypeStoreOnNodeTypeChanged;
|
||||
NodeTypeStore.NodeTypeRemoved += NodeTypeStoreOnNodeTypeChanged;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -50,9 +71,203 @@ namespace Artemis.Core
|
||||
{
|
||||
_nodes.Remove(node);
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{ }
|
||||
{
|
||||
NodeTypeStore.NodeTypeAdded -= NodeTypeStoreOnNodeTypeChanged;
|
||||
NodeTypeStore.NodeTypeRemoved -= NodeTypeStoreOnNodeTypeChanged;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation of IStorageModel
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Load()
|
||||
{
|
||||
bool gotExitNode = false;
|
||||
|
||||
// Create nodes
|
||||
Dictionary<int, INode> nodes = new();
|
||||
foreach (NodeEntity entityNode in Entity.Nodes)
|
||||
{
|
||||
INode? node = LoadNode(entityNode);
|
||||
if (node == null)
|
||||
continue;
|
||||
|
||||
if (node.IsExitNode)
|
||||
gotExitNode = true;
|
||||
|
||||
nodes.Add(entityNode.Id, node);
|
||||
}
|
||||
|
||||
if (!gotExitNode)
|
||||
CreateExitNode("Exit node");
|
||||
|
||||
LoadConnections(nodes);
|
||||
|
||||
_nodes.Clear();
|
||||
_nodes.AddRange(nodes.Values);
|
||||
}
|
||||
|
||||
private INode? LoadNode(NodeEntity nodeEntity)
|
||||
{
|
||||
INode node;
|
||||
if (nodeEntity.IsExitNode)
|
||||
{
|
||||
CreateExitNode(nodeEntity.Name, nodeEntity.Description);
|
||||
node = ExitNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
NodeTypeRegistration? nodeTypeRegistration = NodeTypeStore.Get(nodeEntity.PluginId, nodeEntity.Type);
|
||||
if (nodeTypeRegistration == null)
|
||||
return null;
|
||||
|
||||
// Create the node
|
||||
node = nodeTypeRegistration.NodeData.CreateNode(nodeEntity);
|
||||
}
|
||||
|
||||
// Restore pin collections
|
||||
foreach (NodePinCollectionEntity entityNodePinCollection in nodeEntity.PinCollections)
|
||||
{
|
||||
IPinCollection? collection = node.PinCollections.FirstOrDefault(c => c.Name == entityNodePinCollection.Name &&
|
||||
c.Type.Name == entityNodePinCollection.Type &&
|
||||
(int) c.Direction == entityNodePinCollection.Direction);
|
||||
if (collection == null)
|
||||
continue;
|
||||
|
||||
while (collection.Count() < entityNodePinCollection.Amount)
|
||||
collection.AddPin();
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private void LoadConnections(Dictionary<int, INode> nodes)
|
||||
{
|
||||
foreach (NodeConnectionEntity nodeConnectionEntity in Entity.Connections)
|
||||
{
|
||||
// Find the source and target node
|
||||
if (!nodes.TryGetValue(nodeConnectionEntity.SourceNode, out INode? source) || !nodes.TryGetValue(nodeConnectionEntity.TargetNode, out INode? target))
|
||||
continue;
|
||||
|
||||
IPin? sourcePin = nodeConnectionEntity.SourcePinCollectionId == -1
|
||||
? source.Pins.ElementAtOrDefault(nodeConnectionEntity.SourcePinId)
|
||||
: source.PinCollections.ElementAtOrDefault(nodeConnectionEntity.SourcePinCollectionId)?.ElementAtOrDefault(nodeConnectionEntity.SourcePinId);
|
||||
IPin? targetPin = nodeConnectionEntity.TargetPinCollectionId == -1
|
||||
? target.Pins.ElementAtOrDefault(nodeConnectionEntity.TargetPinId)
|
||||
: target.PinCollections.ElementAtOrDefault(nodeConnectionEntity.TargetPinCollectionId)?.ElementAtOrDefault(nodeConnectionEntity.TargetPinId);
|
||||
|
||||
// Ensure both nodes have the required pins
|
||||
if (sourcePin == null || targetPin == null)
|
||||
continue;
|
||||
|
||||
targetPin.ConnectTo(sourcePin);
|
||||
sourcePin.ConnectTo(targetPin);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Save()
|
||||
{
|
||||
Entity.Name = Name;
|
||||
Entity.Description = Description;
|
||||
|
||||
Entity.Nodes.Clear();
|
||||
int id = 0;
|
||||
|
||||
Dictionary<INode, int> nodes = new();
|
||||
foreach (INode node in Nodes)
|
||||
{
|
||||
NodeEntity nodeEntity = new()
|
||||
{
|
||||
Id = id,
|
||||
PluginId = NodeTypeStore.GetPlugin(node)?.Guid ?? Constants.CorePlugin.Guid,
|
||||
Type = node.GetType().Name,
|
||||
X = node.X,
|
||||
Y = node.Y,
|
||||
Storage = node.Storage,
|
||||
Name = node.Name,
|
||||
Description = node.Description,
|
||||
IsExitNode = node.IsExitNode
|
||||
};
|
||||
|
||||
foreach (IPinCollection nodePinCollection in node.PinCollections)
|
||||
{
|
||||
nodeEntity.PinCollections.Add(new NodePinCollectionEntity
|
||||
{
|
||||
Name = nodePinCollection.Name,
|
||||
Type = nodePinCollection.Type.Name,
|
||||
Direction = (int) nodePinCollection.Direction,
|
||||
Amount = nodePinCollection.Count()
|
||||
});
|
||||
}
|
||||
|
||||
Entity.Nodes.Add(nodeEntity);
|
||||
nodes.Add(node, id);
|
||||
id++;
|
||||
}
|
||||
|
||||
// Store connections
|
||||
Entity.Connections.Clear();
|
||||
foreach (INode node in Nodes)
|
||||
{
|
||||
SavePins(nodes, node, -1, node.Pins);
|
||||
|
||||
int pinCollectionId = 0;
|
||||
foreach (IPinCollection pinCollection in node.PinCollections)
|
||||
{
|
||||
SavePins(nodes, node, pinCollectionId, pinCollection);
|
||||
pinCollectionId++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SavePins(Dictionary<INode, int> nodes, INode node, int collectionId, IEnumerable<IPin> pins)
|
||||
{
|
||||
int sourcePinId = 0;
|
||||
foreach (IPin sourcePin in pins.Where(p => p.Direction == PinDirection.Input))
|
||||
{
|
||||
foreach (IPin targetPin in sourcePin.ConnectedTo)
|
||||
{
|
||||
int targetPinCollectionId = -1;
|
||||
int targetPinId;
|
||||
|
||||
IPinCollection? targetCollection = targetPin.Node.PinCollections.FirstOrDefault(c => c.Contains(targetPin));
|
||||
if (targetCollection != null)
|
||||
{
|
||||
targetPinCollectionId = targetPin.Node.PinCollections.IndexOf(targetCollection);
|
||||
targetPinId = targetCollection.ToList().IndexOf(targetPin);
|
||||
}
|
||||
else
|
||||
targetPinId = targetPin.Node.Pins.IndexOf(targetPin);
|
||||
|
||||
Entity.Connections.Add(new NodeConnectionEntity()
|
||||
{
|
||||
SourceType = sourcePin.Type.Name,
|
||||
SourceNode = nodes[node],
|
||||
SourcePinCollectionId = collectionId,
|
||||
SourcePinId = sourcePinId,
|
||||
TargetType = targetPin.Type.Name,
|
||||
TargetNode = nodes[targetPin.Node],
|
||||
TargetPinCollectionId = targetPinCollectionId,
|
||||
TargetPinId = targetPinId,
|
||||
});
|
||||
}
|
||||
|
||||
sourcePinId++;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event handlers
|
||||
|
||||
private void NodeTypeStoreOnNodeTypeChanged(object? sender, NodeTypeStoreEvent e)
|
||||
{
|
||||
Load();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -61,14 +276,24 @@ namespace Artemis.Core
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
public T Result => ((ExitNode<T>)ExitNode).Value;
|
||||
public T Result => ((ExitNode<T>) ExitNode).Value;
|
||||
|
||||
public override Type ResultType => typeof(T);
|
||||
|
||||
public override void CreateExitNode(string name, string description = "")
|
||||
{
|
||||
ExitNode = new ExitNode<T>(name, description);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
internal NodeScript(NodeScriptEntity entity)
|
||||
: base(entity)
|
||||
{
|
||||
}
|
||||
|
||||
public NodeScript(string name, string description)
|
||||
: base(name, description)
|
||||
{
|
||||
@@ -86,4 +311,4 @@ namespace Artemis.Core
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
@@ -31,6 +32,7 @@ namespace Artemis.Core
|
||||
|
||||
#region Constructors
|
||||
|
||||
[JsonConstructor]
|
||||
internal OutputPin(INode node, string name)
|
||||
: base(node, name)
|
||||
{ }
|
||||
|
||||
Reference in new issue
Block a user