diff --git a/source/ObjectViewer/FunctionScripts.cs b/source/ObjectViewer/FunctionScripts.cs
index 5861b646c..ce178840c 100644
--- a/source/ObjectViewer/FunctionScripts.cs
+++ b/source/ObjectViewer/FunctionScripts.cs
@@ -161,6 +161,105 @@ internal static void ExecuteFunctionScript(FunctionScript Function, TrainBase Tr
case Instructions.MathPi:
Function.Stack[s] = Math.PI;
s++; break;
+ case Instructions.MathLerp:
+ Function.Stack[s - 3] = Easing.Lerp(Function.Stack[s - 3], Function.Stack[s - 2], Function.Stack[s - 1]);
+ s -= 2; break;
+ case Instructions.MathSmoothstep:
+ Function.Stack[s - 3] = Easing.Smoothstep(Function.Stack[s - 3], Function.Stack[s - 2], Function.Stack[s - 1]);
+ s -= 2; break;
+ case Instructions.MathSlerp:
+ Function.Stack[s - 3] = Easing.Slerp(Function.Stack[s - 3], Function.Stack[s - 2], Function.Stack[s - 1]);
+ s -= 2; break;
+ case Instructions.MathEaseInSine:
+ Function.Stack[s - 1] = Easing.EaseInSine(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseOutSine:
+ Function.Stack[s - 1] = Easing.EaseOutSine(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInOutSine:
+ Function.Stack[s - 1] = Easing.EaseInOutSine(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInQuad:
+ Function.Stack[s - 1] = Easing.EaseInQuad(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseOutQuad:
+ Function.Stack[s - 1] = Easing.EaseOutQuad(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInOutQuad:
+ Function.Stack[s - 1] = Easing.EaseInOutQuad(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInCubic:
+ Function.Stack[s - 1] = Easing.EaseInCubic(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseOutCubic:
+ Function.Stack[s - 1] = Easing.EaseOutCubic(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInOutCubic:
+ Function.Stack[s - 1] = Easing.EaseInOutCubic(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInQuart:
+ Function.Stack[s - 1] = Easing.EaseInQuart(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseOutQuart:
+ Function.Stack[s - 1] = Easing.EaseOutQuart(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInOutQuart:
+ Function.Stack[s - 1] = Easing.EaseInOutQuart(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInQuint:
+ Function.Stack[s - 1] = Easing.EaseInQuint(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseOutQuint:
+ Function.Stack[s - 1] = Easing.EaseOutQuint(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInOutQuint:
+ Function.Stack[s - 1] = Easing.EaseInOutQuint(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInExpo:
+ Function.Stack[s - 1] = Easing.EaseInExpo(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseOutExpo:
+ Function.Stack[s - 1] = Easing.EaseOutExpo(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInOutExpo:
+ Function.Stack[s - 1] = Easing.EaseInOutExpo(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInCirc:
+ Function.Stack[s - 1] = Easing.EaseInCirc(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseOutCirc:
+ Function.Stack[s - 1] = Easing.EaseOutCirc(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInOutCirc:
+ Function.Stack[s - 1] = Easing.EaseInOutCirc(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInBack:
+ Function.Stack[s - 1] = Easing.EaseInBack(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseOutBack:
+ Function.Stack[s - 1] = Easing.EaseOutBack(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInOutBack:
+ Function.Stack[s - 1] = Easing.EaseInOutBack(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInElastic:
+ Function.Stack[s - 1] = Easing.EaseInElastic(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseOutElastic:
+ Function.Stack[s - 1] = Easing.EaseOutElastic(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInOutElastic:
+ Function.Stack[s - 1] = Easing.EaseInOutElastic(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInBounce:
+ Function.Stack[s - 1] = Easing.EaseInBounce(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseOutBounce:
+ Function.Stack[s - 1] = Easing.EaseOutBounce(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInOutBounce:
+ Function.Stack[s - 1] = Easing.EaseInOutBounce(Function.Stack[s - 1]);
+ break;
// comparisons
case Instructions.CompareEqual:
Function.Stack[s - 2] = Function.Stack[s - 2] == Function.Stack[s - 1] ? 1.0 : 0.0;
diff --git a/source/OpenBVE/Game/ObjectManager/AnimatedObjects/FunctionScripts.cs b/source/OpenBVE/Game/ObjectManager/AnimatedObjects/FunctionScripts.cs
index 3710a10d0..4e5f61a6b 100644
--- a/source/OpenBVE/Game/ObjectManager/AnimatedObjects/FunctionScripts.cs
+++ b/source/OpenBVE/Game/ObjectManager/AnimatedObjects/FunctionScripts.cs
@@ -163,6 +163,105 @@ internal static void ExecuteFunctionScript(FunctionScript Function, TrainBase Tr
case Instructions.MathPi:
Function.Stack[s] = Math.PI;
s++; break;
+ case Instructions.MathLerp:
+ Function.Stack[s - 3] = Easing.Lerp(Function.Stack[s - 3], Function.Stack[s - 2], Function.Stack[s - 1]);
+ s -= 2; break;
+ case Instructions.MathSmoothstep:
+ Function.Stack[s - 3] = Easing.Smoothstep(Function.Stack[s - 3], Function.Stack[s - 2], Function.Stack[s - 1]);
+ s -= 2; break;
+ case Instructions.MathSlerp:
+ Function.Stack[s - 3] = Easing.Slerp(Function.Stack[s - 3], Function.Stack[s - 2], Function.Stack[s - 1]);
+ s -= 2; break;
+ case Instructions.MathEaseInSine:
+ Function.Stack[s - 1] = Easing.EaseInSine(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseOutSine:
+ Function.Stack[s - 1] = Easing.EaseOutSine(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInOutSine:
+ Function.Stack[s - 1] = Easing.EaseInOutSine(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInQuad:
+ Function.Stack[s - 1] = Easing.EaseInQuad(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseOutQuad:
+ Function.Stack[s - 1] = Easing.EaseOutQuad(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInOutQuad:
+ Function.Stack[s - 1] = Easing.EaseInOutQuad(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInCubic:
+ Function.Stack[s - 1] = Easing.EaseInCubic(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseOutCubic:
+ Function.Stack[s - 1] = Easing.EaseOutCubic(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInOutCubic:
+ Function.Stack[s - 1] = Easing.EaseInOutCubic(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInQuart:
+ Function.Stack[s - 1] = Easing.EaseInQuart(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseOutQuart:
+ Function.Stack[s - 1] = Easing.EaseOutQuart(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInOutQuart:
+ Function.Stack[s - 1] = Easing.EaseInOutQuart(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInQuint:
+ Function.Stack[s - 1] = Easing.EaseInQuint(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseOutQuint:
+ Function.Stack[s - 1] = Easing.EaseOutQuint(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInOutQuint:
+ Function.Stack[s - 1] = Easing.EaseInOutQuint(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInExpo:
+ Function.Stack[s - 1] = Easing.EaseInExpo(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseOutExpo:
+ Function.Stack[s - 1] = Easing.EaseOutExpo(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInOutExpo:
+ Function.Stack[s - 1] = Easing.EaseInOutExpo(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInCirc:
+ Function.Stack[s - 1] = Easing.EaseInCirc(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseOutCirc:
+ Function.Stack[s - 1] = Easing.EaseOutCirc(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInOutCirc:
+ Function.Stack[s - 1] = Easing.EaseInOutCirc(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInBack:
+ Function.Stack[s - 1] = Easing.EaseInBack(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseOutBack:
+ Function.Stack[s - 1] = Easing.EaseOutBack(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInOutBack:
+ Function.Stack[s - 1] = Easing.EaseInOutBack(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInElastic:
+ Function.Stack[s - 1] = Easing.EaseInElastic(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseOutElastic:
+ Function.Stack[s - 1] = Easing.EaseOutElastic(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInOutElastic:
+ Function.Stack[s - 1] = Easing.EaseInOutElastic(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInBounce:
+ Function.Stack[s - 1] = Easing.EaseInBounce(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseOutBounce:
+ Function.Stack[s - 1] = Easing.EaseOutBounce(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInOutBounce:
+ Function.Stack[s - 1] = Easing.EaseInOutBounce(Function.Stack[s - 1]);
+ break;
// comparisons
case Instructions.CompareEqual:
Function.Stack[s - 2] = Function.Stack[s - 2] == Function.Stack[s - 1] ? 1.0 : 0.0;
diff --git a/source/OpenBveApi/FunctionScripts/FunctionScript.Notation.cs b/source/OpenBveApi/FunctionScripts/FunctionScript.Notation.cs
index 95011d77e..9b085a972 100644
--- a/source/OpenBveApi/FunctionScripts/FunctionScript.Notation.cs
+++ b/source/OpenBveApi/FunctionScripts/FunctionScript.Notation.cs
@@ -203,6 +203,13 @@ public static string GetPostfixNotationFromFunctionNotation(string Expression) {
return a[0] + " " + a[1] + " " + f;
}
throw new System.IO.InvalidDataException(f + " is expected to have 2 arguments in " + Expression);
+ case "lerp":
+ case "smoothstep":
+ case "slerp":
+ if (n == 3) {
+ return a[0] + " " + a[1] + " " + a[2] + " " + f;
+ }
+ throw new System.IO.InvalidDataException(f + " is expected to have 3 arguments in " + Expression);
case "minus":
case "reciprocal":
case "floor":
@@ -217,6 +224,36 @@ public static string GetPostfixNotationFromFunctionNotation(string Expression) {
case "cos":
case "tan":
case "arctan":
+ case "easeinsine":
+ case "easeoutsine":
+ case "easeinoutsine":
+ case "easeinquad":
+ case "easeoutquad":
+ case "easeinoutquad":
+ case "easeincubic":
+ case "easeoutcubic":
+ case "easeinoutcubic":
+ case "easeinquart":
+ case "easeoutquart":
+ case "easeinoutquart":
+ case "easeinquint":
+ case "easeoutquint":
+ case "easeinoutquint":
+ case "easeinexpo":
+ case "easeoutexpo":
+ case "easeinoutexpo":
+ case "easeincirc":
+ case "easeoutcirc":
+ case "easeinoutcirc":
+ case "easeinback":
+ case "easeoutback":
+ case "easeinoutback":
+ case "easeinelastic":
+ case "easeoutelastic":
+ case "easeinoutelastic":
+ case "easeinbounce":
+ case "easeoutbounce":
+ case "easeinoutbounce":
if (n == 1) {
return a[0] + " " + f;
}
diff --git a/source/OpenBveApi/FunctionScripts/FunctionScript.cs b/source/OpenBveApi/FunctionScripts/FunctionScript.cs
index d9db77f7c..7a97f64e1 100644
--- a/source/OpenBveApi/FunctionScripts/FunctionScript.cs
+++ b/source/OpenBveApi/FunctionScripts/FunctionScript.cs
@@ -321,6 +321,171 @@ public FunctionScript(HostInterface Host, string Expression, bool Infix)
if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
InstructionSet[n] = Instructions.MathPi;
n++; s++; if (s >= m) m = s; break;
+ case "lerp":
+ if (s < 3) throw new InvalidOperationException(Arguments[i] + " requires at least 3 arguments on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathLerp;
+ n++; s -= 2; break;
+ case "smoothstep":
+ if (s < 3) throw new InvalidOperationException(Arguments[i] + " requires at least 3 arguments on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathSmoothstep;
+ n++; s -= 2; break;
+ case "slerp":
+ if (s < 3) throw new InvalidOperationException(Arguments[i] + " requires at least 3 arguments on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathSlerp;
+ n++; s -= 2; break;
+ case "easeinsine":
+ if (s < 1) throw new InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathEaseInSine;
+ n++; break;
+ case "easeoutsine":
+ if (s < 1) throw new InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathEaseOutSine;
+ n++; break;
+ case "easeinoutsine":
+ if (s < 1) throw new InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathEaseInOutSine;
+ n++; break;
+ case "easeinquad":
+ if (s < 1) throw new InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathEaseInQuad;
+ n++; break;
+ case "easeoutquad":
+ if (s < 1) throw new InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathEaseOutQuad;
+ n++; break;
+ case "easeinoutquad":
+ if (s < 1) throw new InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathEaseInOutQuad;
+ n++; break;
+ case "easeincubic":
+ if (s < 1) throw new InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathEaseInCubic;
+ n++; break;
+ case "easeoutcubic":
+ if (s < 1) throw new InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathEaseOutCubic;
+ n++; break;
+ case "easeinoutcubic":
+ if (s < 1) throw new InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathEaseInOutCubic;
+ n++; break;
+ case "easeinquart":
+ if (s < 1) throw new InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathEaseInQuart;
+ n++; break;
+ case "easeoutquart":
+ if (s < 1) throw new InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathEaseOutQuart;
+ n++; break;
+ case "easeinoutquart":
+ if (s < 1) throw new InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathEaseInOutQuart;
+ n++; break;
+ case "easeinquint":
+ if (s < 1) throw new InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathEaseInQuint;
+ n++; break;
+ case "easeoutquint":
+ if (s < 1) throw new InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathEaseOutQuint;
+ n++; break;
+ case "easeinoutquint":
+ if (s < 1) throw new InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathEaseInOutQuint;
+ n++; break;
+ case "easeinexpo":
+ if (s < 1) throw new InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathEaseInExpo;
+ n++; break;
+ case "easeoutexpo":
+ if (s < 1) throw new InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathEaseOutExpo;
+ n++; break;
+ case "easeinoutexpo":
+ if (s < 1) throw new InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathEaseInOutExpo;
+ n++; break;
+ case "easeincirc":
+ if (s < 1) throw new InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathEaseInCirc;
+ n++; break;
+ case "easeoutcirc":
+ if (s < 1) throw new InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathEaseOutCirc;
+ n++; break;
+ case "easeinoutcirc":
+ if (s < 1) throw new InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathEaseInOutCirc;
+ n++; break;
+ case "easeinback":
+ if (s < 1) throw new InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathEaseInBack;
+ n++; break;
+ case "easeoutback":
+ if (s < 1) throw new InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathEaseOutBack;
+ n++; break;
+ case "easeinoutback":
+ if (s < 1) throw new InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathEaseInOutBack;
+ n++; break;
+ case "easeinelastic":
+ if (s < 1) throw new InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathEaseInElastic;
+ n++; break;
+ case "easeoutelastic":
+ if (s < 1) throw new InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathEaseOutElastic;
+ n++; break;
+ case "easeinoutelastic":
+ if (s < 1) throw new InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathEaseInOutElastic;
+ n++; break;
+ case "easeinbounce":
+ if (s < 1) throw new InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathEaseInBounce;
+ n++; break;
+ case "easeoutbounce":
+ if (s < 1) throw new InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathEaseOutBounce;
+ n++; break;
+ case "easeinoutbounce":
+ if (s < 1) throw new InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression);
+ if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
+ InstructionSet[n] = Instructions.MathEaseInOutBounce;
+ n++; break;
case "==":
if (s < 2) throw new InvalidOperationException(Arguments[i] + " requires at least 2 arguments on the stack in function script " + Expression);
if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1);
diff --git a/source/OpenBveApi/FunctionScripts/Instructions.cs b/source/OpenBveApi/FunctionScripts/Instructions.cs
index 355a66bd0..92a04138d 100644
--- a/source/OpenBveApi/FunctionScripts/Instructions.cs
+++ b/source/OpenBveApi/FunctionScripts/Instructions.cs
@@ -9,6 +9,17 @@ public enum Instructions {
MathIncrement, MathDecrement, MathFusedMultiplyAdd,
MathQuotient, MathMod, MathFloor, MathCeiling, MathRound, MathMin, MathMax, MathAbs, MathSign,
MathExp, MathLog, MathSqrt, MathSin, MathCos, MathTan, MathArcTan, MathPi,
+ MathLerp, MathSmoothstep, MathSlerp,
+ MathEaseInSine, MathEaseOutSine, MathEaseInOutSine,
+ MathEaseInQuad, MathEaseOutQuad, MathEaseInOutQuad,
+ MathEaseInCubic, MathEaseOutCubic, MathEaseInOutCubic,
+ MathEaseInQuart, MathEaseOutQuart, MathEaseInOutQuart,
+ MathEaseInQuint, MathEaseOutQuint, MathEaseInOutQuint,
+ MathEaseInExpo, MathEaseOutExpo, MathEaseInOutExpo,
+ MathEaseInCirc, MathEaseOutCirc, MathEaseInOutCirc,
+ MathEaseInBack, MathEaseOutBack, MathEaseInOutBack,
+ MathEaseInElastic, MathEaseOutElastic, MathEaseInOutElastic,
+ MathEaseInBounce, MathEaseOutBounce, MathEaseInOutBounce,
CompareEqual, CompareUnequal, CompareLess, CompareGreater, CompareLessEqual, CompareGreaterEqual, CompareConditional,
LogicalNot, LogicalAnd, LogicalOr, LogicalNand, LogicalNor, LogicalXor,
/*
diff --git a/source/OpenBveApi/Math/Easing.cs b/source/OpenBveApi/Math/Easing.cs
new file mode 100644
index 000000000..8832afca6
--- /dev/null
+++ b/source/OpenBveApi/Math/Easing.cs
@@ -0,0 +1,368 @@
+namespace OpenBveApi.Math
+{
+ /// Provides standard easing and interpolation functions
+ /// Easing curves follow the reference implementations by Robert Penner and operate on a normalized input in the range 0..1
+ public static class Easing
+ {
+ /// Linearly interpolates between two values
+ /// The first value
+ /// The second value
+ /// The interpolation factor
+ public static double Lerp(double a, double b, double t)
+ {
+ return a + (b - a) * t;
+ }
+
+ /// Performs a Hermite (smoothstep) interpolation between two values
+ /// The first value
+ /// The second value
+ /// The interpolation input
+ public static double Smoothstep(double a, double b, double x)
+ {
+ if (a == b)
+ {
+ return a;
+ }
+ double t = (x - a) / (b - a);
+ return t * t * (3.0 - 2.0 * t);
+ }
+
+ /// Spherically (shortest-arc) interpolates between two angles
+ /// The first angle
+ /// The second angle
+ /// The interpolation factor
+ public static double Slerp(double a, double b, double t)
+ {
+ double delta = b - a;
+ delta = (delta + System.Math.PI) % (2.0 * System.Math.PI);
+ if (delta < 0.0) delta += 2.0 * System.Math.PI;
+ delta -= System.Math.PI;
+ return a + delta * t;
+ }
+
+ /// Eases the input using a sine curve
+ /// The input, in the range 0..1
+ public static double EaseInSine(double t)
+ {
+ return 1.0 - System.Math.Cos(t * System.Math.PI / 2.0);
+ }
+
+ /// Eases the input using a sine curve
+ /// The input, in the range 0..1
+ public static double EaseOutSine(double t)
+ {
+ return System.Math.Sin(t * System.Math.PI / 2.0);
+ }
+
+ /// Eases the input using a sine curve
+ /// The input, in the range 0..1
+ public static double EaseInOutSine(double t)
+ {
+ return -(System.Math.Cos(System.Math.PI * t) - 1.0) / 2.0;
+ }
+
+ /// Eases the input using a quadratic curve
+ /// The input, in the range 0..1
+ public static double EaseInQuad(double t)
+ {
+ return t * t;
+ }
+
+ /// Eases the input using a quadratic curve
+ /// The input, in the range 0..1
+ public static double EaseOutQuad(double t)
+ {
+ double u = 1.0 - t;
+ return 1.0 - u * u;
+ }
+
+ /// Eases the input using a quadratic curve
+ /// The input, in the range 0..1
+ public static double EaseInOutQuad(double t)
+ {
+ if (t < 0.5)
+ {
+ return 2.0 * t * t;
+ }
+ double v = -2.0 * t + 2.0;
+ return 1.0 - v * v / 2.0;
+ }
+
+ /// Eases the input using a cubic curve
+ /// The input, in the range 0..1
+ public static double EaseInCubic(double t)
+ {
+ return t * t * t;
+ }
+
+ /// Eases the input using a cubic curve
+ /// The input, in the range 0..1
+ public static double EaseOutCubic(double t)
+ {
+ double u = 1.0 - t;
+ return 1.0 - u * u * u;
+ }
+
+ /// Eases the input using a cubic curve
+ /// The input, in the range 0..1
+ public static double EaseInOutCubic(double t)
+ {
+ if (t < 0.5)
+ {
+ return 4.0 * t * t * t;
+ }
+ double v = -2.0 * t + 2.0;
+ return 1.0 - v * v * v / 2.0;
+ }
+
+ /// Eases the input using a quartic curve
+ /// The input, in the range 0..1
+ public static double EaseInQuart(double t)
+ {
+ double s = t * t;
+ return s * s;
+ }
+
+ /// Eases the input using a quartic curve
+ /// The input, in the range 0..1
+ public static double EaseOutQuart(double t)
+ {
+ double u = 1.0 - t;
+ double s = u * u;
+ return 1.0 - s * s;
+ }
+
+ /// Eases the input using a quartic curve
+ /// The input, in the range 0..1
+ public static double EaseInOutQuart(double t)
+ {
+ if (t < 0.5)
+ {
+ double s = t * t;
+ return 8.0 * s * s;
+ }
+ double v = -2.0 * t + 2.0;
+ double s2 = v * v;
+ return 1.0 - s2 * s2 / 2.0;
+ }
+
+ /// Eases the input using a quintic curve
+ /// The input, in the range 0..1
+ public static double EaseInQuint(double t)
+ {
+ double s = t * t;
+ return s * s * t;
+ }
+
+ /// Eases the input using a quintic curve
+ /// The input, in the range 0..1
+ public static double EaseOutQuint(double t)
+ {
+ double u = 1.0 - t;
+ double s = u * u;
+ return 1.0 - s * s * u;
+ }
+
+ /// Eases the input using a quintic curve
+ /// The input, in the range 0..1
+ public static double EaseInOutQuint(double t)
+ {
+ if (t < 0.5)
+ {
+ double s = t * t;
+ return 16.0 * s * s * t;
+ }
+ double v = -2.0 * t + 2.0;
+ double s2 = v * v;
+ return 1.0 - s2 * s2 * v / 2.0;
+ }
+
+ /// Eases the input using an exponential curve
+ /// The input, in the range 0..1
+ public static double EaseInExpo(double t)
+ {
+ return t == 0.0 ? 0.0 : System.Math.Pow(2.0, 10.0 * t - 10.0);
+ }
+
+ /// Eases the input using an exponential curve
+ /// The input, in the range 0..1
+ public static double EaseOutExpo(double t)
+ {
+ return t == 1.0 ? 1.0 : 1.0 - System.Math.Pow(2.0, -10.0 * t);
+ }
+
+ /// Eases the input using an exponential curve
+ /// The input, in the range 0..1
+ public static double EaseInOutExpo(double t)
+ {
+ if (t == 0.0)
+ {
+ return 0.0;
+ }
+ if (t == 1.0)
+ {
+ return 1.0;
+ }
+ if (t < 0.5)
+ {
+ return System.Math.Pow(2.0, 20.0 * t - 10.0) / 2.0;
+ }
+ return (2.0 - System.Math.Pow(2.0, -20.0 * t + 10.0)) / 2.0;
+ }
+
+ /// Eases the input using a circular curve
+ /// The input, in the range 0..1
+ public static double EaseInCirc(double t)
+ {
+ return 1.0 - Extensions.SqrtC(1.0 - t * t);
+ }
+
+ /// Eases the input using a circular curve
+ /// The input, in the range 0..1
+ public static double EaseOutCirc(double t)
+ {
+ double u = t - 1.0;
+ return Extensions.SqrtC(1.0 - u * u);
+ }
+
+ /// Eases the input using a circular curve
+ /// The input, in the range 0..1
+ public static double EaseInOutCirc(double t)
+ {
+ if (t < 0.5)
+ {
+ return (1.0 - Extensions.SqrtC(1.0 - 4.0 * t * t)) / 2.0;
+ }
+ double v = -2.0 * t + 2.0;
+ return (Extensions.SqrtC(1.0 - v * v) + 1.0) / 2.0;
+ }
+
+ /// Eases the input using a back curve
+ /// The input, in the range 0..1
+ public static double EaseInBack(double t)
+ {
+ const double c1 = 1.70158;
+ const double c3 = c1 + 1.0;
+ return c3 * t * t * t - c1 * t * t;
+ }
+
+ /// Eases the input using a back curve
+ /// The input, in the range 0..1
+ public static double EaseOutBack(double t)
+ {
+ const double c1 = 1.70158;
+ const double c3 = c1 + 1.0;
+ double u = t - 1.0;
+ return 1.0 + c3 * u * u * u + c1 * u * u;
+ }
+
+ /// Eases the input using a back curve
+ /// The input, in the range 0..1
+ public static double EaseInOutBack(double t)
+ {
+ const double c1 = 1.70158;
+ const double c2 = c1 * 1.525;
+ if (t < 0.5)
+ {
+ double v = 2.0 * t;
+ return (v * v * ((c2 + 1.0) * v - c2)) / 2.0;
+ }
+ double w = 2.0 * t - 2.0;
+ return (w * w * ((c2 + 1.0) * w + c2) + 2.0) / 2.0;
+ }
+
+ /// Eases the input using an elastic curve
+ /// The input, in the range 0..1
+ public static double EaseInElastic(double t)
+ {
+ const double c4 = (2.0 * System.Math.PI) / 3.0;
+ if (t == 0.0)
+ {
+ return 0.0;
+ }
+ if (t == 1.0)
+ {
+ return 1.0;
+ }
+ return -System.Math.Pow(2.0, 10.0 * t - 10.0) * System.Math.Sin((t * 10.0 - 10.75) * c4);
+ }
+
+ /// Eases the input using an elastic curve
+ /// The input, in the range 0..1
+ public static double EaseOutElastic(double t)
+ {
+ const double c4 = (2.0 * System.Math.PI) / 3.0;
+ if (t == 0.0)
+ {
+ return 0.0;
+ }
+ if (t == 1.0)
+ {
+ return 1.0;
+ }
+ return System.Math.Pow(2.0, -10.0 * t) * System.Math.Sin((t * 10.0 - 0.75) * c4) + 1.0;
+ }
+
+ /// Eases the input using an elastic curve
+ /// The input, in the range 0..1
+ public static double EaseInOutElastic(double t)
+ {
+ const double c5 = (2.0 * System.Math.PI) / 4.5;
+ if (t == 0.0)
+ {
+ return 0.0;
+ }
+ if (t == 1.0)
+ {
+ return 1.0;
+ }
+ if (t < 0.5)
+ {
+ return -(System.Math.Pow(2.0, 20.0 * t - 10.0) * System.Math.Sin((20.0 * t - 11.125) * c5)) / 2.0;
+ }
+ return (System.Math.Pow(2.0, -20.0 * t + 10.0) * System.Math.Sin((20.0 * t - 11.125) * c5)) / 2.0 + 1.0;
+ }
+
+ /// Eases the input using a bounce curve
+ /// The input, in the range 0..1
+ public static double EaseInBounce(double t)
+ {
+ return 1.0 - EaseOutBounce(1.0 - t);
+ }
+
+ /// Eases the input using a bounce curve
+ /// The input, in the range 0..1
+ public static double EaseOutBounce(double t)
+ {
+ const double n1 = 7.5625;
+ const double d1 = 2.75;
+ if (t < 1.0 / d1)
+ {
+ return n1 * t * t;
+ }
+ if (t < 2.0 / d1)
+ {
+ double u = t - 1.5 / d1;
+ return n1 * u * u + 0.75;
+ }
+ if (t < 2.5 / d1)
+ {
+ double u = t - 2.25 / d1;
+ return n1 * u * u + 0.9375;
+ }
+ double v = t - 2.625 / d1;
+ return n1 * v * v + 0.984375;
+ }
+
+ /// Eases the input using a bounce curve
+ /// The input, in the range 0..1
+ public static double EaseInOutBounce(double t)
+ {
+ if (t < 0.5)
+ {
+ return (1.0 - EaseOutBounce(1.0 - 2.0 * t)) / 2.0;
+ }
+ return (1.0 + EaseOutBounce(2.0 * t - 1.0)) / 2.0;
+ }
+ }
+}
diff --git a/source/OpenBveApi/OpenBveApi.csproj b/source/OpenBveApi/OpenBveApi.csproj
index 623bd7fd1..8cf63e31d 100644
--- a/source/OpenBveApi/OpenBveApi.csproj
+++ b/source/OpenBveApi/OpenBveApi.csproj
@@ -131,6 +131,7 @@
+
diff --git a/source/RouteViewer/FunctionScripts.cs b/source/RouteViewer/FunctionScripts.cs
index 6ec5b6a6e..85efc5a41 100644
--- a/source/RouteViewer/FunctionScripts.cs
+++ b/source/RouteViewer/FunctionScripts.cs
@@ -160,6 +160,105 @@ internal static void ExecuteFunctionScript(FunctionScript Function, TrainBase Tr
case Instructions.MathPi:
Function.Stack[s] = Math.PI;
s++; break;
+ case Instructions.MathLerp:
+ Function.Stack[s - 3] = Easing.Lerp(Function.Stack[s - 3], Function.Stack[s - 2], Function.Stack[s - 1]);
+ s -= 2; break;
+ case Instructions.MathSmoothstep:
+ Function.Stack[s - 3] = Easing.Smoothstep(Function.Stack[s - 3], Function.Stack[s - 2], Function.Stack[s - 1]);
+ s -= 2; break;
+ case Instructions.MathSlerp:
+ Function.Stack[s - 3] = Easing.Slerp(Function.Stack[s - 3], Function.Stack[s - 2], Function.Stack[s - 1]);
+ s -= 2; break;
+ case Instructions.MathEaseInSine:
+ Function.Stack[s - 1] = Easing.EaseInSine(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseOutSine:
+ Function.Stack[s - 1] = Easing.EaseOutSine(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInOutSine:
+ Function.Stack[s - 1] = Easing.EaseInOutSine(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInQuad:
+ Function.Stack[s - 1] = Easing.EaseInQuad(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseOutQuad:
+ Function.Stack[s - 1] = Easing.EaseOutQuad(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInOutQuad:
+ Function.Stack[s - 1] = Easing.EaseInOutQuad(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInCubic:
+ Function.Stack[s - 1] = Easing.EaseInCubic(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseOutCubic:
+ Function.Stack[s - 1] = Easing.EaseOutCubic(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInOutCubic:
+ Function.Stack[s - 1] = Easing.EaseInOutCubic(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInQuart:
+ Function.Stack[s - 1] = Easing.EaseInQuart(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseOutQuart:
+ Function.Stack[s - 1] = Easing.EaseOutQuart(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInOutQuart:
+ Function.Stack[s - 1] = Easing.EaseInOutQuart(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInQuint:
+ Function.Stack[s - 1] = Easing.EaseInQuint(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseOutQuint:
+ Function.Stack[s - 1] = Easing.EaseOutQuint(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInOutQuint:
+ Function.Stack[s - 1] = Easing.EaseInOutQuint(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInExpo:
+ Function.Stack[s - 1] = Easing.EaseInExpo(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseOutExpo:
+ Function.Stack[s - 1] = Easing.EaseOutExpo(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInOutExpo:
+ Function.Stack[s - 1] = Easing.EaseInOutExpo(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInCirc:
+ Function.Stack[s - 1] = Easing.EaseInCirc(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseOutCirc:
+ Function.Stack[s - 1] = Easing.EaseOutCirc(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInOutCirc:
+ Function.Stack[s - 1] = Easing.EaseInOutCirc(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInBack:
+ Function.Stack[s - 1] = Easing.EaseInBack(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseOutBack:
+ Function.Stack[s - 1] = Easing.EaseOutBack(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInOutBack:
+ Function.Stack[s - 1] = Easing.EaseInOutBack(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInElastic:
+ Function.Stack[s - 1] = Easing.EaseInElastic(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseOutElastic:
+ Function.Stack[s - 1] = Easing.EaseOutElastic(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInOutElastic:
+ Function.Stack[s - 1] = Easing.EaseInOutElastic(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInBounce:
+ Function.Stack[s - 1] = Easing.EaseInBounce(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseOutBounce:
+ Function.Stack[s - 1] = Easing.EaseOutBounce(Function.Stack[s - 1]);
+ break;
+ case Instructions.MathEaseInOutBounce:
+ Function.Stack[s - 1] = Easing.EaseInOutBounce(Function.Stack[s - 1]);
+ break;
// comparisons
case Instructions.CompareEqual:
Function.Stack[s - 2] = Function.Stack[s - 2] == Function.Stack[s - 1] ? 1.0 : 0.0;