NodeOperations.cs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. using System.Collections.Immutable;
  2. using System.Reflection;
  3. using PixiEditor.ChangeableDocument.Changeables.Graph;
  4. using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
  5. using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
  6. using PixiEditor.ChangeableDocument.Changeables.Interfaces;
  7. using PixiEditor.ChangeableDocument.ChangeInfos.NodeGraph;
  8. using PixiEditor.ChangeableDocument.Changes.Structure;
  9. using PixiEditor.DrawingApi.Core;
  10. namespace PixiEditor.ChangeableDocument.Changes.NodeGraph;
  11. public static class NodeOperations
  12. {
  13. private static Dictionary<Type, INodeFactory> allFactories;
  14. private static Dictionary<string, Type> nodeMap;
  15. static NodeOperations()
  16. {
  17. allFactories = new Dictionary<Type, INodeFactory>();
  18. var factoryTypes = typeof(Node).Assembly.GetTypes().Where(x =>
  19. x.IsAssignableTo(typeof(INodeFactory)) && x is { IsAbstract: false, IsInterface: false })
  20. .ToImmutableArray();
  21. foreach (var factoryType in factoryTypes)
  22. {
  23. INodeFactory factory = (INodeFactory)Activator.CreateInstance(factoryType);
  24. allFactories.Add(factory.NodeType, factory);
  25. }
  26. nodeMap = new Dictionary<string, Type>();
  27. var nodeTypes = typeof(Node).Assembly.GetTypes().Where(x =>
  28. x.IsSubclassOf(typeof(Node)) && x is { IsAbstract: false, IsInterface: false })
  29. .ToImmutableArray();
  30. foreach (var nodeType in nodeTypes)
  31. {
  32. NodeInfoAttribute? attribute = nodeType.GetCustomAttribute<NodeInfoAttribute>();
  33. if (attribute != null)
  34. {
  35. nodeMap.Add(attribute.UniqueName, nodeType);
  36. }
  37. else
  38. {
  39. #if DEBUG
  40. throw new InvalidOperationException($"Node {nodeType.Name} does not have NodeInfoAttribute");
  41. #endif
  42. }
  43. }
  44. }
  45. public static bool TryGetNodeType(string nodeUniqueName, out Type nodeType)
  46. {
  47. return nodeMap.TryGetValue(nodeUniqueName, out nodeType);
  48. }
  49. public static Node CreateNode(Type nodeType, IReadOnlyDocument target, params object[] optionalParameters)
  50. {
  51. Node node = null;
  52. if (allFactories.TryGetValue(nodeType, out INodeFactory factory))
  53. {
  54. node = factory.CreateNode(target);
  55. }
  56. else
  57. {
  58. node = (Node)Activator.CreateInstance(nodeType, optionalParameters);
  59. }
  60. return node;
  61. }
  62. public static List<ConnectProperty_ChangeInfo> AppendMember(InputProperty<Texture?> parentInput,
  63. OutputProperty<Texture> toAddOutput,
  64. InputProperty<Texture> toAddInput, Guid memberId)
  65. {
  66. List<ConnectProperty_ChangeInfo> changes = new();
  67. IOutputProperty? previouslyConnected = null;
  68. if (parentInput.Connection != null)
  69. {
  70. previouslyConnected = parentInput.Connection;
  71. }
  72. toAddOutput.ConnectTo(parentInput);
  73. if (previouslyConnected != null)
  74. {
  75. previouslyConnected.ConnectTo(toAddInput);
  76. changes.Add(new ConnectProperty_ChangeInfo(previouslyConnected.Node.Id, memberId,
  77. previouslyConnected.InternalPropertyName, toAddInput.InternalPropertyName));
  78. }
  79. changes.Add(new ConnectProperty_ChangeInfo(memberId, parentInput.Node.Id,
  80. toAddOutput.InternalPropertyName, parentInput.InternalPropertyName));
  81. return changes;
  82. }
  83. public static List<IChangeInfo> DetachStructureNode(StructureNode structureNode)
  84. {
  85. List<IChangeInfo> changes = new();
  86. if (structureNode.Background.Connection != null)
  87. {
  88. // connect connection to next input if possible
  89. var connections = structureNode.Output.Connections.ToArray();
  90. var output = structureNode.Background.Connection;
  91. foreach (var input in connections)
  92. {
  93. output.ConnectTo(input);
  94. changes.Add(new ConnectProperty_ChangeInfo(output.Node.Id, input.Node.Id,
  95. output.InternalPropertyName, input.InternalPropertyName));
  96. }
  97. structureNode.Background.Connection.DisconnectFrom(structureNode.Background);
  98. changes.Add(new ConnectProperty_ChangeInfo(null, structureNode.Id, null,
  99. structureNode.Background.InternalPropertyName));
  100. }
  101. var outputs = structureNode.Output.Connections.ToArray();
  102. foreach (var outputConnection in outputs)
  103. {
  104. structureNode.Output.DisconnectFrom(outputConnection);
  105. changes.Add(new ConnectProperty_ChangeInfo(null, outputConnection.Node.Id, null,
  106. outputConnection.InternalPropertyName));
  107. }
  108. return changes;
  109. }
  110. public static ConnectionsData CreateConnectionsData(Node node)
  111. {
  112. var originalOutputConnections = new Dictionary<PropertyConnection, List<PropertyConnection>>();
  113. foreach (var outputProp in node.OutputProperties)
  114. {
  115. PropertyConnection outputConnection = new(outputProp.Node.Id, outputProp.InternalPropertyName);
  116. originalOutputConnections.Add(outputConnection, new List<PropertyConnection>());
  117. foreach (var conn in outputProp.Connections)
  118. {
  119. originalOutputConnections[outputConnection]
  120. .Add(new PropertyConnection(conn.Node.Id, conn.InternalPropertyName));
  121. }
  122. }
  123. var originalInputConnections = node.InputProperties.Select(x =>
  124. (new PropertyConnection(x.Node.Id, x.InternalPropertyName),
  125. new PropertyConnection(x.Connection?.Node.Id, x.Connection?.InternalPropertyName)))
  126. .ToList();
  127. return new ConnectionsData(originalOutputConnections, originalInputConnections);
  128. }
  129. public static List<IChangeInfo> ConnectStructureNodeProperties(ConnectionsData originalConnections, Node node,
  130. IReadOnlyNodeGraph graph)
  131. {
  132. List<IChangeInfo> changes = new();
  133. foreach (var connections in originalConnections.originalOutputConnections)
  134. {
  135. PropertyConnection outputConnection = connections.Key;
  136. IOutputProperty outputProp = node.GetOutputProperty(outputConnection.PropertyName);
  137. foreach (var connection in connections.Value)
  138. {
  139. var inputNode = graph.AllNodes.FirstOrDefault(x => x.Id == connection.NodeId);
  140. IInputProperty property = inputNode.GetInputProperty(connection.PropertyName);
  141. outputProp.ConnectTo(property);
  142. changes.Add(new ConnectProperty_ChangeInfo(node.Id, property.Node.Id, outputProp.InternalPropertyName,
  143. property.InternalPropertyName));
  144. }
  145. }
  146. foreach (var connection in originalConnections.originalInputConnections)
  147. {
  148. var outputNode = graph.AllNodes.FirstOrDefault(x => x.Id == connection.Item2?.NodeId);
  149. if (outputNode is null)
  150. continue;
  151. IOutputProperty output = outputNode.GetOutputProperty(connection.Item2.PropertyName);
  152. if (output is null)
  153. continue;
  154. IInputProperty? input =
  155. node.GetInputProperty(connection.Item1.PropertyName);
  156. if (input != null)
  157. {
  158. output.ConnectTo(input);
  159. changes.Add(new ConnectProperty_ChangeInfo(output.Node.Id, node.Id,
  160. output.InternalPropertyName,
  161. input.InternalPropertyName));
  162. }
  163. }
  164. return changes;
  165. }
  166. public static List<IChangeInfo> DetachNode(Changeables.Graph.NodeGraph target, Node? node)
  167. {
  168. List<IChangeInfo> changes = new();
  169. if (node == null)
  170. {
  171. return changes;
  172. }
  173. foreach (var input in node.InputProperties)
  174. {
  175. if (input.Connection == null)
  176. {
  177. continue;
  178. }
  179. input.Connection.DisconnectFrom(input);
  180. changes.Add(new ConnectProperty_ChangeInfo(null, node.Id, null, input.InternalPropertyName));
  181. }
  182. foreach (var output in node.OutputProperties)
  183. {
  184. foreach (var input in output.Connections.ToArray())
  185. {
  186. output.DisconnectFrom(input);
  187. changes.Add(new ConnectProperty_ChangeInfo(null, input.Node.Id, null, input.InternalPropertyName));
  188. }
  189. }
  190. return changes;
  191. }
  192. }
  193. public record PropertyConnection(Guid? NodeId, string? PropertyName);