Bézier曲線のコントロール

みなさま、お久しぶりでございます。年度末の波が怒濤のごとく押し寄せている訳ですが、その為に書いてない訳ではありません。
MySQLのコネクタをモノに使用と四苦八苦していまして他に手が回りません。あと、socketライブラリも独自に実装している途中なのですが、なんとなく気に入らなかったりします。
あれこれやっていると本当に睡眠時間も短くなるし、困ったものです。
ところで、以前作図的な手法をお伝えしたベジエ曲線のコントロールですが、算術的にまとめてみようと思い立った訳です。が、途中でウィキペディアの英語版にそのものの記事が出ているのに気がつきました。またもや徒労です。ついでですので適当に解説しておきます。

ベジエ曲線について少々掘り下げてみます。

リニアカーブ(直線)
まず2点P0―P1間においてdirectionが存在しない場合。ややこしい書き方ですがanchorとdirectionが重なるケース(要するに直線、これも立派なベジエ曲線です)です。これを分割するには単純に比率で計算できます。

B(t) = P0 + t(P1-P0) = (1-t)P0 + tP1  (0<=t<=1)

2次曲線
コントロールポイントが3点のケースです。要するにdirectionがひとつanchorに重なっている状態のパスです。この場合、曲線は2次の放物線になります。

B(t) = (1-t)²P0 + 2t(1-t)P1 + t²P2 (0<=t<=1)

3次曲線
最後にanchor及びdirectionが双方ともそろっているケースです。

B(t)=(1-t)³P0+3t(1-t)²P1+3t²(1-t)P2+t³P3 (0<=t<=1)

以上で必要な情報は出そろった訳です。詳しくはen版のwikipediaにて「Bézier curve」の項目を参照してみるとよろしいかと思います。
さて、ここまでの物をさらりと実装してみましょう。

function linearSprit (p0, p1, t) { //linear bezier
    var dirPt = new Array();
    for (i=0;i<2;i++){
        dirPt[i] = p1[i] * t + p0[i] * (1 – t);
        }
    return dirPt;
    }

function nwDirection(p0, p1, p2, t){ //quadratic bezier
    var dirPt = new Array()
    for (i=0;i<2;i++){
        dirPt[i] = (Math.pow ((1 – t), 2) )* p0[i]
                    + 2 * (1 – t) * t * p1[i]
                    + t * t * p2[i] ;
        }
    return dirPt;
    }

function nwAnchor(p0,p1,p2,p3, t){ //cublic bezier
    var spPt = new Array()
    for (i=0;i<2;i++){
        spPt[i] = (Math.pow ((1 – t), 3)) * p0[i]
                    + 3 * (Math.pow ((1 – t), 2)) * t * p1[i]
                    + 3 * (Math.pow (t, 2)) * (1 – t) * p2[i]
                    + Math.pow (t, 3) * p3[i] ;
        }
    return spPt;
    }

3つのファンクションがあります。ここでリニアを扱うものは既存のanchorに付随するdirectionをコントロールする為に使用します。
また、2次のものは新しく追加されるanchorのdirectionを生成する為に、最後の3次は追加するanchorそのものです。

次に利用方法を。2つのポイントで構成された線に比率に応じた位置にアンカーを追加するものです。

    var pt = new Array();
    var nwPt = new Array();

    var ratio = prompt (“sprit ratio?”, “0.5”) – 0;

    pt[0] = tg.pathPoints[0].anchor;
    pt[1] = tg.pathPoints[0].rightDirection;
    pt[2] = tg.pathPoints[1].leftDirection;
    pt[3] = tg.pathPoints[1].anchor;
        
    tg.pathPoints[0].rightDirection = linearSprit(pt[0], pt[1], ratio);
    tg.pathPoints[1].leftDirection = linearSprit(pt[2], pt[3], ratio);

    tg.pathPoints.add();
    tg.pathPoints[2].leftDirection = linearSprit(pt[2], pt[3], ratio);
    tg.pathPoints[2].anchor = pt[3];
    tg.pathPoints[2].rightDirection = tg.pathPoints[1].rightDirection;
    
    tg.pathPoints[1].anchor = nwAnchor (pt[0], pt[1], pt[2], pt[3], ratio);
    tg.pathPoints[1].leftDirection = nwDirection (pt[0], pt[1], pt[2], ratio);
    tg.pathPoints[1].rightDirection = nwDirection (pt[1], pt[2], pt[3], ratio);
    }

わりと単純にで、順番に既存のポイントを引数にファンクションを呼び出していくだけです。

複数のポイントが存在するモノでは以降のアンカーをずらしていく処理が必要になります。その際、push、popを利用したりすると便利です。

広告

Bézier曲線のコントロール」への2件のフィードバック

  1. 追加されたアンカーに多少歪みが出るのでおかしいと思いいろいろ試して
    14行目?の
    tg.pathPoints[2].leftDirection = pt[2];

    tg.pathPoints[2].leftDirection = linearSprit(pt[2], pt[3], ratio);
    に修正した所、歪みがなくなりました。

  2. …失敗してるorz
    上の方で分割前のコントロールポイント調整してそのままにしてた。(後から中身を移すからそこで調整しても意味が無かった…)
    どうも、ありがとうございます。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Google フォト

Google アカウントを使ってコメントしています。 ログアウト /  変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中