Node.cs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. using PixiEditor.ChangeableDocument.Changeables.Animations;
  2. using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
  3. using PixiEditor.Numerics;
  4. namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
  5. public abstract class Node : IReadOnlyNode, IDisposable
  6. {
  7. private List<InputProperty> inputs = new();
  8. private List<OutputProperty> outputs = new();
  9. private List<IReadOnlyNode> _connectedNodes = new();
  10. public Guid Id { get; internal set; } = Guid.NewGuid();
  11. public IReadOnlyCollection<InputProperty> InputProperties => inputs;
  12. public IReadOnlyCollection<OutputProperty> OutputProperties => outputs;
  13. public IReadOnlyCollection<IReadOnlyNode> ConnectedOutputNodes => _connectedNodes;
  14. IReadOnlyCollection<IInputProperty> IReadOnlyNode.InputProperties => inputs;
  15. IReadOnlyCollection<IOutputProperty> IReadOnlyNode.OutputProperties => outputs;
  16. public VecD Position { get; set; }
  17. public ChunkyImage? Execute(KeyFrameTime frameTime)
  18. {
  19. foreach (var input in inputs)
  20. {
  21. if (input.Connection != null)
  22. {
  23. input.Value = input.Connection.Value;
  24. }
  25. }
  26. return OnExecute(frameTime);
  27. }
  28. public abstract ChunkyImage? OnExecute(KeyFrameTime frameTime);
  29. public abstract bool Validate();
  30. public void RemoveKeyFrame(Guid keyFrameGuid)
  31. {
  32. // TODO: Implement
  33. }
  34. public void SetKeyFrameLength(Guid keyFrameGuid, int startFrame, int duration)
  35. {
  36. // TODO: Implement
  37. }
  38. public void AddFrame<T>(Guid keyFrameGuid, int startFrame, int duration, T value)
  39. {
  40. // TODO: Implement
  41. }
  42. public void TraverseBackwards(Func<IReadOnlyNode, bool> action)
  43. {
  44. var visited = new HashSet<IReadOnlyNode>();
  45. var queueNodes = new Queue<IReadOnlyNode>();
  46. queueNodes.Enqueue(this);
  47. while (queueNodes.Count > 0)
  48. {
  49. var node = queueNodes.Dequeue();
  50. if (!visited.Add(node))
  51. {
  52. continue;
  53. }
  54. if (!action(node))
  55. {
  56. return;
  57. }
  58. foreach (var inputProperty in node.InputProperties)
  59. {
  60. if (inputProperty.Connection != null)
  61. {
  62. queueNodes.Enqueue(inputProperty.Node);
  63. }
  64. }
  65. }
  66. }
  67. public void TraverseForwards(Func<IReadOnlyNode, bool> action)
  68. {
  69. var visited = new HashSet<IReadOnlyNode>();
  70. var queueNodes = new Queue<IReadOnlyNode>();
  71. queueNodes.Enqueue(this);
  72. while (queueNodes.Count > 0)
  73. {
  74. var node = queueNodes.Dequeue();
  75. if (!visited.Add(node))
  76. {
  77. continue;
  78. }
  79. if (!action(node))
  80. {
  81. return;
  82. }
  83. foreach (var outputProperty in node.OutputProperties)
  84. {
  85. foreach (var outputNode in ConnectedOutputNodes)
  86. {
  87. queueNodes.Enqueue(outputNode);
  88. }
  89. }
  90. }
  91. }
  92. protected InputProperty<T> CreateInput<T>(string propName, string displayName, T defaultValue)
  93. {
  94. var property = new InputProperty<T>(this, propName, displayName, defaultValue);
  95. if (InputProperties.Any(x => x.InternalPropertyName == propName))
  96. {
  97. throw new InvalidOperationException($"Input with name {propName} already exists.");
  98. }
  99. inputs.Add(property);
  100. return property;
  101. }
  102. protected OutputProperty<T> CreateOutput<T>(string propName, string displayName, T defaultValue)
  103. {
  104. var property = new OutputProperty<T>(this, propName, displayName, defaultValue);
  105. outputs.Add(property);
  106. property.Connected += (input, _) => _connectedNodes.Add(input.Node);
  107. property.Disconnected += (input, _) => _connectedNodes.Remove(input.Node);
  108. return property;
  109. }
  110. public virtual void Dispose()
  111. {
  112. foreach (var input in inputs)
  113. {
  114. if (input is { Connection: null, Value: IDisposable disposable })
  115. {
  116. disposable.Dispose();
  117. }
  118. }
  119. foreach (var output in outputs)
  120. {
  121. if (output.Value is IDisposable disposable)
  122. {
  123. foreach (var connection in output.Connections)
  124. {
  125. connection.Value = default!;
  126. }
  127. disposable.Dispose();
  128. }
  129. }
  130. }
  131. public abstract Node CreateCopy();
  132. public Node Clone()
  133. {
  134. var clone = CreateCopy();
  135. clone.Id = Guid.NewGuid();
  136. clone.inputs = new List<InputProperty>();
  137. clone.outputs = new List<OutputProperty>();
  138. clone._connectedNodes = new List<IReadOnlyNode>();
  139. foreach (var input in inputs)
  140. {
  141. var newInput = input.Clone(clone);
  142. clone.inputs.Add(newInput);
  143. }
  144. foreach (var output in outputs)
  145. {
  146. var newOutput = output.Clone(clone);
  147. clone.outputs.Add(newOutput);
  148. }
  149. return clone;
  150. }
  151. public InputProperty? GetInputProperty(string inputProperty)
  152. {
  153. return inputs.FirstOrDefault(x => x.InternalPropertyName == inputProperty);
  154. }
  155. public OutputProperty? GetOutputProperty(string outputProperty)
  156. {
  157. return outputs.FirstOrDefault(x => x.InternalPropertyName == outputProperty);
  158. }
  159. }