Nodes - Added gradient nodes

Nodes - Added color gradient pin type
Data bindings - Changed color gradient data bindings to now take a color gradient
This commit is contained in:
Robert committed 2022-09-23 21:41:08 +02:00
1 parent 32a444fbeb
commit 9c117d2773
48 files changed
+830 -344

No files matched your search

@@ -37,6 +37,9 @@
<DependentUpon>DataModelEventNodeCustomView.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Update="Nodes\Color\Screens\ColorGradientNodeCustomView.axaml.cs">
<DependentUpon>ColorGradientNodeCustomView.axaml</DependentUpon>
</Compile>
</ItemGroup>
</Project>
@@ -0,0 +1,102 @@
using Artemis.Core;
using Artemis.Core.Events;
using SkiaSharp;
namespace Artemis.VisualScripting.Nodes.Color;
[Node("Color Gradient (Advanced)", "Outputs a Color Gradient from colors and positions", "Color", OutputType = typeof(ColorGradient))]
public class ColorGradientFromPinsNode : Node
{
public OutputPin<ColorGradient> Gradient { get; set; }
public InputPinCollection<SKColor> Colors { get; set; }
public InputPinCollection<Numeric> Positions { get; set; }
public ColorGradientFromPinsNode() : base("Color Gradient", "Outputs a Color Gradient from colors and positions")
{
Colors = CreateInputPinCollection<SKColor>("Colors", 0);
Positions = CreateInputPinCollection<Numeric>("Positions", 0);
Gradient = CreateOutputPin<ColorGradient>("Gradient");
Colors.PinAdded += OnPinAdded;
Colors.PinRemoved += OnPinRemoved;
Positions.PinAdded += OnPinAdded;
Positions.PinRemoved += OnPinRemoved;
}
private void OnPinRemoved(object? sender, SingleValueEventArgs<IPin> e)
{
int colorsCount = Colors.Count();
int positionsCount = Positions.Count();
if (colorsCount == positionsCount)
return;
while (colorsCount > positionsCount)
{
IPin pinToRemove = Colors.Last();
Colors.Remove(pinToRemove);
--colorsCount;
}
while (positionsCount > colorsCount)
{
IPin pinToRemove = Positions.Last();
Positions.Remove(pinToRemove);
--positionsCount;
}
RenamePins();
}
private void OnPinAdded(object? sender, SingleValueEventArgs<IPin> e)
{
int colorsCount = Colors.Count();
int positionsCount = Positions.Count();
if (colorsCount == positionsCount)
return;
while (colorsCount < positionsCount)
{
Colors.Add(Colors.CreatePin());
++colorsCount;
}
while (positionsCount < colorsCount)
{
Positions.Add(Positions.CreatePin());
++positionsCount;
}
RenamePins();
}
private void RenamePins()
{
int colors = 0;
foreach (IPin item in Colors)
{
item.Name = $"Color #{++colors}";
}
int positions = 0;
foreach (IPin item in Positions)
{
item.Name = $"Position #{++positions}";
}
}
public override void Evaluate()
{
List<ColorGradientStop> stops = new List<ColorGradientStop>();
InputPin<SKColor>[] colors = Colors.Pins.ToArray();
InputPin<Numeric>[] positions = Positions.Pins.ToArray();
for (int i = 0; i < colors.Length; i++)
{
stops.Add(new ColorGradientStop(colors[i].Value, positions[i].Value));
}
Gradient.Value = new ColorGradient(stops);
}
}
@@ -0,0 +1,108 @@
using System.Collections.Specialized;
using Artemis.Core;
using Artemis.VisualScripting.Nodes.Color.Screens;
using SkiaSharp;
namespace Artemis.VisualScripting.Nodes.Color;
[Node("Color Gradient (Simple)", "Outputs a color gradient with the given colors", "Color", OutputType = typeof(ColorGradient))]
public class ColorGradientNode : Node<ColorGradient, ColorGradientNodeCustomViewModel>
{
private readonly List<InputPin> _inputPins;
public ColorGradientNode() : base("Color Gradient", "Outputs a color gradient with the given colors")
{
_inputPins = new List<InputPin>();
Gradient = ColorGradient.GetUnicornBarf();
Output = CreateOutputPin<ColorGradient>();
ViewModelPosition = CustomNodeViewModelPosition.AbovePins;
}
public ColorGradient Gradient { get; private set; }
public OutputPin<ColorGradient> Output { get; }
public override void Initialize(INodeScript script)
{
UpdateGradient();
ComputeInputPins();
// Not expecting storage to get modified, but lets just make sure
StorageModified += OnStorageModified;
}
public override void Evaluate()
{
ColorGradientStop[] stops = Gradient.ToArray();
if (_inputPins.Count != stops.Length)
return;
for (int i = 0; i < _inputPins.Count; i++)
{
// if nothing is connected, leave the stop alone.
if (_inputPins[i].ConnectedTo.Count == 0)
continue;
// if the pin has a connection, update the stop.
if (_inputPins[i].PinValue is SKColor color)
stops[i].Color = color;
}
Output.Value = Gradient;
}
private void DisconnectAllInputPins()
{
foreach (InputPin item in _inputPins)
item.DisconnectAll();
}
private void UpdateGradient()
{
Gradient.CollectionChanged -= OnGradientCollectionChanged;
if (Storage != null)
Gradient = Storage;
else
Storage = Gradient;
Gradient.CollectionChanged += OnGradientCollectionChanged;
}
private void ComputeInputPins()
{
int newAmount = Gradient.Count;
if (newAmount == _inputPins.Count)
return;
while (newAmount > _inputPins.Count)
_inputPins.Add(CreateOrAddInputPin(typeof(SKColor), string.Empty));
while (newAmount < _inputPins.Count)
{
InputPin pin = _inputPins.Last();
RemovePin(pin);
_inputPins.Remove(pin);
}
int index = 0;
foreach (InputPin item in _inputPins)
item.Name = $"Color #{++index}";
}
private void OnStorageModified(object? sender, EventArgs e)
{
UpdateGradient();
ComputeInputPins();
}
private void OnGradientCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
// if the user reorders the gradient, let it slide and do nothing.
// of course, the user might want to change the input pins since they will no longer line up.
if (e.Action == NotifyCollectionChangedAction.Move)
return;
// DisconnectAllInputPins();
ComputeInputPins();
}
}
@@ -23,7 +23,7 @@ public class RampSKColorNode : Node<ColorGradient, RampSKColorNodeCustomViewMode
public override void Evaluate()
{
Output.Value = Storage?.GetColor(Input.Value) ?? SKColor.Empty;
Output.Value = Storage?.GetColor(Input.Value % 1.0) ?? SKColor.Empty;
}
#endregion
@@ -0,0 +1,15 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:screens="clr-namespace:Artemis.VisualScripting.Nodes.Color.Screens"
xmlns:gradientPicker="clr-namespace:Artemis.UI.Shared.Controls.GradientPicker;assembly=Artemis.UI.Shared"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.VisualScripting.Nodes.Color.Screens.ColorGradientNodeCustomView"
x:DataType="screens:ColorGradientNodeCustomViewModel">
<gradientPicker:GradientPickerButton Classes="condensed"
ColorGradient="{CompiledBinding Gradient}"
VerticalAlignment="Top"
FlyoutOpened="GradientPickerButton_OnFlyoutOpened"
FlyoutClosed="GradientPickerButton_OnFlyoutClosed" />
</UserControl>
@@ -0,0 +1,27 @@
using Artemis.UI.Shared.Controls.GradientPicker;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.VisualScripting.Nodes.Color.Screens;
public class ColorGradientNodeCustomView : ReactiveUserControl<ColorGradientNodeCustomViewModel>
{
public ColorGradientNodeCustomView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private void GradientPickerButton_OnFlyoutOpened(GradientPickerButton sender, EventArgs args)
{
}
private void GradientPickerButton_OnFlyoutClosed(GradientPickerButton sender, EventArgs args)
{
ViewModel?.StoreGradient();
}
}
@@ -0,0 +1,28 @@
using Artemis.Core;
using Artemis.UI.Shared.Services.NodeEditor;
using Artemis.UI.Shared.Services.NodeEditor.Commands;
using Artemis.UI.Shared.VisualScripting;
namespace Artemis.VisualScripting.Nodes.Color.Screens;
public class ColorGradientNodeCustomViewModel : CustomNodeViewModel
{
private readonly ColorGradientNode _node;
private readonly INodeEditorService _nodeEditorService;
/// <inheritdoc />
public ColorGradientNodeCustomViewModel(ColorGradientNode node, INodeScript script, INodeEditorService nodeEditorService) : base(node, script)
{
_node = node;
_nodeEditorService = nodeEditorService;
Gradient = _node.Gradient;
}
public ColorGradient Gradient { get; }
public void StoreGradient()
{
_nodeEditorService.ExecuteCommand(Script, new UpdateStorage<ColorGradient>(_node, Gradient));
}
}
@@ -56,6 +56,9 @@ public class DataModelEventNode : Node<DataModelPathEntity, DataModelEventNodeCu
// If the path is a regular value, evaluate the current value
else if (_oldValuePin != null && _newValuePin != null)
{
if (_newValuePin.IsNumeric)
pathValue = new Numeric(pathValue);
if (Equals(_lastValue, pathValue))
{
TimeSinceLastTrigger.Value = (DateTime.Now - _lastTrigger).TotalMilliseconds;
@@ -119,6 +122,9 @@ public class DataModelEventNode : Node<DataModelPathEntity, DataModelEventNodeCu
if (propertyType == null)
return;
if (Numeric.IsTypeCompatible(propertyType))
propertyType = typeof(Numeric);
_oldValuePin = CreateOrAddOutputPin(propertyType, "Old value");
_newValuePin = CreateOrAddOutputPin(propertyType, "New value");
_lastValue = null;