NO

Author Topic: Bezier Curve Demo  (Read 1442 times)

MichaelW

  • Guest
Bezier Curve Demo
« on: July 31, 2015, 10:39:26 AM »
Code: [Select]

// This is a simple interactive Bezier curve demo that allows the user to position the end and
// control points by dragging them with the mouse, while it updates the curve in real time.
//
// Tested only with Version 8.00.60 (Win64).

#include <windows.h>

#define TARGET_RADIUS   8
#define CLIENTWIDTH     400
#define CLIENTHEIGHT    300

// This function sizes the specified window so the client area is the specified width and height and
// optionally centers the window on the screen. Unlike AdjustWindowRect and AdjustWindowRectEx,
// this function can handle a window with the WS_OVERLAPPED style.

void SetClientSize( HWND hwnd, int pixelWidth, int pixelHeight, BOOL bCenter )
{
    int x, y, w, h;
    RECT rcc, rcw;

    GetClientRect( hwnd, &rcc );
    GetWindowRect( hwnd, &rcw );

    w = (rcw.right - rcw.left) - (rcc.right - pixelWidth) - 1;
    h = (rcw.bottom - rcw.top) - (rcc.bottom - pixelHeight) - 1;

    if( bCenter )
    {
        x = (GetSystemMetrics( SM_CXSCREEN ) / 2) - w / 2;
        y = (GetSystemMetrics( SM_CYSCREEN ) / 2) - h / 2;
    }
    else
    {
        x = rcw.left;
        y = rcw.top;
    } 

    MoveWindow( hwnd, x, y, w, h, TRUE );
}

void DrawTarget( HDC hdc, POINT pt, COLORREF clr )
{
    HBRUSH hbr, hbrDef;
    HPEN hpen, hpenDef;

    hbr = GetStockObject( NULL_BRUSH );
    hbrDef = SelectObject( hdc, hbr );
    hpen = CreatePen( PS_SOLID, 3, clr );
    hpenDef = SelectObject( hdc, hpen );
    Ellipse( hdc, pt.x-TARGET_RADIUS, pt.y-TARGET_RADIUS, pt.x+TARGET_RADIUS, pt.y+TARGET_RADIUS );
    SelectObject( hdc, hpenDef );
    SelectObject( hdc, hbrDef );
    DeleteObject( hpen );
}

int HitTestTarget( LPARAM cursorCoords, POINT pt, BOOL b2x )
{   
    int mx, my, px, py;

    mx = LOWORD(cursorCoords);
    my = HIWORD(cursorCoords);
    px = pt.x;
    py = pt.y;

    if( b2x )
        return (abs((mx-px)*(mx-px))+abs((my-py)*(my-py))<=4*(TARGET_RADIUS+1)*(TARGET_RADIUS+1));
    else
        return (abs((mx-px)*(mx-px))+abs((my-py)*(my-py))<=(TARGET_RADIUS+1)*(TARGET_RADIUS+1));
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    PAINTSTRUCT ps;
    POINT pt0, pt1;
    HDC hdc;
    HPEN hpenDef;
    RECT rc;
    BOOL tooClose;
    int i;

    static HDC hdcMem;
    static HBITMAP hbmp, hbmpDef;
    static HPEN hpen0, hpen1;

    // Arrays dimensioned with unused element 0 so iTarget can serve as a flag and an index.

    static POINT points[5];
    static COLORREF colors[5];
    static int iTarget;

    switch( uMsg )
    {
        case WM_CREATE:

            SetClientSize( hwnd, CLIENTWIDTH, CLIENTHEIGHT, TRUE );

            hdc = GetDC( hwnd );
            hdcMem = CreateCompatibleDC( hdc );
            GetClientRect( hwnd, &rc );
            hbmp = CreateCompatibleBitmap( hdc, rc.right, rc.bottom );
            ReleaseDC( hwnd, hdc );

            hbmpDef= SelectObject( hdcMem, hbmp );

            // For PolyBezier the line width is the pen width.
            // One pen to draw with and one to erase with:

            hpen0 = CreatePen( PS_SOLID, 2, RGB(255,255,255) );
            hpen1 = CreatePen( PS_SOLID, 2, RGB(0,0,0) );

            FillRect( hdcMem, &rc, GetStockObject( WHITE_BRUSH ));

            // End points are blue and red, control points are cyan and magenta.

            points[1].x = CLIENTWIDTH/5;
            points[1].y = CLIENTHEIGHT/2;
            colors[1] = RGB(0,0,255);
            points[2].x = 2*CLIENTWIDTH/5;
            points[2].y = CLIENTHEIGHT/2;
            colors[2] = RGB(0,255,255);
            points[3].x = 3*CLIENTWIDTH/5;
            points[3].y = CLIENTHEIGHT/2;
            colors[3] = RGB(255,0,255);
            points[4].x = 4*CLIENTWIDTH/5;
            points[4].y = CLIENTHEIGHT/2;
            colors[4] = RGB(255,0,0);

            DrawTarget( hdcMem, points[1], colors[1] );
            DrawTarget( hdcMem, points[2], colors[2] );
            DrawTarget( hdcMem, points[3], colors[3] );
            DrawTarget( hdcMem, points[4], colors[4] );

            hpenDef = SelectObject( hdcMem, hpen1 );
            PolyBezier( hdcMem, &points[1], 4 );
            SelectObject( hdcMem, hpenDef );

            break;

        case WM_LBUTTONDOWN:
         
            // Confine the targets to the client area.         

            GetClientRect( hwnd, &rc );
            pt0.x = rc.left+(TARGET_RADIUS+2);
            pt0.y = rc.top+(TARGET_RADIUS+2);
            pt1.x = rc.right-(TARGET_RADIUS+2);
            pt1.y = rc.bottom-(TARGET_RADIUS+2);
            ClientToScreen( hwnd, &pt0 );
            ClientToScreen( hwnd, &pt1 );
            SetRect( &rc, pt0.x, pt0.y, pt1.x, pt1.y );
            ClipCursor( &rc );

            iTarget = 0;

            for( i = 1; i<=4; i++ )
                if( HitTestTarget( lParam, points[i], FALSE ) ) iTarget = i;

            break;

        case WM_MOUSEMOVE:

            if( iTarget > 0 && (wParam & MK_LBUTTON) )
            {
                // This to avoid having targets overlap, which could cause one to overlay the other,
                // and since the mouse would then select them as a unit there would be no way to
                // separate them.

                tooClose = FALSE;

                for( i = 1; i <= 4; i++ )
                {
                    if( i != iTarget )
                    {
                        if( HitTestTarget( lParam, points[i], TRUE ) ) tooClose = TRUE;
                    }
                }

                if( tooClose == FALSE )
                {

                    hpenDef = SelectObject( hdcMem, hpen0 );
                    PolyBezier( hdcMem, &points[1], 4 );
                    SelectObject( hdcMem, hpenDef );

                    DrawTarget( hdcMem, points[iTarget], RGB(255,255,255) );

                    points[iTarget].x = LOWORD(lParam);
                    points[iTarget].y = HIWORD(lParam);

                    DrawTarget( hdcMem, points[1], colors[1] );
                    DrawTarget( hdcMem, points[2], colors[2] );
                    DrawTarget( hdcMem, points[3], colors[3] );
                    DrawTarget( hdcMem, points[4], colors[4] );

                    hpenDef = SelectObject( hdcMem, hpen1 );
                    PolyBezier( hdcMem, &points[1], 4 );
                    SelectObject( hdcMem, hpenDef );

                    InvalidateRect( hwnd, NULL, FALSE );
                }
            }

            break;

        case WM_LBUTTONUP:

            // Release the cursor.

            ClipCursor( NULL );

            break;

        case WM_PAINT: 
 
            BeginPaint(hwnd, &ps);
            BitBlt( ps.hdc, 0, 0, CLIENTWIDTH, CLIENTHEIGHT, hdcMem, 0, 0, SRCCOPY );
            EndPaint(hwnd, &ps);

            break;

        case WM_COMMAND:

            // This allows the user to close the window by pressing the Escape key.
            // This behavior depends on the message loop calling IsDialogMessage.

            if( wParam == IDCANCEL ) DestroyWindow(hwnd);

            break;

        case WM_CLOSE:

            DestroyWindow( hwnd );

        case WM_DESTROY:
 
            SelectObject( hdcMem, hbmpDef );
            DeleteObject( hbmp );
            DeleteObject( hpen0 );
            DeleteObject( hpen1 );
            DeleteDC( hdcMem );
            PostQuitMessage( 0 );

            break;
 
        default:

            return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }

    return (LRESULT) NULL;
}

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
    int w,h,x,y;   
    HWND hwnd;
    WNDCLASSEX wcx; 
    MSG msg;

    wcx.cbSize = sizeof(wcx);
    wcx.style = CS_HREDRAW | CS_VREDRAW;
    wcx.lpfnWndProc = WindowProc;
    wcx.cbClsExtra = 0;
    wcx.cbWndExtra = 0;
    wcx.hInstance = hInstance;
    wcx.hIcon = LoadIcon(NULL,IDI_APPLICATION);
    wcx.hCursor = LoadCursor(NULL,IDC_ARROW);
    wcx.hbrBackground = GetStockObject(WHITE_BRUSH);
    wcx.lpszMenuName =  0;
    wcx.lpszClassName = "BezierDemoClass";
    wcx.hIconSm = LoadImage( hInstance,MAKEINTRESOURCE(5), IMAGE_ICON,GetSystemMetrics(SM_CXSMICON),
                            GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR ); 
    RegisterClassEx(&wcx);

    w = 800;
    h = 600;
    x = (GetSystemMetrics( SM_CXSCREEN ) - w)/2;
    y = (GetSystemMetrics( SM_CYSCREEN ) - h)/2;
   
    hwnd = CreateWindowEx( 0, "BezierDemoClass", "Bezier Demo", WS_OVERLAPPED | WS_SYSMENU,
                           x, y, w, h, NULL, NULL, NULL, NULL );

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd); 

    while( GetMessage( &msg, hwnd, 0, 0 ) > 0 )
    {
        if( IsDialogMessage( hwnd, &msg ) == 0 )
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    exit(0);
}


Edit: The abs() calls are not necessary.
« Last Edit: September 15, 2015, 03:52:36 PM by MichaelW »

Offline Bitbeisser

  • Global Moderator
  • Member
  • *****
  • Posts: 754
Re: Bezier Curve Demo
« Reply #1 on: January 04, 2016, 05:55:31 AM »
Nice one...  ;)

Ralf