@King_Of_Demons
Junior C# Developer

Как вращать кривую Безье в функций на WinAPI?

Задача состоит в том что надо через WinApi нарисовать в окне кривую Безье, и что бы она крутилась автоматически. Кривую рисую а когда доходит дело до вращения не получается. Через таймер делал изменение координат, и всеравно не получается. Помогите кто знает как правильно реальзовать вращение.
#include <windows.h>
#include <tchar.h>
#include <math.h>

static TCHAR szWindowClass[] = _T("DesktopApp");
static TCHAR szTitle[] = _T("PPE");
HINSTANCE hInst;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int CALLBACK WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR  lpCmdLine, _In_ int  nCmdShow)
{
    WNDCLASSEX wcex;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(NULL));
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName = NULL;
    wcex.lpszClassName = szWindowClass;
    wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(NULL));

    if (!RegisterClassEx(&wcex))
    {
        MessageBox(NULL,
            _T("Call to RegisterClassEx failed!"),
            _T("Windows Desktop Guided Tour"),
            NULL);
        return 1;
    }
    hInst = hInstance;
    HWND hWnd = CreateWindow(
        szWindowClass,
        szTitle,
        WS_OVERLAPPEDWINDOW, 500, 500, 500, 500,
        //CW_USEDEFAULT, CW_USEDEFAULT,
        //CW_USEDEFAULT, CW_USEDEFAULT,
        NULL,
        NULL,
        hInstance,
        NULL
    );
    if (!hWnd)
    {
        MessageBox(NULL,
            _T("Call to CreateWindow failed!"),
            _T("Windows Desktop Guided Tour"),
            NULL);
        return 1;
    }
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }


    return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    PAINTSTRUCT ps;
    HDC hdc;
    TCHAR greeting[] = _T("Кривая безье, через математические функций");


    switch (message)
    {

    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);

        SetTextColor(hdc, RGB(50, 70, 70));
        SetBkMode(hdc, TRANSPARENT);
        TextOut(hdc, 10, 10, greeting, _tcslen(greeting));

        {//отрисовываем кривую Безье
            int x[4] = { 20, 60, 160, 220 };
            int y[4] = { 110, 20, 190, 110 };
            double xu, yu;
            SetTimer(hWnd, 1, 1000, nullptr);
                for (double u = 0.0; u <= 1.0; u += 0.0001)
                {
                    xu = (pow(1 - u, 3) * x[0] + 3 * u * pow(1 - u, 2) * x[1] + 3 * pow(u, 2) * (1 - u) * x[2] + pow(u, 3) * x[3]);
                    yu = pow(1 - u, 3) * y[0] + 3 * u * pow(1 - u, 2) * y[1] + 3 * pow(u, 2) * (1 - u) * y[2] + pow(u, 3) * y[3];
                    SetPixel(hdc, (int)xu, (int)yu, RGB(125, 120, 235));
                }
        }
        EndPaint(hWnd, &ps);
        break;
    	
    case WM_TIMER:
        break;
    	
    case WM_DESTROY:
        KillTimer(hWnd, 1);
        PostQuitMessage(0);
        break;

    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
        break;
    }
    return 0;
}
  • Вопрос задан
  • 185 просмотров
Пригласить эксперта
Ответы на вопрос 2
Nipheris
@Nipheris Куратор тега C++
1. Рисовать что-либо нужно в WM_PAINT. Но вы НЕ должны вызывать повторную инвалидацию в WM_PAINT, да и работать с таймером нежелательно.
2. По срабатыванию таймера (т.е. или в WM_TIMER, или в колбэке, как вам удобно) нужно делать инвалидацию окна с помощью InvalidateRect или UpdateWindow. Вы НЕ должны рисовать в обработчике таймера.

Почитайте ещё про виндовый механизм инвалидация-рисование, это критически важно для понимания происходящего. Вы не должны просить перерисовать что-либо напрямую. Вместо этого вы заявляете Винде, что вам больше "не нравится" какая-то область в окне (или всё окно целиком) и вы считаете эту область устаревшей и требующей обновления. Винда принимает это к сведению, и затем, через какое-то время, она пришлёт вам WM_PAINT, где вы всё нарисуете как считаете нужным.

Это удобно тем, что например если окно скрыто, Винда не посылает вам WM_PAINT и вы ничего не рисуете вообще, экономя ресурсы машины.

Если вы хотите добиться анимации, вам нужно регулярно (раз в N мс) заявлять Винде, что у вас устарело содержимое окна.
Ответ написан
Комментировать
wataru
@wataru Куратор тега C++
Разработчик на С++, экс-олимпиадник.
Для поворота достаточно перед SetPixel повернуть xu и yu на угол, пропорциональный прошедшему времени. Ну или количеству тиков, но лучше по таймеру ориентироваться.

Чтобы окно перерисовывалось периодически само надо делать как тут: запустить таймер на несколько миллисекунд и по его срабатыванию перерисовывать окно. Можно или как в этом примере стирать и рисовать заново, а можно вызывать WM_PAINT через какой-нибудь RedrawWindow:
RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы