2
0
Эх сурвалжийг харах

Use HashSet for node zone calculations

CPKreuz 3 долоо хоног өмнө
parent
commit
6e0d354bd1

+ 1 - 1
src/PixiEditor/Models/Handlers/INodeHandler.cs

@@ -34,7 +34,7 @@ public interface INodeHandler : INotifyPropertyChanged, IDisposable
     public void TraverseForwards(Func<INodeHandler, INodeHandler, bool> func);
     public void TraverseForwards(Func<INodeHandler, INodeHandler, INodePropertyHandler, bool> func);
     public void TraverseForwards(Func<INodeHandler, INodeHandler, INodePropertyHandler, INodePropertyHandler, bool> func);
-    public List<NodeFrameViewModelBase> Frames { get; }
+    public HashSet<NodeFrameViewModelBase> Frames { get; }
     public IReadOnlyDictionary<string, INodePropertyHandler> InputPropertyMap { get; }
     public IReadOnlyDictionary<string, INodePropertyHandler> OutputPropertyMap { get; }
 }

+ 112 - 0
src/PixiEditor/Models/Structures/ObservableHashSet.cs

@@ -0,0 +1,112 @@
+using System.Collections;
+using System.Collections.Specialized;
+using System.Runtime.Serialization;
+
+namespace PixiEditor.Models.Structures;
+
+public class ObservableHashSet<T> : ISet<T>, IReadOnlySet<T>, IDeserializationCallback, ISerializable, INotifyCollectionChanged
+{
+    private readonly HashSet<T> setImplementation;
+
+    public ObservableHashSet()
+    {
+        setImplementation = new HashSet<T>();
+    }
+
+    public ObservableHashSet(IEnumerable<T> collection)
+    {
+        setImplementation = new HashSet<T>(collection);
+    }
+    
+    public bool Add(T item)
+    {
+        var isAdded = setImplementation.Add(item);
+
+        if (isAdded)
+        {
+            CallCollectionChanged(NotifyCollectionChangedAction.Add, item);
+        }
+        
+        return isAdded;
+    }
+
+    void ICollection<T>.Add(T item) => Add(item);
+
+    public void Clear()
+    {
+        setImplementation.Clear();
+        CallCollectionChanged(NotifyCollectionChangedAction.Reset);
+    }
+
+    public bool Remove(T item)
+    {
+        var isRemoved = setImplementation.Remove(item);
+
+        if (isRemoved)
+        {
+            CallCollectionChanged(NotifyCollectionChangedAction.Remove, item);
+        }
+        
+        return isRemoved;
+    }
+
+    public void ExceptWith(IEnumerable<T> other)
+    {
+        setImplementation.ExceptWith(other);
+        CallCollectionChanged(NotifyCollectionChangedAction.Reset);
+    }
+
+    public void IntersectWith(IEnumerable<T> other)
+    {
+        setImplementation.IntersectWith(other);
+        CallCollectionChanged(NotifyCollectionChangedAction.Reset);
+    }
+
+    public void SymmetricExceptWith(IEnumerable<T> other)
+    {
+        setImplementation.SymmetricExceptWith(other);
+        CallCollectionChanged(NotifyCollectionChangedAction.Reset);
+    }
+
+    public void UnionWith(IEnumerable<T> other)
+    {
+        setImplementation.UnionWith(other);
+        CallCollectionChanged(NotifyCollectionChangedAction.Reset);
+    }
+
+    public bool IsProperSubsetOf(IEnumerable<T> other) => setImplementation.IsProperSubsetOf(other);
+
+    public bool IsProperSupersetOf(IEnumerable<T> other) => setImplementation.IsProperSupersetOf(other);
+
+    public bool IsSubsetOf(IEnumerable<T> other) => setImplementation.IsSubsetOf(other);
+
+    public bool IsSupersetOf(IEnumerable<T> other) => setImplementation.IsSupersetOf(other);
+
+    public bool Overlaps(IEnumerable<T> other) => setImplementation.Overlaps(other);
+
+    public bool SetEquals(IEnumerable<T> other) => setImplementation.SetEquals(other);
+
+    public bool Contains(T item) => setImplementation.Contains(item);
+
+    public void CopyTo(T[] array, int arrayIndex) => setImplementation.CopyTo(array, arrayIndex);
+
+    public IEnumerator<T> GetEnumerator() => setImplementation.GetEnumerator();
+
+    IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)setImplementation).GetEnumerator();
+
+    public int Count => setImplementation.Count;
+
+    bool ICollection<T>.IsReadOnly => ((ISet<T>)setImplementation).IsReadOnly;
+
+    void IDeserializationCallback.OnDeserialization(object? sender) => setImplementation.OnDeserialization(sender);
+
+    void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) => setImplementation.GetObjectData(info, context);
+
+    private void CallCollectionChanged(NotifyCollectionChangedAction action) =>
+        CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(action));
+    
+    private void CallCollectionChanged(NotifyCollectionChangedAction action, T item) =>
+        CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(action, item));
+
+    public event NotifyCollectionChangedEventHandler? CollectionChanged;
+}

+ 8 - 12
src/PixiEditor/ViewModels/Document/NodeGraphViewModel.cs

@@ -144,8 +144,9 @@ internal class NodeGraphViewModel : ViewModelBase, INodeGraphHandler, IDisposabl
 
     private void UpdatesFramesPartOf(INodeHandler node)
     {
-        var lastKnownFramesPartOf = node.Frames.OfType<NodeZoneViewModel>().ToList();
-        var currentlyPartOf = new List<NodeZoneViewModel>();
+        var lastKnownFramesPartOf = node.Frames.OfType<NodeZoneViewModel>().ToHashSet();
+        var startLookup = Frames.OfType<NodeZoneViewModel>().ToDictionary(x => x.Start);
+        var currentlyPartOf = new HashSet<NodeZoneViewModel>();
         
         node.TraverseBackwards(x =>
         {
@@ -155,10 +156,7 @@ internal class NodeGraphViewModel : ViewModelBase, INodeGraphHandler, IDisposabl
             if (x is not IPairNodeStartViewModel)
                 return true;
 
-            var zone = Frames
-                .OfType<NodeZoneViewModel>()
-                .First(z => z.Start == x);
-
+            var zone = startLookup[x];
             currentlyPartOf.Add(zone);
 
             return true;
@@ -166,14 +164,12 @@ internal class NodeGraphViewModel : ViewModelBase, INodeGraphHandler, IDisposabl
 
         foreach (var frame in currentlyPartOf)
         {
-            if (!frame.Nodes.Contains(node))
-                frame.Nodes.Add(node);
-            
-            if (!node.Frames.Contains(frame))
-                node.Frames.Add(frame);
+            frame.Nodes.Add(node);
+            node.Frames.Add(frame);
         }
 
-        foreach (var removedFrom in lastKnownFramesPartOf.Except(currentlyPartOf))
+        lastKnownFramesPartOf.ExceptWith(currentlyPartOf);
+        foreach (var removedFrom in lastKnownFramesPartOf)
         {
             removedFrom.Nodes.Remove(node);
             node.Frames.Remove(removedFrom);

+ 3 - 2
src/PixiEditor/ViewModels/Nodes/NodeFrameViewModelBase.cs

@@ -5,6 +5,7 @@ using Avalonia.Media;
 using CommunityToolkit.Mvvm.ComponentModel;
 using PixiEditor.Models.Handlers;
 using Drawie.Numerics;
+using PixiEditor.Models.Structures;
 
 namespace PixiEditor.ViewModels.Nodes;
 
@@ -16,7 +17,7 @@ public abstract class NodeFrameViewModelBase : ObservableObject
     private VecD bottomRight;
     private VecD size;
     
-    public ObservableCollection<INodeHandler> Nodes { get; }
+    public ObservableHashSet<INodeHandler> Nodes { get; }
 
     public string InternalName { get; init; }
     
@@ -35,7 +36,7 @@ public abstract class NodeFrameViewModelBase : ObservableObject
     public NodeFrameViewModelBase(Guid id, IEnumerable<INodeHandler> nodes)
     {
         Id = id;
-        Nodes = new ObservableCollection<INodeHandler>(nodes);
+        Nodes = new(nodes);
 
         Nodes.CollectionChanged += OnCollectionChanged;
         AddHandlers(Nodes);

+ 1 - 1
src/PixiEditor/ViewModels/Nodes/NodeViewModel.cs

@@ -436,7 +436,7 @@ internal abstract class NodeViewModel : ObservableObject, INodeHandler
         }
     }
 
-    public List<NodeFrameViewModelBase> Frames { get; } = [];
+    public HashSet<NodeFrameViewModelBase> Frames { get; } = [];
 
     public virtual void Dispose()
     {

+ 1 - 2
src/PixiEditor/ViewModels/Nodes/NodeZoneViewModel.cs

@@ -218,9 +218,8 @@ public sealed class NodeZoneViewModel : NodeFrameViewModelBase
         const int defaultXOffset = 30;
         const int defaultYOffset = 45;
 
-        for (var i = 0; i < Nodes.Count; i++)
+        foreach (var node in Nodes)
         {
-            var node = Nodes[i];
             var pos = node.PositionBindable;
             var size = new VecD(node.UiSize.Size.Width, node.UiSize.Size.Height);