説明なし
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

Formula.cs 8.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. using System;
  2. using Unity.VisualScripting.Dependencies.NCalc;
  3. using UnityEngine;
  4. using NCalc = Unity.VisualScripting.Dependencies.NCalc.Expression;
  5. namespace Unity.VisualScripting
  6. {
  7. /// <summary>
  8. /// Evaluates a mathematical or logical formula with multiple arguments.
  9. /// </summary>
  10. public sealed class Formula : MultiInputUnit<object>
  11. {
  12. [SerializeAs(nameof(Formula))]
  13. private string _formula;
  14. private NCalc ncalc;
  15. /// <summary>
  16. /// A mathematical or logical expression tree.
  17. /// </summary>
  18. [DoNotSerialize]
  19. [Inspectable, UnitHeaderInspectable]
  20. [InspectorTextArea]
  21. public string formula
  22. {
  23. get => _formula;
  24. set
  25. {
  26. _formula = value;
  27. InitializeNCalc();
  28. }
  29. }
  30. /// <summary>
  31. /// Whether input arguments should only be fetched once and then reused.
  32. /// </summary>
  33. [Serialize]
  34. [Inspectable(order = int.MaxValue)]
  35. [InspectorExpandTooltip]
  36. public bool cacheArguments { get; set; }
  37. /// <summary>
  38. /// The result of the calculation or evaluation.
  39. /// </summary>
  40. [DoNotSerialize]
  41. [PortLabelHidden]
  42. public ValueOutput result { get; private set; }
  43. protected override int minInputCount => 0;
  44. protected override void Definition()
  45. {
  46. base.Definition();
  47. result = ValueOutput(nameof(result), Evaluate);
  48. InputsAllowNull();
  49. foreach (var input in multiInputs)
  50. {
  51. Requirement(input, result);
  52. }
  53. InitializeNCalc();
  54. }
  55. private void InitializeNCalc()
  56. {
  57. if (string.IsNullOrEmpty(formula))
  58. {
  59. ncalc = null;
  60. return;
  61. }
  62. ncalc = new NCalc(formula);
  63. ncalc.Options = EvaluateOptions.IgnoreCase;
  64. ncalc.EvaluateParameter += EvaluateTreeParameter;
  65. ncalc.EvaluateFunction += EvaluateTreeFunction;
  66. }
  67. private object Evaluate(Flow flow)
  68. {
  69. if (ncalc == null)
  70. {
  71. throw new InvalidOperationException("No formula provided.");
  72. }
  73. ncalc.UpdateUnityTimeParameters();
  74. return ncalc.Evaluate(flow);
  75. }
  76. private void EvaluateTreeFunction(Flow flow, string name, FunctionArgs args)
  77. {
  78. if (name == "v2" || name == "V2")
  79. {
  80. if (args.Parameters.Length != 2)
  81. {
  82. throw new ArgumentException($"v2() takes at exactly 2 arguments. {args.Parameters.Length} provided.");
  83. }
  84. args.Result = new Vector2
  85. (
  86. ConversionUtility.Convert<float>(args.Parameters[0].Evaluate(flow)),
  87. ConversionUtility.Convert<float>(args.Parameters[1].Evaluate(flow))
  88. );
  89. }
  90. else if (name == "v3" || name == "V3")
  91. {
  92. if (args.Parameters.Length != 3)
  93. {
  94. throw new ArgumentException($"v3() takes at exactly 3 arguments. {args.Parameters.Length} provided.");
  95. }
  96. args.Result = new Vector3
  97. (
  98. ConversionUtility.Convert<float>(args.Parameters[0].Evaluate(flow)),
  99. ConversionUtility.Convert<float>(args.Parameters[1].Evaluate(flow)),
  100. ConversionUtility.Convert<float>(args.Parameters[2].Evaluate(flow))
  101. );
  102. }
  103. else if (name == "v4" || name == "V4")
  104. {
  105. if (args.Parameters.Length != 4)
  106. {
  107. throw new ArgumentException($"v4() takes at exactly 4 arguments. {args.Parameters.Length} provided.");
  108. }
  109. args.Result = new Vector4
  110. (
  111. ConversionUtility.Convert<float>(args.Parameters[0].Evaluate(flow)),
  112. ConversionUtility.Convert<float>(args.Parameters[1].Evaluate(flow)),
  113. ConversionUtility.Convert<float>(args.Parameters[2].Evaluate(flow)),
  114. ConversionUtility.Convert<float>(args.Parameters[3].Evaluate(flow))
  115. );
  116. }
  117. }
  118. public object GetParameterValue(Flow flow, string name)
  119. {
  120. if (name.Length == 1)
  121. {
  122. var character = name[0];
  123. if (char.IsLetter(character))
  124. {
  125. character = char.ToLower(character);
  126. var index = GetArgumentIndex(character);
  127. if (index < multiInputs.Count)
  128. {
  129. var input = multiInputs[index];
  130. if (cacheArguments && !flow.IsLocal(input))
  131. {
  132. flow.SetValue(input, flow.GetValue<object>(input));
  133. }
  134. return flow.GetValue<object>(input);
  135. }
  136. }
  137. }
  138. else
  139. {
  140. if (Variables.Graph(flow.stack).IsDefined(name))
  141. {
  142. return Variables.Graph(flow.stack).Get(name);
  143. }
  144. var self = flow.stack.self;
  145. if (self != null)
  146. {
  147. if (Variables.Object(self).IsDefined(name))
  148. {
  149. return Variables.Object(self).Get(name);
  150. }
  151. }
  152. var scene = flow.stack.scene;
  153. if (scene != null)
  154. {
  155. if (Variables.Scene(scene).IsDefined(name))
  156. {
  157. return Variables.Scene(scene).Get(name);
  158. }
  159. }
  160. if (Variables.Application.IsDefined(name))
  161. {
  162. return Variables.Application.Get(name);
  163. }
  164. if (Variables.Saved.IsDefined(name))
  165. {
  166. return Variables.Saved.Get(name);
  167. }
  168. }
  169. throw new InvalidOperationException($"Unknown expression tree parameter: '{name}'.\nSupported parameter names are alphabetical indices and variable names.");
  170. }
  171. private void EvaluateTreeParameter(Flow flow, string name, ParameterArgs args)
  172. {
  173. // [param.fieldOrProperty]
  174. // [param.parmeterLessMethod()]
  175. if (name.Contains("."))
  176. {
  177. var parts = name.Split('.');
  178. if (parts.Length == 2)
  179. {
  180. var parameterName = parts[0];
  181. var memberName = parts[1].TrimEnd("()");
  182. var variableValue = GetParameterValue(flow, parameterName);
  183. var manipulator = new Member(variableValue.GetType(), memberName, Type.EmptyTypes);
  184. var target = variableValue;
  185. if (manipulator.isInvocable)
  186. {
  187. args.Result = manipulator.Invoke(target);
  188. }
  189. else if (manipulator.isGettable)
  190. {
  191. args.Result = manipulator.Get(target);
  192. }
  193. else
  194. {
  195. throw new InvalidOperationException($"Cannot get or invoke expression tree parameter: [{parameterName}.{memberName}]");
  196. }
  197. }
  198. else
  199. {
  200. throw new InvalidOperationException($"Cannot parse expression tree parameter: [{name}]");
  201. }
  202. }
  203. else
  204. {
  205. args.Result = GetParameterValue(flow, name);
  206. }
  207. }
  208. public static string GetArgumentName(int index)
  209. {
  210. if (index > ('z' - 'a'))
  211. {
  212. throw new NotImplementedException("Argument indices above 26 are not yet supported.");
  213. }
  214. return ((char)('a' + index)).ToString();
  215. }
  216. public static int GetArgumentIndex(char name)
  217. {
  218. if (name < 'a' || name > 'z')
  219. {
  220. throw new NotImplementedException("Unalphabetical argument names are not yet supported.");
  221. }
  222. return name - 'a';
  223. }
  224. }
  225. }