ConnectProperties_Change.cs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. using PixiEditor.ChangeableDocument.Changeables.Graph;
  2. using PixiEditor.ChangeableDocument.Changeables.Graph.Context;
  3. using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
  4. using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
  5. using PixiEditor.ChangeableDocument.ChangeInfos.NodeGraph;
  6. using PixiEditor.DrawingApi.Core.Shaders.Generation;
  7. namespace PixiEditor.ChangeableDocument.Changes.NodeGraph;
  8. internal class ConnectProperties_Change : Change
  9. {
  10. public Guid InputNodeId { get; }
  11. public Guid OutputNodeId { get; }
  12. public string InputProperty { get; }
  13. public string OutputProperty { get; }
  14. private IOutputProperty? originalConnection;
  15. [GenerateMakeChangeAction]
  16. public ConnectProperties_Change(Guid inputNodeId, Guid outputNodeId, string inputProperty, string outputProperty)
  17. {
  18. InputNodeId = inputNodeId;
  19. OutputNodeId = outputNodeId;
  20. InputProperty = inputProperty;
  21. OutputProperty = outputProperty;
  22. }
  23. public override bool InitializeAndValidate(Document target)
  24. {
  25. Node inputNode = target.FindNode(InputNodeId);
  26. Node outputNode = target.FindNode(OutputNodeId);
  27. if (inputNode == null || outputNode == null)
  28. {
  29. return false;
  30. }
  31. InputProperty? inputProp = inputNode.GetInputProperty(InputProperty);
  32. OutputProperty? outputProp = outputNode.GetOutputProperty(OutputProperty);
  33. if (inputProp == null || outputProp == null)
  34. {
  35. return false;
  36. }
  37. if(IsLoop(inputProp, outputProp))
  38. {
  39. return false;
  40. }
  41. bool canConnect = CheckTypeCompatibility(inputProp, outputProp);
  42. if (!canConnect)
  43. {
  44. return false;
  45. }
  46. originalConnection = inputProp.Connection;
  47. return true;
  48. }
  49. private bool IsLoop(InputProperty input, OutputProperty output)
  50. {
  51. if (input.Node == output.Node)
  52. {
  53. return true;
  54. }
  55. if(input.Node.OutputProperties.Any(x => x.Connections.Any(y => y.Node == output.Node)))
  56. {
  57. return true;
  58. }
  59. return false;
  60. }
  61. public override OneOf<None, IChangeInfo, List<IChangeInfo>> Apply(Document target, bool firstApply,
  62. out bool ignoreInUndo)
  63. {
  64. Node inputNode = target.FindNode(InputNodeId);
  65. Node outputNode = target.FindNode(OutputNodeId);
  66. InputProperty inputProp = inputNode.GetInputProperty(InputProperty);
  67. OutputProperty outputProp = outputNode.GetOutputProperty(OutputProperty);
  68. outputProp.ConnectTo(inputProp);
  69. ignoreInUndo = false;
  70. return new ConnectProperty_ChangeInfo(outputNode.Id, inputNode.Id, OutputProperty, InputProperty);
  71. }
  72. public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
  73. {
  74. Node inputNode = target.FindNode(InputNodeId);
  75. Node outputNode = target.FindNode(OutputNodeId);
  76. InputProperty inputProp = inputNode.GetInputProperty(InputProperty);
  77. OutputProperty outputProp = outputNode.GetOutputProperty(OutputProperty);
  78. outputProp.DisconnectFrom(inputProp);
  79. ConnectProperty_ChangeInfo change = new(null, InputNodeId, null, InputProperty);
  80. inputProp.Connection = originalConnection;
  81. List<IChangeInfo> changes = new() { change, };
  82. if (originalConnection != null)
  83. {
  84. ConnectProperty_ChangeInfo oldConnection = new(originalConnection.Node.Id, InputNodeId,
  85. originalConnection.InternalPropertyName, InputProperty);
  86. changes.Add(oldConnection);
  87. }
  88. return changes;
  89. }
  90. private static bool CheckTypeCompatibility(InputProperty input, OutputProperty output)
  91. {
  92. if (input.ValueType != output.ValueType)
  93. {
  94. if (IsCrossExpression(output.Value, input.ValueType))
  95. {
  96. return true;
  97. }
  98. var outputValue = output.Value;
  99. if(IsExpressionToConstant(output, input, out var result))
  100. {
  101. outputValue = result;
  102. }
  103. if(IsConstantToExpression(input, outputValue, out result))
  104. {
  105. return ConversionTable.TryConvert(result, output.ValueType, out _);
  106. }
  107. if (outputValue == null)
  108. {
  109. return true;
  110. }
  111. if (outputValue.GetType().IsAssignableTo(input.ValueType))
  112. {
  113. return true;
  114. }
  115. if (ConversionTable.TryConvert(outputValue, input.ValueType, out _))
  116. {
  117. return true;
  118. }
  119. return false;
  120. }
  121. return true;
  122. }
  123. private static bool IsConstantToExpression(InputProperty input, object objValue, out object result)
  124. {
  125. if (input.Value is Delegate func && func.Method.ReturnType.IsAssignableTo(typeof(ShaderExpressionVariable)))
  126. {
  127. try
  128. {
  129. var actualArg = func.DynamicInvoke(FuncContext.NoContext);
  130. if(actualArg is ShaderExpressionVariable variable)
  131. {
  132. result = variable.GetConstant();
  133. return true;
  134. }
  135. }
  136. catch
  137. {
  138. result = null;
  139. return false;
  140. }
  141. }
  142. result = null;
  143. return false;
  144. }
  145. private static bool IsExpressionToConstant(OutputProperty output, InputProperty input, out object o)
  146. {
  147. if (output.Value is Delegate func && func.Method.ReturnType.IsAssignableTo(typeof(ShaderExpressionVariable)))
  148. {
  149. try
  150. {
  151. o = func.DynamicInvoke(FuncContext.NoContext);
  152. if(o is ShaderExpressionVariable variable)
  153. {
  154. o = variable.GetConstant();
  155. }
  156. return true;
  157. }
  158. catch
  159. {
  160. o = null;
  161. return false;
  162. }
  163. }
  164. o = null;
  165. return false;
  166. }
  167. private static bool IsCrossExpression(object first, Type secondType)
  168. {
  169. if (first is Delegate func && func.Method.ReturnType.IsAssignableTo(typeof(ShaderExpressionVariable)))
  170. {
  171. return secondType.IsAssignableTo(typeof(Delegate));
  172. }
  173. return false;
  174. }
  175. }