From e3a03233834d00f9ad4ce9aab879c8c9887afd9e Mon Sep 17 00:00:00 2001 From: Darth Affe Date: Thu, 15 Jul 2021 17:38:35 +0200 Subject: [PATCH] Added methods to remove pins at runtime --- .../Controls/Wrapper/VisualScriptNode.cs | 113 +++++++++++++++--- .../Wrapper/VisualScriptPinCollection.cs | 10 +- src/Artemis.VisualScripting/Model/InputPin.cs | 50 ++++++++ .../Model/OutputPin.cs | 40 +++++++ src/Artemis.VisualScripting/Model/Pin.cs | 8 ++ 5 files changed, 205 insertions(+), 16 deletions(-) diff --git a/src/Artemis.VisualScripting/Editor/Controls/Wrapper/VisualScriptNode.cs b/src/Artemis.VisualScripting/Editor/Controls/Wrapper/VisualScriptNode.cs index 40bf11d6c..5c499eeec 100644 --- a/src/Artemis.VisualScripting/Editor/Controls/Wrapper/VisualScriptNode.cs +++ b/src/Artemis.VisualScripting/Editor/Controls/Wrapper/VisualScriptNode.cs @@ -1,5 +1,8 @@ using System; +using System.Collections.Generic; using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; using Artemis.Core.VisualScripting; using Artemis.VisualScripting.Events; using Artemis.VisualScripting.Internal; @@ -97,29 +100,109 @@ namespace Artemis.VisualScripting.Editor.Controls.Wrapper this.Script = script; this.Node = node; + Node.PropertyChanged += OnNodePropertyChanged; + _locationOffset = script.SurfaceSize / 2.0; - foreach (IPin pin in node.Pins) - { - if (pin.Direction == PinDirection.Input) - InputPins.Add(new VisualScriptPin(this, pin)); - else - OutputPins.Add(new VisualScriptPin(this, pin)); - } - - foreach (IPinCollection pinCollection in node.PinCollections) - { - if (pinCollection.Direction == PinDirection.Input) - InputPinCollections.Add(new VisualScriptPinCollection(this, pinCollection)); - else - OutputPinCollections.Add(new VisualScriptPinCollection(this, pinCollection)); - } + ValidatePins(); } #endregion #region Methods + private void OnNodePropertyChanged(object sender, PropertyChangedEventArgs args) + { + if (string.Equals(args.PropertyName, nameof(Node.Pins), StringComparison.OrdinalIgnoreCase) + || string.Equals(args.PropertyName, nameof(Node.PinCollections), StringComparison.OrdinalIgnoreCase)) + ValidatePins(); + } + + private void ValidatePins() + { + if (Node == null) + { + InputPins.Clear(); + OutputPins.Clear(); + InputPinCollections.Clear(); + OutputPinCollections.Clear(); + return; + } + + #region Remove Excessive + + HashSet pins = new(Node.Pins); + HashSet pinCollections = new(Node.PinCollections); + + void ValidatePinList(ObservableCollection list) + { + List pinsToRemove = new(); + foreach (VisualScriptPin pin in list) + if ((pin.Pin == null) || !pins.Contains(pin.Pin)) + pinsToRemove.Add(pin); + foreach (VisualScriptPin pin in pinsToRemove) + { + pin.DisconnectAll(); + list.Remove(pin); + } + } + + void ValidatePinCollectionList(ObservableCollection list) + { + List pinCollectionsToRemove = new(); + foreach (VisualScriptPinCollection pinCollection in list) + if ((pinCollection.PinCollection == null) || !pinCollections.Contains(pinCollection.PinCollection)) + pinCollectionsToRemove.Add(pinCollection); + foreach (VisualScriptPinCollection pinCollection in pinCollectionsToRemove) + { + pinCollection.RemoveAll(); + list.Remove(pinCollection); + } + } + + ValidatePinList(InputPins); + ValidatePinList(OutputPins); + ValidatePinCollectionList(InputPinCollections); + ValidatePinCollectionList(OutputPinCollections); + + #endregion + + #region Add Missing + + HashSet existingPins = new(InputPins.Concat(OutputPins).Select(x => x.Pin)); + HashSet existingPinCollections = new(InputPinCollections.Concat(OutputPinCollections).Select(x => x.PinCollection)); + + foreach (IPin pin in Node.Pins) + { + if (pin.Direction == PinDirection.Input) + { + if (!existingPins.Contains(pin)) + InputPins.Add(new VisualScriptPin(this, pin)); + } + else + { + if (!existingPins.Contains(pin)) + OutputPins.Add(new VisualScriptPin(this, pin)); + } + } + + foreach (IPinCollection pinCollection in Node.PinCollections) + { + if (pinCollection.Direction == PinDirection.Input) + { + if (!existingPinCollections.Contains(pinCollection)) + InputPinCollections.Add(new VisualScriptPinCollection(this, pinCollection)); + } + else + { + if (!existingPinCollections.Contains(pinCollection)) + OutputPinCollections.Add(new VisualScriptPinCollection(this, pinCollection)); + } + } + + #endregion + } + public void SnapNodeToGrid() { X -= X % Script.GridSize; diff --git a/src/Artemis.VisualScripting/Editor/Controls/Wrapper/VisualScriptPinCollection.cs b/src/Artemis.VisualScripting/Editor/Controls/Wrapper/VisualScriptPinCollection.cs index 3e20fe8c5..80aaa60f2 100644 --- a/src/Artemis.VisualScripting/Editor/Controls/Wrapper/VisualScriptPinCollection.cs +++ b/src/Artemis.VisualScripting/Editor/Controls/Wrapper/VisualScriptPinCollection.cs @@ -1,4 +1,5 @@ -using System.Collections.ObjectModel; +using System.Collections.Generic; +using System.Collections.ObjectModel; using Artemis.Core.VisualScripting; using Artemis.VisualScripting.ViewModel; @@ -53,6 +54,13 @@ namespace Artemis.VisualScripting.Editor.Controls.Wrapper Pins.Remove(pin); } + public void RemoveAll() + { + List pins = new(Pins); + foreach (VisualScriptPin pin in pins) + RemovePin(pin); + } + #endregion } } diff --git a/src/Artemis.VisualScripting/Model/InputPin.cs b/src/Artemis.VisualScripting/Model/InputPin.cs index 29a3a6237..80ea27b8b 100644 --- a/src/Artemis.VisualScripting/Model/InputPin.cs +++ b/src/Artemis.VisualScripting/Model/InputPin.cs @@ -48,4 +48,54 @@ namespace Artemis.VisualScripting.Model #endregion } + + public sealed class InputPin : Pin + { + #region Properties & Fields + + public override Type Type { get; } + public override object PinValue => Value; + public override PinDirection Direction => PinDirection.Input; + + private object _value; + public object Value + { + get + { + if (!IsEvaluated) + Evaluate(); + + return _value; + } + + 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; + } + } + + #endregion + + #region Constructors + + internal InputPin(INode node, Type type, string name) + : base(node, name) + { + this.Type = type; + } + + #endregion + + #region Methods + + private void Evaluate() + { + Value = ConnectedTo.Count > 0 ? ConnectedTo[0].PinValue : default; + } + + #endregion + } } diff --git a/src/Artemis.VisualScripting/Model/OutputPin.cs b/src/Artemis.VisualScripting/Model/OutputPin.cs index d1cd3acbe..5cd238f80 100644 --- a/src/Artemis.VisualScripting/Model/OutputPin.cs +++ b/src/Artemis.VisualScripting/Model/OutputPin.cs @@ -38,4 +38,44 @@ namespace Artemis.VisualScripting.Model #endregion } + + public sealed class OutputPin : Pin + { + #region Properties & Fields + + public override Type Type { get; } + public override object PinValue => Value; + public override PinDirection Direction => PinDirection.Output; + + private object _value; + public object Value + { + get + { + if (!IsEvaluated) + Node?.Evaluate(); + + return _value; + } + 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; + } + } + + #endregion + + #region Constructors + + internal OutputPin(INode node, Type type, string name) + : base(node, name) + { + this.Type = type; + } + + #endregion + } } diff --git a/src/Artemis.VisualScripting/Model/Pin.cs b/src/Artemis.VisualScripting/Model/Pin.cs index b99310237..7a1368a57 100644 --- a/src/Artemis.VisualScripting/Model/Pin.cs +++ b/src/Artemis.VisualScripting/Model/Pin.cs @@ -47,11 +47,19 @@ namespace Artemis.VisualScripting.Model public void ConnectTo(IPin pin) { _connectedTo.Add(pin); + OnPropertyChanged(nameof(ConnectedTo)); } public void DisconnectFrom(IPin pin) { _connectedTo.Remove(pin); + OnPropertyChanged(nameof(ConnectedTo)); + } + + public void DisconnectAll() + { + _connectedTo.Clear(); + OnPropertyChanged(nameof(ConnectedTo)); } private void OnNodeResetting(object sender, EventArgs e)