ベジェ曲線


  1. ベジェ曲線とは
     ベジェ曲線は、与えられた折れ線の内部の滑らかな曲線となります。曲線は与えられた点の座標とパラメータtの多項式で決定されます。ベジェは最初にこの曲線を考案したフランス人(Bezier)の名前です。

  2. ベジェ曲線の式

    1. 直線のパラメトリック表現
      2点を接続する直線l(t)は、
       L(t) = P*t + Q*(1-t) t=0.0..1.0
      で表現できます。P,Qは座標(x,y)を示すベクタ(値の組)で、tは0から1までの少数です。
       L(t)はt=0のときQ、t=1のときP、t=1/2のとき中点 (P+Q)/2 となります。この表現をパラメトリックあるいは媒介変数による定義と呼びます。tの一時式ですから、直線になります。
       実際にはPは座標ですから、P*tを、P=(Px,Py)として 
        Lx(t) = Px*t + Qx*(1-t) ;Ly(t) = Py*t + Qy*(1-t)
      と座標毎に計算します。
       このパラメトリック表現を利用すると、tの多項式で曲線を表現出来ます。

    2. 3点ベジェ

      1. 3点ベジェ
        A,B点と一点Cを制御点とするtの2次パラメトリック曲線を考えます。

      2. 3点ベジェの式
        媒介変数tの2次式で表現され、3点A,B,Cから以下のように生成します。
        t=0でA点、t=1でC点となります。A,B,Cは実際には、(x,y,z)の各座標値のベクトルとなります。

         r(t)=A・(1-t)2 + 2・B・t(1-t) + C・t2

        tの2次式ですから、ピークを一つ持つ単純な2次曲線になります。

      3. 3点ベジェと円
        (0,1),(1,1),(1,0)から生成されるベジェ曲線は正確な円にはなりません。ただし、簡単なアフィン変換で円とすることができます。

      4. 演習
        A=(1,0),B=(1,1),C=(0,1)として、t=0,t=0.25、t=0.5,t=0.75 t=1.0の場合のr(t)の値を求め、単位円の場合と比較しなさい。

    3. 4点ベジェ曲線

      1. 4点ベジェ曲線
        4点で制御されるベジェ曲線は、点Aから始まり、B,Cに制御され、D点で終端する曲線です。4点A,B,C,Dを制御点とするベジェ曲線は、次の式で生成されます。A,B,C,Dは位置ベクトル、tは[0..1]のスカラー(ベクトルでない)で、tを媒介変数とする3次式となっています。
         r(t)=A・(1-t)3 + 3・B・t・(1-t)2 + 3・C・t2・(1-t) + D・t3
        この式は t=0 のとき点A、t=1のとき点Bとなります。tが0から1に変化するとともに、r(t)の値は A>B>C>D の順に移っていきます。

      2. 4点ベジェ曲線の例
        1. 外側の多角形が制御点 A,B,C,D で、内部がベジェ曲線です。


        2. B,C点がA-Bの逆側の場合


      3. ベジェ曲線の点を計算するプログラム

        1. 概要
          ベジェ曲線を表示するjava言語プログラム例を示します。
           genBezier(double pcont[][2],double point[][2],int num) 
          は、4点の座標をpCont[][]とし、そのベジェ曲線上の座標を計算してpoint[][]に出力します。座標の数を関数の値とします。

        2. ベジェ曲線を計算するプログラム
            int genBezier(double pCont[][],double point[][])
            {
              double t;int tc;
              tc=0;
              for (t=0.0;t<=1.001;t=t+0.05){
                point[tc][0]=pCont[0][0]*(1-t)*(1-t)*(1-t) + 3*pCont[1][0]*t*(1-t)*(1-t) +
                     3*pCont[2][0]*t*t*(1-t) + pCont[3][0]*t*t*t;
                point[tc][1]=pCont[0][1]*(1-t)*(1-t)*(1-t) + 3*pCont[1][1]*t*(1-t)*(1-t) +
                             3*pCont[2][1]*t*t*(1-t) + pCont[3][1]*t*t*t;
                tc++;
              }
              return(tc);
            }

    4. n点のベジェ

      1. 一般式
        一般に、n個の制御点 Pi から構成されるベジェ曲線は次式となります。ここで、(n i)は組み合わせ数で、( a + b)nを展開したときの係数になります。n=2 の場合3点ベジェ、n=3 の場合は4点ベジェになります。
        1. n次のベジェ式


          ここで、はn個からi個を取り出す場合の数を示します。

      2. カステルジュの公式
        一般に次の公式が証明できます。これは、n-1次のベジェから、n次のベジェ曲線の値が計算できることを示し、幾何学的解釈の理論的根拠になります。
        1. ド・カステルジュの公式



      3. 演習
        n=4の場合の、ベジェ曲線の展開形(Σや(n i)を使用しない)を求めなさい。ただし、(4 0)=1,(4 1)=4,(4 2)=6,(4 3)=4 (4 4)=1 を利用する。

    5. ベジェ曲線の性質

      1. 閉包性
        ベジェ曲線は、4点が凸多角形の場合、その中に入ります。

      2. 両端で接する
        直線 A-B、C-D は曲線の開始点と終端点で曲線の接線になります。

      3. 幾何学(作図)的解釈
        A-B,B-C,C-Dの中点をP,Q,Rとする。P-Q,Q-Rの中点をS,T、S-Tの中点をWとすると、Wはt=1/2のベジェ曲線の点となります。
        一般に、各線分をm:nの比で分割した点wはベジェ曲線の点となります。

        1. ベジェ曲線と制御点


    6. ベジェ曲線の接続

      1. 接続
        点の数が多い場合、n点のベジェ曲線ではなく、低次(3)のベジェ曲線を接続して使用します。

      2. 接続条件
        接続する場合、二つの曲線の端点を一致させるだけでなく、両側の制御点を直線上に配置します。これにより、接続点での傾きを一致させることができます。

        1. 二つの4点ベジェの接続


      3. ユーザインタフェース
        4点ベジェの接続を利用してベジェ曲線を編集する方式があります。A,Dは曲線を通過する点とし、B,CはA-B,C-Dの方向と接する強さを定める点として、端点をクリックしたときの現れる矢印として利用します。

        1. 曲線の編集


      4. 演習
        ベジェ曲線を利用してハート形を作画したい。どのように作図したら良いか説明しなさい。

  3. プロジェクト(Java)

    1. プロジェクトの概要
      4点のベジェ曲線を表示するJbuilderで作成したアプレット(Java)プログラムを紹介します。
      4点の近くでマウスでドラッグすると、制御点が移動し、曲線が滑らかに変形します。中点ボタンをクリックすると、制御点の中点を結ぶ線分が表示され、t=1/2の位置の曲線の位置を計算する過程が明示されます。多項式だけで、こんなに自在な曲線が出来るのは、ちょっと驚きですね。



    2. プログラム

      1. cBezierクラス
        cBezierクラスで、ベジェ曲線の計算、表示、等を行います。新しいクラスを作成するには、Jbuilderのファイルメニューから、「新規クラス」を選択します。新規クラスをプロジェクトメニューで「メイク」すると、定義したクラスが利用可能になります。

        1. int genBezier(double pCont[][],double point[][])
          先に説明した関数で、pCont[][]の制御点の座標から、point[][]にベジェ曲線上の点を表示します。tの増分を変更すれば、補間点の数を調整できます。pCont[i][0] はi番目の制御点のx座標になります。

        2. drawPoly(Graphics g, double buffer[][],int num,int type,boolean pt)
          buffer[][]配列の点を折れ線で接続します。numは点の数、typeで線の色を変更します。ptがtrueの場合、点に○のマークを付けます。点のx座標が負の場合、その点で折れ線が分離することを示します。

        3. BezierCont(double pCont[][],double point[][])
          中点を接続する補助線を描画します。

      2. Applet1クラス
        アプレットの本体クラスです。マウスを利用するため、MouseListener, MouseMotionListenerをimplementsします。

        1. init()
          下記のマウスリスナを追加します。
          addMouseListener(this);
          addMouseMotionListener(this);

        2. paint(Graphics g)
          制御多角形、ベジェ曲線を表示します。チェックボックスがチェックされている場合、補助線も表示します。
        3. double dist(double p1[], double p2[])
          2点間の距離を返します。

        4. public void mousePressed(MouseEvent e)
          マウスが押されたとき、近くに制御点があるか判断し、あれば、その番号をmatchに設定します。
          public void mousePressed(MouseEvent e){
            //マウスのボタンが押された
            match=-1;
            double mpoint[]=new double[2];
            //近い点を探す
            mpoint[0]=e.getX();mpoint[1]=e.getY();
            for(int i=0;i<4;i++){
              if (dist(mpoint,pCont[i])<10.0){
                match=i;//見つかった
                //System.out.println("match:"+match);
                break;
              }
            }
          }

        5. public void mouseDragged(MouseEvent e)
          マウスがドラッグされた場合の処理です。制御点が選択されている場合、点をマウス座標で置き換えます。
          public void mouseDragged(MouseEvent e){
            //マウスのドラッグがあった
            if (match >= 0) {
              pCont[match][0]=e.getX();
              pCont[match][1]=e.getY();
            }
            repaint();//再表示
          }

    3. ダウンロード  
      このプロジェクト(Jbuilder)をダウンロードできます。次の行をクリックして、Bezier.exeファイルを適当なフォルダに保存します。
        ダウンロード開始
       このファイルは自己解凍型の圧縮ファイルです。このファイルを実行すると指定したフォルダに必要なファイルが生成されます。

  4. プロジェクト(VC)

    1. プロジェクトの機能

      1. 表示
        実行すると、4点単位のベジェ曲線を表示するvisualCのプログラムを紹介します。

      2. 変形
        制御点の近くでクリックし、マウスをドラッグすると折れ線が移動し、曲線が変形します。

      3. 中点
        中点ボタンを押すと、中点を結んだ曲線が表示されます。再度押すと表示が消えます。

    2. プロジェクトの作成

      1. 作成
        プロジェクト名はdlCureveとします。中点ボタンを追加します。

      2. 変数
        double pCont[7][2]={{10.0,200.0},{80.0,20.0},{120.0,50.0},{150.0,200.0},
        {180.0,260.0},{250.0,280.0},{300.0,200.0}}; 最初の制御点の位置を指定します。
        NumOfPoint=7  ;制御点の数を指定します。

      3. 構成

        1. OnLButtonDown(UINT nFlags, CPoint point)
          マウスボタンがクリックされると呼び出されます。近くに制御点があれば、その点の番号をmatchに設定し、ない場合は負の値を設定します。
          1. void CDlCurveDlg::OnLButtonDown(UINT nFlags, CPoint point)
            {
            // TODO: この位置にメッセージ ハンドラ用のコードを追加するかまたはデフォルトの処理を呼び出してください
            match=-1;
            double mpoint[2],di;
            mpoint[0]=point.x;mpoint[1]=point.y;
            for(int i=0;i<numofpoint;i++){
            if (di=dist(mpoint,pCont[i])<10){
            match=i;break;
            }
            }
            CDialog::OnLButtonDown(nFlags, point);
            }
          2. double CDlCurveDlg::dist(double p1[], double p2[])
            {
            //2点間の距離を返す
            return sqrt((p1[0]-p2[0])*(p1[0]-p2[0])+(p1[1]-p2[1])*(p1[1]-p2[1]));
            }
        2. OnMouseMove(UINT nFlags, CPoint mpoint)
          マウスをドラッグすると呼び出されます。
          matchが正に場合、その点をマウスの座標に変更します。
          Invalidate();で再描画を行います。

          1. void CDlCurveDlg::OnMouseMove(UINT nFlags, CPoint mpoint)
            {
            // TODO: この位置にメッセージ ハンドラ用のコードを追加するかまたはデフォルトの処理を呼び出してください
            if (match >= 0) {
            pCont[match][0]=mpoint.x;
            pCont[match][1]=mpoint.y;
            Invalidate(TRUE);
            }
            CDialog::OnMouseMove(nFlags, mpoint);
            }

        3. OnLButtonUp(UINT nFlags, CPoint point)
          マウスボタンを離すと呼び出されます。matchを負に戻します。
          1. void CDlCurveDlg::OnLButtonUp(UINT nFlags, CPoint point)
            {
            // TODO: この位置にメッセージ ハンドラ用のコードを追加するかまたはデフォルトの処理を呼び出してください
            match = -1;
            CDialog::OnLButtonUp(nFlags, point);
            }

        4. OnPaint()
          これは、Invalidate()、または、システムのメッセージにより随時呼び出されます。drawPoly(pCont,NumOfPoint,0);で制御線を表示します。tc=frc.BezierCont(pCont,point);で曲線を表示します。CFreeCurve frc; は曲線に関するクラスです。
          midPointが1の場合、中間点の作業線を表示します。

          1. void CDlCurveDlg::OnPaint()
            {
            int tc;
            CFreeCurve frc;
            CPaintDC pDC(this);
            // TODO: この場所にネイティブ データ用の描画コードを追加します。
            drawPoly(pCont,NumOfPoint,0);
            tc=frc.BezierCont(pCont,point);
            if(midPoint )drawPoly(point,5,2);
            tc=frc.Bezier(pCont,point,NumOfPoint);
            drawPoly(point,tc,1);

            CDialog::OnPaint();
            }

          2. drawPoly( double buffer[][2],int num,int type)
            buffer[]配列の点を、num個接続した折れ線を表示します。途中でx座標が負の場合、その絶対値の値まで移動します(線は表示しない)。

        5. OnmidPoint()
          中点ボタンが押されると、midPointの値を反転します。

      4. CFreeCurveクラス
        1. クラスの作成
          ベジェ曲線を作成する関数はCFreeCurveクラスに作成しています。クラスを作成するには、ワークスペースでdlCureveクラスを右クリックし、クラスの新規作成を選択します。基本クラスはCWndとします。

        2. int CFreeCurve::Bezier(double qcont[][2],double point[][2],int num)
          qcont[][2]の制御点を4点ずつ切り出し、0.05刻みでベジェ曲線の点を計算して、point[][]に返します。作成した点の数を戻り値とします。

          1. int CFreeCurve::Bezier(double qcont[][2],double point[][2],int num)
            {
            double t;int tc;
            tc=0;
            double pCont[4][2];
            int count=(num-1)/3;
            for(int k=0;k < count;k++){
             for(int i=0;i<4;i++){
              pCont[i][0]=qcont[3*k+i][0];
              pCont[i][1]=qcont[3*k+i][1];
             }
            for (t=0.0;t<=1.001;t=t+0.05){
             point[tc][0]=pCont[0][0]*(1-t)*(1-t)*(1-t) + 3*pCont[1][0]*t*(1-t)*(1-t) +
             3*pCont[2][0]*t*t*(1-t) + pCont[3][0]*t*t*t;
             point[tc][1]=pCont[0][1]*(1-t)*(1-t)*(1-t) + 3*pCont[1][1]*t*(1-t)*(1-t) +
             3*pCont[2][1]*t*t*(1-t) + pCont[3][1]*t*t*t;
             tc++;
            }
            }
            return(tc);
            }

        3. BezierCont(double pCont[][2],double point[][2])
          ベジェ曲線の性質を確認するための、中点間の線を表示します。

    3. ダウンロード
      このプロジェクトはダウンロードできる。次の行をクリックして、*.exeファイルを適当なフォルダに保存する。
      ダウンロード開始

      このファイルは自己解凍型の圧縮ファイルである。このファイルを実行すると同じフォルダにプロジェクトのファイルを展開します。.dsw ファイルをダブルクリックすると、visualCが立ち上がり、ビルドメニューで実行を選択すれば、プログラムを作成し実行できます。

  5. 課題

    1. 3点ベジェ
      3点のベジェ曲線を表示するプログラムを作成しよう。

    2. 制御点の表示
      制御点の周りに、クリックで選択できる範囲の円を描こう
       参考 :BOOL Ellipse( int x1, int y1, int x2, int y2 );

    3. 曲線の数学モデル
      ベジェ以外の数学モデルを調べよう。

    4. 接続
      4点ベジェを接続するとき、連続する3点を直線上にのせると、二つの曲線を滑らかに接続することができます。頂点Aを移動したとき、頂点Bが直線上にのるようプログラムを変更しよう。



    5. 回転体への応用
      回転体の輪郭線をベジェ曲線で表現しよう


トップに戻る