次は、GDI+版です。Direct2Dを触ってみたのですが、ウィンドウメッセージをまたいで扱うデータが存在することから、結局C++クラスを導入することにしました。

最初に作ったC++クラスを使わない版も併せて公開しておきます。

とりあえずC++クラス化しただけというわけで、雑な作りですが勘弁願います。

#include <windows.h>
#include <windowsx.h>
#include <gdiplus.h>
 
#include <math.h>
#ifdef _MSC_VER
#define hypot _hypot
#pragma comment(lib, "gdiplus.lib")
#endif
 
namespace gp = Gdiplus;
 
void DrawKochCurveGdiplus(gp::Graphics& g, gp::Pen* pen,
    double startX, double startY,
    double endX, double endY, int level)
{
    double dx = endX - startX;
    double dy = endY - startY;
    double trisectionLengthX = dx / 3.;
    double trisectionLengthY = dy / 3.;
    double triangleHeight = hypot(
        trisectionLengthX, trisectionLengthY) * 0.5 * sqrt(3.);
    //下向きがyの正の向きであることに注意
    double angle = atan2(-dy, dx);
    double vertexX = startX + dx * 0.5 - triangleHeight * sin(angle);
    double vertexY = startY + dy * 0.5 - triangleHeight * cos(angle);
    double mid1x = startX + trisectionLengthX;
    double mid1y = startY + trisectionLengthY;
    double mid2x = mid1x + trisectionLengthX;
    double mid2y = mid1y + trisectionLengthY;
    if (level == 0)
    {
        g.DrawLine(
            pen,
            static_cast<float>(startX),
            static_cast<float>(startY),
            static_cast<float>(mid1x),
            static_cast<float>(mid1y));
        g.DrawLine(
            pen,
            static_cast<float>(mid1x),
            static_cast<float>(mid1y),
            static_cast<float>(vertexX),
            static_cast<float>(vertexY));
        g.DrawLine(
            pen,
            static_cast<float>(vertexX),
            static_cast<float>(vertexY),
            static_cast<float>(mid2x),
            static_cast<float>(mid2y));
        g.DrawLine(
            pen,
            static_cast<float>(mid2x),
            static_cast<float>(mid2y),
            static_cast<float>(endX),
            static_cast<float>(endY));
    }
    else
    {
        level--;
        DrawKochCurveGdiplus(
            g, pen, startX, startY, mid1x, mid1y, level);
        DrawKochCurveGdiplus(
            g, pen, mid1x, mid1y, vertexX, vertexY, level);
        DrawKochCurveGdiplus(
            g, pen, vertexX, vertexY, mid2x, mid2y, level);
        DrawKochCurveGdiplus(
            g, pen, mid2x, mid2y, endX, endY, level);
    }
}
 
class MyWindow
{
public:
    static ATOM Register(HINSTANCE hinst)
    {
        WNDCLASS wc;
        wc.style = CS_HREDRAW | CS_VREDRAW;
        wc.lpfnWndProc = WndProc;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hInstance = hinst;
        wc.hIcon = static_cast<HICON>(LoadImage(
            0, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED));
        wc.hCursor = static_cast<HCURSOR>(LoadImage(
            0, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED));
        wc.hbrBackground = static_cast<HBRUSH>(
            GetStockObject(WHITE_BRUSH));
        wc.lpszMenuName = 0;
        wc.lpszClassName = TEXT("Test");
        wncClassAtom = RegisterClass(&wc);
        return wncClassAtom;
    }
 
    HWND Create(HINSTANCE hinst)
    {
        return CreateWindow(
            reinterpret_cast<LPCTSTR>(
                static_cast<UINT_PTR>(wncClassAtom)),
            TEXT("GDI+ Test"), WS_OVERLAPPEDWINDOW,
            CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
            0, 0, hinst, this);
    }
 
    HWND GetHWnd() const
    {
        return hwnd;
    }
private:
    void OnPaint(HWND hwnd)
    {
        PAINTSTRUCT ps;
        gp::Graphics g(BeginPaint(hwnd, &ps));
        g.SetSmoothingMode(gp::SmoothingModeHighQuality);
        gp::Pen pen(gp::Color(), 2.f);
        DrawKochCurveGdiplus(g, &pen, 10, 100, 300, 100, 3);
        EndPaint(hwnd, &ps);
    }
 
    void OnDestroy(HWND)
    {
        PostQuitMessage(0);
    }
 
    static bool OnNCCreate(HWND hwnd, CREATESTRUCT const* pcs)
    {
        SetWindowLongPtr(hwnd, GWLP_USERDATA,
            reinterpret_cast<LONG_PTR>(pcs->lpCreateParams));
        static_cast<MyWindow*>(pcs->lpCreateParams)->hwnd = hwnd;
        return true;
    }
 
    static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
        MyWindow* p = reinterpret_cast<MyWindow*>(
            GetWindowLongPtr(hwnd, GWLP_USERDATA));
        switch (msg)
        {
            HANDLE_MSG(hwnd, WM_NCCREATE, OnNCCreate);
            HANDLE_MSG(hwnd, WM_PAINT, p->OnPaint);
            HANDLE_MSG(hwnd, WM_DESTROY, p->OnDestroy);
        }
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }
 
    HWND hwnd;
 
    static ATOM wncClassAtom;
};
ATOM MyWindow::wncClassAtom;
 
int WINAPI WinMain(HINSTANCE hinst, HINSTANCE, PTSTR, int cmdShow)
{
    if (!MyWindow::Register(hinst))
        return 1;
 
    MyWindow wnd;
    if (!wnd.Create(hinst))
        return 1;
    gp::GdiplusStartupInput gsi;
    ULONG_PTR gdiplusToken;
    if (gp::GdiplusStartup(&gdiplusToken, &gsi, 0) != gp::Ok)
        return -1;
 
    ShowWindow(wnd.GetHWnd(), cmdShow);
    UpdateWindow(wnd.GetHWnd());
 
    MSG msg;
    for (;;)
    {
        int ret = GetMessage(&msg, 0, 0, 0);
        if (ret == -1)
            return -1;
        else if (ret == 0)
            break;
 
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    gp::GdiplusShutdown(gdiplusToken);
    return static_cast<int>(msg.wParam);
}

スポンサード リンク

この記事のカテゴリ