« 2011年07月 | メイン | 2014年06月 »

2014年05月22日

イージングのマジックナンバーを自分で求めてみた

easeOutBackなイージング(iPhoneのホーム画面をスワイプした時みたいにちょっと行き過ぎてからフワッと戻ってくるやつ)をいい感じにするために、行き過ぎ度合いを調整するパラメータovershootをあれこれ弄っている時に1.70158というマジックナンバーなデフォルト値がふと気になって、これどうやって求めたんだろうなと。

内容的には行き過ぎた時の値がちょうど始値と終値の差分の10%分になるように設定された値なんですが、せっかくなので自分でも求めてみようと思い立ってちょっと計算してみることにしました。


イージングによる変化中の数値は r^2 * ( (s + 1) * r + s) + 1 で求められています

s = overshoot
r = ratio - 1 [ 0 <= ratio <= 1 ]

これをrの3次関数として書き直すと
f(r) = (s + 1) * r^3 + s * r^2 + 1

さて、ここから行き過ぎたときの値を求めるわけですが、easeOutBack は次のような曲線を描きます。

極大となる時、すなわち接線の傾きが0となる時の r の値をまず知りたい。
そこで微分を使います(うわーすげえ懐かしい

f'(r) = 3 * (s + 1) * r^2 + 2 * s * r

これが0となる時、すなわち極大および極小となる時のrの値を求めると

r = 0 または r = -(2 * s) / (3 * s + 3)
つまり ratio = 1 または ratio = (s + 3) / (3 * s + 3) となります

さて、この r の値をあらためて f(r) に代入します。
r=0 は ratio=1 となるので easeOutBack の場合、終点かつ極小で f(0) = 1 となり
イージングでは始点が0、終点が1となるので合ってますね。

となるともうひとつの r の値が極大をとることになります。
f(r) に代入して整理すると 4/27 * s^3 / ((s + 1)^2) + 1 が得られますが、ここで極大値に注目します。
マジックナンバー 1.70158 は始値と終値の差分の10%から算出されたということで、極大値は1.1となるので

4/27 * s^3 / ((s + 1)^2) + 1 = 1.1

これをsの3次方程式として整理すると

40 * s^3 - 27 * s^2 - 54 * s - 27 = 0

カルダノの公式より実数解のみ求めると

s = 1.7015401988668

となり、マジックナンバー 1.70158 に非常に近い値が得られました。
誤差については計算過程で浮動小数が一部丸められたせいなのかと一瞬思ったのですが、それよりは厳密に計算せずに一桁ずつ入れながら10%付近になるように調整したくさい感じがすごくしています。


さて、せっかく自分で求めておきながらこのまま終わると非常にもったいないので、10%だけでなく20%以降についても求めてみます。
立式自体は非常に簡単で、先ほどの式の右辺を変えるだけ。

p = 超過分パーセント値/10 として
40 * s^3 - 27 * p * s^2 - 54 * p * s - 27 * p = 0

10%: 1.701540198866824
20%: 2.5923889015163
30%: 3.3940516581445603
40%: 4.155744652639194
50%: 4.894859521133737
60%: 5.619622918334311
70%: 6.334566669331999
80%: 7.042439379340938
90%: 7.745023855643343
100%: 8.443535601593252


極大をとるときのratioの値も分かる簡易計算機置いておきます。


これからはなんとなく数値を変えるのではなくちゃんと計算して求めることができますよってことで、特定のラインをギリギリ超えないようにするためにはどういう値に設定すればいいのか試行錯誤せずとも・・・とはいえ適当な数値入れながら何度も調整していく方が作ってて楽しいよねーという身も蓋もない話もありますがw

あ、ちなみにeaseBounceはマジックナンバーだらけなんですが、さすがにやる気なかったので丸投げしておきます→誰か