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

Nodes - Added XML docs for remaining types

Nodes - Fully implemented nullable reference types
This commit is contained in:
Robert 2021-09-25 21:35:54 +02:00
parent c1dab91c16
commit c21acf87a7
30 changed files with 644 additions and 265 deletions

View File

@ -93,6 +93,13 @@ namespace Artemis.Core
}
}
/// <summary>
/// Returns the index of the provided element inside the read only collection
/// </summary>
/// <typeparam name="T">The type of element to find</typeparam>
/// <param name="self">The collection to search in</param>
/// <param name="elementToFind">The element to find</param>
/// <returns>If found, the index of the element to find; otherwise -1</returns>
public static int IndexOf<T>(this IReadOnlyCollection<T> self, T elementToFind)
{
int i = 0;
@ -102,6 +109,7 @@ namespace Artemis.Core
return i;
i++;
}
return -1;
}
}

View File

@ -6,6 +6,9 @@ using Artemis.Storage.Entities.Profile.Conditions;
namespace Artemis.Core
{
/// <summary>
/// Represents a condition that is based on a <see cref="DataModelEvent" />
/// </summary>
public class EventCondition : CorePropertyChanged, INodeScriptCondition
{
private readonly string _displayName;
@ -78,7 +81,7 @@ namespace Artemis.Core
}
else
{
_eventNode = new EventDefaultNode() {X = -300};
_eventNode = new EventDefaultNode {X = -300};
_eventNode.UpdateDataModelEvent(dataModelEvent);
}

View File

@ -3,11 +3,17 @@ using Artemis.Storage.Entities.Profile.Conditions;
namespace Artemis.Core
{
/// <summary>
/// Represents a condition that is based on a data model value
/// </summary>
public class StaticCondition : CorePropertyChanged, INodeScriptCondition
{
private readonly StaticConditionEntity _entity;
private readonly string _displayName;
private readonly StaticConditionEntity _entity;
/// <summary>
/// Creates a new instance of the <see cref="StaticCondition" /> class
/// </summary>
public StaticCondition(ProfileElement profileElement)
{
_entity = new StaticConditionEntity();

View File

@ -4,7 +4,7 @@ using System.Collections.Generic;
namespace Artemis.Core
{
/// <summary>
/// Represents a data model event that can trigger <see cref="DataModelConditionEvent" />s.
/// Represents an event that is part of a data model
/// </summary>
public interface IDataModelEvent
{

View File

@ -82,7 +82,6 @@ namespace Artemis.Core
private TimeSpan _position;
private TimeSpan _lastDelta;
private TimeLineEventOverlapMode _eventOverlapMode;
private TimelinePlayMode _playMode;
private TimelineStopMode _stopMode;
private readonly List<Timeline> _extraTimelines;

View File

@ -114,17 +114,20 @@ namespace Artemis.Core.Modules
private readonly List<(DefaultCategoryName, string)> _defaultProfilePaths = new();
private readonly List<(DefaultCategoryName, string)> _pendingDefaultProfilePaths = new();
protected Module()
{
DefaultProfilePaths = new ReadOnlyCollection<(DefaultCategoryName, string)>(_defaultProfilePaths);
HiddenProperties = new(HiddenPropertiesList);
}
/// <summary>
/// Gets a list of all properties ignored at runtime using <c>IgnoreProperty(x => x.y)</c>
/// </summary>
protected internal readonly List<PropertyInfo> HiddenPropertiesList = new();
/// <summary>
/// The base constructor of the <see cref="Module" /> class.
/// </summary>
protected Module()
{
DefaultProfilePaths = new ReadOnlyCollection<(DefaultCategoryName, string)>(_defaultProfilePaths);
HiddenProperties = new ReadOnlyCollection<PropertyInfo>(HiddenPropertiesList);
}
/// <summary>
/// Gets a read only collection of default profile paths
/// </summary>
@ -237,7 +240,7 @@ namespace Artemis.Core.Modules
/// <returns></returns>
public virtual DataModelPropertyAttribute GetDataModelDescription()
{
return new() {Name = Plugin.Info.Name, Description = Plugin.Info.Description};
return new DataModelPropertyAttribute {Name = Plugin.Info.Name, Description = Plugin.Info.Description};
}
/// <summary>

View File

@ -44,9 +44,12 @@ namespace Artemis.Core.ScriptingProviders
/// </summary>
public abstract class ScriptingProvider : PluginFeature
{
/// <summary>
/// The base constructor of the <see cref="ScriptingProvider" /> class
/// </summary>
protected ScriptingProvider()
{
Scripts = new(InternalScripts);
Scripts = new ReadOnlyCollection<Script>(InternalScripts);
}
/// <summary>

View File

@ -2,20 +2,27 @@
namespace Artemis.Core.Events
{
/// <summary>
/// Represents an event argument containing a single value of type <typeparamref name="T" />
/// </summary>
/// <typeparam name="T">The type of value the argument contains</typeparam>
public class SingleValueEventArgs<T> : EventArgs
{
#region Properties & Fields
#region Constructors
public T Value { get; }
internal SingleValueEventArgs(T value)
{
Value = value;
}
#endregion
#region Constructors
#region Properties & Fields
public SingleValueEventArgs(T value)
{
this.Value = value;
}
/// <summary>
/// Gets the value of the argument
/// </summary>
public T Value { get; }
#endregion
}

View File

@ -3,42 +3,18 @@ using Newtonsoft.Json;
namespace Artemis.Core
{
/// <summary>
/// Represents an input pin containing a value of type <typeparamref name="T" /> on a <see cref="INode" />
/// </summary>
public sealed class InputPin<T> : Pin
{
#region Properties & Fields
public override Type Type { get; } = typeof(T);
public override object PinValue => Value;
public override PinDirection Direction => PinDirection.Input;
private T _value;
public T Value
{
get
{
if (!IsEvaluated)
Evaluate();
return _value;
}
private set
{
_value = value;
IsEvaluated = true;
OnPropertyChanged(nameof(PinValue));
}
}
#endregion
#region Constructors
[JsonConstructor]
internal InputPin(INode node, string name)
: base(node, name)
{
Value = default;
}
#endregion
@ -47,25 +23,29 @@ namespace Artemis.Core
private void Evaluate()
{
if (ConnectedTo.Count > 0)
if (ConnectedTo[0].PinValue is T value)
if (ConnectedTo.Count > 0 && ConnectedTo[0].PinValue is T value)
Value = value;
}
#endregion
}
public sealed class InputPin : Pin
{
#region Properties & Fields
public override Type Type { get; }
public override object PinValue => Value;
/// <inheritdoc />
public override Type Type { get; } = typeof(T);
/// <inheritdoc />
public override object? PinValue => Value;
/// <inheritdoc />
public override PinDirection Direction => PinDirection.Input;
private object _value;
private T? _value;
public object Value
/// <summary>
/// Gets or sets the value of the input pin
/// </summary>
public T? Value
{
get
{
@ -77,8 +57,6 @@ namespace Artemis.Core
private set
{
if (!Type.IsInstanceOfType(value)) throw new ArgumentException($"Value of type '{value?.GetType().Name ?? "null"}' can't be assigned to a pin of type {Type.Name}.");
_value = value;
IsEvaluated = true;
OnPropertyChanged(nameof(PinValue));
@ -86,13 +64,20 @@ namespace Artemis.Core
}
#endregion
}
/// <summary>
/// Represents an input pin on a <see cref="INode" />
/// </summary>
public sealed class InputPin : Pin
{
#region Constructors
internal InputPin(INode node, Type type, string name)
: base(node, name)
{
this.Type = type;
Type = type;
_value = type.GetDefault();
}
#endregion
@ -115,5 +100,54 @@ namespace Artemis.Core
}
#endregion
#region Properties & Fields
/// <inheritdoc />
public override Type Type { get; }
/// <inheritdoc />
public override object? PinValue => Value;
/// <inheritdoc />
public override PinDirection Direction => PinDirection.Input;
private object? _value;
/// <summary>
/// Gets or sets the value of the input pin
/// </summary>
public object? Value
{
get
{
if (!IsEvaluated)
Evaluate();
return _value;
}
private set
{
if (Type.IsValueType && value == null)
{
// We can't take null for value types so set it to the default value for that type
_value = Type.GetDefault();
}
else if (value != null)
{
// If a value was given make sure it matches
if (!Type.IsInstanceOfType(value))
throw new ArgumentException($"Value of type '{value.GetType().Name}' can't be assigned to a pin of type {Type.Name}.");
}
// Otherwise we're good and we can put a null here if it happens to be that
_value = value;
IsEvaluated = true;
OnPropertyChanged(nameof(PinValue));
}
}
#endregion
}
}

View File

@ -5,53 +5,63 @@ using System.Linq;
namespace Artemis.Core
{
/// <summary>
/// Represents a collection of input pins containing values of type <typeparamref name="T" />
/// </summary>
/// <typeparam name="T">The type of value the pins in this collection hold</typeparam>
public sealed class InputPinCollection<T> : PinCollection
{
#region Properties & Fields
public override PinDirection Direction => PinDirection.Input;
public override Type Type => typeof(T);
public new IEnumerable<InputPin<T>> Pins => base.Pins.Cast<InputPin<T>>();
public IEnumerable<T> Values => Pins.Select(p => p.Value);
#endregion
#region Constructors
internal InputPinCollection(INode node, string name, int initialCount)
: base(node, name, initialCount)
{ }
{
}
#endregion
#region Methods
protected override IPin CreatePin() => new InputPin<T>(Node, string.Empty);
/// <inheritdoc />
protected override IPin CreatePin()
{
return new InputPin<T>(Node, string.Empty);
}
#endregion
#region Properties & Fields
/// <inheritdoc />
public override PinDirection Direction => PinDirection.Input;
/// <inheritdoc />
public override Type Type => typeof(T);
/// <summary>
/// Gets an enumerable of the pins in this collection
/// </summary>
public new IEnumerable<InputPin<T>> Pins => base.Pins.Cast<InputPin<T>>();
/// <summary>
/// Gets an enumerable of the values of the pins in this collection
/// </summary>
public IEnumerable<T> Values => Pins.Where(p => p.Value != null).Select(p => p.Value!);
#endregion
}
/// <summary>
/// Represents a collection of input pins
/// </summary>
public sealed class InputPinCollection : PinCollection
{
#region Properties & Fields
public override PinDirection Direction => PinDirection.Input;
public override Type Type { get; }
public new IEnumerable<InputPin> Pins => base.Pins.Cast<InputPin>();
public IEnumerable Values => Pins.Select(p => p.Value);
#endregion
#region Constructors
internal InputPinCollection(INode node, Type type, string name, int initialCount)
: base(node, name, 0)
{
this.Type = type;
Type = type;
// Can't do this in the base constructor because the type won't be set yet
for (int i = 0; i < initialCount; i++)
@ -62,7 +72,31 @@ namespace Artemis.Core
#region Methods
protected override IPin CreatePin() => new InputPin(Node, Type, string.Empty);
/// <inheritdoc />
protected override IPin CreatePin()
{
return new InputPin(Node, Type, string.Empty);
}
#endregion
#region Properties & Fields
/// <inheritdoc />
public override PinDirection Direction => PinDirection.Input;
/// <inheritdoc />
public override Type Type { get; }
/// <summary>
/// Gets an enumerable of the pins in this collection
/// </summary>
public new IEnumerable<InputPin> Pins => base.Pins.Cast<InputPin>();
/// <summary>
/// Gets an enumerable of the values of the pins in this collection
/// </summary>
public IEnumerable Values => Pins.Where(p => p.Value != null).Select(p => p.Value);
#endregion
}

View File

@ -65,7 +65,7 @@ namespace Artemis.Core
}
/// <summary>
/// Represents a node script with a result value of type <paramref name="T" />
/// Represents a node script with a result value of type <typeparamref name="T" />
/// </summary>
/// <typeparam name="T">The type of result value</typeparam>
public interface INodeScript<out T> : INodeScript
@ -73,6 +73,6 @@ namespace Artemis.Core
/// <summary>
/// Gets the result of the script
/// </summary>
T Result { get; }
T? Result { get; }
}
}

View File

@ -4,24 +4,76 @@ using Artemis.Core.Events;
namespace Artemis.Core
{
/// <summary>
/// Represents a pin containing a value on a <see cref="INode" />
/// </summary>
public interface IPin
{
/// <summary>
/// Gets the node the pin belongs to
/// </summary>
INode Node { get; }
/// <summary>
/// Gets or sets the name of the pin
/// </summary>
string Name { get; set; }
PinDirection Direction { get; }
Type Type { get; }
object PinValue { get; }
/// <summary>
/// Gets the direction of the pin
/// </summary>
PinDirection Direction { get; }
/// <summary>
/// Gets the type of value the pin holds
/// </summary>
Type Type { get; }
/// <summary>
/// Gets the value the pin holds
/// </summary>
object? PinValue { get; }
/// <summary>
/// Gets a read only list of pins this pin is connected to
/// </summary>
IReadOnlyList<IPin> ConnectedTo { get; }
/// <summary>
/// Gets or sets a boolean indicating whether this pin is evaluated or not
/// </summary>
bool IsEvaluated { get; set; }
/// <summary>
/// Occurs when the pin connects to another pin
/// </summary>
event EventHandler<SingleValueEventArgs<IPin>> PinConnected;
/// <summary>
/// Occurs when the pin disconnects from another pin
/// </summary>
event EventHandler<SingleValueEventArgs<IPin>> PinDisconnected;
/// <summary>
/// Resets the pin, causing it to re-evaluate the next time its value is requested
/// </summary>
void Reset();
/// <summary>
/// Connects the pin to the provided <paramref name="pin"></paramref>
/// </summary>
/// <param name="pin">The pin to connect this pin to</param>
void ConnectTo(IPin pin);
/// <summary>
/// Disconnects the pin to the provided <paramref name="pin"></paramref>
/// </summary>
/// <param name="pin">The pin to disconnect this pin to</param>
void DisconnectFrom(IPin pin);
/// <summary>
/// Disconnects all pins this pin is connected to
/// </summary>
void DisconnectAll();
}
}

View File

@ -4,16 +4,56 @@ using Artemis.Core.Events;
namespace Artemis.Core
{
/// <summary>
/// Represents a collection of <see cref="IPin" />s on a <see cref="INode" />
/// </summary>
public interface IPinCollection : IEnumerable<IPin>
{
/// <summary>
/// Gets the node the pin collection belongs to
/// </summary>
INode Node { get; }
/// <summary>
/// Gets the name of the pin collection
/// </summary>
string Name { get; }
/// <summary>
/// Gets the direction of the pin collection and all its pins
/// </summary>
PinDirection Direction { get; }
/// <summary>
/// Gets the type of values the pin collection and all its pins holds
/// </summary>
Type Type { get; }
/// <summary>
/// Occurs when a pin was added to the collection
/// </summary>
event EventHandler<SingleValueEventArgs<IPin>> PinAdded;
/// <summary>
/// Occurs when a pin was removed from the collection
/// </summary>
event EventHandler<SingleValueEventArgs<IPin>> PinRemoved;
/// <summary>
/// Creates a new pin and adds it to the collection
/// </summary>
/// <returns>The newly added pin</returns>
IPin AddPin();
/// <summary>
/// Removes the provided <paramref name="pin" /> from the collection
/// </summary>
/// <param name="pin">The pin to remove</param>
bool Remove(IPin pin);
/// <summary>
/// Resets the pin collection, causing its pins to re-evaluate the next time its value is requested
/// </summary>
void Reset();
}
}

View File

@ -27,7 +27,7 @@ namespace Artemis.Core.Internal
foreach (var (property, inputPin) in _propertyPins)
{
if (inputPin.ConnectedTo.Any())
_propertyValues[property] = inputPin.Value;
_propertyValues[property] = inputPin.Value!;
else
_propertyValues.Remove(property);
}

View File

@ -9,7 +9,7 @@
public InputPin<T> Input { get; }
public T Value { get; private set; }
public T? Value { get; private set; }
public override bool IsExitNode => true;

View File

@ -239,10 +239,14 @@ namespace Artemis.Core
/// <inheritdoc />
public abstract void Evaluate();
/// <inheritdoc />
public virtual void Reset()
{
foreach (IPin pin in _pins)
pin.Reset();
foreach (IPinCollection pinCollection in _pinCollections)
pinCollection.Reset();
Resetting?.Invoke(this, EventArgs.Empty);
}

View File

@ -2,36 +2,67 @@
namespace Artemis.Core
{
/// <summary>
/// Represents an attribute that can be used to provide metadata on a node
/// </summary>
public class NodeAttribute : Attribute
{
#region Properties & Fields
/// <summary>
/// Gets the name of the node
/// </summary>
public string Name { get; }
public string Description { get; set; }
public string Category { get; set; }
public Type InputType { get; set; }
public Type OutputType { get; set; }
/// <summary>
/// Gets the description of the node
/// </summary>
public string Description { get; } = string.Empty;
/// <summary>
/// Gets the category of the node
/// </summary>
public string Category { get; } = string.Empty;
/// <summary>
/// Gets the primary input type of the node
/// </summary>
public Type? InputType { get; init; }
/// <summary>
/// Gets the primary output type of the node
/// </summary>
public Type? OutputType { get; init; }
#endregion
#region Constructors
/// <summary>
/// Creates a new instance of the <see cref="NodeAttribute" /> class
/// </summary>
public NodeAttribute(string name)
{
this.Name = name;
Name = name;
}
/// <summary>
/// Creates a new instance of the <see cref="NodeAttribute" /> class
/// </summary>
public NodeAttribute(string name, string description)
{
this.Name = name;
this.Description = description;
Name = name;
Description = description;
}
/// <summary>
/// Creates a new instance of the <see cref="NodeAttribute" /> class
/// </summary>
public NodeAttribute(string name, string description, string category)
{
this.Name = name;
this.Description = description;
this.Category = category;
Name = name;
Description = description;
Category = category;
}
#endregion

View File

@ -3,42 +3,80 @@ using Artemis.Storage.Entities.Profile.Nodes;
namespace Artemis.Core
{
/// <summary>
/// Represents node data describing a certain <see cref="INode" />
/// </summary>
public class NodeData
{
#region Properties & Fields
public Plugin Plugin { get; }
public Type Type { get; }
public string Name { get; }
public string Description { get; }
public string Category { get; }
public Type? InputType { get; }
public Type? OutputType { get; }
private Func<INodeScript, NodeEntity?, INode> _create;
#endregion
#region Constructors
internal NodeData(Plugin plugin, Type type, string name, string description, string category, Type? inputType, Type? outputType, Func<INodeScript, NodeEntity?, INode>? create)
internal NodeData(Plugin plugin, Type type, string name, string description, string category, Type? inputType, Type? outputType, Func<INodeScript, NodeEntity?, INode> create)
{
this.Plugin = plugin;
this.Type = type;
this.Name = name;
this.Description = description;
this.Category = category;
this.InputType = inputType;
this.OutputType = outputType;
this._create = create;
Plugin = plugin;
Type = type;
Name = name;
Description = description;
Category = category;
InputType = inputType;
OutputType = outputType;
_create = create;
}
#endregion
#region Methods
public INode CreateNode(INodeScript script, NodeEntity? entity) => _create(script, entity);
/// <summary>
/// Creates a new instance of the node this data represents
/// </summary>
/// <param name="script">The script to create the node for</param>
/// <param name="entity">An optional storage entity to apply to the node</param>
/// <returns>The returning node of type <see cref="Type" /></returns>
public INode CreateNode(INodeScript script, NodeEntity? entity)
{
return _create(script, entity);
}
#endregion
#region Properties & Fields
/// <summary>
/// Gets the plugin that provided this node data
/// </summary>
public Plugin Plugin { get; }
/// <summary>
/// Gets the type of <see cref="INode" /> this data represents
/// </summary>
public Type Type { get; }
/// <summary>
/// Gets the name of the node this data represents
/// </summary>
public string Name { get; }
/// <summary>
/// Gets the description of the node this data represents
/// </summary>
public string Description { get; }
/// <summary>
/// Gets the category of the node this data represents
/// </summary>
public string Category { get; }
/// <summary>
/// Gets the primary input type of the node this data represents
/// </summary>
public Type? InputType { get; }
/// <summary>
/// Gets the primary output of the node this data represents
/// </summary>
public Type? OutputType { get; }
private readonly Func<INodeScript, NodeEntity?, INode> _create;
#endregion
}

View File

@ -60,12 +60,19 @@ namespace Artemis.Core
#region Constructors
public NodeScript(string name, string description, object? context = null)
/// <summary>
/// Creates a new instance of the <see cref="NodeScript"/> class with a name, description and optional context
/// </summary>
/// <param name="name">The name of the node script</param>
/// <param name="description">The description of the node script</param>
/// <param name="context">The context of the node script, usually a <see cref="Profile" /> or <see cref="ProfileConfiguration" /></param>
protected NodeScript(string name, string description, object? context = null)
{
Name = name;
Description = description;
Context = context;
Entity = new NodeScriptEntity();
ExitNode = null!;
NodeTypeStore.NodeTypeAdded += NodeTypeStoreOnNodeTypeChanged;
NodeTypeStore.NodeTypeRemoved += NodeTypeStoreOnNodeTypeChanged;
@ -77,6 +84,7 @@ namespace Artemis.Core
Description = description;
Entity = entity;
Context = context;
ExitNode = null!;
NodeTypeStore.NodeTypeAdded += NodeTypeStoreOnNodeTypeChanged;
NodeTypeStore.NodeTypeRemoved += NodeTypeStoreOnNodeTypeChanged;
@ -176,6 +184,8 @@ namespace Artemis.Core
if (collection == null)
continue;
while (collection.Count() > entityNodePinCollection.Amount)
collection.Remove(collection.Last());
while (collection.Count() < entityNodePinCollection.Amount)
collection.AddPin();
}
@ -333,7 +343,7 @@ namespace Artemis.Core
}
/// <summary>
/// Represents a node script with a result value of type <paramref name="T" />
/// Represents a node script with a result value of type <typeparamref name="T" />
/// </summary>
/// <typeparam name="T">The type of result value</typeparam>
public class NodeScript<T> : NodeScript, INodeScript<T>
@ -341,7 +351,7 @@ namespace Artemis.Core
#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();
@ -362,6 +372,7 @@ namespace Artemis.Core
Load();
}
/// <inheritdoc />
public NodeScript(string name, string description, object? context = null)
: base(name, description, context)
{

View File

@ -3,55 +3,39 @@ using Newtonsoft.Json;
namespace Artemis.Core
{
/// <summary>
/// Represents an output pin containing a value of type <typeparamref name="T" /> on a <see cref="INode" />
/// </summary>
public sealed class OutputPin<T> : Pin
{
#region Properties & Fields
public override Type Type { get; } = typeof(T);
public override object PinValue => Value;
public override PinDirection Direction => PinDirection.Output;
private T _value;
public T Value
{
get
{
if (!IsEvaluated)
Node?.Evaluate();
return _value;
}
set
{
_value = value;
IsEvaluated = true;
OnPropertyChanged(nameof(PinValue));
}
}
#endregion
#region Constructors
[JsonConstructor]
internal OutputPin(INode node, string name)
: base(node, name)
{ }
#endregion
{
_value = default;
}
public sealed class OutputPin : Pin
{
#endregion
#region Properties & Fields
public override Type Type { get; }
public override object PinValue => Value;
/// <inheritdoc />
public override Type Type { get; } = typeof(T);
/// <inheritdoc />
public override object? PinValue => Value;
/// <inheritdoc />
public override PinDirection Direction => PinDirection.Output;
private object _value;
public object Value
private T? _value;
/// <summary>
/// Gets or sets the value of the output pin
/// </summary>
public T? Value
{
get
{
@ -62,8 +46,72 @@ namespace Artemis.Core
}
set
{
if (!Type.IsInstanceOfType(value)) throw new ArgumentException($"Value of type '{value?.GetType().Name ?? "null"}' can't be assigned to a pin of type {Type.Name}.");
_value = value;
IsEvaluated = true;
OnPropertyChanged(nameof(PinValue));
}
}
#endregion
}
/// <summary>
/// Represents an output pin on a <see cref="INode" />
/// </summary>
public sealed class OutputPin : Pin
{
#region Constructors
internal OutputPin(INode node, Type type, string name)
: base(node, name)
{
Type = type;
_value = type.GetDefault();
}
#endregion
#region Properties & Fields
/// <inheritdoc />
public override Type Type { get; }
/// <inheritdoc />
public override object? PinValue => Value;
/// <inheritdoc />
public override PinDirection Direction => PinDirection.Output;
private object? _value;
/// <summary>
/// Gets or sets the value of the output pin
/// </summary>
public object? Value
{
get
{
if (!IsEvaluated)
Node?.Evaluate();
return _value;
}
set
{
if (Type.IsValueType && value == null)
{
// We can't take null for value types so set it to the default value for that type
_value = Type.GetDefault();
}
else if (value != null)
{
// If a value was given make sure it matches
if (!Type.IsInstanceOfType(value))
throw new ArgumentException($"Value of type '{value.GetType().Name}' can't be assigned to a pin of type {Type.Name}.");
}
// Otherwise we're good and we can put a null here if it happens to be that
_value = value;
IsEvaluated = true;
OnPropertyChanged(nameof(PinValue));
@ -71,15 +119,5 @@ namespace Artemis.Core
}
#endregion
#region Constructors
internal OutputPin(INode node, Type type, string name)
: base(node, name)
{
this.Type = type;
}
#endregion
}
}

View File

@ -2,11 +2,17 @@
namespace Artemis.Core
{
/// <summary>
/// Represents a collection of output pins containing values of type <typeparamref name="T" />
/// </summary>
/// <typeparam name="T">The type of value the pins in this collection hold</typeparam>
public sealed class OutputPinCollection<T> : PinCollection
{
#region Properties & Fields
/// <inheritdoc />
public override PinDirection Direction => PinDirection.Output;
/// <inheritdoc />
public override Type Type => typeof(T);
#endregion
@ -21,6 +27,7 @@ namespace Artemis.Core
#region Methods
/// <inheritdoc />
protected override IPin CreatePin() => new OutputPin<T>(Node, string.Empty);
#endregion

View File

@ -5,12 +5,35 @@ using Artemis.Core.Events;
namespace Artemis.Core
{
/// <inheritdoc cref="IPin" />
public abstract class Pin : CorePropertyChanged, IPin
{
#region Constructors
/// <summary>
/// Creates a new instance of the <see cref="Pin" /> class on the provided node with the provided name
/// </summary>
/// <param name="node">The node the pin belongs to</param>
/// <param name="name">The name of the pin</param>
protected Pin(INode node, string name = "")
{
Node = node;
_name = name;
}
#endregion
/// <inheritdoc />
public event EventHandler<SingleValueEventArgs<IPin>>? PinConnected;
/// <inheritdoc />
public event EventHandler<SingleValueEventArgs<IPin>>? PinDisconnected;
#region Properties & Fields
/// <inheritdoc />
public INode Node { get; }
/// <inheritdoc />
public string Name
{
get => _name;
@ -19,6 +42,7 @@ namespace Artemis.Core
private bool _isEvaluated;
/// <inheritdoc />
public bool IsEvaluated
{
get => _isEvaluated;
@ -27,36 +51,30 @@ namespace Artemis.Core
private readonly List<IPin> _connectedTo = new();
private string _name;
/// <inheritdoc />
public IReadOnlyList<IPin> ConnectedTo => new ReadOnlyCollection<IPin>(_connectedTo);
/// <inheritdoc />
public abstract PinDirection Direction { get; }
/// <inheritdoc />
public abstract Type Type { get; }
public abstract object PinValue { get; }
#endregion
#region Events
public event EventHandler<SingleValueEventArgs<IPin>> PinConnected;
public event EventHandler<SingleValueEventArgs<IPin>> PinDisconnected;
#endregion
#region Constructors
protected Pin(INode node, string name = "")
{
this.Node = node;
this.Name = name;
if (Node != null)
Node.Resetting += OnNodeResetting;
}
/// <inheritdoc />
public abstract object? PinValue { get; }
#endregion
#region Methods
/// <inheritdoc />
public void Reset()
{
IsEvaluated = false;
}
/// <inheritdoc />
public void ConnectTo(IPin pin)
{
_connectedTo.Add(pin);
@ -65,6 +83,7 @@ namespace Artemis.Core
PinConnected?.Invoke(this, new SingleValueEventArgs<IPin>(pin));
}
/// <inheritdoc />
public void DisconnectFrom(IPin pin)
{
_connectedTo.Remove(pin);
@ -73,6 +92,7 @@ namespace Artemis.Core
PinDisconnected?.Invoke(this, new SingleValueEventArgs<IPin>(pin));
}
/// <inheritdoc />
public void DisconnectAll()
{
List<IPin> connectedPins = new(_connectedTo);
@ -84,11 +104,6 @@ namespace Artemis.Core
PinDisconnected?.Invoke(this, new SingleValueEventArgs<IPin>(pin));
}
private void OnNodeResetting(object sender, EventArgs e)
{
IsEvaluated = false;
}
#endregion
}
}

View File

@ -6,34 +6,22 @@ using Artemis.Core.Events;
namespace Artemis.Core
{
/// <inheritdoc cref="IPinCollection"/>
public abstract class PinCollection : IPinCollection
{
#region Properties & Fields
public INode Node { get; }
public string Name { get; }
public abstract PinDirection Direction { get; }
public abstract Type Type { get; }
private readonly ObservableCollection<IPin> _pins = new();
public ReadOnlyObservableCollection<IPin> Pins => new(_pins);
#endregion
#region Events
public event EventHandler<SingleValueEventArgs<IPin>> PinAdded;
public event EventHandler<SingleValueEventArgs<IPin>> PinRemoved;
#endregion
#region Constructors
/// <summary>
/// Creates a new instance of the <see cref="PinCollection" /> class
/// </summary>
/// <param name="node">The node the pin collection belongs to</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 output pin collection</returns>
protected PinCollection(INode node, string name, int initialCount)
{
this.Node = node;
this.Name = name;
Node = node ?? throw new ArgumentNullException(nameof(node));
Name = name;
for (int i = 0; i < initialCount; i++)
AddPin();
@ -41,9 +29,38 @@ namespace Artemis.Core
#endregion
/// <inheritdoc />
public event EventHandler<SingleValueEventArgs<IPin>>? PinAdded;
/// <inheritdoc />
public event EventHandler<SingleValueEventArgs<IPin>>? PinRemoved;
#region Properties & Fields
/// <inheritdoc />
public INode Node { get; }
/// <inheritdoc />
public string Name { get; }
/// <inheritdoc />
public abstract PinDirection Direction { get; }
/// <inheritdoc />
public abstract Type Type { get; }
private readonly ObservableCollection<IPin> _pins = new();
/// <summary>
/// Gets a read only observable collection of the pins
/// </summary>
public ReadOnlyObservableCollection<IPin> Pins => new(_pins);
#endregion
#region Methods
/// <inheritdoc />
public IPin AddPin()
{
IPin pin = CreatePin();
@ -54,6 +71,7 @@ namespace Artemis.Core
return pin;
}
/// <inheritdoc />
public bool Remove(IPin pin)
{
bool removed = _pins.Remove(pin);
@ -64,10 +82,29 @@ namespace Artemis.Core
return removed;
}
/// <inheritdoc />
public void Reset()
{
foreach (IPin pin in _pins)
pin.Reset();
}
/// <summary>
/// Creates a new pin to be used in this collection
/// </summary>
/// <returns>The resulting pin</returns>
protected abstract IPin CreatePin();
public IEnumerator<IPin> GetEnumerator() => Pins.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// <inheritdoc />
public IEnumerator<IPin> GetEnumerator()
{
return Pins.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}

View File

@ -1,8 +1,18 @@
namespace Artemis.Core
{
/// <summary>
/// Represents a direction in which pin data flows
/// </summary>
public enum PinDirection
{
/// <summary>
/// An input direction
/// </summary>
Input,
/// <summary>
/// An output direction
/// </summary>
Output
}
}

View File

@ -63,7 +63,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
}
}
private void DataBindingOnDataBindingToggled(object? sender, DataBindingEventArgs e)
private void DataBindingOnDataBindingToggled(object sender, DataBindingEventArgs e)
{
OnPropertyChanged(nameof(DataBindingEnabled));
}

View File

@ -22,8 +22,7 @@ namespace Artemis.UI.Stylet
{
base.Dispose();
ScreenExtensions.TryDispose(_rootViewModel);
if (Kernel != null)
Kernel.Dispose();
Kernel?.Dispose();
}
protected override void ConfigureBootstrapper()

View File

@ -277,7 +277,7 @@ namespace Artemis.VisualScripting.Editor.Controls
FitScript();
}
private void OnVisualScriptNodeCollectionChanged(object? sender, EventArgs e)
private void OnVisualScriptNodeCollectionChanged(object sender, EventArgs e)
{
if (AutoFitScript)
FitScript();

View File

@ -103,19 +103,18 @@
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type wrapper:VisualScriptPinCollection}">
<StackPanel Margin="0,4" Orientation="Vertical">
<StackPanel Margin="0,2" Orientation="Vertical">
<TextBlock Text="{Binding PinCollection.Name}" Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType={x:Type controls:VisualScriptNodePresenter}}}" />
<ItemsControl Margin="0,4"
<ItemsControl Margin="0,5"
Style="{StaticResource StylePinListInput}"
ItemsSource="{Binding Pins}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type core:Pin}">
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal">
<controls:VisualScriptPinPresenter Margin="0,5,-3,3"
Pin="{Binding .}" />
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal" Margin="0 8 0 0">
<controls:VisualScriptPinPresenter Pin="{Binding .}" Margin="0 0 2 0" />
<Button Style="{StaticResource StyleButtonPinsModify}"
Command="{Binding DataContext.RemovePinCommand, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"
CommandParameter="{Binding .}"/>
CommandParameter="{Binding .}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
@ -146,7 +145,7 @@
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<Button Style="{StaticResource StyleButtonPinsModify}"
Command="{Binding DataContext.RemovePinCommand, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"
CommandParameter="{Binding .}"/>
CommandParameter="{Binding .}" />
<controls:VisualScriptPinPresenter Margin="-3,5,0,3"
Pin="{Binding .}" />
</StackPanel>
@ -184,7 +183,7 @@
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center"
FontFamily="{TemplateBinding FontFamily}"
FontSize="14" FontWeight="Bold"
Text="{Binding Node.Node.Name, RelativeSource={RelativeSource TemplatedParent}}"/>
Text="{Binding Node.Node.Name, RelativeSource={RelativeSource TemplatedParent}}" />
</DockPanel>
</Border>
@ -208,7 +207,7 @@
</Border>
<Border x:Name="BrdCustomView" Grid.Column="1">
<ContentControl s:View.Model="{TemplateBinding CustomViewModel}"/>
<ContentControl s:View.Model="{TemplateBinding CustomViewModel}" />
</Border>
<Border x:Name="BrdOutputPins" Grid.Column="2">
@ -231,7 +230,7 @@
<Setter Property="Padding" Value="8,10" />
<Setter Property="Foreground" Value="#FFFFFFFF"/>
<Setter Property="Foreground" Value="#FFFFFFFF" />
<Setter Property="Background" Value="#80101010" />
<Setter Property="TitleBrush" Value="#FF444444" />

View File

@ -110,7 +110,7 @@ namespace Artemis.VisualScripting.Nodes
CreateOutputPin(dataBindingRegistration.ValueType, dataBindingRegistration.DisplayName);
}
private void ProfileOnChildRemoved(object? sender, EventArgs e)
private void ProfileOnChildRemoved(object sender, EventArgs e)
{
if (Script.Context is not Profile profile)
return;

View File

@ -8,7 +8,7 @@ using NoStringEvaluating.Models.Values;
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", InputType = typeof(float), OutputType = typeof(float))]
public class MathExpressionNode : Node<string, MathExpressionNodeCustomViewModel>
{
private readonly INoStringEvaluator _evaluator;
@ -42,6 +42,7 @@ namespace Artemis.VisualScripting.Nodes.Maths
public override void Evaluate()
{
var test = _evaluator.ToString();
if (Storage != null)
Output.Value = (float) _evaluator.CalcNumber(Storage, _variables);
}