mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-11 19:36:28 +00:00
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:
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;
|
||||
|
||||
Reference in new issue
Block a user