NodeGraphViewModel.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. using System.Collections.ObjectModel;
  2. using PixiEditor.AvaloniaUI.Models.DocumentModels;
  3. using PixiEditor.AvaloniaUI.Models.Handlers;
  4. using PixiEditor.AvaloniaUI.ViewModels.Nodes;
  5. using PixiEditor.ChangeableDocument.Actions.Generated;
  6. using PixiEditor.Numerics;
  7. namespace PixiEditor.AvaloniaUI.ViewModels.Document;
  8. internal class NodeGraphViewModel : ViewModelBase, INodeGraphHandler
  9. {
  10. public DocumentViewModel DocumentViewModel { get; }
  11. public ObservableCollection<INodeHandler> AllNodes { get; } = new();
  12. public ObservableCollection<NodeConnectionViewModel> Connections { get; } = new();
  13. public INodeHandler? OutputNode { get; private set; }
  14. private DocumentInternalParts Internals { get; }
  15. public NodeGraphViewModel(DocumentViewModel documentViewModel, DocumentInternalParts internals)
  16. {
  17. DocumentViewModel = documentViewModel;
  18. Internals = internals;
  19. }
  20. public void AddNode(INodeHandler node)
  21. {
  22. if(OutputNode == null)
  23. {
  24. OutputNode = node; // TODO: this is not really correct yet, a way to check what node type is added is needed
  25. }
  26. AllNodes.Add(node);
  27. }
  28. public void RemoveNode(Guid nodeId)
  29. {
  30. var node = AllNodes.FirstOrDefault(x => x.Id == nodeId);
  31. if (node != null)
  32. {
  33. AllNodes.Remove(node);
  34. }
  35. }
  36. public void SetConnection(NodeConnectionViewModel connection)
  37. {
  38. var existingInputConnection = Connections.FirstOrDefault(x => x.InputProperty == connection.InputProperty);
  39. if (existingInputConnection != null)
  40. {
  41. Connections.Remove(existingInputConnection);
  42. existingInputConnection.InputProperty.ConnectedOutput = null;
  43. existingInputConnection.OutputProperty.ConnectedInputs.Remove(existingInputConnection.InputProperty);
  44. }
  45. connection.InputProperty.ConnectedOutput = connection.OutputProperty;
  46. connection.OutputProperty.ConnectedInputs.Add(connection.InputProperty);
  47. Connections.Add(connection);
  48. }
  49. public void RemoveConnection(Guid nodeId, string property)
  50. {
  51. var connection = Connections.FirstOrDefault(x => x.InputProperty.Node.Id == nodeId && x.InputProperty.PropertyName == property);
  52. if (connection != null)
  53. {
  54. connection.InputProperty.ConnectedOutput = null;
  55. connection.OutputProperty.ConnectedInputs.Remove(connection.InputProperty);
  56. Connections.Remove(connection);
  57. }
  58. }
  59. public void RemoveConnections(Guid nodeId)
  60. {
  61. var connections = Connections.Where(x => x.InputProperty.Node.Id == nodeId || x.OutputProperty.Node.Id == nodeId).ToList();
  62. foreach (var connection in connections)
  63. {
  64. connection.InputProperty.ConnectedOutput = null;
  65. connection.OutputProperty.ConnectedInputs.Remove(connection.InputProperty);
  66. Connections.Remove(connection);
  67. }
  68. }
  69. public bool TryTraverse(Func<INodeHandler, bool> func)
  70. {
  71. if (OutputNode == null) return false;
  72. var queue = CalculateExecutionQueue(OutputNode);
  73. while (queue.Count > 0)
  74. {
  75. var node = queue.Dequeue();
  76. func(node);
  77. }
  78. return true;
  79. }
  80. private Queue<INodeHandler> CalculateExecutionQueue(INodeHandler outputNode)
  81. {
  82. // backwards breadth-first search
  83. var visited = new HashSet<INodeHandler>();
  84. var queueNodes = new Queue<INodeHandler>();
  85. List<INodeHandler> finalQueue = new();
  86. queueNodes.Enqueue(outputNode);
  87. while (queueNodes.Count > 0)
  88. {
  89. var node = queueNodes.Dequeue();
  90. if (!visited.Add(node))
  91. {
  92. continue;
  93. }
  94. finalQueue.Add(node);
  95. foreach (var input in node.Inputs)
  96. {
  97. if (input.ConnectedOutput == null)
  98. {
  99. continue;
  100. }
  101. queueNodes.Enqueue(input.ConnectedOutput.Node);
  102. }
  103. }
  104. finalQueue.Reverse();
  105. return new Queue<INodeHandler>(finalQueue);
  106. }
  107. public void SetNodePosition(INodeHandler node, VecD newPos)
  108. {
  109. Internals.ActionAccumulator.AddActions(new NodePosition_Action(node.Id, newPos));
  110. }
  111. public void EndChangeNodePosition()
  112. {
  113. Internals.ActionAccumulator.AddFinishedActions(new EndNodePosition_Action());
  114. }
  115. public void CreateNode(Type nodeType)
  116. {
  117. Internals.ActionAccumulator.AddFinishedActions(new CreateNode_Action(nodeType, Guid.NewGuid()));
  118. }
  119. public void ConnectProperties(INodePropertyHandler start, INodePropertyHandler end)
  120. {
  121. INodeHandler inputNode = start.IsInput ? start.Node : end.Node;
  122. INodeHandler outputNode = start.IsInput ? end.Node : start.Node;
  123. string inputProperty = start.IsInput ? start.PropertyName : end.PropertyName;
  124. string outputProperty = start.IsInput ? end.PropertyName : start.PropertyName;
  125. Internals.ActionAccumulator.AddFinishedActions(new ConnectProperties_Action(inputNode.Id, outputNode.Id, inputProperty, outputProperty));
  126. }
  127. }