概要

ウィンドウに3次ベジェ曲線を描画するサンプルです。
3次ベジェ曲線は、2点の始終点と2点の制御点より曲線を表現する方法で以下の式で表すことができます。
x(t)=(1-t)^3 \times x0+ 3(1-t)^2 t \times x1+3(1-t) t^2 \times x2+t^3 \times x3
y(t)=(1-t)^3 \times y0+ 3(1-t)^2 t \times y1+3(1-t) t^2 \times y2+t^3 \times y3
0 \leqq t \leqq 1
本プログラムは、以下の4点の諸元の3次ベジェ曲線を描画しています。
n xn yn
始点 0 10 10
制御点 1 50 70
制御点 2 100 20
終点 3 150 10
下図が本プログラムの実行結果です。赤丸が曲線を決める4点、青の曲線がPolyBezier APIで描画された3次ベジェ曲線です。

下図は、上記の式に上表の4点の座標を当てはめた以下の式のtを0から0.1ずつ増やして1になるまで加算しながら算出された点を青円で描画した結果です。
3次ベジェ曲線と一致しているのが分かるかと思います。
x(t)=(1-t)^3 \times 10+ 3(1-t)^2 t \times 50+3(1-t) t^2 \times 100+t^3 \times 150
y(t)=(1-t)^3 \times 10+ 3(1-t)^2 t \times 70+3(1-t) t^2 \times 20+t^3 \times 10

下表はtを変化させた場合の座標値の結果です。すなわち青い円の中心座標です。
t xn yn

テスト環境

コンパイラ

Visual C++ 2008 Standard 32/64bit
Visual C++ 2013 Express 32/64bit

実行環境

Windows 10 Home 32/64bit
Windows 7 EnterPrise Service Pack 1 64bit

プログラムソースの概要

_tWinMain関数

Windowsから最初に_tWinMain関数が呼び出されます。
ウィンドウを作成する場合は、RegisterClass APIによりウィンドウクラスを定義してからCreateWindow APIを呼び出しウィンドウを作成します。
Windowsは入力等のイベントが発生するとアプリケーションにメッセージを送付します。
メッセージはキューに保管されます。アプリケーションはメッセージを取り出し、該当ウィンドウにメッセージを配信します。
メッセージの取り出しから配信までループで処理を行いウィンドウから終了メッセージが届くと、ループを抜けるように記述します。 これらの一連の処理は、通常にCreateWindow APIの後に記述しこれをメッセージループと呼んでいます。
詳細は以下を参照してください。
メッセージループ

WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)ウィンドウプロシージャー

RegisterClass APIにより登録することによりWindowsから呼び出されます。
第2引数にメッセージの種類が格納されていますので、switchステートメントによりメッセージごとの処理を振り分けます。
自分で処理しないメッセージはDefWindowProc APIに渡せばWindowsが標準的な処理を行ってくれます。

case WM_DESTROY:

ウィンドウが閉じるときに呼び出されます。
PostQuitMessage APIにより終了コードを指定して、ウィンドウプロシージャーを終了させます。

case WM_PAINT:

ウィンドウを再描画する必要があるときに呼び出されます。
他のウィンドウに隠れ再びフォアグラウンドになった場合などウィンドウの再描画はWindowsが面倒を見ないのでプログラマの仕事となっています。
BeginPaint APIを呼び出して、描画に必要なデバイスコンテキストのハンドルを取得します。
BeginPaint APIの第2引数のポインタにはPAINTSTRUCT構造体のポインタを渡します。
BeginPaint API終了後、PAINTSTRUCT構造体には再描画が必要な領域の座標等の値が格納されています。
線を使用して描画するには、ペンを作成します。
ペンの作成方法の詳細は以下を参照してください。
GDIのペンの作成方法
ペンを作成したらそのペンで描画させるためにSelectObject APIによりペンを選択します。戻り値は、前回のペンのハンドルですので保存しておき、デバイスコンテキストの解放前にペンの設定をもとに戻すときに使用します。
PolyBezier APIにより4つの点から3次ベジェ曲線を描画します。
Ellipse APIにより指定している座標を示す円を描画します
SelectObject APIでペンを元に戻します。
再描画が終了したことをWindowsに知らせるためにEndPaint APIを使用してます。このAPIを呼び出さないと何度もWM_PAINTメッセージが発生し暴走します。

プログラムソース

bezier.cpp

//       Bezier曲線の描画
//      Visual C++ 2008/2013    32/64bit

#include <windows.h>
#include <tchar.h>

//      ウィンドウプロシージャー
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);


TCHAR szClassNme[] = TEXT("Bezier");


int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPreInst,
                   TCHAR* lpszCmdLine, int nCmdShow){
    HWND hWnd;
    MSG lpMsg;
    WNDCLASS myProg;

    if (!hPreInst) {
        myProg.style            =CS_HREDRAW | CS_VREDRAW;
        myProg.lpfnWndProc        =WndProc;
        myProg.cbClsExtra   =0;
        myProg.cbWndExtra        =0;
        myProg.hInstance        =hInstance;
        myProg.hIcon            =NULL;
        myProg.hCursor            =LoadCursor(NULL, IDC_ARROW);
        myProg.hbrBackground    =(HBRUSH)GetStockObject(WHITE_BRUSH);
        myProg.lpszMenuName        =NULL;
        myProg.lpszClassName    =szClassNme;
        if (!RegisterClass(&myProg))
            return FALSE;
    }
    hWnd = CreateWindow(szClassNme,
        TEXT("Bezier"),
        WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT,
        CW_USEDEFAULT,
        220,
        240,
        NULL,
        NULL,
        hInstance,
        NULL);
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    while (GetMessage(&lpMsg, NULL, 0, 0)) {
        TranslateMessage(&lpMsg);
        DispatchMessage(&lpMsg);
    }
    return int(lpMsg.wParam);
}


//      ウィンドウプロシージャー

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){
    HDC hdc;
    PAINTSTRUCT ps;
        HPEN hPen, hOldPen;
        static POINT p1[]={ {10,10},{50,70},{100,20},{150,10}} ;

    switch (msg) {

        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        case WM_PAINT:
            hdc = BeginPaint(hWnd, &ps);

                        hPen = CreatePen(PS_SOLID, 1, RGB(0, 0, 255));
                        hOldPen = (HPEN)SelectObject(hdc, hPen);

                        PolyBezier(hdc,p1,4);

                        DeleteObject(hPen);

                        hPen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));

                        SelectObject(hdc, hPen);
                        for(int n=0;n<4;n++){
                                Ellipse(hdc,p1[n].x-4,p1[n].y-4,p1[n].x+4,p1[n].y+4);

                        }

                        SelectObject(hdc, hOldPen);
                        DeleteObject(hPen);

                        EndPaint(hWnd, &ps);
                        break;
        default:
            return(DefWindowProc(hWnd, msg, wParam, lParam));
    }
    return (0L);
}

ソースファイルと実行ファイルのダウンロード

ダウンロード bezier.zip(37.2kByte)

ZIPファイルに含まれるファイル
bezier.cpp
bezier.exe