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

Nodes - Changed storage to be based on generics

Nodes - Added XML docs to most types
This commit is contained in:
Robert 2021-09-24 23:05:16 +02:00
parent 8413b8d6db
commit c1dab91c16
28 changed files with 522 additions and 347 deletions

View File

@ -76,7 +76,8 @@ namespace Artemis.Core.Services
node.Y = entity.Y; node.Y = entity.Y;
try try
{ {
node.Storage = CoreJson.DeserializeObject(entity.Storage, true); if (node is Node nodeImplementation)
nodeImplementation.DeserializeStorage(entity.Storage);
} }
catch catch
{ {

View File

@ -12,6 +12,7 @@ namespace Artemis.Core
public override PinDirection Direction => PinDirection.Input; public override PinDirection Direction => PinDirection.Input;
private T _value; private T _value;
public T Value public T Value
{ {
get get
@ -37,7 +38,8 @@ namespace Artemis.Core
[JsonConstructor] [JsonConstructor]
internal InputPin(INode node, string name) internal InputPin(INode node, string name)
: base(node, name) : base(node, name)
{ } {
}
#endregion #endregion
@ -62,6 +64,7 @@ namespace Artemis.Core
public override PinDirection Direction => PinDirection.Input; public override PinDirection Direction => PinDirection.Input;
private object _value; private object _value;
public object Value public object Value
{ {
get get
@ -98,9 +101,19 @@ namespace Artemis.Core
private void Evaluate() private void Evaluate()
{ {
Value = ConnectedTo.Count > 0 ? ConnectedTo[0].PinValue : Type.GetDefault(); if (Type.IsValueType)
{
if (ConnectedTo.Count > 0 && ConnectedTo[0].PinValue != null)
Value = ConnectedTo[0].PinValue;
else
Value = Type.GetDefault()!;
}
else
{
Value = ConnectedTo[0].PinValue;
}
} }
#endregion #endregion
} }
} }

View File

@ -1,29 +1,73 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using Artemis.Storage.Entities.Profile.Nodes;
namespace Artemis.Core namespace Artemis.Core
{ {
/// <summary>
/// Represents a kind of node inside a <see cref="INodeScript" />
/// </summary>
public interface INode : INotifyPropertyChanged public interface INode : INotifyPropertyChanged
{ {
/// <summary>
/// Gets the name of the node
/// </summary>
string Name { get; } string Name { get; }
/// <summary>
/// Gets the description of the node
/// </summary>
string Description { get; } string Description { get; }
/// <summary>
/// Gets a boolean indicating whether the node is the exit node of the script
/// </summary>
bool IsExitNode { get; } bool IsExitNode { get; }
/// <summary>
/// Gets a boolean indicating whether the node is a default node that connot be removed
/// </summary>
bool IsDefaultNode { get; } bool IsDefaultNode { get; }
/// <summary>
/// Gets or sets the X-position of the node
/// </summary>
public double X { get; set; } public double X { get; set; }
/// <summary>
/// Gets or sets the Y-position of the node
/// </summary>
public double Y { get; set; } public double Y { get; set; }
public object? Storage { get; set; }
/// <summary>
/// Gets a read-only collection of the pins on this node
/// </summary>
public IReadOnlyCollection<IPin> Pins { get; } public IReadOnlyCollection<IPin> Pins { get; }
public IReadOnlyCollection<IPinCollection> PinCollections { get; }
/// <summary>
/// Gets a read-only collection of the pin collections on this node
/// </summary>
public IReadOnlyCollection<IPinCollection> PinCollections { get; }
/// <summary>
/// Called when the node resets
/// </summary>
event EventHandler Resetting; event EventHandler Resetting;
/// <summary>
/// Called when the node was loaded from storage or newly created
/// </summary>
/// <param name="script">The script the node is contained in</param>
void Initialize(INodeScript script); void Initialize(INodeScript script);
/// <summary>
/// Evaluates the value of the output pins of this node
/// </summary>
void Evaluate(); void Evaluate();
/// <summary>
/// Resets the node causing all pins to re-evaluate the next time <see cref="Evaluate"/> is called
/// </summary>
void Reset(); void Reset();
} }
} }

View File

@ -5,27 +5,74 @@ using Artemis.Core.Events;
namespace Artemis.Core namespace Artemis.Core
{ {
/// <summary>
/// Represents a node script
/// </summary>
public interface INodeScript : INotifyPropertyChanged, IDisposable public interface INodeScript : INotifyPropertyChanged, IDisposable
{ {
/// <summary>
/// Gets the name of the node script.
/// </summary>
string Name { get; } string Name { get; }
/// <summary>
/// Gets the description of the node script.
/// </summary>
string Description { get; } string Description { get; }
/// <summary>
/// Gets an enumerable of all the nodes on this script.
/// </summary>
IEnumerable<INode> Nodes { get; } IEnumerable<INode> Nodes { get; }
/// <summary>
/// Gets the return type of the node script.
/// </summary>
Type ResultType { get; } Type ResultType { get; }
/// <summary>
/// Gets or sets the context of the node script, usually a <see cref="Profile" /> or
/// <see cref="ProfileConfiguration" />.
/// </summary>
object? Context { get; set; } object? Context { get; set; }
/// <summary>
/// Occurs whenever a node was added to the script
/// </summary>
event EventHandler<SingleValueEventArgs<INode>>? NodeAdded; event EventHandler<SingleValueEventArgs<INode>>? NodeAdded;
/// <summary>
/// Occurs whenever a node was removed from the script
/// </summary>
event EventHandler<SingleValueEventArgs<INode>>? NodeRemoved; event EventHandler<SingleValueEventArgs<INode>>? NodeRemoved;
/// <summary>
/// Runs the script, evaluating nodes where needed
/// </summary>
void Run(); void Run();
/// <summary>
/// Adds a node to the script
/// </summary>
/// <param name="node">The node to add</param>
void AddNode(INode node); void AddNode(INode node);
/// <summary>
/// Removes a node from the script
/// </summary>
/// <param name="node">The node to remove</param>
void RemoveNode(INode node); void RemoveNode(INode node);
} }
/// <summary>
/// Represents a node script with a result value of type <paramref name="T" />
/// </summary>
/// <typeparam name="T">The type of result value</typeparam>
public interface INodeScript<out T> : INodeScript public interface INodeScript<out T> : INodeScript
{ {
/// <summary>
/// Gets the result of the script
/// </summary>
T Result { get; } T Result { get; }
} }
} }

View File

@ -1,16 +1,24 @@
using Ninject; using System;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using Ninject;
using Ninject.Parameters; using Ninject.Parameters;
namespace Artemis.Core namespace Artemis.Core
{ {
/// <summary>
/// Represents a kind of node inside a <see cref="NodeScript" />
/// </summary>
public abstract class Node : CorePropertyChanged, INode public abstract class Node : CorePropertyChanged, INode
{ {
/// <inheritdoc />
public event EventHandler? Resetting;
#region Properties & Fields #region Properties & Fields
private string _name; private string _name;
/// <inheritdoc />
public string Name public string Name
{ {
get => _name; get => _name;
@ -18,6 +26,8 @@ namespace Artemis.Core
} }
private string _description; private string _description;
/// <inheritdoc />
public string Description public string Description
{ {
get => _description; get => _description;
@ -26,6 +36,7 @@ namespace Artemis.Core
private double _x; private double _x;
/// <inheritdoc />
public double X public double X
{ {
get => _x; get => _x;
@ -33,51 +44,62 @@ namespace Artemis.Core
} }
private double _y; private double _y;
/// <inheritdoc />
public double Y public double Y
{ {
get => _y; get => _y;
set => SetAndNotify(ref _y, value); set => SetAndNotify(ref _y, value);
} }
private object? _storage; /// <inheritdoc />
public object? Storage
{
get => _storage;
set => SetAndNotify(ref _storage, value);
}
public virtual bool IsExitNode => false; public virtual bool IsExitNode => false;
/// <inheritdoc />
public virtual bool IsDefaultNode => false; public virtual bool IsDefaultNode => false;
private readonly List<IPin> _pins = new(); private readonly List<IPin> _pins = new();
/// <inheritdoc />
public IReadOnlyCollection<IPin> Pins => new ReadOnlyCollection<IPin>(_pins); public IReadOnlyCollection<IPin> Pins => new ReadOnlyCollection<IPin>(_pins);
private readonly List<IPinCollection> _pinCollections = new(); private readonly List<IPinCollection> _pinCollections = new();
/// <inheritdoc />
public IReadOnlyCollection<IPinCollection> PinCollections => new ReadOnlyCollection<IPinCollection>(_pinCollections); public IReadOnlyCollection<IPinCollection> PinCollections => new ReadOnlyCollection<IPinCollection>(_pinCollections);
#endregion #endregion
#region Events
public event EventHandler Resetting;
#endregion
#region Construtors #region Construtors
/// <summary>
/// Creates a new instance of the <see cref="Node" /> class with an empty name and description
/// </summary>
protected Node() protected Node()
{ } {
_name = string.Empty;
_description = string.Empty;
}
/// <summary>
/// Creates a new instance of the <see cref="Node" /> class with the provided name and description
/// </summary>
protected Node(string name, string description) protected Node(string name, string description)
{ {
Name = name; _name = name;
Description = description; _description = description;
} }
#endregion #endregion
#region Methods #region Methods
/// <summary>
/// Creates a new input pin and adds it to the <see cref="Pins" /> collection
/// </summary>
/// <param name="name">The name of the pin</param>
/// <typeparam name="T">The type of value the pin will hold</typeparam>
/// <returns>The newly created pin</returns>
protected InputPin<T> CreateInputPin<T>(string name = "") protected InputPin<T> CreateInputPin<T>(string name = "")
{ {
InputPin<T> pin = new(this, name); InputPin<T> pin = new(this, name);
@ -86,6 +108,12 @@ namespace Artemis.Core
return pin; return pin;
} }
/// <summary>
/// Creates a new input pin and adds it to the <see cref="Pins" /> collection
/// </summary>
/// <param name="type">The type of value the pin will hold</param>
/// <param name="name">The name of the pin</param>
/// <returns>The newly created pin</returns>
protected InputPin CreateInputPin(Type type, string name = "") protected InputPin CreateInputPin(Type type, string name = "")
{ {
InputPin pin = new(this, type, name); InputPin pin = new(this, type, name);
@ -94,6 +122,12 @@ namespace Artemis.Core
return pin; return pin;
} }
/// <summary>
/// Creates a new output pin and adds it to the <see cref="Pins" /> collection
/// </summary>
/// <param name="name">The name of the pin</param>
/// <typeparam name="T">The type of value the pin will hold</typeparam>
/// <returns>The newly created pin</returns>
protected OutputPin<T> CreateOutputPin<T>(string name = "") protected OutputPin<T> CreateOutputPin<T>(string name = "")
{ {
OutputPin<T> pin = new(this, name); OutputPin<T> pin = new(this, name);
@ -102,6 +136,12 @@ namespace Artemis.Core
return pin; return pin;
} }
/// <summary>
/// Creates a new output pin and adds it to the <see cref="Pins" /> collection
/// </summary>
/// <param name="type">The type of value the pin will hold</param>
/// <param name="name">The name of the pin</param>
/// <returns>The newly created pin</returns>
protected OutputPin CreateOutputPin(Type type, string name = "") protected OutputPin CreateOutputPin(Type type, string name = "")
{ {
OutputPin pin = new(this, type, name); OutputPin pin = new(this, type, name);
@ -109,7 +149,12 @@ namespace Artemis.Core
OnPropertyChanged(nameof(Pins)); OnPropertyChanged(nameof(Pins));
return pin; return pin;
} }
/// <summary>
/// Removes the provided <paramref name="pin" /> from the node and it's <see cref="Pins" /> collection
/// </summary>
/// <param name="pin">The pin to remove</param>
/// <returns><see langword="true" /> if the pin was removed; otherwise <see langword="false" />.</returns>
protected bool RemovePin(Pin pin) protected bool RemovePin(Pin pin)
{ {
bool isRemoved = _pins.Remove(pin); bool isRemoved = _pins.Remove(pin);
@ -122,6 +167,13 @@ namespace Artemis.Core
return isRemoved; return isRemoved;
} }
/// <summary>
/// Creates a new input pin collection and adds it to the <see cref="PinCollections" /> collection
/// </summary>
/// <typeparam name="T">The type of value the pins of this collection will hold</typeparam>
/// <param name="name">The name of the pin collection</param>
/// <param name="initialCount">The amount of pins to initially add to the collection</param>
/// <returns>The resulting input pin collection</returns>
protected InputPinCollection<T> CreateInputPinCollection<T>(string name = "", int initialCount = 1) protected InputPinCollection<T> CreateInputPinCollection<T>(string name = "", int initialCount = 1)
{ {
InputPinCollection<T> pin = new(this, name, initialCount); InputPinCollection<T> pin = new(this, name, initialCount);
@ -130,6 +182,13 @@ namespace Artemis.Core
return pin; return pin;
} }
/// <summary>
/// Creates a new input pin collection and adds it to the <see cref="PinCollections" /> collection
/// </summary>
/// <param name="type">The type of value the pins of this collection will hold</param>
/// <param name="name">The name of the pin collection</param>
/// <param name="initialCount">The amount of pins to initially add to the collection</param>
/// <returns>The resulting input pin collection</returns>
protected InputPinCollection CreateInputPinCollection(Type type, string name = "", int initialCount = 1) protected InputPinCollection CreateInputPinCollection(Type type, string name = "", int initialCount = 1)
{ {
InputPinCollection pin = new(this, type, name, initialCount); InputPinCollection pin = new(this, type, name, initialCount);
@ -138,6 +197,13 @@ namespace Artemis.Core
return pin; return pin;
} }
/// <summary>
/// Creates a new output pin collection and adds it to the <see cref="PinCollections" /> collection
/// </summary>
/// <typeparam name="T">The type of value the pins of this collection will hold</typeparam>
/// <param name="name">The name of the pin collection</param>
/// <param name="initialCount">The amount of pins to initially add to the collection</param>
/// <returns>The resulting output pin collection</returns>
protected OutputPinCollection<T> CreateOutputPinCollection<T>(string name = "", int initialCount = 1) protected OutputPinCollection<T> CreateOutputPinCollection<T>(string name = "", int initialCount = 1)
{ {
OutputPinCollection<T> pin = new(this, name, initialCount); OutputPinCollection<T> pin = new(this, name, initialCount);
@ -146,12 +212,18 @@ namespace Artemis.Core
return pin; return pin;
} }
/// <summary>
/// Removes the provided <paramref name="pinCollection" /> from the node and it's <see cref="PinCollections" />
/// collection
/// </summary>
/// <param name="pinCollection">The pin collection to remove</param>
/// <returns><see langword="true" /> if the pin collection was removed; otherwise <see langword="false" />.</returns>
protected bool RemovePinCollection(PinCollection pinCollection) protected bool RemovePinCollection(PinCollection pinCollection)
{ {
bool isRemoved = _pinCollections.Remove(pinCollection); bool isRemoved = _pinCollections.Remove(pinCollection);
if (isRemoved) if (isRemoved)
{ {
foreach (IPin pin in pinCollection) foreach (IPin pin in pinCollection)
pin.DisconnectAll(); pin.DisconnectAll();
OnPropertyChanged(nameof(PinCollections)); OnPropertyChanged(nameof(PinCollections));
} }
@ -159,51 +231,122 @@ namespace Artemis.Core
return isRemoved; return isRemoved;
} }
/// <inheritdoc />
public virtual void Initialize(INodeScript script) public virtual void Initialize(INodeScript script)
{ } {
}
/// <inheritdoc />
public abstract void Evaluate(); public abstract void Evaluate();
/// <inheritdoc />
public virtual void Reset() public virtual void Reset()
{ {
Resetting?.Invoke(this, new EventArgs()); Resetting?.Invoke(this, EventArgs.Empty);
}
/// <summary>
/// Called whenever the node must show it's custom view model, if <see langword="null" />, no custom view model is used
/// </summary>
/// <returns>The custom view model, if <see langword="null" />, no custom view model is used</returns>
public virtual ICustomNodeViewModel? GetCustomViewModel()
{
return null;
}
/// <summary>
/// Serializes the <see cref="Storage" /> object into a string
/// </summary>
/// <returns>The serialized object</returns>
internal virtual string SerializeStorage()
{
return string.Empty;
}
/// <summary>
/// Deserializes the <see cref="Storage" /> object and sets it
/// </summary>
/// <param name="serialized">The serialized object</param>
internal virtual void DeserializeStorage(string serialized)
{
} }
#endregion #endregion
} }
public abstract class Node<T> : CustomViewModelNode where T : ICustomNodeViewModel /// <summary>
/// Represents a kind of node inside a <see cref="NodeScript" /> containing storage value of type
/// <typeparamref name="TStorage" />.
/// </summary>
/// <typeparam name="TStorage">The type of value the node stores</typeparam>
public abstract class Node<TStorage> : Node
{ {
private TStorage? _storage;
/// <inheritdoc />
protected Node()
{
}
/// <inheritdoc />
protected Node(string name, string description) : base(name, description)
{
}
/// <summary>
/// Gets or sets the storage object of this node, this is saved across sessions
/// </summary>
public TStorage? Storage
{
get => _storage;
set => SetAndNotify(ref _storage, value);
}
internal override string SerializeStorage()
{
return CoreJson.SerializeObject(Storage, true);
}
internal override void DeserializeStorage(string serialized)
{
Storage = CoreJson.DeserializeObject<TStorage>(serialized) ?? default(TStorage);
}
}
/// <summary>
/// Represents a kind of node inside a <see cref="NodeScript" /> containing storage value of type
/// <typeparamref name="TStorage" /> and a view model of type <typeparamref name="TViewModel" />.
/// </summary>
/// <typeparam name="TStorage">The type of value the node stores</typeparam>
/// <typeparam name="TViewModel">The type of view model the node uses</typeparam>
public abstract class Node<TStorage, TViewModel> : Node<TStorage> where TViewModel : ICustomNodeViewModel
{
/// <inheritdoc />
protected Node()
{
}
/// <inheritdoc />
protected Node(string name, string description) : base(name, description)
{
}
[Inject] [Inject]
internal IKernel Kernel { get; set; } = null!; internal IKernel Kernel { get; set; } = null!;
protected Node() /// <summary>
{ } /// Called when a view model is required
/// </summary>
protected Node(string name, string description) : base(name, description) public virtual TViewModel GetViewModel()
{ }
public virtual T GetViewModel()
{ {
return Kernel.Get<T>(new ConstructorArgument("node", this)); return Kernel.Get<TViewModel>(new ConstructorArgument("node", this));
} }
/// <inheritdoc />
public override ICustomNodeViewModel GetCustomViewModel() public override ICustomNodeViewModel GetCustomViewModel()
{ {
return GetViewModel(); return GetViewModel();
} }
} }
public abstract class CustomViewModelNode : Node
{
/// <inheritdoc />
protected CustomViewModelNode()
{ }
/// <inheritdoc />
protected CustomViewModelNode(string name, string description) : base(name, description)
{ }
public abstract ICustomNodeViewModel GetCustomViewModel();
}
} }

View File

@ -9,6 +9,9 @@ using Artemis.Storage.Entities.Profile.Nodes;
namespace Artemis.Core namespace Artemis.Core
{ {
/// <summary>
/// Represents a node script
/// </summary>
public abstract class NodeScript : CorePropertyChanged, INodeScript, IStorageModel public abstract class NodeScript : CorePropertyChanged, INodeScript, IStorageModel
{ {
private void NodeTypeStoreOnNodeTypeChanged(object? sender, NodeTypeStoreEvent e) private void NodeTypeStoreOnNodeTypeChanged(object? sender, NodeTypeStoreEvent e)
@ -16,23 +19,41 @@ namespace Artemis.Core
Load(); Load();
} }
/// <inheritdoc />
public event EventHandler<SingleValueEventArgs<INode>>? NodeAdded; public event EventHandler<SingleValueEventArgs<INode>>? NodeAdded;
/// <inheritdoc />
public event EventHandler<SingleValueEventArgs<INode>>? NodeRemoved; public event EventHandler<SingleValueEventArgs<INode>>? NodeRemoved;
#region Properties & Fields #region Properties & Fields
internal NodeScriptEntity Entity { get; } internal NodeScriptEntity Entity { get; }
/// <inheritdoc />
public string Name { get; } public string Name { get; }
/// <inheritdoc />
public string Description { get; } public string Description { get; }
private readonly List<INode> _nodes = new(); private readonly List<INode> _nodes = new();
/// <inheritdoc />
public IEnumerable<INode> Nodes => new ReadOnlyCollection<INode>(_nodes); public IEnumerable<INode> Nodes => new ReadOnlyCollection<INode>(_nodes);
/// <summary>
/// Gets or sets the exit node of the script
/// </summary>
protected INode ExitNode { get; set; } protected INode ExitNode { get; set; }
/// <summary>
/// Gets a boolean indicating whether the exit node is connected to any other nodes
/// </summary>
public abstract bool ExitNodeConnected { get; } public abstract bool ExitNodeConnected { get; }
/// <inheritdoc />
public abstract Type ResultType { get; } public abstract Type ResultType { get; }
/// <inheritdoc />
public object? Context { get; set; } public object? Context { get; set; }
#endregion #endregion
@ -65,6 +86,7 @@ namespace Artemis.Core
#region Methods #region Methods
/// <inheritdoc />
public void Run() public void Run()
{ {
foreach (INode node in Nodes) foreach (INode node in Nodes)
@ -73,6 +95,7 @@ namespace Artemis.Core
ExitNode.Evaluate(); ExitNode.Evaluate();
} }
/// <inheritdoc />
public void AddNode(INode node) public void AddNode(INode node)
{ {
_nodes.Add(node); _nodes.Add(node);
@ -80,6 +103,7 @@ namespace Artemis.Core
NodeAdded?.Invoke(this, new SingleValueEventArgs<INode>(node)); NodeAdded?.Invoke(this, new SingleValueEventArgs<INode>(node));
} }
/// <inheritdoc />
public void RemoveNode(INode node) public void RemoveNode(INode node)
{ {
_nodes.Remove(node); _nodes.Remove(node);
@ -90,6 +114,7 @@ namespace Artemis.Core
NodeRemoved?.Invoke(this, new SingleValueEventArgs<INode>(node)); NodeRemoved?.Invoke(this, new SingleValueEventArgs<INode>(node));
} }
/// <inheritdoc />
public void Dispose() public void Dispose()
{ {
NodeTypeStore.NodeTypeAdded -= NodeTypeStoreOnNodeTypeChanged; NodeTypeStore.NodeTypeAdded -= NodeTypeStoreOnNodeTypeChanged;
@ -147,9 +172,7 @@ namespace Artemis.Core
// Restore pin collections // Restore pin collections
foreach (NodePinCollectionEntity entityNodePinCollection in nodeEntity.PinCollections) foreach (NodePinCollectionEntity entityNodePinCollection in nodeEntity.PinCollections)
{ {
IPinCollection? collection = node.PinCollections.FirstOrDefault(c => c.Name == entityNodePinCollection.Name && IPinCollection? collection = node.PinCollections.ElementAtOrDefault(entityNodePinCollection.Id);
c.Type.Name == entityNodePinCollection.Type &&
(int) c.Direction == entityNodePinCollection.Direction);
if (collection == null) if (collection == null)
continue; continue;
@ -161,12 +184,12 @@ namespace Artemis.Core
} }
/// <summary> /// <summary>
/// Loads missing connections between the nodes of this node script from the <see cref="Entity"/> /// Loads missing connections between the nodes of this node script from the <see cref="Entity" />
/// </summary> /// </summary>
public void LoadConnections() public void LoadConnections()
{ {
List<INode> nodes = Nodes.ToList(); List<INode> nodes = Nodes.ToList();
foreach (NodeConnectionEntity nodeConnectionEntity in Entity.Connections) foreach (NodeConnectionEntity nodeConnectionEntity in Entity.Connections.OrderBy(p => p.SourcePinCollectionId))
{ {
INode? source = nodes.ElementAtOrDefault(nodeConnectionEntity.SourceNode); INode? source = nodes.ElementAtOrDefault(nodeConnectionEntity.SourceNode);
if (source == null) if (source == null)
@ -191,15 +214,12 @@ namespace Artemis.Core
// Clear existing connections on input pins, we don't want none of that now // Clear existing connections on input pins, we don't want none of that now
if (targetPin.Direction == PinDirection.Input) if (targetPin.Direction == PinDirection.Input)
{
while (targetPin.ConnectedTo.Any()) while (targetPin.ConnectedTo.Any())
targetPin.DisconnectFrom(targetPin.ConnectedTo[0]); targetPin.DisconnectFrom(targetPin.ConnectedTo[0]);
}
if (sourcePin.Direction == PinDirection.Input) if (sourcePin.Direction == PinDirection.Input)
{ while (sourcePin.ConnectedTo.Any())
while (sourcePin.ConnectedTo.Any())
sourcePin.DisconnectFrom(sourcePin.ConnectedTo[0]); sourcePin.DisconnectFrom(sourcePin.ConnectedTo[0]);
}
// Only connect the nodes if they aren't already connected (LoadConnections may be called twice or more) // Only connect the nodes if they aren't already connected (LoadConnections may be called twice or more)
if (!targetPin.ConnectedTo.Contains(sourcePin)) if (!targetPin.ConnectedTo.Contains(sourcePin))
@ -231,21 +251,24 @@ namespace Artemis.Core
Type = node.GetType().Name, Type = node.GetType().Name,
X = node.X, X = node.X,
Y = node.Y, Y = node.Y,
Storage = CoreJson.SerializeObject(node.Storage, true),
Name = node.Name, Name = node.Name,
Description = node.Description, Description = node.Description,
IsExitNode = node.IsExitNode IsExitNode = node.IsExitNode
}; };
if (node is Node nodeImplementation)
nodeEntity.Storage = nodeImplementation.SerializeStorage();
int collectionId = 0;
foreach (IPinCollection nodePinCollection in node.PinCollections) foreach (IPinCollection nodePinCollection in node.PinCollections)
{ {
nodeEntity.PinCollections.Add(new NodePinCollectionEntity nodeEntity.PinCollections.Add(new NodePinCollectionEntity
{ {
Name = nodePinCollection.Name, Id = collectionId,
Type = nodePinCollection.Type.Name,
Direction = (int) nodePinCollection.Direction, Direction = (int) nodePinCollection.Direction,
Amount = nodePinCollection.Count() Amount = nodePinCollection.Count()
}); });
collectionId++;
} }
Entity.Nodes.Add(nodeEntity); Entity.Nodes.Add(nodeEntity);
@ -309,13 +332,21 @@ namespace Artemis.Core
#endregion #endregion
} }
/// <summary>
/// Represents a node script with a result value of type <paramref name="T" />
/// </summary>
/// <typeparam name="T">The type of result value</typeparam>
public class NodeScript<T> : NodeScript, INodeScript<T> public class NodeScript<T> : NodeScript, INodeScript<T>
{ {
#region Properties & Fields #region Properties & Fields
/// <inheritdoc />
public T Result => ((ExitNode<T>) ExitNode).Value; public T Result => ((ExitNode<T>) ExitNode).Value;
/// <inheritdoc />
public override bool ExitNodeConnected => ((ExitNode<T>) ExitNode).Input.ConnectedTo.Any(); public override bool ExitNodeConnected => ((ExitNode<T>) ExitNode).Input.ConnectedTo.Any();
/// <inheritdoc />
public override Type ResultType => typeof(T); public override Type ResultType => typeof(T);
#endregion #endregion

View File

@ -2,8 +2,7 @@
{ {
public class NodePinCollectionEntity public class NodePinCollectionEntity
{ {
public string Name { get; set; } public int Id { get; set; }
public string Type { get; set; }
public int Direction { set; get; } public int Direction { set; get; }
public int Amount { get; set; } public int Amount { get; set; }
} }

View File

@ -155,10 +155,10 @@ namespace Artemis.VisualScripting.Editor.Controls
{ {
CustomViewModel?.OnDeactivate(); CustomViewModel?.OnDeactivate();
if (Node?.Node is CustomViewModelNode customViewModelNode) if (Node?.Node is Node customViewModelNode)
{ {
CustomViewModel = customViewModelNode.GetCustomViewModel(); CustomViewModel = customViewModelNode.GetCustomViewModel();
CustomViewModel.OnActivate(); CustomViewModel?.OnActivate();
} }
else else
CustomViewModel = null; CustomViewModel = null;

View File

@ -1,38 +1,11 @@
using System.ComponentModel; using Artemis.VisualScripting.Nodes.CustomViewModels;
using Artemis.VisualScripting.Nodes.CustomViewModels;
using SkiaSharp;
namespace Artemis.VisualScripting.Nodes.Color.CustomViewModels namespace Artemis.VisualScripting.Nodes.Color.CustomViewModels
{ {
public class StaticSKColorValueNodeCustomViewModel : CustomNodeViewModel public class StaticSKColorValueNodeCustomViewModel : CustomNodeViewModel
{ {
private readonly StaticSKColorValueNode _node;
public StaticSKColorValueNodeCustomViewModel(StaticSKColorValueNode node) : base(node) public StaticSKColorValueNodeCustomViewModel(StaticSKColorValueNode node) : base(node)
{ {
_node = node;
}
public SKColor Input
{
get => (SKColor) (_node.Storage ?? SKColor.Empty);
set => _node.Storage = value;
}
public override void OnActivate()
{
_node.PropertyChanged += NodeOnPropertyChanged;
}
public override void OnDeactivate()
{
_node.PropertyChanged -= NodeOnPropertyChanged;
}
private void NodeOnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(Node.Storage))
OnPropertyChanged(nameof(Input));
} }
} }
} }

View File

@ -12,5 +12,5 @@
</UserControl.Resources> </UserControl.Resources>
<shared:ColorPicker VerticalAlignment="Center" <shared:ColorPicker VerticalAlignment="Center"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Color="{Binding Input, Converter={StaticResource SKColorToColorConverter}}" /> Color="{Binding Node.Storage, Converter={StaticResource SKColorToColorConverter}}" />
</UserControl> </UserControl>

View File

@ -5,7 +5,7 @@ using SkiaSharp;
namespace Artemis.VisualScripting.Nodes.Color namespace Artemis.VisualScripting.Nodes.Color
{ {
[Node("Color-Value", "Outputs a configurable color value.", "Static", InputType = typeof(SKColor), OutputType = typeof(SKColor))] [Node("Color-Value", "Outputs a configurable color value.", "Static", InputType = typeof(SKColor), OutputType = typeof(SKColor))]
public class StaticSKColorValueNode : Node<StaticSKColorValueNodeCustomViewModel> public class StaticSKColorValueNode : Node<SKColor, StaticSKColorValueNodeCustomViewModel>
{ {
#region Constructors #region Constructors
@ -27,17 +27,9 @@ namespace Artemis.VisualScripting.Nodes.Color
public override void Evaluate() public override void Evaluate()
{ {
Output.Value = Storage as SKColor? ?? SKColor.Empty; Output.Value = Storage;
} }
public override void Initialize(INodeScript script)
{
if (Storage is string && SKColor.TryParse(Storage.ToString(), out SKColor parsed))
Storage = parsed;
else
Storage = SKColor.Empty;
}
#endregion #endregion
} }
} }

View File

@ -1,6 +1,4 @@
using System; using Artemis.Core;
using System.ComponentModel;
using Artemis.Core;
using Artemis.Core.Events; using Artemis.Core.Events;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Stylet; using Stylet;
@ -16,19 +14,12 @@ namespace Artemis.VisualScripting.Nodes.CustomViewModels
_node = node; _node = node;
} }
public Enum Input
{
get => _node.Storage as Enum;
set => _node.Storage = value;
}
public BindableCollection<ValueDescription> EnumValues { get; } = new(); public BindableCollection<ValueDescription> EnumValues { get; } = new();
public override void OnActivate() public override void OnActivate()
{ {
_node.InputPin.PinConnected += InputPinOnPinConnected; _node.InputPin.PinConnected += InputPinOnPinConnected;
_node.InputPin.PinDisconnected += InputPinOnPinDisconnected; _node.InputPin.PinDisconnected += InputPinOnPinDisconnected;
_node.PropertyChanged += NodeOnPropertyChanged;
if (_node.InputPin.Value != null && _node.InputPin.Value.GetType().IsEnum) if (_node.InputPin.Value != null && _node.InputPin.Value.GetType().IsEnum)
EnumValues.AddRange(EnumUtilities.GetAllValuesAndDescriptions(_node.InputPin.Value.GetType())); EnumValues.AddRange(EnumUtilities.GetAllValuesAndDescriptions(_node.InputPin.Value.GetType()));
@ -39,7 +30,6 @@ namespace Artemis.VisualScripting.Nodes.CustomViewModels
{ {
_node.InputPin.PinConnected -= InputPinOnPinConnected; _node.InputPin.PinConnected -= InputPinOnPinConnected;
_node.InputPin.PinDisconnected -= InputPinOnPinDisconnected; _node.InputPin.PinDisconnected -= InputPinOnPinDisconnected;
_node.PropertyChanged -= NodeOnPropertyChanged;
base.OnDeactivate(); base.OnDeactivate();
} }
@ -55,11 +45,5 @@ namespace Artemis.VisualScripting.Nodes.CustomViewModels
if (_node.InputPin.Value != null && _node.InputPin.Value.GetType().IsEnum) if (_node.InputPin.Value != null && _node.InputPin.Value.GetType().IsEnum)
EnumValues.AddRange(EnumUtilities.GetAllValuesAndDescriptions(_node.InputPin.Value.GetType())); EnumValues.AddRange(EnumUtilities.GetAllValuesAndDescriptions(_node.InputPin.Value.GetType()));
} }
private void NodeOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(Node.Storage))
OnPropertyChanged(nameof(Input));
}
} }
} }

View File

@ -1,132 +1,32 @@
using System.ComponentModel; using Artemis.Core;
namespace Artemis.VisualScripting.Nodes.CustomViewModels namespace Artemis.VisualScripting.Nodes.CustomViewModels
{ {
public class StaticDoubleValueNodeCustomViewModel : CustomNodeViewModel public class StaticDoubleValueNodeCustomViewModel : CustomNodeViewModel
{ {
private readonly StaticDoubleValueNode _node; public StaticDoubleValueNodeCustomViewModel(INode node) : base(node)
public StaticDoubleValueNodeCustomViewModel(StaticDoubleValueNode node) : base(node)
{ {
_node = node;
}
public double Input
{
get => (double) (_node.Storage ?? 0.0);
set => _node.Storage = value;
}
public override void OnActivate()
{
_node.PropertyChanged += NodeOnPropertyChanged;
}
public override void OnDeactivate()
{
_node.PropertyChanged -= NodeOnPropertyChanged;
}
private void NodeOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(Node.Storage))
OnPropertyChanged(nameof(Input));
} }
} }
public class StaticFloatValueNodeCustomViewModel : CustomNodeViewModel public class StaticFloatValueNodeCustomViewModel : CustomNodeViewModel
{ {
private readonly StaticFloatValueNode _node; public StaticFloatValueNodeCustomViewModel(INode node) : base(node)
public StaticFloatValueNodeCustomViewModel(StaticFloatValueNode node) : base(node)
{ {
_node = node;
}
public float Input
{
get => (float) (_node.Storage ?? 0f);
set => _node.Storage = value;
}
public override void OnActivate()
{
_node.PropertyChanged += NodeOnPropertyChanged;
}
public override void OnDeactivate()
{
_node.PropertyChanged -= NodeOnPropertyChanged;
}
private void NodeOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(Node.Storage))
OnPropertyChanged(nameof(Input));
} }
} }
public class StaticIntegerValueNodeCustomViewModel : CustomNodeViewModel public class StaticIntegerValueNodeCustomViewModel : CustomNodeViewModel
{ {
private readonly StaticIntegerValueNode _node; public StaticIntegerValueNodeCustomViewModel(INode node) : base(node)
public StaticIntegerValueNodeCustomViewModel(StaticIntegerValueNode node) : base(node)
{ {
_node = node;
}
public int Input
{
get => _node.Storage is long longInput ? (int) longInput : (int) (_node.Storage ?? 0);
set => _node.Storage = value;
}
public override void OnActivate()
{
_node.PropertyChanged += NodeOnPropertyChanged;
}
public override void OnDeactivate()
{
_node.PropertyChanged -= NodeOnPropertyChanged;
}
private void NodeOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(Node.Storage))
OnPropertyChanged(nameof(Input));
} }
} }
public class StaticStringValueNodeCustomViewModel : CustomNodeViewModel public class StaticStringValueNodeCustomViewModel : CustomNodeViewModel
{ {
private readonly StaticStringValueNode _node; public StaticStringValueNodeCustomViewModel(INode node) : base(node)
public StaticStringValueNodeCustomViewModel(StaticStringValueNode node) : base(node)
{ {
_node = node;
}
public string Input
{
get => (string) _node.Storage;
set => _node.Storage = value;
}
public override void OnActivate()
{
_node.PropertyChanged += NodeOnPropertyChanged;
}
public override void OnDeactivate()
{
_node.PropertyChanged -= NodeOnPropertyChanged;
}
private void NodeOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(Node.Storage))
OnPropertyChanged(nameof(Input));
} }
} }
} }

View File

@ -12,7 +12,7 @@
materialDesign:ComboBoxAssist.ClassicMode="True" materialDesign:ComboBoxAssist.ClassicMode="True"
VerticalAlignment="Center" VerticalAlignment="Center"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
SelectedValue="{Binding Input}" SelectedValue="{Binding Node.Storage}"
ItemsSource="{Binding EnumValues}" ItemsSource="{Binding EnumValues}"
SelectedValuePath="Value" SelectedValuePath="Value"
DisplayMemberPath="Description"> DisplayMemberPath="Description">

View File

@ -8,5 +8,5 @@
d:DesignHeight="450" d:DesignWidth="800"> d:DesignHeight="450" d:DesignWidth="800">
<TextBox VerticalAlignment="Center" <TextBox VerticalAlignment="Center"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Text="{Binding Input}" /> Text="{Binding Node.Storage}" />
</UserControl> </UserControl>

View File

@ -8,5 +8,5 @@
d:DesignHeight="450" d:DesignWidth="800"> d:DesignHeight="450" d:DesignWidth="800">
<TextBox VerticalAlignment="Center" <TextBox VerticalAlignment="Center"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Text="{Binding Input}" /> Text="{Binding Node.Storage}" />
</UserControl> </UserControl>

View File

@ -8,5 +8,5 @@
d:DesignHeight="450" d:DesignWidth="800"> d:DesignHeight="450" d:DesignWidth="800">
<TextBox VerticalAlignment="Center" <TextBox VerticalAlignment="Center"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Text="{Binding Input}" /> Text="{Binding Node.Storage}" />
</UserControl> </UserControl>

View File

@ -8,5 +8,5 @@
d:DesignHeight="450" d:DesignWidth="800"> d:DesignHeight="450" d:DesignWidth="800">
<TextBox VerticalAlignment="Center" <TextBox VerticalAlignment="Center"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Text="{Binding Input}" /> Text="{Binding Node.Storage}" />
</UserControl> </UserControl>

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Events; using Artemis.Core.Events;
@ -8,19 +9,69 @@ using Artemis.VisualScripting.Nodes.DataModel.CustomViewModels;
namespace Artemis.VisualScripting.Nodes.DataModel namespace Artemis.VisualScripting.Nodes.DataModel
{ {
[Node("Data Model-Event", "Responds to a data model event trigger", "External", OutputType = typeof(bool))] [Node("Data Model-Event", "Responds to a data model event trigger", "External", OutputType = typeof(bool))]
public class DataModelEventNode : Node<DataModelEventNodeCustomViewModel>, IDisposable public class DataModelEventNode : Node<DataModelPathEntity, DataModelEventNodeCustomViewModel>, IDisposable
{ {
private DataModelPath _dataModelPath; private DataModelPath _dataModelPath;
private DateTime _lastTrigger; private DateTime _lastTrigger;
private int _currentIndex; private int _currentIndex;
private Type _currentType;
private bool _updating;
public DataModelEventNode() : base("Data Model-Event", "Responds to a data model event trigger") public DataModelEventNode() : base("Data Model-Event", "Responds to a data model event trigger")
{ {
Input = CreateInputPin<object>(); _currentType = typeof(object);
Input.PinConnected += InputOnPinConnected; CreateCycleValues(typeof(object), 1);
Output = CreateOutputPin(typeof(object));
} }
public InputPin<object> Input { get; } private void CreateCycleValues(Type type, int initialCount)
{
if (CycleValues != null)
{
CycleValues.PinAdded -= CycleValuesOnPinAdded;
CycleValues.PinRemoved -= CycleValuesOnPinRemoved;
foreach (IPin pin in CycleValues)
{
pin.PinConnected -= OnPinConnected;
pin.PinDisconnected -= OnPinDisconnected;
}
RemovePinCollection(CycleValues);
}
CycleValues = CreateInputPinCollection(type, "", initialCount);
CycleValues.PinAdded += CycleValuesOnPinAdded;
CycleValues.PinRemoved += CycleValuesOnPinRemoved;
foreach (IPin pin in CycleValues)
{
pin.PinConnected += OnPinConnected;
pin.PinDisconnected += OnPinDisconnected;
}
}
private void CycleValuesOnPinAdded(object sender, SingleValueEventArgs<IPin> e)
{
e.Value.PinConnected += OnPinConnected;
e.Value.PinDisconnected += OnPinDisconnected;
}
private void CycleValuesOnPinRemoved(object sender, SingleValueEventArgs<IPin> e)
{
e.Value.PinConnected -= OnPinConnected;
e.Value.PinDisconnected -= OnPinDisconnected;
}
private void OnPinDisconnected(object sender, SingleValueEventArgs<IPin> e)
{
ProcessPinDisconnected();
}
private void OnPinConnected(object sender, SingleValueEventArgs<IPin> e)
{
IPin source = e.Value;
IPin target = (IPin) sender;
ProcessPinConnected(source, target);
}
public InputPinCollection CycleValues { get; set; } public InputPinCollection CycleValues { get; set; }
public OutputPin Output { get; set; } public OutputPin Output { get; set; }
@ -35,70 +86,101 @@ namespace Artemis.VisualScripting.Nodes.DataModel
public override void Initialize(INodeScript script) public override void Initialize(INodeScript script)
{ {
Script = script; Script = script;
CreateCycleValues();
CreateOutput();
if (Storage is not DataModelPathEntity pathEntity) if (Storage == null)
return; return;
DataModelPath = new DataModelPath(pathEntity); DataModelPath = new DataModelPath(Storage);
} }
public override void Evaluate() public override void Evaluate()
{ {
object outputValue = null; object outputValue = null;
if (DataModelPath?.GetValue() is DataModelEvent dataModelEvent) if (DataModelPath?.GetValue() is IDataModelEvent dataModelEvent)
{ {
if (dataModelEvent.LastTrigger > _lastTrigger) if (dataModelEvent.LastTrigger > _lastTrigger)
{ {
_lastTrigger = dataModelEvent.LastTrigger; _lastTrigger = dataModelEvent.LastTrigger;
_currentIndex++; _currentIndex++;
if (_currentIndex > CycleValues.Count()) if (_currentIndex >= CycleValues.Count())
_currentIndex = 0; _currentIndex = 0;
} }
outputValue = _currentIndex == 0 outputValue = CycleValues.ElementAt(_currentIndex).PinValue;
? Input.Value
: CycleValues.ElementAt(_currentIndex - 1).PinValue;
} }
Output.Value = outputValue ?? Output.Type.GetDefault(); if (Output.Type.IsInstanceOfType(outputValue))
Output.Value = outputValue;
else if (Output.Type.IsValueType)
Output.Value = Output.Type.GetDefault()!;
} }
private void InputOnPinConnected(object sender, SingleValueEventArgs<IPin> e) private void ProcessPinConnected(IPin source, IPin target)
{ {
CreateCycleValues(); if (_updating)
CreateOutput(); return;
}
private void CreateCycleValues() try
{
int pinCount = CycleValues?.Count() ?? 1;
Type inputType = Input.ConnectedTo.FirstOrDefault()?.Type ?? typeof(object);
if (CycleValues != null)
{ {
if (inputType == CycleValues.Type) _updating = true;
return;
RemovePinCollection(CycleValues);
}
CycleValues = CreateInputPinCollection(inputType, "", pinCount); // No need to change anything if the types haven't changed
if (_currentType == source.Type)
return;
int reconnectIndex = CycleValues.ToList().IndexOf(target);
ChangeCurrentType(source.Type);
if (reconnectIndex != -1)
{
CycleValues.ToList()[reconnectIndex].ConnectTo(source);
source.ConnectTo(CycleValues.ToList()[reconnectIndex]);
}
}
finally
{
_updating = false;
}
} }
private void CreateOutput() private void ChangeCurrentType(Type type)
{ {
Type inputType = Input.ConnectedTo.FirstOrDefault()?.Type ?? typeof(object); CreateCycleValues(type, CycleValues.Count());
if (Output != null) List<IPin> oldOutputConnections = Output.ConnectedTo.ToList();
RemovePin(Output);
Output = CreateOutputPin(type);
foreach (IPin oldOutputConnection in oldOutputConnections.Where(o => o.Type.IsAssignableFrom(Output.Type)))
{ {
if (inputType == Output.Type) oldOutputConnection.DisconnectAll();
return; oldOutputConnection.ConnectTo(Output);
RemovePin(Output); Output.ConnectTo(oldOutputConnection);
} }
Output = CreateOutputPin(inputType); _currentType = type;
}
private void ProcessPinDisconnected()
{
if (_updating)
return;
try
{
// If there's still a connected pin, stick to the current type
if (CycleValues.Any(v => v.ConnectedTo.Any()))
return;
ChangeCurrentType(typeof(object));
}
finally
{
_updating = false;
}
}
private void UpdatePinsType(IPin source)
{
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@ -7,7 +7,7 @@ using Stylet;
namespace Artemis.VisualScripting.Nodes.DataModel namespace Artemis.VisualScripting.Nodes.DataModel
{ {
[Node("Data Model-Value", "Outputs a selectable data model value.", "External")] [Node("Data Model-Value", "Outputs a selectable data model value.", "External")]
public class DataModelNode : Node<DataModelNodeCustomViewModel>, IDisposable public class DataModelNode : Node<DataModelPathEntity, DataModelNodeCustomViewModel>, IDisposable
{ {
private DataModelPath _dataModelPath; private DataModelPath _dataModelPath;
@ -28,10 +28,10 @@ namespace Artemis.VisualScripting.Nodes.DataModel
{ {
Script = script; Script = script;
if (Storage is not DataModelPathEntity pathEntity) if (Storage == null)
return; return;
DataModelPath = new DataModelPath(pathEntity); DataModelPath = new DataModelPath(Storage);
DataModelPath.PathValidated += DataModelPathOnPathValidated; DataModelPath.PathValidated += DataModelPathOnPathValidated;
UpdateOutputPin(false); UpdateOutputPin(false);

View File

@ -15,9 +15,7 @@ namespace Artemis.VisualScripting.Nodes.Easing.CustomViewModels
public EasingTypeNodeCustomViewModel(EasingTypeNode node) : base(node) public EasingTypeNodeCustomViewModel(EasingTypeNode node) : base(node)
{ {
_node = node; _node = node;
EasingViewModels = new BindableCollection<NodeEasingViewModel>(Enum.GetValues(typeof(Easings.Functions)) EasingViewModels = new BindableCollection<NodeEasingViewModel>(Enum.GetValues(typeof(Easings.Functions)).Cast<Easings.Functions>().Select(e => new NodeEasingViewModel(e)));
.Cast<Easings.Functions>()
.Select(e => new NodeEasingViewModel(e)));
} }
public BindableCollection<NodeEasingViewModel> EasingViewModels { get; } public BindableCollection<NodeEasingViewModel> EasingViewModels { get; }
@ -35,7 +33,7 @@ namespace Artemis.VisualScripting.Nodes.Easing.CustomViewModels
public override void OnActivate() public override void OnActivate()
{ {
_node.PropertyChanged += NodeOnPropertyChanged; _node.PropertyChanged += NodeOnPropertyChanged;
SelectedEasingViewModel = EasingViewModels.FirstOrDefault(vm => vm.EasingFunction == _node.EasingFunction); SelectedEasingViewModel = GetNodeEasingViewModel();
} }
public override void OnDeactivate() public override void OnDeactivate()
@ -45,11 +43,16 @@ namespace Artemis.VisualScripting.Nodes.Easing.CustomViewModels
private void NodeOnPropertyChanged(object sender, PropertyChangedEventArgs e) private void NodeOnPropertyChanged(object sender, PropertyChangedEventArgs e)
{ {
if (e.PropertyName == nameof(Node.Storage)) if (e.PropertyName == nameof(_node.Storage))
{ {
_selectedEasingViewModel = EasingViewModels.FirstOrDefault(vm => vm.EasingFunction == _node.EasingFunction); _selectedEasingViewModel = GetNodeEasingViewModel();
NotifyOfPropertyChange(nameof(SelectedEasingViewModel)); NotifyOfPropertyChange(nameof(SelectedEasingViewModel));
} }
} }
private NodeEasingViewModel GetNodeEasingViewModel()
{
return EasingViewModels.FirstOrDefault(vm => vm.EasingFunction == _node.Storage);
}
} }
} }

View File

@ -4,7 +4,7 @@ using Artemis.VisualScripting.Nodes.Easing.CustomViewModels;
namespace Artemis.VisualScripting.Nodes.Easing namespace Artemis.VisualScripting.Nodes.Easing
{ {
[Node("Easing Type", "Outputs a selectable easing type.", "Easing", OutputType = typeof(Easings.Functions))] [Node("Easing Type", "Outputs a selectable easing type.", "Easing", OutputType = typeof(Easings.Functions))]
public class EasingTypeNode : Node<EasingTypeNodeCustomViewModel> public class EasingTypeNode : Node<Easings.Functions, EasingTypeNodeCustomViewModel>
{ {
public EasingTypeNode() : base("Easing Type", "Outputs a selectable easing type.") public EasingTypeNode() : base("Easing Type", "Outputs a selectable easing type.")
{ {
@ -12,11 +12,10 @@ namespace Artemis.VisualScripting.Nodes.Easing
} }
public OutputPin<Easings.Functions> Output { get; } public OutputPin<Easings.Functions> Output { get; }
public Easings.Functions EasingFunction => Storage as Easings.Functions? ?? Easings.Functions.Linear;
public override void Evaluate() public override void Evaluate()
{ {
Output.Value = EasingFunction; Output.Value = Storage;
} }
} }
} }

View File

@ -1,12 +1,11 @@
using System; using System;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Events;
using Artemis.VisualScripting.Nodes.CustomViewModels; using Artemis.VisualScripting.Nodes.CustomViewModels;
namespace Artemis.VisualScripting.Nodes namespace Artemis.VisualScripting.Nodes
{ {
[Node("Enum Equals", "Determines the equality between an input and a selected enum value", InputType = typeof(Enum), OutputType = typeof(bool))] [Node("Enum Equals", "Determines the equality between an input and a selected enum value", InputType = typeof(Enum), OutputType = typeof(bool))]
public class EnumEqualsNode : Node<EnumEqualsNodeCustomViewModel> public class EnumEqualsNode : Node<Enum, EnumEqualsNodeCustomViewModel>
{ {
public EnumEqualsNode() : base("Enum Equals", "Determines the equality between an input and a selected enum value") public EnumEqualsNode() : base("Enum Equals", "Determines the equality between an input and a selected enum value")
{ {

View File

@ -7,7 +7,7 @@ using Artemis.VisualScripting.Nodes.CustomViewModels;
namespace Artemis.VisualScripting.Nodes namespace Artemis.VisualScripting.Nodes
{ {
[Node("Layer/Folder Property", "Outputs the property of a selected layer or folder", "External")] [Node("Layer/Folder Property", "Outputs the property of a selected layer or folder", "External")]
public class LayerPropertyNode : Node<LayerPropertyNodeCustomViewModel> public class LayerPropertyNode : Node<LayerPropertyNodeEntity, LayerPropertyNodeCustomViewModel>
{ {
private readonly object _layerPropertyLock = new(); private readonly object _layerPropertyLock = new();
@ -44,7 +44,7 @@ namespace Artemis.VisualScripting.Nodes
{ {
Script = script; Script = script;
if (script.Context is Profile profile) if (script.Context is Profile profile)
profile.ChildRemoved += ProfileOnChildRemoved; profile.ChildRemoved += ProfileOnChildRemoved;
LoadLayerProperty(); LoadLayerProperty();
@ -54,15 +54,13 @@ namespace Artemis.VisualScripting.Nodes
{ {
lock (_layerPropertyLock) lock (_layerPropertyLock)
{ {
if (Script.Context is not Profile profile) if (Script.Context is not Profile profile || Storage == null)
return; return;
if (Storage is not LayerPropertyNodeEntity entity)
return; RenderProfileElement element = profile.GetAllRenderElements().FirstOrDefault(l => l.EntityId == Storage.ElementId);
RenderProfileElement element = profile.GetAllRenderElements().FirstOrDefault(l => l.EntityId == entity.ElementId);
ProfileElement = element; ProfileElement = element;
LayerProperty = element?.GetAllLayerProperties().FirstOrDefault(p => p.Path == entity.PropertyPath); LayerProperty = element?.GetAllLayerProperties().FirstOrDefault(p => p.Path == Storage.PropertyPath);
CreatePins(); CreatePins();
} }
} }

View File

@ -1,37 +1,12 @@
using System.ComponentModel; using Artemis.Core;
using Artemis.VisualScripting.Nodes.CustomViewModels; using Artemis.VisualScripting.Nodes.CustomViewModels;
namespace Artemis.VisualScripting.Nodes.Maths.CustomViewModels namespace Artemis.VisualScripting.Nodes.Maths.CustomViewModels
{ {
public class MathExpressionNodeCustomViewModel : CustomNodeViewModel public class MathExpressionNodeCustomViewModel : CustomNodeViewModel
{ {
private readonly MathExpressionNode _node; public MathExpressionNodeCustomViewModel(INode node) : base(node)
public MathExpressionNodeCustomViewModel(MathExpressionNode node) : base(node)
{ {
_node = node;
}
public string Input
{
get => (string)_node.Storage;
set => _node.Storage = value;
}
public override void OnActivate()
{
_node.PropertyChanged += NodeOnPropertyChanged;
}
public override void OnDeactivate()
{
_node.PropertyChanged -= NodeOnPropertyChanged;
}
private void NodeOnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(Node.Storage))
OnPropertyChanged(nameof(Input));
} }
} }
} }

View File

@ -8,5 +8,5 @@
d:DesignHeight="450" d:DesignWidth="800"> d:DesignHeight="450" d:DesignWidth="800">
<TextBox VerticalAlignment="Bottom" <TextBox VerticalAlignment="Bottom"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Text="{Binding Input}" /> Text="{Binding Node.Storage}" />
</UserControl> </UserControl>

View File

@ -9,12 +9,13 @@ using NoStringEvaluating.Models.Values;
namespace Artemis.VisualScripting.Nodes.Maths namespace Artemis.VisualScripting.Nodes.Maths
{ {
[Node("Math Expression", "Outputs the result of a math expression.", "Math", OutputType = typeof(int))] [Node("Math Expression", "Outputs the result of a math expression.", "Math", OutputType = typeof(int))]
public class MathExpressionNode : Node<MathExpressionNodeCustomViewModel> public class MathExpressionNode : Node<string, MathExpressionNodeCustomViewModel>
{ {
private readonly INoStringEvaluator _evaluator; private readonly INoStringEvaluator _evaluator;
private readonly PinsVariablesContainer _variables; private readonly PinsVariablesContainer _variables;
#region Constructors #region Constructors
public MathExpressionNode(INoStringEvaluator evaluator) public MathExpressionNode(INoStringEvaluator evaluator)
: base("Math Expression", "Outputs the result of a math expression.") : base("Math Expression", "Outputs the result of a math expression.")
{ {
@ -24,7 +25,7 @@ namespace Artemis.VisualScripting.Nodes.Maths
Values.PinAdded += (_, _) => SetPinNames(); Values.PinAdded += (_, _) => SetPinNames();
Values.PinRemoved += (_, _) => SetPinNames(); Values.PinRemoved += (_, _) => SetPinNames();
_variables = new PinsVariablesContainer(Values); _variables = new PinsVariablesContainer(Values);
SetPinNames(); SetPinNames();
} }
@ -41,8 +42,8 @@ namespace Artemis.VisualScripting.Nodes.Maths
public override void Evaluate() public override void Evaluate()
{ {
if (Storage is string formula) if (Storage != null)
Output.Value = (float) _evaluator.CalcNumber(formula, _variables); Output.Value = (float) _evaluator.CalcNumber(Storage, _variables);
} }
private void SetPinNames() private void SetPinNames()

View File

@ -4,7 +4,7 @@ using Artemis.VisualScripting.Nodes.CustomViewModels;
namespace Artemis.VisualScripting.Nodes namespace Artemis.VisualScripting.Nodes
{ {
[Node("Integer-Value", "Outputs an configurable integer value.", "Static", OutputType = typeof(int))] [Node("Integer-Value", "Outputs an configurable integer value.", "Static", OutputType = typeof(int))]
public class StaticIntegerValueNode : Node<StaticIntegerValueNodeCustomViewModel> public class StaticIntegerValueNode : Node<int, StaticIntegerValueNodeCustomViewModel>
{ {
#region Properties & Fields #region Properties & Fields
@ -26,16 +26,14 @@ namespace Artemis.VisualScripting.Nodes
public override void Evaluate() public override void Evaluate()
{ {
Output.Value = Storage as int? ?? 0; Output.Value = Storage;
} }
public override void Initialize(INodeScript script) => Storage ??= 0;
#endregion #endregion
} }
[Node("Double-Value", "Outputs a configurable double value.", "Static", OutputType = typeof(double))] [Node("Double-Value", "Outputs a configurable double value.", "Static", OutputType = typeof(double))]
public class StaticDoubleValueNode : Node<StaticDoubleValueNodeCustomViewModel> public class StaticDoubleValueNode : Node<double, StaticDoubleValueNodeCustomViewModel>
{ {
#region Properties & Fields #region Properties & Fields
@ -60,13 +58,11 @@ namespace Artemis.VisualScripting.Nodes
Output.Value = Storage as double? ?? 0.0; Output.Value = Storage as double? ?? 0.0;
} }
public override void Initialize(INodeScript script) => Storage ??= 0.0;
#endregion #endregion
} }
[Node("Float-Value", "Outputs a configurable float value.", "Static", OutputType = typeof(float))] [Node("Float-Value", "Outputs a configurable float value.", "Static", OutputType = typeof(float))]
public class StaticFloatValueNode : Node<StaticFloatValueNodeCustomViewModel> public class StaticFloatValueNode : Node<float, StaticFloatValueNodeCustomViewModel>
{ {
#region Properties & Fields #region Properties & Fields
@ -88,19 +84,14 @@ namespace Artemis.VisualScripting.Nodes
public override void Evaluate() public override void Evaluate()
{ {
if (Storage is double doubleValue) Output.Value = Storage;
Storage = (float) doubleValue;
Output.Value = Storage as float? ?? 0.0f;
} }
public override void Initialize(INodeScript script) => Storage ??= 0.0f;
#endregion #endregion
} }
[Node("String-Value", "Outputs a configurable string value.", "Static", OutputType = typeof(string))] [Node("String-Value", "Outputs a configurable string value.", "Static", OutputType = typeof(string))]
public class StaticStringValueNode : Node<StaticStringValueNodeCustomViewModel> public class StaticStringValueNode : Node<string, StaticStringValueNodeCustomViewModel>
{ {
#region Properties & Fields #region Properties & Fields
@ -122,7 +113,7 @@ namespace Artemis.VisualScripting.Nodes
public override void Evaluate() public override void Evaluate()
{ {
Output.Value = Storage as string; Output.Value = Storage;
} }
#endregion #endregion