NodeGraphViewModel.cs 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. using System.Collections.ObjectModel;
  2. using PixiEditor.AvaloniaUI.Models.Handlers;
  3. using PixiEditor.AvaloniaUI.ViewModels.Nodes;
  4. namespace PixiEditor.AvaloniaUI.ViewModels.Document;
  5. internal class NodeGraphViewModel : ViewModelBase, INodeGraphHandler
  6. {
  7. public DocumentViewModel DocumentViewModel { get; }
  8. public ObservableCollection<INodeHandler> AllNodes { get; } = new();
  9. public ObservableCollection<NodeConnectionViewModel> Connections { get; } = new();
  10. public INodeHandler? OutputNode { get; private set; }
  11. public NodeGraphViewModel(DocumentViewModel documentViewModel)
  12. {
  13. DocumentViewModel = documentViewModel;
  14. }
  15. public void AddNode(INodeHandler node)
  16. {
  17. if(OutputNode == null)
  18. {
  19. OutputNode = node; // TODO: this is not really correct yet, a way to check what node type is added is needed
  20. }
  21. AllNodes.Add(node);
  22. }
  23. public void RemoveNode(Guid nodeId)
  24. {
  25. var node = AllNodes.FirstOrDefault(x => x.Id == nodeId);
  26. if (node != null)
  27. {
  28. AllNodes.Remove(node);
  29. }
  30. }
  31. public void SetConnection(NodeConnectionViewModel connection)
  32. {
  33. var existingInputConnection = Connections.FirstOrDefault(x => x.InputProperty == connection.InputProperty);
  34. if (existingInputConnection != null)
  35. {
  36. Connections.Remove(existingInputConnection);
  37. }
  38. Connections.Add(connection);
  39. }
  40. public bool TryTraverse(Func<INodeHandler, bool> func)
  41. {
  42. if (OutputNode == null) return false;
  43. var queue = CalculateExecutionQueue(OutputNode);
  44. while (queue.Count > 0)
  45. {
  46. var node = queue.Dequeue();
  47. func(node);
  48. }
  49. return true;
  50. }
  51. private Queue<INodeHandler> CalculateExecutionQueue(INodeHandler outputNode)
  52. {
  53. // backwards breadth-first search
  54. var visited = new HashSet<INodeHandler>();
  55. var queueNodes = new Queue<INodeHandler>();
  56. List<INodeHandler> finalQueue = new();
  57. queueNodes.Enqueue(outputNode);
  58. while (queueNodes.Count > 0)
  59. {
  60. var node = queueNodes.Dequeue();
  61. if (!visited.Add(node))
  62. {
  63. continue;
  64. }
  65. finalQueue.Add(node);
  66. foreach (var input in node.Inputs)
  67. {
  68. if (input.ConnectedOutput == null)
  69. {
  70. continue;
  71. }
  72. queueNodes.Enqueue(input.ConnectedOutput.Node);
  73. }
  74. }
  75. finalQueue.Reverse();
  76. return new Queue<INodeHandler>(finalQueue);
  77. }
  78. }