using Node = System.Func; namespace MrGameEng.Formulas; /// /// The built-in function set available inside formulas. Each entry validates its argument count at /// compile time and returns a that evaluates its operands then the math. Kept /// deterministic and side-effect-free so formulas stay pure. /// internal static class FormulaFunctions { public static Node Build(string name, List args) { switch (name) { case "abs": return Unary(name, args, MathF.Abs); case "sign": return Unary(name, args, x => MathF.Sign(x)); case "floor": return Unary(name, args, MathF.Floor); case "ceil": return Unary(name, args, MathF.Ceiling); case "round": return Unary(name, args, MathF.Round); case "sqrt": return Unary(name, args, MathF.Sqrt); case "exp": return Unary(name, args, MathF.Exp); case "log": return args.Count == 2 ? Binary(name, args, MathF.Log) : Unary(name, args, MathF.Log); case "sin": return Unary(name, args, MathF.Sin); case "cos": return Unary(name, args, MathF.Cos); case "tan": return Unary(name, args, MathF.Tan); case "min": return Binary(name, args, MathF.Min); case "max": return Binary(name, args, MathF.Max); case "pow": return Binary(name, args, MathF.Pow); case "clamp": return Ternary(name, args, (x, lo, hi) => Math.Clamp(x, lo, hi)); case "lerp": return Ternary(name, args, (a, b, t) => a + (b - a) * t); case "step": return Binary(name, args, (edge, x) => x < edge ? 0f : 1f); default: throw new FormulaException($"Unknown function '{name}'."); } } private static Node Unary(string name, List args, Func op) { Require(name, args, 1); var a = args[0]; return ctx => op(a(ctx)); } private static Node Binary(string name, List args, Func op) { Require(name, args, 2); var a = args[0]; var b = args[1]; return ctx => op(a(ctx), b(ctx)); } private static Node Ternary(string name, List args, Func op) { Require(name, args, 3); var a = args[0]; var b = args[1]; var c = args[2]; return ctx => op(a(ctx), b(ctx), c(ctx)); } private static void Require(string name, List args, int count) { if (args.Count != count) { throw new FormulaException( $"Function '{name}' expects {count} argument(s) but got {args.Count}." ); } } }