ベジェ曲線

2003/7/1作成

ベジェ曲線

ベジェ曲線とは、与えられた制御点を補間する曲線です。4つの制御点(P1,P2,P3,P4))の場合、
Q(t)=(1-t)3P1+3(1-t)2tP2+3(1-t)t2P3+t3P4 (0≦t≦1)
という補間式になり、これを3次ベジェ曲線と呼びます。この3次ベジェ曲線はアウトラインフォントの定義に使用されていたり、身近な例ではWindows標準のスクリーンセーバーであるラインアートでも使用されています。

ここでは、適当な4点とその中点から2本の3次ベジェ曲線を描画します。

プログラム


/********************************************************/
/*  制御点初期位置、変位設定                            */
/********************************************************/

/*  P1=(a,b)    P2=(c,d)    P3=(e,f)    P4=(g,h)    */
/*  dP1=(i,j)   dP2=(k,l)   dP3=(m,n)   dP4=(o,p)   */

a = 10;     b = 10;
c = 100;    d = 10;
e = 100;    f = 50;
g = 10;     h = 50;

i = 1;  j = 1;
k = 1;  l = -1;
m = -1; n = 1;
o = -1; p = -1;


/********************************************************/
/*  処理ループ                                          */
/********************************************************/

while( 1 ) {
    /*  M1、M2計算  */
    /*  M1=(q,r)=(P1+P4)/2  M2=(s,t)=(P2+P3)/2  */
    q = ( a + g ) / 2;
    r = ( b + h ) / 2;
    s = ( c + e ) / 2;
    t = ( d + f ) / 2;


    /********************************************************/
    /*  描画                                                */
    /********************************************************/

    /*  画面消去    */
    fill( 0, 0, 127, 87, 0 );

    /*  制御点四角形描画    */
    line( a, b, c, d, 1 );
    line( c, d, e, f, 1 );
    line( e, f, g, h, 1 );
    line( g, h, a, b, 1 );

    /*  ベジェ曲線その1描画 */
    for( u = 0, v = 1; u < 10; u++, v++ ) {
        w = ( 1.0 - u / 10.0 ) * ( 1.0 - u / 10.0 ) * ( 1.0 - u / 10.0 ) * q
            + 3.0 * ( 1.0 - u / 10.0 ) * ( 1.0 - u / 10.0 ) * ( u / 10.0 ) * a
            + 3.0 * ( 1.0 - u / 10.0 ) * ( u / 10.0 ) * ( u / 10.0 ) * c
            + ( u / 10.0 ) * ( u / 10.0 ) * ( u / 10.0 ) * s;
        x = ( 1.0 - u / 10.0 ) * ( 1.0 - u / 10.0 ) * ( 1.0 - u / 10.0 ) * r
            + 3.0 * ( 1.0 - u / 10.0 ) * ( 1.0 - u / 10.0 ) * ( u / 10.0 ) * b
            + 3.0 * ( 1.0 - u / 10.0 ) * ( u / 10.0 ) * ( u / 10.0 ) * d
            + ( u / 10.0 ) * ( u / 10.0 ) * ( u / 10.0 ) * t;
        y = ( 1.0 - v / 10.0 ) * ( 1.0 - v / 10.0 ) * ( 1.0 - v / 10.0 ) * q
            + 3.0 * ( 1.0 - v / 10.0 ) * ( 1.0 - v / 10.0 ) * ( v / 10.0 ) * a
            + 3.0 * ( 1.0 - v / 10.0 ) * ( v / 10.0 ) * ( v / 10.0 ) * c
            + ( v / 10.0 ) * ( v / 10.0 ) * ( v / 10.0 ) * s;
        z = ( 1.0 - v / 10.0 ) * ( 1.0 - v / 10.0 ) * ( 1.0 - v / 10.0 ) * r
            + 3.0 * ( 1.0 - v / 10.0 ) * ( 1.0 - v / 10.0 ) * ( v / 10.0 ) * b
            + 3.0 * ( 1.0 - v / 10.0 ) * ( v / 10.0 ) * ( v / 10.0 ) * d
            + ( v / 10.0 ) * ( v / 10.0 ) * ( v / 10.0 ) * t;
        line( w, x, y, z, 3 );
    }
    /*  ベジェ曲線その1描画 */
    for( u = 0, v = 1; u < 10; u++, v++ ) {
        w = ( 1.0 - u / 10.0 ) * ( 1.0 - u / 10.0 ) * ( 1.0 - u / 10.0 ) * s
            + 3.0 * ( 1.0 - u / 10.0 ) * ( 1.0 - u / 10.0 ) * ( u / 10.0 ) * e
            + 3.0 * ( 1.0 - u / 10.0 ) * ( u / 10.0 ) * ( u / 10.0 ) * g
            + ( u / 10.0 ) * ( u / 10.0 ) * ( u / 10.0 ) * q;
        x = ( 1.0 - u / 10.0 ) * ( 1.0 - u / 10.0 ) * ( 1.0 - u / 10.0 ) * t
            + 3.0 * ( 1.0 - u / 10.0 ) * ( 1.0 - u / 10.0 ) * ( u / 10.0 ) * f
            + 3.0 * ( 1.0 - u / 10.0 ) * ( u / 10.0 ) * ( u / 10.0 ) * h
            + ( u / 10.0 ) * ( u / 10.0 ) * ( u / 10.0 ) * r;
        y = ( 1.0 - v / 10.0 ) * ( 1.0 - v / 10.0 ) * ( 1.0 - v / 10.0 ) * s
            + 3.0 * ( 1.0 - v / 10.0 ) * ( 1.0 - v / 10.0 ) * ( v / 10.0 ) * e
            + 3.0 * ( 1.0 - v / 10.0 ) * ( v / 10.0 ) * ( v / 10.0 ) * g
            + ( v / 10.0 ) * ( v / 10.0 ) * ( v / 10.0 ) * q;
        z = ( 1.0 - v / 10.0 ) * ( 1.0 - v / 10.0 ) * ( 1.0 - v / 10.0 ) * t
            + 3.0 * ( 1.0 - v / 10.0 ) * ( 1.0 - v / 10.0 ) * ( v / 10.0 ) * f
            + 3.0 * ( 1.0 - v / 10.0 ) * ( v / 10.0 ) * ( v / 10.0 ) * h
            + ( v / 10.0 ) * ( v / 10.0 ) * ( v / 10.0 ) * r;
        line( w, x, y, z, 3 );
    }


    /********************************************************/
    /*  制御点移動                                          */
    /********************************************************/

    /*  端まで来たら変位を符号反転  */
    if( a + i > 127 || a + i < 0 )  i = -i;
    if( b + j > 87 || b + j < 0 )   j = -j;
    if( c + k > 127 || c + k < 0 )  k = -k;
    if( d + l > 87 || d + l < 0 )   l = -l;
    if( e + m > 127 || e + m < 0 )  m = -m;
    if( f + n > 87 || f + n < 0 )   n = -n;
    if( g + o > 127 || g + o < 0 )  o = -o;
    if( h + p > 87 || h + p < 0 )   p = -p;

    /*  移動    */
    a += i;
    b += j;
    c += k;
    d += l;
    e += m;
    f += n;
    g += o;
    h += p;
}

ダウンロード

bezier.zip

改良案

ベジェ曲線の本数をもっと増やせば見栄えが随分と変わるでしょう。しかし、このプログラムでは変数a-zの全てを使い尽くしているので、まずは独自に変数を宣言する所から始めないといけません。

シンプルライブラリのデフォルト変数は全てint型であるため、このプログラムでは計算時に実数に一旦変換しています。double/floatで宣言した変数を使用すれば/10.0としている部分を全て取り払えるのでソースコードがすっきりと読みやすくなります。

制御点を独立した変数ではなく配列変数で宣言すればループ処理が可能となり、更にソースコードを整理整頓できるでしょう。