mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Nodes - Implemented saving/loading
Nodes - Added data model node
This commit is contained in:
parent
61d7f1e1fb
commit
afc4bee7ac
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
15
src/Artemis.Core/VisualScripting/CustomNodeViewModel.cs
Normal file
15
src/Artemis.Core/VisualScripting/CustomNodeViewModel.cs
Normal file
@ -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)
|
||||
{ }
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Artemis.Storage.Entities.Profile.Conditions;
|
||||
using Artemis.Storage.Entities.Profile.Nodes;
|
||||
|
||||
namespace Artemis.Storage.Entities.Profile.Abstract
|
||||
{
|
||||
@ -15,5 +16,7 @@ namespace Artemis.Storage.Entities.Profile.Abstract
|
||||
|
||||
public DataModelConditionGroupEntity DisplayCondition { get; set; }
|
||||
public TimelineEntity Timeline { get; set; }
|
||||
|
||||
public NodeScriptEntity NodeScript { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
namespace Artemis.Storage.Entities.Profile.Nodes
|
||||
{
|
||||
public class NodeConnectionEntity
|
||||
{
|
||||
public string SourceType { get; set; }
|
||||
public int SourceNode { get; set; }
|
||||
public int TargetNode { get; set; }
|
||||
public int SourcePinCollectionId { get; set; }
|
||||
public int SourcePinId { get; set; }
|
||||
public string TargetType { get; set; }
|
||||
public int TargetPinCollectionId { get; set; }
|
||||
public int TargetPinId { get; set; }
|
||||
}
|
||||
}
|
||||
26
src/Artemis.Storage/Entities/Profile/Nodes/NodeEntity.cs
Normal file
26
src/Artemis.Storage/Entities/Profile/Nodes/NodeEntity.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Artemis.Storage.Entities.Profile.Nodes
|
||||
{
|
||||
public class NodeEntity
|
||||
{
|
||||
public NodeEntity()
|
||||
{
|
||||
PinCollections = new List<NodePinCollectionEntity>();
|
||||
}
|
||||
|
||||
public int Id { get; set; }
|
||||
public string Type { get; set; }
|
||||
public Guid PluginId { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
public bool IsExitNode { get; set; }
|
||||
public double X { get; set; }
|
||||
public double Y { get; set; }
|
||||
public object Storage { get; set; }
|
||||
|
||||
public List<NodePinCollectionEntity> PinCollections { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
namespace Artemis.Storage.Entities.Profile.Nodes
|
||||
{
|
||||
public class NodePinCollectionEntity
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Type { get; set; }
|
||||
public int Direction { set; get; }
|
||||
public int Amount { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Artemis.Storage.Entities.Profile.Nodes
|
||||
{
|
||||
public class NodeScriptEntity
|
||||
{
|
||||
public NodeScriptEntity()
|
||||
{
|
||||
Nodes = new List<NodeEntity>();
|
||||
Connections = new List<NodeConnectionEntity>();
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
|
||||
public List<NodeEntity> Nodes { get; set; }
|
||||
public List<NodeConnectionEntity> Connections { get; set; }
|
||||
}
|
||||
}
|
||||
@ -1482,6 +1482,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Artemis.Core": "1.0.0",
|
||||
"Artemis.UI.Shared": "2.0.0",
|
||||
"JetBrains.Annotations": "2021.1.0",
|
||||
"Stylet": "1.3.6"
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<DocumentationFile>bin\Artemis.VisualScripting.xml</DocumentationFile>
|
||||
<DocumentationFile></DocumentationFile>
|
||||
<NoWarn></NoWarn>
|
||||
<WarningLevel>5</WarningLevel>
|
||||
</PropertyGroup>
|
||||
@ -42,6 +42,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />
|
||||
<ProjectReference Include="..\Artemis.UI.Shared\Artemis.UI.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@ -383,7 +383,7 @@ namespace Artemis.VisualScripting.Editor.Controls
|
||||
if (_creationBoxParent.ContextMenu != null)
|
||||
_creationBoxParent.ContextMenu.IsOpen = false;
|
||||
|
||||
INode node = nodeData.CreateNode();
|
||||
INode node = nodeData.CreateNode(null);
|
||||
Script.AddNode(node);
|
||||
|
||||
InitializeNode(node, _lastRightClickLocation);
|
||||
|
||||
@ -207,9 +207,18 @@
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- TODO: Figure out how to only use this for CustomViewModelNode -->
|
||||
<Border x:Name="BrdCustomView" Grid.Column="1">
|
||||
<ContentControl s:View.Model="{Binding Node.CustomViewModel}"/>
|
||||
<Border.Resources>
|
||||
<DataTemplate DataType="{x:Type core:CustomViewModelNode}">
|
||||
<ContentControl s:View.Model="{Binding BaseCustomViewModel}"/>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate DataType="{x:Type core:Node}">
|
||||
<Border />
|
||||
</DataTemplate>
|
||||
</Border.Resources>
|
||||
|
||||
<ContentControl Content="{Binding Node}" />
|
||||
</Border>
|
||||
|
||||
<Border x:Name="BrdOutputPins" Grid.Column="2">
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Input;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.VisualScripting.Nodes.CustomViewModels
|
||||
{
|
||||
public class DataModelNodeCustomViewModel : CustomNodeViewModel
|
||||
{
|
||||
private readonly DataModelNode _node;
|
||||
|
||||
public DataModelNodeCustomViewModel(DataModelNode node, IDataModelUIService dataModelUIService) : base(node)
|
||||
{
|
||||
_node = node;
|
||||
|
||||
Execute.OnUIThreadSync(() =>
|
||||
{
|
||||
SelectionViewModel = dataModelUIService.GetDynamicSelectionViewModel(module: null);
|
||||
SelectionViewModel.PropertySelected += SelectionViewModelOnPropertySelected;
|
||||
});
|
||||
}
|
||||
|
||||
public DataModelDynamicViewModel SelectionViewModel { get; set; }
|
||||
|
||||
private void SelectionViewModelOnPropertySelected(object? sender, DataModelInputDynamicEventArgs e)
|
||||
{
|
||||
_node.DataModelPath = SelectionViewModel.DataModelPath;
|
||||
if (_node.DataModelPath != null)
|
||||
{
|
||||
_node.DataModelPath.Save();
|
||||
_node.Storage = _node.DataModelPath.Entity;
|
||||
}
|
||||
else
|
||||
{
|
||||
_node.Storage = null;
|
||||
}
|
||||
|
||||
_node.UpdateOutputPin();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,15 +1,24 @@
|
||||
using Stylet;
|
||||
using Artemis.Core;
|
||||
|
||||
namespace Artemis.VisualScripting.Nodes.CustomViewModels
|
||||
{
|
||||
public class StaticDoubleValueNodeCustomViewModel : PropertyChangedBase
|
||||
public class StaticDoubleValueNodeCustomViewModel : CustomNodeViewModel
|
||||
{
|
||||
private double _input;
|
||||
private readonly StaticDoubleValueNode _node;
|
||||
|
||||
public StaticDoubleValueNodeCustomViewModel(StaticDoubleValueNode node) : base(node)
|
||||
{
|
||||
_node = node;
|
||||
}
|
||||
|
||||
public double Input
|
||||
{
|
||||
get => _input;
|
||||
set => SetAndNotify(ref _input, value);
|
||||
get => (double) _node.Storage;
|
||||
set
|
||||
{
|
||||
_node.Storage = value;
|
||||
OnPropertyChanged(nameof(Input));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,15 +1,24 @@
|
||||
using Stylet;
|
||||
using Artemis.Core;
|
||||
|
||||
namespace Artemis.VisualScripting.Nodes.CustomViewModels
|
||||
{
|
||||
public class StaticIntegerValueNodeCustomViewModel : PropertyChangedBase
|
||||
public class StaticIntegerValueNodeCustomViewModel : CustomNodeViewModel
|
||||
{
|
||||
private int _input;
|
||||
private readonly StaticIntegerValueNode _node;
|
||||
|
||||
public StaticIntegerValueNodeCustomViewModel(StaticIntegerValueNode node) : base(node)
|
||||
{
|
||||
_node = node;
|
||||
}
|
||||
|
||||
public int Input
|
||||
{
|
||||
get => _input;
|
||||
set => SetAndNotify(ref _input, value);
|
||||
get => (int)(long) _node.Storage;
|
||||
set
|
||||
{
|
||||
_node.Storage = value;
|
||||
OnPropertyChanged(nameof(Input));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,15 +1,24 @@
|
||||
using Stylet;
|
||||
using Artemis.Core;
|
||||
|
||||
namespace Artemis.VisualScripting.Nodes.CustomViewModels
|
||||
{
|
||||
public class StaticStringValueNodeCustomViewModel : PropertyChangedBase
|
||||
public class StaticStringValueNodeCustomViewModel : CustomNodeViewModel
|
||||
{
|
||||
private string _input;
|
||||
private readonly StaticStringValueNode _node;
|
||||
|
||||
public StaticStringValueNodeCustomViewModel(StaticStringValueNode node) : base(node)
|
||||
{
|
||||
_node = node;
|
||||
}
|
||||
|
||||
public string Input
|
||||
{
|
||||
get => _input;
|
||||
set => SetAndNotify(ref _input, value);
|
||||
get => (string) _node.Storage;
|
||||
set
|
||||
{
|
||||
_node.Storage = value;
|
||||
OnPropertyChanged(nameof(Input));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
<UserControl x:Class="Artemis.VisualScripting.Nodes.CustomViews.DataModelNodeCustomView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Artemis.VisualScripting.Nodes.CustomViews"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<ContentControl s:View.Model="{Binding SelectionViewModel}"></ContentControl>
|
||||
</UserControl>
|
||||
48
src/Artemis.VisualScripting/Nodes/DataModelNode.cs
Normal file
48
src/Artemis.VisualScripting/Nodes/DataModelNode.cs
Normal file
@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
using Artemis.VisualScripting.Nodes.CustomViewModels;
|
||||
|
||||
namespace Artemis.VisualScripting.Nodes
|
||||
{
|
||||
[Node("Data Model-Value", "Outputs a selectable data model value.")]
|
||||
public class DataModelNode : Node<DataModelNodeCustomViewModel>
|
||||
{
|
||||
public DataModelNode() : base("Data Model", "Outputs a selectable data model value")
|
||||
{
|
||||
}
|
||||
|
||||
public OutputPin Output { get; private set; }
|
||||
public DataModelPath DataModelPath { get; set; }
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
if (Storage is not DataModelPathEntity pathEntity)
|
||||
return;
|
||||
|
||||
DataModelPath = new DataModelPath(null, pathEntity);
|
||||
CustomViewModel.SelectionViewModel.ChangeDataModelPath(DataModelPath);
|
||||
UpdateOutputPin();
|
||||
}
|
||||
|
||||
public override void Evaluate()
|
||||
{
|
||||
if (DataModelPath.IsValid && Output != null)
|
||||
Output.Value = DataModelPath.GetValue()!;
|
||||
}
|
||||
|
||||
public void UpdateOutputPin()
|
||||
{
|
||||
if (Pins.Contains(Output))
|
||||
{
|
||||
RemovePin(Output);
|
||||
Output = null;
|
||||
}
|
||||
|
||||
Type type = DataModelPath?.GetPropertyType();
|
||||
if (type != null)
|
||||
Output = CreateOutputPin(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -26,9 +26,11 @@ namespace Artemis.VisualScripting.Nodes
|
||||
|
||||
public override void Evaluate()
|
||||
{
|
||||
Output.Value = CustomViewModel.Input;
|
||||
Output.Value = (int) (Storage as long? ?? 0);
|
||||
}
|
||||
|
||||
public override void Initialize() => Storage ??= 0;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@ -55,9 +57,11 @@ namespace Artemis.VisualScripting.Nodes
|
||||
|
||||
public override void Evaluate()
|
||||
{
|
||||
Output.Value = CustomViewModel.Input;
|
||||
Output.Value = Storage as double? ?? 0.0;
|
||||
}
|
||||
|
||||
public override void Initialize() => Storage ??= 0.0;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@ -84,9 +88,9 @@ namespace Artemis.VisualScripting.Nodes
|
||||
|
||||
public override void Evaluate()
|
||||
{
|
||||
Output.Value = CustomViewModel.Input;
|
||||
Output.Value = Storage as string;
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
@ -57,6 +57,28 @@
|
||||
"resolved": "5.0.10",
|
||||
"contentHash": "x70WuqMDuP75dajqSLvO+AnI/BbwS6da+ukTO7rueV7VoXoQ5CRA9FV4r7cOS4OUr2NS1Up7LDIutjCxQycRvg=="
|
||||
},
|
||||
"MaterialDesignColors": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.0.1",
|
||||
"contentHash": "Azl8nN23SD6QPE0PdsfpKiIqWTvH7rzXwgXPiFSEt91NFOrwB5cx3iq/sbINWMZunhXJ32+jVUHiV03B8eJbZw=="
|
||||
},
|
||||
"MaterialDesignExtensions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.3.0",
|
||||
"contentHash": "dlxWtdrMH8aHNib3dWJhNQ/nNiA2b/CNvr90w/5KB6erTisuTpyYVx2l2+UGCZvwhSX5mHTHQYHfjgAKbDrgjg==",
|
||||
"dependencies": {
|
||||
"MaterialDesignColors": "1.2.7",
|
||||
"MaterialDesignThemes": "3.2.0"
|
||||
}
|
||||
},
|
||||
"MaterialDesignThemes": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.1.0",
|
||||
"contentHash": "WqrO9AbtdE4pLPtDk/C5BZRnkgWFwVGyUHWj7tRJrgnKl089DEobVXBCLeqp2mkgBeFHj4Xe3AfWyhmlnO6AZA==",
|
||||
"dependencies": {
|
||||
"MaterialDesignColors": "2.0.1"
|
||||
}
|
||||
},
|
||||
"McMaster.NETCore.Plugins": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.4.0",
|
||||
@ -104,6 +126,11 @@
|
||||
"Microsoft.NETCore.Platforms": "3.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Xaml.Behaviors.Wpf": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.1.31",
|
||||
"contentHash": "LZpuf82ACZWldmfMuv3CTUMDh3o0xo0uHUaybR5HgqVLDBJJ9RZLykplQ/bTJd0/VDt3EhD4iDgUgbdIUAM+Kg=="
|
||||
},
|
||||
"NETStandard.Library": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.6.1",
|
||||
@ -336,6 +363,11 @@
|
||||
"System.Threading.Timer": "4.0.1"
|
||||
}
|
||||
},
|
||||
"SharpVectors.Reloaded": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.7.5",
|
||||
"contentHash": "v9U5sSMGFE2zCMbh42BYHkaRYkmwwhsKMGcNRdHAKqD1ryOf4mhqnJR0o07hwg5KIEmCI9bDdrgYSmiZWlL+eA=="
|
||||
},
|
||||
"SkiaSharp": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.80.2",
|
||||
@ -344,6 +376,23 @@
|
||||
"System.Memory": "4.5.3"
|
||||
}
|
||||
},
|
||||
"SkiaSharp.Views.Desktop.Common": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.80.2",
|
||||
"contentHash": "0vBvweMysgl1wgjuTQUhdJMD5z5nBjtYqmnHPeX+qHfkc336Wj2L3jEqwmGb0YP+RV47gFGz0EzMAW6szZch9w==",
|
||||
"dependencies": {
|
||||
"SkiaSharp": "2.80.2"
|
||||
}
|
||||
},
|
||||
"SkiaSharp.Views.WPF": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.80.2",
|
||||
"contentHash": "Fzo2+MNwHDh9Cob8sk7OO26kp3bhofjXMwlEK8IncF1ehu9hi3sH9iQDJrue9a88VEJJ+yyLISPUFcmXlGHSyQ==",
|
||||
"dependencies": {
|
||||
"SkiaSharp": "2.80.2",
|
||||
"SkiaSharp.Views.Desktop.Common": "2.80.2"
|
||||
}
|
||||
},
|
||||
"System.AppContext": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
@ -1266,6 +1315,24 @@
|
||||
"LiteDB": "5.0.10",
|
||||
"Serilog": "2.10.0"
|
||||
}
|
||||
},
|
||||
"artemis.ui.shared": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Artemis.Core": "1.0.0",
|
||||
"Humanizer.Core": "2.11.10",
|
||||
"MaterialDesignExtensions": "3.3.0",
|
||||
"MaterialDesignThemes": "4.1.0",
|
||||
"Microsoft.Xaml.Behaviors.Wpf": "1.1.31",
|
||||
"Ninject": "3.3.4",
|
||||
"Ninject.Extensions.Conventions": "3.3.0",
|
||||
"SharpVectors.Reloaded": "1.7.5",
|
||||
"SkiaSharp": "2.80.2",
|
||||
"SkiaSharp.Views.WPF": "2.80.2",
|
||||
"Stylet": "1.3.6",
|
||||
"System.Buffers": "4.5.1",
|
||||
"System.Numerics.Vectors": "4.5.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user