mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Added some nodes
This commit is contained in:
parent
86c7bd2877
commit
fb8f76facb
57
src/Artemis.VisualScripting/Nodes/Color/LerpSKColorNode.cs
Normal file
57
src/Artemis.VisualScripting/Nodes/Color/LerpSKColorNode.cs
Normal file
@ -0,0 +1,57 @@
|
||||
using Artemis.Core;
|
||||
using RGB.NET.Core;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.VisualScripting.Nodes.Color;
|
||||
|
||||
[Node("Lerp (Color)", "Interpolates linear between the two colors A and B", "Color", InputType = typeof(SKColor), OutputType = typeof(SKColor))]
|
||||
public class LerpSKColorNode : Node
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
public InputPin<SKColor> A { get; }
|
||||
public InputPin<SKColor> B { get; }
|
||||
public InputPin<Numeric> T { get; }
|
||||
|
||||
public OutputPin<SKColor> Result { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public LerpSKColorNode()
|
||||
: base("Lerp", "Interpolates linear between the two values A and B")
|
||||
{
|
||||
A = CreateInputPin<SKColor>("A");
|
||||
B = CreateInputPin<SKColor>("B");
|
||||
T = CreateInputPin<Numeric>("T");
|
||||
|
||||
Result = CreateOutputPin<SKColor>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Evaluate()
|
||||
{
|
||||
SKColor a = A.Value;
|
||||
SKColor b = B.Value;
|
||||
float t = ((float)T.Value).Clamp(0f, 1f);
|
||||
|
||||
float aAlpha = a.Alpha.GetPercentageFromByteValue();
|
||||
float aRed = a.Red.GetPercentageFromByteValue();
|
||||
float aGreen = a.Green.GetPercentageFromByteValue();
|
||||
float aBlue = a.Blue.GetPercentageFromByteValue();
|
||||
|
||||
float alpha = ((b.Alpha.GetPercentageFromByteValue() - aAlpha) * t) + aAlpha;
|
||||
float red = ((b.Red.GetPercentageFromByteValue() - aRed) * t) + aRed;
|
||||
float green = ((b.Green.GetPercentageFromByteValue() - aGreen) * t) + aGreen;
|
||||
float blue = ((b.Blue.GetPercentageFromByteValue() - aBlue) * t) + aBlue;
|
||||
|
||||
Result.Value = new SKColor(red.GetByteValueFromPercentage(), green.GetByteValueFromPercentage(), blue.GetByteValueFromPercentage(), alpha.GetByteValueFromPercentage());
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
38
src/Artemis.VisualScripting/Nodes/Color/RgbSKColorNode.cs
Normal file
38
src/Artemis.VisualScripting/Nodes/Color/RgbSKColorNode.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using Artemis.Core;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.VisualScripting.Nodes.Color;
|
||||
|
||||
[Node("RGB Color", "Creates a color from red, green and blue values", "Color", InputType = typeof(Numeric), OutputType = typeof(SKColor))]
|
||||
public class RgbSKColorNode : Node
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
public InputPin<Numeric> R { get; set; }
|
||||
public InputPin<Numeric> G { get; set; }
|
||||
public InputPin<Numeric> B { get; set; }
|
||||
public OutputPin<SKColor> Output { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public RgbSKColorNode()
|
||||
: base("RGB Color", "Creates a color from red, green and blue values")
|
||||
{
|
||||
R = CreateInputPin<Numeric>("R");
|
||||
G = CreateInputPin<Numeric>("G");
|
||||
B = CreateInputPin<Numeric>("B");
|
||||
|
||||
Output = CreateOutputPin<SKColor>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Evaluate() => Output.Value = new SKColor(R.Value, G.Value, B.Value);
|
||||
|
||||
#endregion
|
||||
}
|
||||
39
src/Artemis.VisualScripting/Nodes/Mathematics/Clamp.cs
Normal file
39
src/Artemis.VisualScripting/Nodes/Mathematics/Clamp.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using Artemis.Core;
|
||||
using RGB.NET.Core;
|
||||
|
||||
namespace Artemis.VisualScripting.Nodes.Mathematics;
|
||||
|
||||
[Node("Clamp", "Clamps the value to be in between min and max", "Mathematics", InputType = typeof(Numeric), OutputType = typeof(Numeric))]
|
||||
public class ClampNode : Node
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
public InputPin<Numeric> Value { get; }
|
||||
public InputPin<Numeric> Min { get; }
|
||||
public InputPin<Numeric> Max { get; }
|
||||
|
||||
public OutputPin<Numeric> Result { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public ClampNode()
|
||||
: base("Clamp", "Clamps the value to be in between min and max")
|
||||
{
|
||||
Value = CreateInputPin<Numeric>("Value");
|
||||
Min = CreateInputPin<Numeric>("Min");
|
||||
Max = CreateInputPin<Numeric>("Max");
|
||||
|
||||
Result = CreateOutputPin<Numeric>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Evaluate() => Result.Value = ((float)Value.Value).Clamp(Min.Value, Max.Value);
|
||||
|
||||
#endregion
|
||||
}
|
||||
45
src/Artemis.VisualScripting/Nodes/Mathematics/LerpNode.cs
Normal file
45
src/Artemis.VisualScripting/Nodes/Mathematics/LerpNode.cs
Normal file
@ -0,0 +1,45 @@
|
||||
using Artemis.Core;
|
||||
using RGB.NET.Core;
|
||||
|
||||
namespace Artemis.VisualScripting.Nodes.Mathematics;
|
||||
|
||||
[Node("Lerp", "Interpolates linear between the two values A and B", "Mathematics", InputType = typeof(Numeric), OutputType = typeof(Numeric))]
|
||||
public class LerpNode : Node
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
public InputPin<Numeric> A { get; }
|
||||
public InputPin<Numeric> B { get; }
|
||||
public InputPin<Numeric> T { get; }
|
||||
|
||||
public OutputPin<Numeric> Result { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public LerpNode()
|
||||
: base("Lerp", "Interpolates linear between the two values A and B")
|
||||
{
|
||||
A = CreateInputPin<Numeric>("A");
|
||||
B = CreateInputPin<Numeric>("B");
|
||||
T = CreateInputPin<Numeric>("T");
|
||||
|
||||
Result = CreateOutputPin<Numeric>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Evaluate()
|
||||
{
|
||||
float a = A.Value;
|
||||
float b = B.Value;
|
||||
float t = ((float)T.Value).Clamp(0f, 1f);
|
||||
Result.Value = ((b - a) * t) + a;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
48
src/Artemis.VisualScripting/Nodes/Mathematics/RangeNode.cs
Normal file
48
src/Artemis.VisualScripting/Nodes/Mathematics/RangeNode.cs
Normal file
@ -0,0 +1,48 @@
|
||||
using Artemis.Core;
|
||||
using RGB.NET.Core;
|
||||
|
||||
namespace Artemis.VisualScripting.Nodes.Mathematics;
|
||||
|
||||
[Node("Range", "Selects the best integer value in the given range by the given percentage", "Static", InputType = typeof(Numeric), OutputType = typeof(Numeric))]
|
||||
public class RangeNode : Node
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
public InputPin<Numeric> Min { get; }
|
||||
public InputPin<Numeric> Max { get; }
|
||||
public InputPin<Numeric> Percentage { get; }
|
||||
|
||||
public OutputPin<Numeric> Result { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public RangeNode()
|
||||
: base("Range", "Selects the best integer value in the given range by the given percentage")
|
||||
{
|
||||
Min = CreateInputPin<Numeric>("Min");
|
||||
Max = CreateInputPin<Numeric>("Max");
|
||||
Percentage = CreateInputPin<Numeric>("Percentage");
|
||||
|
||||
Result = CreateOutputPin<Numeric>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Evaluate()
|
||||
{
|
||||
int min = Min.Value;
|
||||
int max = Max.Value;
|
||||
float percentage = ((float)Percentage.Value).Clamp(0f, 1f);
|
||||
|
||||
int range = max - min;
|
||||
|
||||
Result.Value = percentage >= 1.0f ? max : ((int)(percentage * (range + 1)) + min);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
35
src/Artemis.VisualScripting/Nodes/Mathematics/Saturate.cs
Normal file
35
src/Artemis.VisualScripting/Nodes/Mathematics/Saturate.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using Artemis.Core;
|
||||
using RGB.NET.Core;
|
||||
|
||||
namespace Artemis.VisualScripting.Nodes.Mathematics;
|
||||
|
||||
[Node("Saturate", "Clamps the value to be in between 0 and 1", "Mathematics", InputType = typeof(Numeric), OutputType = typeof(Numeric))]
|
||||
public class SaturateNode : Node
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
public InputPin<Numeric> Value { get; }
|
||||
|
||||
public OutputPin<Numeric> Result { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public SaturateNode()
|
||||
: base("Clamp", "Clamps the value to be in between 0 and 1")
|
||||
{
|
||||
Value = CreateInputPin<Numeric>();
|
||||
|
||||
Result = CreateOutputPin<Numeric>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Evaluate() => Result.Value = ((float)Value.Value).Clamp(0f, 1f);
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
using Artemis.Core;
|
||||
|
||||
namespace Artemis.VisualScripting.Nodes.Static;
|
||||
|
||||
[Node("Random", "Generates a random value between 0 and 1", "Static", OutputType = typeof(Numeric))]
|
||||
public class RandomNumericValueNode : Node
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private static readonly Random RANDOM = new();
|
||||
|
||||
public OutputPin<Numeric> Output { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public RandomNumericValueNode()
|
||||
: base("Random", "Generates a random value between 0 and 1")
|
||||
{
|
||||
Output = CreateOutputPin<Numeric>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Evaluate() => Output.Value = RANDOM.NextSingle();
|
||||
|
||||
#endregion
|
||||
}
|
||||
117
src/Artemis.VisualScripting/Nodes/Timing/DelayNode.cs
Normal file
117
src/Artemis.VisualScripting/Nodes/Timing/DelayNode.cs
Normal file
@ -0,0 +1,117 @@
|
||||
using System.Diagnostics;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Events;
|
||||
using RGB.NET.Core;
|
||||
|
||||
namespace Artemis.VisualScripting.Nodes.Timing;
|
||||
|
||||
[Node("Delay", "Delays the resolution of the input pin(s) for the given time after each update", "Timing", InputType = typeof(object), OutputType = typeof(object))]
|
||||
public class DelayNode : Node
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private long _lastUpdateTimestamp = 0;
|
||||
|
||||
public InputPin<Numeric> Delay { get; }
|
||||
public InputPinCollection Input { get; }
|
||||
|
||||
public OutputPin<bool> IsUpdated { get; }
|
||||
public OutputPin<Numeric> NextUpdateTime { get; }
|
||||
|
||||
private Dictionary<IPin, OutputPin> _pinPairs = new();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public DelayNode()
|
||||
: base("Delay", "Delays the resolution of the input pin(s) for the given time after each update")
|
||||
{
|
||||
Delay = CreateInputPin<Numeric>("Delay");
|
||||
Input = CreateInputPinCollection(typeof(object), initialCount: 0);
|
||||
|
||||
IsUpdated = CreateOutputPin<bool>("Updated");
|
||||
NextUpdateTime = CreateOutputPin<Numeric>("Next Update");
|
||||
|
||||
Input.PinAdded += OnInputPinAdded;
|
||||
Input.PinRemoved += OnInputPinRemoved;
|
||||
|
||||
Input.Add(Input.CreatePin());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
private void OnInputPinAdded(object? sender, SingleValueEventArgs<IPin> args)
|
||||
{
|
||||
IPin inputPin = args.Value;
|
||||
_pinPairs.Add(inputPin, CreateOutputPin(typeof(object)));
|
||||
|
||||
inputPin.PinConnected += OnInputPinConnected;
|
||||
inputPin.PinDisconnected += OnInputPinDisconnected;
|
||||
|
||||
UpdatePinNames();
|
||||
}
|
||||
|
||||
private void OnInputPinRemoved(object? sender, SingleValueEventArgs<IPin> args)
|
||||
{
|
||||
IPin inputPin = args.Value;
|
||||
RemovePin(_pinPairs[inputPin]);
|
||||
_pinPairs.Remove(inputPin);
|
||||
|
||||
inputPin.PinConnected -= OnInputPinConnected;
|
||||
inputPin.PinDisconnected -= OnInputPinDisconnected;
|
||||
|
||||
UpdatePinNames();
|
||||
}
|
||||
|
||||
private void OnInputPinConnected(object? sender, SingleValueEventArgs<IPin> args)
|
||||
{
|
||||
if (sender is not IPin inputPin || !_pinPairs.ContainsKey(inputPin)) return;
|
||||
|
||||
OutputPin outputPin = _pinPairs[inputPin];
|
||||
outputPin.ChangeType(args.Value.Type);
|
||||
}
|
||||
|
||||
private void OnInputPinDisconnected(object? sender, SingleValueEventArgs<IPin> args)
|
||||
{
|
||||
if (sender is not IPin inputPin || !_pinPairs.ContainsKey(inputPin)) return;
|
||||
|
||||
OutputPin outputPin = _pinPairs[inputPin];
|
||||
outputPin.ChangeType(typeof(object));
|
||||
}
|
||||
|
||||
private void UpdatePinNames()
|
||||
{
|
||||
int counter = 1;
|
||||
foreach (IPin inputPin in Input.Pins)
|
||||
{
|
||||
string name = counter.ToString();
|
||||
inputPin.Name = name;
|
||||
_pinPairs[inputPin].Name = name;
|
||||
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Evaluate()
|
||||
{
|
||||
double nextUpdateIn = Delay.Value - TimerHelper.GetElapsedTime(_lastUpdateTimestamp);
|
||||
NextUpdateTime.Value = nextUpdateIn;
|
||||
|
||||
if (nextUpdateIn <= 0)
|
||||
{
|
||||
IsUpdated.Value = true;
|
||||
foreach ((IPin input, OutputPin output) in _pinPairs)
|
||||
output.Value = input.PinValue;
|
||||
|
||||
_lastUpdateTimestamp = Stopwatch.GetTimestamp();
|
||||
}
|
||||
else
|
||||
IsUpdated.Value = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
41
src/Artemis.VisualScripting/Nodes/Timing/EdgeNode.cs
Normal file
41
src/Artemis.VisualScripting/Nodes/Timing/EdgeNode.cs
Normal file
@ -0,0 +1,41 @@
|
||||
using Artemis.Core;
|
||||
|
||||
namespace Artemis.VisualScripting.Nodes.Timing;
|
||||
|
||||
[Node("Edge", "Outputs true on each edge when the input changes", "Timing", InputType = typeof(bool), OutputType = typeof(bool))]
|
||||
public class EdgeNode : Node
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private bool _lastInput;
|
||||
|
||||
public InputPin<bool> Input { get; }
|
||||
public OutputPin<bool> Output { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public EdgeNode()
|
||||
: base("Edge", "Outputs true on each edge when the input changes")
|
||||
{
|
||||
Input = CreateInputPin<bool>();
|
||||
Output = CreateOutputPin<bool>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Evaluate()
|
||||
{
|
||||
bool input = Input.Value;
|
||||
|
||||
Output.Value = input != _lastInput;
|
||||
|
||||
_lastInput = input;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
45
src/Artemis.VisualScripting/Nodes/Timing/FlipFlopNode.cs
Normal file
45
src/Artemis.VisualScripting/Nodes/Timing/FlipFlopNode.cs
Normal file
@ -0,0 +1,45 @@
|
||||
using Artemis.Core;
|
||||
|
||||
namespace Artemis.VisualScripting.Nodes.Timing;
|
||||
|
||||
[Node("FlipFlop", "Inverts the output when the input changes from false to true", "Timing", InputType = typeof(bool), OutputType = typeof(bool))]
|
||||
public class FlipFlopNode : Node
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private bool _lastInput;
|
||||
private bool _currentValue;
|
||||
|
||||
public InputPin<bool> Input { get; }
|
||||
public OutputPin<bool> Output { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public FlipFlopNode()
|
||||
: base("FlipFlop", "Inverts the output when the input changes from false to true")
|
||||
{
|
||||
Input = CreateInputPin<bool>();
|
||||
Output = CreateOutputPin<bool>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Evaluate()
|
||||
{
|
||||
bool input = Input.Value;
|
||||
if (input && !_lastInput)
|
||||
{
|
||||
_currentValue = !_currentValue;
|
||||
Output.Value = _currentValue;
|
||||
}
|
||||
|
||||
_lastInput = input;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
113
src/Artemis.VisualScripting/Nodes/Timing/LatchNode.cs
Normal file
113
src/Artemis.VisualScripting/Nodes/Timing/LatchNode.cs
Normal file
@ -0,0 +1,113 @@
|
||||
using System.Diagnostics;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Events;
|
||||
using RGB.NET.Core;
|
||||
|
||||
namespace Artemis.VisualScripting.Nodes.Timing;
|
||||
|
||||
[Node("Latch", "Only passes the input to the output as long as the control-pin is true. If the control pin is false the last passed value is provided.", "Timing", InputType = typeof(object), OutputType = typeof(object))]
|
||||
public class LatchNode : Node
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private long _lastUpdateTimestamp = 0;
|
||||
|
||||
public InputPin<bool> Control { get; }
|
||||
public InputPinCollection Input { get; }
|
||||
|
||||
//TODO DarthAffe 21.08.2022: Find something to output to aling in- and outputs
|
||||
public OutputPin<Numeric> LastUpdateTime { get; }
|
||||
|
||||
private Dictionary<IPin, OutputPin> _pinPairs = new();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public LatchNode()
|
||||
: base("Latch", "Only passes the input to the output as long as the control-pin is true. If the control pin is false the last passed value is provided.")
|
||||
{
|
||||
Control = CreateInputPin<bool>("Control");
|
||||
Input = CreateInputPinCollection(typeof(object), initialCount: 0);
|
||||
|
||||
LastUpdateTime = CreateOutputPin<Numeric>("Last Update");
|
||||
|
||||
Input.PinAdded += OnInputPinAdded;
|
||||
Input.PinRemoved += OnInputPinRemoved;
|
||||
|
||||
Input.Add(Input.CreatePin());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
private void OnInputPinAdded(object? sender, SingleValueEventArgs<IPin> args)
|
||||
{
|
||||
IPin inputPin = args.Value;
|
||||
_pinPairs.Add(inputPin, CreateOutputPin(typeof(object)));
|
||||
|
||||
inputPin.PinConnected += OnInputPinConnected;
|
||||
inputPin.PinDisconnected += OnInputPinDisconnected;
|
||||
|
||||
UpdatePinNames();
|
||||
}
|
||||
|
||||
private void OnInputPinRemoved(object? sender, SingleValueEventArgs<IPin> args)
|
||||
{
|
||||
IPin inputPin = args.Value;
|
||||
RemovePin(_pinPairs[inputPin]);
|
||||
_pinPairs.Remove(inputPin);
|
||||
|
||||
inputPin.PinConnected -= OnInputPinConnected;
|
||||
inputPin.PinDisconnected -= OnInputPinDisconnected;
|
||||
|
||||
UpdatePinNames();
|
||||
}
|
||||
|
||||
private void OnInputPinConnected(object? sender, SingleValueEventArgs<IPin> args)
|
||||
{
|
||||
if (sender is not IPin inputPin || !_pinPairs.ContainsKey(inputPin)) return;
|
||||
|
||||
OutputPin outputPin = _pinPairs[inputPin];
|
||||
outputPin.ChangeType(args.Value.Type);
|
||||
}
|
||||
|
||||
private void OnInputPinDisconnected(object? sender, SingleValueEventArgs<IPin> args)
|
||||
{
|
||||
if (sender is not IPin inputPin || !_pinPairs.ContainsKey(inputPin)) return;
|
||||
|
||||
OutputPin outputPin = _pinPairs[inputPin];
|
||||
outputPin.ChangeType(typeof(object));
|
||||
}
|
||||
|
||||
private void UpdatePinNames()
|
||||
{
|
||||
int counter = 1;
|
||||
foreach (IPin inputPin in Input.Pins)
|
||||
{
|
||||
string name = counter.ToString();
|
||||
inputPin.Name = name;
|
||||
_pinPairs[inputPin].Name = name;
|
||||
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Evaluate()
|
||||
{
|
||||
if (Control.Value)
|
||||
{
|
||||
foreach ((IPin input, OutputPin output) in _pinPairs)
|
||||
output.Value = input.PinValue;
|
||||
|
||||
LastUpdateTime.Value = 0;
|
||||
_lastUpdateTimestamp = Stopwatch.GetTimestamp();
|
||||
}
|
||||
else
|
||||
LastUpdateTime.Value = TimerHelper.GetElapsedTime(_lastUpdateTimestamp);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
136
src/Artemis.VisualScripting/Nodes/Timing/SequencerNode.cs
Normal file
136
src/Artemis.VisualScripting/Nodes/Timing/SequencerNode.cs
Normal file
@ -0,0 +1,136 @@
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Events;
|
||||
|
||||
namespace Artemis.VisualScripting.Nodes.Timing;
|
||||
|
||||
[Node("Sequencer", "Advances on input every time the control has a rising edge (change to true)", "Timing", OutputType = typeof(object))]
|
||||
public class SequencerNode : Node
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private int _currentIndex;
|
||||
private Type _currentType;
|
||||
private bool _updating;
|
||||
private IPin? _currentCyclePin;
|
||||
|
||||
private bool _lastInput;
|
||||
|
||||
public InputPin<bool> Input { get; }
|
||||
public InputPinCollection CycleValues { get; }
|
||||
|
||||
public OutputPin Output { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public SequencerNode()
|
||||
: base("Sequencer", "Advances on input every time the control has a rising edge (change to true)")
|
||||
{
|
||||
_currentType = typeof(object);
|
||||
|
||||
Input = CreateInputPin<bool>("Control");
|
||||
CycleValues = CreateInputPinCollection(typeof(object), "", 0);
|
||||
Output = CreateOutputPin(typeof(object));
|
||||
|
||||
CycleValues.PinAdded += CycleValuesOnPinAdded;
|
||||
CycleValues.PinRemoved += CycleValuesOnPinRemoved;
|
||||
CycleValues.Add(CycleValues.CreatePin());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
public override void Evaluate()
|
||||
{
|
||||
bool input = Input.Value;
|
||||
|
||||
if (input != _lastInput)
|
||||
{
|
||||
_currentIndex++;
|
||||
|
||||
if (_currentIndex >= CycleValues.Count())
|
||||
_currentIndex = 0;
|
||||
|
||||
_currentCyclePin = null;
|
||||
}
|
||||
|
||||
_currentCyclePin ??= CycleValues.ElementAt(_currentIndex);
|
||||
|
||||
object? outputValue = _currentCyclePin.PinValue;
|
||||
if (Output.Type.IsInstanceOfType(outputValue))
|
||||
Output.Value = outputValue;
|
||||
else if (Output.Type.IsValueType)
|
||||
Output.Value = Output.Type.GetDefault()!;
|
||||
|
||||
_lastInput = input;
|
||||
}
|
||||
|
||||
private void CycleValuesOnPinAdded(object? sender, SingleValueEventArgs<IPin> e)
|
||||
{
|
||||
e.Value.PinConnected += OnPinConnected;
|
||||
e.Value.PinDisconnected += OnPinDisconnected;
|
||||
|
||||
_currentCyclePin = null;
|
||||
}
|
||||
|
||||
private void CycleValuesOnPinRemoved(object? sender, SingleValueEventArgs<IPin> e)
|
||||
{
|
||||
e.Value.PinConnected -= OnPinConnected;
|
||||
e.Value.PinDisconnected -= OnPinDisconnected;
|
||||
|
||||
_currentCyclePin = null;
|
||||
}
|
||||
|
||||
private void OnPinDisconnected(object? sender, SingleValueEventArgs<IPin> e) => ProcessPinDisconnected();
|
||||
|
||||
private void OnPinConnected(object? sender, SingleValueEventArgs<IPin> e) => ProcessPinConnected(e.Value);
|
||||
|
||||
private void ProcessPinConnected(IPin source)
|
||||
{
|
||||
if (_updating)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
_updating = true;
|
||||
|
||||
// No need to change anything if the types haven't changed
|
||||
if (_currentType != source.Type)
|
||||
ChangeCurrentType(source.Type);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_updating = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void ChangeCurrentType(Type type)
|
||||
{
|
||||
CycleValues.ChangeType(type);
|
||||
Output.ChangeType(type);
|
||||
|
||||
_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;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user