1
0
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:
Darth Affe 2022-08-21 23:51:01 +02:00
parent 86c7bd2877
commit fb8f76facb
12 changed files with 746 additions and 0 deletions

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View File

@ -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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}