NodeGraphViewModel.cs 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. using System.Collections.ObjectModel;
  2. using System.Reflection;
  3. using Avalonia.Input;
  4. using PixiEditor.Models.Commands.Attributes.Commands;
  5. using PixiEditor.ChangeableDocument.Actions;
  6. using PixiEditor.ChangeableDocument.Actions.Generated;
  7. using PixiEditor.ChangeableDocument.Changeables.Graph;
  8. using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
  9. using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
  10. using PixiEditor.ChangeableDocument.ChangeInfos;
  11. using PixiEditor.ChangeableDocument.ChangeInfos.NodeGraph;
  12. using PixiEditor.Models.DocumentModels;
  13. using PixiEditor.Models.Handlers;
  14. using PixiEditor.Numerics;
  15. using PixiEditor.ViewModels.Nodes;
  16. namespace PixiEditor.ViewModels.Document;
  17. internal class NodeGraphViewModel : ViewModelBase, INodeGraphHandler
  18. {
  19. public DocumentViewModel DocumentViewModel { get; }
  20. public ObservableCollection<INodeHandler> AllNodes { get; } = new();
  21. public ObservableCollection<NodeConnectionViewModel> Connections { get; } = new();
  22. public ObservableCollection<NodeFrameViewModelBase> Frames { get; } = new();
  23. public StructureTree StructureTree { get; } = new();
  24. public INodeHandler? OutputNode { get; private set; }
  25. private DocumentInternalParts Internals { get; }
  26. public NodeGraphViewModel(DocumentViewModel documentViewModel, DocumentInternalParts internals)
  27. {
  28. DocumentViewModel = documentViewModel;
  29. Internals = internals;
  30. }
  31. public void AddNode(INodeHandler node)
  32. {
  33. if (OutputNode == null &&
  34. node.InternalName == typeof(OutputNode).GetCustomAttribute<NodeInfoAttribute>().UniqueName)
  35. {
  36. OutputNode = node;
  37. }
  38. AllNodes.Add(node);
  39. StructureTree.Update(this);
  40. }
  41. public void RemoveNode(Guid nodeId)
  42. {
  43. var node = AllNodes.FirstOrDefault(x => x.Id == nodeId);
  44. if (node != null)
  45. {
  46. AllNodes.Remove(node);
  47. }
  48. StructureTree.Update(this);
  49. }
  50. public void AddFrame(Guid frameId, IEnumerable<Guid> nodes)
  51. {
  52. var frame = new NodeFrameViewModel(frameId, AllNodes.Where(x => nodes.Contains(x.Id)));
  53. Frames.Add(frame);
  54. }
  55. public void AddZone(Guid frameId, string internalName, Guid startId, Guid endId)
  56. {
  57. var start = AllNodes.First(x => x.Id == startId);
  58. var end = AllNodes.First(x => x.Id == endId);
  59. var zone = new NodeZoneViewModel(frameId, internalName, start, end);
  60. Frames.Add(zone);
  61. }
  62. public void RemoveFrame(Guid guid)
  63. {
  64. var frame = Frames.FirstOrDefault(x => x.Id == guid);
  65. if (frame == null) return;
  66. Frames.Remove(frame);
  67. }
  68. public void SetConnection(NodeConnectionViewModel connection)
  69. {
  70. var existingInputConnection = Connections.FirstOrDefault(x => x.InputProperty == connection.InputProperty);
  71. if (existingInputConnection != null)
  72. {
  73. Connections.Remove(existingInputConnection);
  74. existingInputConnection.InputProperty.ConnectedOutput = null;
  75. existingInputConnection.OutputProperty.ConnectedInputs.Remove(existingInputConnection.InputProperty);
  76. }
  77. connection.InputProperty.ConnectedOutput = connection.OutputProperty;
  78. connection.OutputProperty.ConnectedInputs.Add(connection.InputProperty);
  79. Connections.Add(connection);
  80. StructureTree.Update(this);
  81. }
  82. public void RemoveConnection(Guid nodeId, string property)
  83. {
  84. var connection = Connections.FirstOrDefault(x =>
  85. x.InputProperty.Node.Id == nodeId && x.InputProperty.PropertyName == property);
  86. if (connection != null)
  87. {
  88. connection.InputProperty.ConnectedOutput = null;
  89. connection.OutputProperty.ConnectedInputs.Remove(connection.InputProperty);
  90. Connections.Remove(connection);
  91. }
  92. StructureTree.Update(this);
  93. }
  94. public void RemoveConnections(Guid nodeId)
  95. {
  96. var connections = Connections
  97. .Where(x => x.InputProperty.Node.Id == nodeId || x.OutputProperty.Node.Id == nodeId).ToList();
  98. foreach (var connection in connections)
  99. {
  100. connection.InputProperty.ConnectedOutput = null;
  101. connection.OutputProperty.ConnectedInputs.Remove(connection.InputProperty);
  102. Connections.Remove(connection);
  103. }
  104. StructureTree.Update(this);
  105. }
  106. public bool TryTraverse(Func<INodeHandler, bool> func)
  107. {
  108. if (OutputNode == null) return false;
  109. var queue = CalculateExecutionQueue(OutputNode);
  110. while (queue.Count > 0)
  111. {
  112. var node = queue.Dequeue();
  113. func(node);
  114. }
  115. return true;
  116. }
  117. private Queue<INodeHandler> CalculateExecutionQueue(INodeHandler outputNode)
  118. {
  119. var finalQueue = new HashSet<INodeHandler>();
  120. var queueNodes = new Queue<INodeHandler>();
  121. queueNodes.Enqueue(outputNode);
  122. while (queueNodes.Count > 0)
  123. {
  124. var node = queueNodes.Dequeue();
  125. if (finalQueue.Contains(node))
  126. {
  127. continue;
  128. }
  129. bool canAdd = true;
  130. foreach (var input in node.Inputs)
  131. {
  132. if (input.ConnectedOutput == null)
  133. {
  134. continue;
  135. }
  136. if (finalQueue.Contains(input.ConnectedOutput.Node))
  137. {
  138. continue;
  139. }
  140. canAdd = false;
  141. if (finalQueue.Contains(input.ConnectedOutput.Node))
  142. {
  143. finalQueue.Remove(input.ConnectedOutput.Node);
  144. finalQueue.Add(input.ConnectedOutput.Node);
  145. }
  146. if (!queueNodes.Contains(input.ConnectedOutput.Node))
  147. {
  148. queueNodes.Enqueue(input.ConnectedOutput.Node);
  149. }
  150. }
  151. if (canAdd)
  152. {
  153. finalQueue.Add(node);
  154. }
  155. else
  156. {
  157. queueNodes.Enqueue(node);
  158. }
  159. }
  160. return new Queue<INodeHandler>(finalQueue);
  161. }
  162. public void SetNodePosition(INodeHandler node, VecD newPos)
  163. {
  164. Internals.ActionAccumulator.AddActions(new NodePosition_Action(node.Id, newPos));
  165. }
  166. public void UpdatePropertyValue(INodeHandler node, string property, object? value)
  167. {
  168. Internals.ActionAccumulator.AddFinishedActions(new UpdatePropertyValue_Action(node.Id, property, value));
  169. }
  170. public void EndChangeNodePosition()
  171. {
  172. Internals.ActionAccumulator.AddFinishedActions(new EndNodePosition_Action());
  173. }
  174. public void CreateNode(Type nodeType, VecD pos = default)
  175. {
  176. IAction change;
  177. PairNodeAttribute? pairAttribute = nodeType.GetCustomAttribute<PairNodeAttribute>(true);
  178. List<IAction> changes = new();
  179. if (pairAttribute != null)
  180. {
  181. Guid startId = Guid.NewGuid();
  182. Guid endId = Guid.NewGuid();
  183. changes.Add(new CreateNodePair_Action(startId, endId, Guid.NewGuid(), nodeType));
  184. if (pos != default)
  185. {
  186. changes.Add(new NodePosition_Action(startId, pos));
  187. changes.Add(new EndNodePosition_Action());
  188. changes.Add(new NodePosition_Action(endId, new VecD(pos.X + 400, pos.Y)));
  189. changes.Add(new EndNodePosition_Action());
  190. }
  191. }
  192. else
  193. {
  194. Guid nodeId = Guid.NewGuid();
  195. changes.Add(new CreateNode_Action(nodeType, nodeId));
  196. if (pos != default)
  197. {
  198. changes.Add(new NodePosition_Action(nodeId, pos));
  199. changes.Add(new EndNodePosition_Action());
  200. }
  201. }
  202. Internals.ActionAccumulator.AddFinishedActions(changes.ToArray());
  203. }
  204. public void RemoveNodes(Guid[] selectedNodes)
  205. {
  206. IAction[] actions = new IAction[selectedNodes.Length];
  207. for (int i = 0; i < selectedNodes.Length; i++)
  208. {
  209. actions[i] = new DeleteNode_Action(selectedNodes[i]);
  210. }
  211. Internals.ActionAccumulator.AddFinishedActions(actions);
  212. }
  213. // TODO: Remove this
  214. public void CreateNodeFrameAroundEverything()
  215. {
  216. CreateNodeFrame(AllNodes);
  217. }
  218. public void CreateNodeFrame(IEnumerable<INodeHandler> nodes)
  219. {
  220. Internals.ActionAccumulator.AddFinishedActions(new CreateNodeFrame_Action(Guid.NewGuid(),
  221. nodes.Select(x => x.Id)));
  222. }
  223. public void ConnectProperties(INodePropertyHandler? start, INodePropertyHandler? end)
  224. {
  225. if (start == null && end == null) return;
  226. INodeHandler inputNode = null, outputNode = null;
  227. string inputProperty = null, outputProperty = null;
  228. var input = start?.IsInput == true ? start : end;
  229. var output = start?.IsInput == false ? start : end;
  230. if (input == null && output != null)
  231. {
  232. input = output.ConnectedInputs.FirstOrDefault();
  233. output = null;
  234. }
  235. if (input != null)
  236. {
  237. inputNode = input.Node;
  238. inputProperty = input.PropertyName;
  239. }
  240. if (output != null)
  241. {
  242. outputNode = output.Node;
  243. outputProperty = output.PropertyName;
  244. }
  245. if (input == null) return;
  246. IAction action = input != null && output != null
  247. ? new ConnectProperties_Action(inputNode.Id, outputNode.Id, inputProperty, outputProperty)
  248. : new DisconnectProperty_Action(inputNode.Id, inputProperty);
  249. Internals.ActionAccumulator.AddFinishedActions(action);
  250. }
  251. }