From 3ec39aca2bb43b46610eddf5e0adac458395330c Mon Sep 17 00:00:00 2001 From: Len Woodward Date: Mon, 3 Apr 2023 13:03:10 -0700 Subject: [PATCH] [Feat] Expression Utilities, Arch (#8) * expressions for eq, gt, if, lt * move Ease class to EaseFunctions, use new expr utils * move ease enum up one level * remove unnecessary imports * use enum from new location * use enum make method * update classes in readme --- README.md | 4 +- scripts/generateEasings | 5 +- scripts/generateTimeline | 2 +- src/Ease.php | 246 ++++++--------------------------- src/EaseFunctions.php | 289 +++++++++++++++++++++++++++++++++++++++ src/Enums/Ease.php | 38 ----- src/Keyframe.php | 2 - src/Timeline.php | 2 - src/Tween.php | 30 ++-- src/Utils/Expr.php | 59 ++++++++ 10 files changed, 408 insertions(+), 269 deletions(-) create mode 100644 src/EaseFunctions.php delete mode 100644 src/Enums/Ease.php create mode 100644 src/Utils/Expr.php diff --git a/README.md b/README.md index f6c7e2a..ba5fa1f 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ For now this package can only be used within a Laravel app, but there are plans ```php use ProjektGopher\FFMpegTween\Tween; use ProjektGopher\FFMpegTween\Timing; -use ProjektGopher\FFMpegTween\Enums\Ease; +use ProjektGopher\FFMpegTween\Ease; $x = (new Tween()) ->from("50") @@ -54,7 +54,7 @@ $x = (new Tween()) use ProjektGopher\FFMpegTween\Keyframe; use ProjektGopher\FFMpegTween\Timeline; use ProjektGopher\FFMpegTween\Timing; -use ProjektGopher\FFMpegTween\Enums\Ease; +use ProjektGopher\FFMpegTween\Ease; $x = new Timeline() $x->keyframe((new Keyframe) diff --git a/scripts/generateEasings b/scripts/generateEasings index 23ac8ab..746558a 100755 --- a/scripts/generateEasings +++ b/scripts/generateEasings @@ -3,16 +3,15 @@ require_once __DIR__.'/../vendor/autoload.php'; -use ProjektGopher\FFMpegTween\Enums\Ease as AvailableEasings; use ProjektGopher\FFMpegTween\Ease; echo 'starting... (fail fast)'.PHP_EOL; $time = "X/H"; -foreach (AvailableEasings::cases() as $ease) { +foreach (Ease::cases() as $ease) { echo "Generating snapshot for {$ease->value} easing...".PHP_EOL; - $easeMultiplier = Ease::{$ease->value}($time); + $easeMultiplier = $ease->make($time); $input = "-f lavfi -i \"color=c=black:s=256x256:d=1\""; $margin = '28'; diff --git a/scripts/generateTimeline b/scripts/generateTimeline index 92614e7..891fb57 100755 --- a/scripts/generateTimeline +++ b/scripts/generateTimeline @@ -5,7 +5,7 @@ require_once __DIR__.'/../vendor/autoload.php'; use ProjektGopher\FFMpegTween\Timeline; use ProjektGopher\FFMpegTween\Keyframe; -use ProjektGopher\FFMpegTween\Enums\Ease; +use ProjektGopher\FFMpegTween\Ease; use ProjektGopher\FFMpegTween\Timing; echo 'Generating video sample using Timeline...'.PHP_EOL; diff --git a/src/Ease.php b/src/Ease.php index fa2b3f3..25fbda7 100644 --- a/src/Ease.php +++ b/src/Ease.php @@ -2,210 +2,48 @@ namespace ProjektGopher\FFMpegTween; -/** - * Ease - * - * Returns a string which will evaluate to a value between 0 and 1. - * This allows us to multiply the _change_ in value by the chosen - * easing function to get the actual value at a given time. - * - * (paraphrased from easings.net) - * The argument `$time` should be a `string` which represents the absolute - * progress of the animation in the bounds of 0 (beginning of the animation) - * and 1 (end of animation). - */ -class Ease +enum Ease: string { - public static function Linear(string $time): string - { - return $time; - } - - public static function EaseInSine(string $time): string - { - return "1-cos((({$time})*PI)/2)"; - } - - public static function EaseOutSine(string $time): string - { - return "sin((({$time})*PI)/2)"; - } - - public static function EaseInOutSine(string $time): string - { - return "-(cos(PI*({$time}))-1)/2"; - } - - public static function EaseInQuad(string $time): string - { - return "pow(({$time})\\,2)"; - } - - public static function EaseOutQuad(string $time): string - { - return "1-(1-({$time}))*(1-({$time}))"; - } - - public static function EaseInOutQuad(string $time): string - { - return "if(lt(({$time})\\,0.5)\\,2*pow(({$time})\\,2)\\,1-pow(-2*({$time})+2\\,2)/2)"; - } - - public static function EaseInCubic(string $time): string - { - return "pow(({$time})\\,3)"; - } - - public static function EaseOutCubic(string $time): string - { - return "1-pow(1-({$time})\\,3)"; - } - - public static function EaseInOutCubic(string $time): string - { - return "if(lt(({$time})\\,0.5)\\,4*pow(({$time})\\,3)\\,1-pow(-2*({$time})+2\\,3)/2)"; - } - - public static function EaseInQuart(string $time): string - { - return "pow(({$time})\\,4)"; - } - - public static function EaseOutQuart(string $time): string - { - return "1-pow(1-({$time})\\,4)"; - } - - public static function EaseInOutQuart(string $time): string - { - return "if(lt(({$time})\\,0.5)\\,8*pow(({$time})\\,4)\\,1-pow(-2*({$time})+2\\,4)/2)"; - } - - public static function EaseInQuint(string $time): string - { - return "pow(({$time})\\,5)"; - } - - public static function EaseOutQuint(string $time): string - { - return "1-pow(1-({$time})\\,5)"; - } - - public static function EaseInOutQuint(string $time): string - { - return "if(lt(({$time})\\,0.5)\\,16*pow(({$time})\\,5)\\,1-pow(-2*({$time})+2\\,5)/2)"; - } - - public static function EaseInExpo(string $time): string - { - return "if(eq(({$time})\\,0)\\,0\\,pow(2\\,10*({$time})-10))"; - } - - public static function EaseOutExpo(string $time): string - { - return "if(eq(({$time})\\,1)\\,1\\,1-pow(2\\,-10*({$time})))"; - } - - public static function EaseInOutExpo(string $time): string - { - $firstExp = "pow(2\\,20*({$time})-10)/2"; - $secondExp = "(2-pow(2\\,-20*({$time})+10))/2"; - - // We have to set the return value for 0 and 1 explicitly as we lose accuracy at the 4th decimal place. - return "if(eq(({$time})\\,0)\\,0\\,if(eq(({$time})\\,1)\\,1\\,if(lt(({$time})\\,0.5)\\,{$firstExp}\\,{$secondExp})))"; - } - - public static function EaseInCirc(string $time): string - { - return "1-sqrt(1-pow(({$time})\\,2))"; - } - - public static function EaseOutCirc(string $time): string - { - return "sqrt(1-pow(({$time})-1\\,2))"; - } - - public static function EaseInOutCirc(string $time): string - { - return "if(lt(({$time})\\,0.5)\\,(1-sqrt(1-pow(2*({$time})\\,2)))/2\\,(sqrt(1-pow(-2*({$time})+2\\,2))+1)/2)"; - } - - public static function EaseInBack(string $time): string - { - $c1 = 1.70158; - $c3 = $c1 + 1; - - return "{$c3}*pow(({$time})\\,3)-{$c1}*pow(({$time})\\,2)"; - } - - public static function EaseOutBack(string $time): string - { - $c1 = 1.70158; - $c3 = $c1 + 1; - - return "1+{$c3}*pow(({$time})-1\\,3)+{$c1}*pow(({$time})-1\\,2)"; - } - - public static function EaseInOutBack(string $time): string - { - $c1 = 1.70158; - $c2 = $c1 * 1.525; - - return "if(lt(({$time})\\,0.5)\\,(pow(2*({$time})\\,2)*(({$c2}+1)*2*({$time})-{$c2}))/2\\,(pow(2*({$time})-2\\,2)*(({$c2}+1)*(({$time})*2-2)+{$c2})+2)/2)"; - } - - public static function EaseInElastic(string $time): string - { - $c4 = (2 * M_PI) / 3; - - return "if(eq(({$time})\\,0)\\,0\\,if(eq(({$time})\\,1)\\,1\\,-pow(2\\,10*({$time})-10)*sin((({$time})*10-10.75)*{$c4})))"; - } - - public static function EaseOutElastic(string $time): string - { - $c4 = (2 * M_PI) / 3; - - return "if(eq(({$time})\\,0)\\,0\\,if(eq(({$time})\\,1)\\,1\\,pow(2\\,-10*({$time}))*sin((({$time})*10-0.75)*{$c4})+1))"; - } - - public static function EaseInOutElastic(string $time): string - { - $c5 = (2 * M_PI) / 4.5; - $ltExpr = "-(pow(2\\,20*({$time})-10)*sin((20*({$time})-11.125)*{$c5}))/2"; - $gtExpr = "(pow(2\\,-20*({$time})+10)*sin((20*({$time})-11.125)*{$c5}))/2+1"; - - return "if(eq(({$time})\\,0)\\,0\\,if(eq(({$time})\\,1)\\,1\\,if(lt(({$time})\\,0.5)\\,{$ltExpr}\\,{$gtExpr})))"; - } - - public static function EaseInBounce(string $time): string - { - $x = self::EaseOutBounce("1-({$time})"); - - return "1-({$x})"; - } - - public static function EaseOutBounce(string $time): string - { - $n1 = 7.5625; - $d1 = 2.75; - $firstExpr = "{$n1}*pow(({$time})\\,2)"; - $secondTime = "(({$time})-1.5/{$d1})"; - $secondExpr = "{$n1}*{$secondTime}*{$secondTime}+0.75"; - $thirdTime = "(({$time})-2.25/{$d1})"; - $thirdExpr = "{$n1}*{$thirdTime}*{$thirdTime}+0.9375"; - $fourthTime = "(({$time})-2.65/{$d1})"; - $fourthExpr = "{$n1}*{$fourthTime}*{$fourthTime}+0.984375"; - - return "if(lt(({$time})\\, 1/{$d1})\\,{$firstExpr}\\,if(lt(({$time})\\,2/{$d1})\\,{$secondExpr}\\,if(lt(({$time})\\,2.5/{$d1})\\,{$thirdExpr}\\,{$fourthExpr})))"; - } - - public static function EaseInOutBounce(string $time): string - { - $x1 = self::EaseOutBounce("1-2*({$time})"); - $x2 = self::EaseOutBounce("2*({$time})-1"); - $ltExpr = "(1-({$x1}))/2"; - $gtExpr = "(1+({$x2}))/2"; - - return "if(lt(({$time})\\,0.5)\\,{$ltExpr}\\,{$gtExpr})"; + case Linear = 'Linear'; + case InSine = 'EaseInSine'; + case OutSine = 'EaseOutSine'; + case InOutSine = 'EaseInOutSine'; + case InQuad = 'EaseInQuad'; + case OutQuad = 'EaseOutQuad'; + case InOutQuad = 'EaseInOutQuad'; + case InCubic = 'EaseInCubic'; + case OutCubic = 'EaseOutCubic'; + case InOutCubic = 'EaseInOutCubic'; + case InQuart = 'EaseInQuart'; + case OutQuart = 'EaseOutQuart'; + case InOutQuart = 'EaseInOutQuart'; + case InQuint = 'EaseInQuint'; + case OutQuint = 'EaseOutQuint'; + case InOutQuint = 'EaseInOutQuint'; + case InExpo = 'EaseInExpo'; + case OutExpo = 'EaseOutExpo'; + case InOutExpo = 'EaseInOutExpo'; + case InCirc = 'EaseInCirc'; + case OutCirc = 'EaseOutCirc'; + case InOutCirc = 'EaseInOutCirc'; + case InBack = 'EaseInBack'; + case OutBack = 'EaseOutBack'; + case InOutBack = 'EaseInOutBack'; + case InElastic = 'EaseInElastic'; + case OutElastic = 'EaseOutElastic'; + case InOutElastic = 'EaseInOutElastic'; + case InBounce = 'EaseInBounce'; + case OutBounce = 'EaseOutBounce'; + case InOutBounce = 'EaseInOutBounce'; + + public function make(string $time): string + { + // Wrap the result in parentheses to ensure that it + // doesn't interfere with surrounding expressions + return implode([ + '(', + EaseFunctions::{$this->value}($time), + ')', + ]); } } diff --git a/src/EaseFunctions.php b/src/EaseFunctions.php new file mode 100644 index 0000000..a557af3 --- /dev/null +++ b/src/EaseFunctions.php @@ -0,0 +1,289 @@ +delay; } - public function ease(AvailableEasings $ease): self + public function ease(Ease $ease): self { - $easeString = Ease::{$ease->value}("(t-{$this->delay})/{$this->duration}"); - $this->ease = "({$easeString})"; + $this->ease = $ease->make("(t-{$this->delay})/{$this->duration}"); return $this; } @@ -73,21 +72,18 @@ private function getDelta(): string public function build(): string { if (! $this->ease) { - $this->ease(AvailableEasings::Linear); + $this->ease(Ease::Linear); } - // if t is less than delay, from - // else if t is greater than delay + duration, to - // else, from + delta*ease - - // if( lt(t,{$this->delay}), - // {$this->from}, - // if( gt(t,{$this->delay}+{$this->duration}), - // {$this->to}, - // {$this->from}+({$this->getDelta()}*{$this->ease}) - // ) - // ) - return "if(lt(t\,{$this->delay})\,{$this->from}\,if(gt(t\,{$this->delay}+{$this->duration})\,{$this->to}\,{$this->from}+({$this->getDelta()}*{$this->ease})))"; + return Expr::if( + x: Expr::lt('t', $this->delay), + y: $this->from, + z: Expr::if( + x: Expr::gt('t', "{$this->delay}+{$this->duration}"), + y: $this->to, + z: "{$this->from}+({$this->getDelta()}*{$this->ease})" + ), + ); } public function __toString(): string diff --git a/src/Utils/Expr.php b/src/Utils/Expr.php new file mode 100644 index 0000000..d37f8a1 --- /dev/null +++ b/src/Utils/Expr.php @@ -0,0 +1,59 @@ +