こちら(と元記事)で紹介しているベジェ曲線だが、tに応じて(x, y)を取得する形式になっている。
点Aから点Bまでの曲線を描く際にはこれで十分なのだが、例えばCSS3のtransitionのeasingの指定に使うときのような、xに対するyが必要な時もある。
そこで、こちらの136行あたりでやっていることをBezieクラスに追加してみた。
仕組みは単純で、おもむろにtを決めたのちそのtにおける(x, y)に対してニュートン法、二分法と試して方程式を解いている。
一応、コードを続きに。
/** あるtについてxの微分値を求める */ private float sampleCurveDerivativeX(float t) { if (n == 2) { // 2次ベジェ return 2.0f * this.Ax * t + this.Bx; } else { // //3次ベジェ return (3.0f * this.Ax * t + 2.0f * this.Bx) * t + this.Cx; } } /** xを与えるとtについての方程式を求めてyを返す * 変なbezieじゃなければだいたい軽いはず */ public float CalcYfromX(float x) { float epsilon = 1e-5f; // 閾値 float x2, t0, t1, t2, d2, i; for(t2 = x, i = 0; i < 8; i++) { x2 = GetPointAtTime (t2).x - x; if (Mathf.Abs (x2) < epsilon) { return GetPointAtTime (t2).y; } d2 = sampleCurveDerivativeX (t2); if (Mathf.Abs (d2) < 1e-6f) { break; } t2 = t2 - x2 / d2; } t0 = 0f; t1 = 1f; t2 = x; if (t2 < t0) { return GetPointAtTime (t0).y; } if (t2 > t1) { return GetPointAtTime (t1).y; } while (t0 < t1) { x2 = GetPointAtTime (t2).x; if (Mathf.Abs (x2 - x) < epsilon) { return GetPointAtTime (t2).y; } if (x > x2) { t0 = t2; } else { t1 = t2; } t2 = (t1 - t0) * 0.5f + t0; } return GetPointAtTime (t2).y; // 失敗 }
とりあえず作ったのでGetPointAtTime(t)してxもしくはyを使うような雑な感じ。