лаба по компьютерной графике
Меня интересует не математическая часть, а именно как это реализовать простейшим способом на голом win32 api. Это дело принципиальное, я хочу разобраться именно в win api. Кто знает как это все дело сделать просьба подсказать. у самого кое какие мысли есть, но пока не уверен в правильности подхода к самой задачи. Другими словами, на примере этого задания хочу понять как написать простейшую (не "hello world") программу под винду.
Спасибо за внимание -)
создаешь окошко, получаешь контекст рисования(или графического устройства, уже и не помню точно как оно называется), далее смотришь функции для рисования, рисуешь, пишешь обработчик для события WM_PAINT(кажись так оно называется, сорри давно это было) в котором перерисовываешь свое изображение, открываешь шампанское - лаба написана!
ЗЫ: ах да, еще же перемещение и масштабирование, тогда придется еще реализовать обработчик события для нажатия клавиши, в нем проверять чего там было нажато и производить нужное действие. а вот теперь точно можно открывать шампанское и праздновать.
Когда начинаю рисовать в окне (WM_PAINT) чтобы было все относительно центра - делаю так :
GetClientRect(hWnd, &rect); //
center.x=(rect.bottom-rect.top)/2;
center.y=(rect.right-rect.left)/2;
rect и center - статические переменные объявленные в оконной функции.
При это у меня происходит смещение вниз и влево.
То есть если сделать вот так
то расстояние от левого и верхнего края будет больше чем расстояние от нижнего и правого края клиентской области (ну то есть белой) , а вот если рисовать вот так
то эллипс полностью "прижмется" ко всем границам клиентской части и при этом будет окружностью =) В чём может быть проблема ? не правильно высчитываю центр ?
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
HWND hMainWnd;
MSG msg;
char szClassName[] = "MyClass";
WNDCLASSEX wc;
// Заполняем структуру класса окна
wc.cbSize = sizeof(wc); // размер структуры в байтах
wc.style = CS_HREDRAW | CS_VREDRAW; // стиль класса окна
wc.lpfnWndProc = WndProc; // указатель на оконную процедуру
wc.cbClsExtra = 0; // ноль
wc.cbWndExtra = 0; // тоже ноль
wc.hInstance = hInstance; // дескриптор приложения
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); // дескриптор пиктограммы
wc.hCursor = LoadCursor(NULL, IDC_ARROW); // дескриптор курсора
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); // дескриптор кисти, для закраски фона окна
wc.lpszMenuName = NULL; // указатель на строку, содержащую имя меню
wc.lpszClassName = szClassName; // указатель на строку, содержащую имя класса окна
wc.hIconSm = NULL; // маленькая пиктограмма (дескриптор)
// Регистрируем класс окна
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, "Cannot register class", "Error", MB_OK);
return 0;
}
// Создаем основное окно приложения
hMainWnd = CreateWindow(
szClassName, // имя зарегестрированного класса
"First win32 program", // имя окна
WS_SYSMENU, // стиль окна
CW_USEDEFAULT, // горизонтальная позиция
CW_USEDEFAULT, // вертикальная позиция
640, // ширина окна
640, // высота окна
NULL, // дескриптор родительского окна
NULL, // дескриптор меню или идентификатор жлемента управления
hInstance, // дескриптор экземпляра приложения
NULL
);
if (!hMainWnd)
{
MessageBox(NULL, "Cannot create main window", "Error", MB_OK);
return 0;
}
// Показываем наше окно
ShowWindow(hMainWnd, nCmdShow);
// Выполняем цикл обработки сообщений до закрытия приложения
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HDC hDC; // Контекст дисплея
PAINTSTRUCT ps;
static RECT rect,rect2;
static POINT pt[4]; // Массив точек для рисования кривой Безье
static HPEN hPenDef,hPenRed4,hPenBlack2,hDASHPen,hDCPen; // Перья
static HBRUSH hBrush; // Кисть
static POINT center; // Центр клиентской области
static int i(0); // Счетчик точек для заполнения массива
switch (msg)
{
case WM_CREATE:
hDCPen = (HPEN)GetStockObject(DC_PEN); // Стандартное DC перо
hDASHPen = CreatePen(PS_DASH, 1, RGB(0,0,0)); // Стандартное пунктирное перо
hPenRed4 = CreatePen(PS_SOLID, 4, RGB(255,0,0)); // Красное толстое толщиной 4 пиксела
hPenBlack2 = CreatePen(PS_SOLID, 2, RGB(0,0,0)); // Чёрное толстое толщиной 2 пиксела
hBrush = (HBRUSH)GetStockObject(DC_BRUSH); // Стандартная DC кисть
GetClientRect(hWnd, &rect); // Клиентская область
center.x=(rect.bottom-rect.top)/2;
center.y=(rect.right-rect.left)/2;
return 0;
case WM_PAINT:
hDC = BeginPaint(hWnd, &ps); // Захватываем контекст дисплея
{
char *digits[]={"0","1","2","3","4","5","6","7","8","9","10"};
SelectObject(hDC, (HGDIOBJ)hBrush); // Выбираем заготовленную DC кисть
SetDCBrushColor(hDC, RGB(0,0,0)); // Устанавливаем фон закраски DC кистью
// Устанавливаем разметку на будущих осях координат
for(int j=1; j<=10; j++)
{
Ellipse(hDC, center.x+j*20-2, center.y-2, center.x+j*20+2, center.y+2);
TextOut(hDC, center.x+j*20+1, center.y+1, digits[j], 2);
Ellipse(hDC, center.x-j*20-2, center.y-2, center.x-j*20+2, center.y+2);
TextOut(hDC, center.x-j*20+1, center.y+1, digits[j], 2);
Ellipse(hDC, center.x-2, center.y+j*20-2, center.x+2, center.y+j*20+2);
TextOut(hDC, center.x+1, center.y+j*20+1, digits[j], 2);
Ellipse(hDC, center.x-2, center.y-j*20-2, center.x+2, center.y-j*20+2);
TextOut(hDC, center.x+1, center.y-j*20+1, digits[j], 2);
}
SelectObject(hDC, (HGDIOBJ)hDASHPen); // Для разметочных линий пунктирное перо
for(int i=0; i<=20; i++)
{
MoveToEx(hDC, center.x-10*20, center.y-10*20+i*20, NULL);
LineTo(hDC, center.x+10*20, center.y-10*20+i*20);
MoveToEx(hDC, center.x-10*20+i*20, center.y-10*20, NULL);
LineTo(hDC, center.x-10*20+i*20, center.y+10*20);
}
// Рисуем оси координат (поверх разметки)
hPenDef = (HPEN)SelectObject(hDC, (HGDIOBJ)hPenBlack2); // Используем черное перо 2 пиксела толщиной для осей
MoveToEx(hDC, center.x, 0, NULL);
LineTo(hDC, center.x, rect.bottom);
MoveToEx(hDC, 0, center.y, NULL);
LineTo(hDC, rect.right, center.y);
}
SelectObject(hDC, (HGDIOBJ)hPenRed4); // Устанавливаем пером по умолчанию заготовленное перо
PolyBezier(hDC,pt,4); // Рисуем кривую Безье используя стандартную функцию win32 api
// Ellipse(hDC,center.x-200,center.y-200,center.x+200,center.y+200);
// Ellipse(hDC,rect.left,rect.top,rect.right,rect.bottom);
SelectObject(hDC, (HGDIOBJ)hPenDef); // Возвращаем перо по используемое по умолчанию
EndPaint(hWnd, &ps); // Передаем контекст устройства системе
return 0;
case WM_LBUTTONDOWN:
if (i>3) i=0; // Если точки уже были выставленны то обнуляем счетчик
pt.x = LOWORD(lParam); // Координата x это нижние 16 разрядов lParam
pt.y = HIWORD(lParam); // Координата н это верхние 16 разрядов lParam
hDC = GetDC(hWnd); // Берем контекст дисплея для изменения
hPenDef = (HPEN)SelectObject(hDC, (HGDIOBJ)hDCPen); // Устанавливаем заранее заготовленное перо
SetDCPenColor(hDC, RGB(0,255,0)); // Устанавливаем цвет DC пера
SelectObject(hDC, (HGDIOBJ)hBrush); // Выбираем заготовленную DC кисть
SetDCBrushColor(hDC, RGB(0,255,0)); // Устанавливаем фон закраски DC кистью
Ellipse(hDC, pt.x-3,pt.y-3,pt.x+3,pt.y+3); // Рисуем элипс (окружность) радиусом 3 вокруг точки
SelectObject(hDC, (HGDIOBJ)hPenDef); // Возвращаем перо по используемое по умолчанию
ReleaseDC(hWnd, hDC); // Возвращаем контекст устройства системе
i++; // Увеличиваем счетчик для заполнения следущей точки
if (i > 3) // если уже было нарисовано 4 точки для кривой
InvalidateRect(hWnd, NULL, TRUE); // помечаем ВСЮ клиенскую область как недействительную
return 0;
case WM_CLOSE:
DeleteObject(hDCPen); // Удаляем заранее заготовленные перья
DeleteObject(hPenRed4);
DeleteObject(hPenBlack2);
DeleteObject(hDASHPen);
DeleteObject(hBrush); // Удаляем кисть
DestroyWindow(hWnd); // Уничтащаем окно переданное оконной процедуре
return 0;
case WM_DESTROY:
PostQuitMessage(0); // Посылаем системе сообщение о том, что всё готово к завершению работы программы
return 0;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
InvalidateRect(hWnd, NULL, TRUE);
return 0;
GetClientRect(hWnd, &rect); //
center.x=(rect.bottom-rect.top)/2;
center.y=(rect.right-rect.left)/2;
нужно делать на WM_SIZE. В принципе .top и .left можно убрать, т.к. они всегда 0
center.y = rect.bottom/2;
center.x = rect.right/2;
Ellipse(hDC, center.x-200, center.y-200, center.x+200, center.y+200);
и все будет по центру...
И каши не увидел, и эллипс по центру.
А точки наставленные стираются, но это и понятно.
TextOut() надо делать с strlen-ом..
Кстати удалять стоковые объекты не обязательно. Вообще странный подход к рисованию, но вникать не стал.
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
HWND hMainWnd;
MSG msg;
char szClassName[] = "MyClass";
WNDCLASSEX wc;
// Заполняем структуру класса окна
wc.cbSize = sizeof(wc);// размер структуры в байтах
wc.style = CS_HREDRAW | CS_VREDRAW;// стиль класса окна
wc.lpfnWndProc = WndProc;// указатель на оконную процедуру
wc.cbClsExtra = 0;// ноль
wc.cbWndExtra = 0;// тоже ноль
wc.hInstance = hInstance;// дескриптор приложения
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);// дескриптор пиктограммы
wc.hCursor = LoadCursor(NULL, IDC_ARROW);// дескриптор курсора
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);// дескриптор кисти, для закраски фона окна
wc.lpszMenuName = NULL;// указатель на строку, содержащую имя меню
wc.lpszClassName = szClassName;// указатель на строку, содержащую имя класса окна
wc.hIconSm = NULL;// маленькая пиктограмма (дескриптор)
// Регистрируем класс окна
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, "Cannot register class", "Error", MB_OK);
return 0;
}
// Создаем основное окно приложения
hMainWnd = CreateWindow(
szClassName,// имя зарегестрированного класса
"First win32 program",// имя окна
WS_SYSMENU,// стиль окна
50,// горизонтальная позиция
50,// вертикальная позиция
640,// ширина окна
640,// высота окна
NULL,// дескриптор родительского окна
NULL,// дескриптор меню или идентификатор жлемента управления
hInstance,// дескриптор экземпляра приложения
NULL
);
if (!hMainWnd)
{
MessageBox(NULL, "Cannot create main window", "Error", MB_OK);
return 0;
}
// Показываем наше окно
ShowWindow(hMainWnd, nCmdShow);
// Выполняем цикл обработки сообщений до закрытия приложения
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
void get_net(HDC hdc, RECT rect, BYTE step, BYTE num)
{
for(BYTE i(0); i<=2*num; i++)
{
MoveToEx(hdc, rect.right/2-num*step, rect.bottom/2-num*step+i*step, NULL);
LineTo(hdc, rect.right/2+num*step, rect.bottom/2-num*step+i*step);
MoveToEx(hdc, rect.right/2-num*step+i*step, rect.bottom/2-num*step, NULL);
LineTo(hdc, rect.right/2-num*step+i*step, rect.bottom/2+num*step);
}
}
void get_dots(HDC hdc, RECT rect, BYTE step, BYTE num, BYTE r)
{
for(BYTE j(1); j<=num; j++)
{
Ellipse(hdc, rect.right/2+j*step-r, rect.bottom/2-r, rect.right/2+j*step+r, rect.bottom/2+r);
Ellipse(hdc, rect.right/2-j*step-r, rect.bottom/2-r, rect.right/2-j*step+r, rect.bottom/2+r);
Ellipse(hdc, rect.right/2-r, rect.bottom/2+j*step-r, rect.right/2+r, rect.bottom/2+j*step+r);
Ellipse(hdc, rect.right/2-r, rect.bottom/2-j*step-r, rect.right/2+r, rect.bottom/2-j*step+r);
}
}
void get_digits(HDC hdc, RECT rect, BYTE step, BYTE num)
{
char *digits[]={"0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15"};
for(BYTE k(1); k<=num; k++)
{
TextOut(hdc, rect.right/2+k*step+1, rect.bottom/2+1, digits[k], 2);
TextOut(hdc, rect.right/2-k*step+1, rect.bottom/2+1, digits[k], 2);
TextOut(hdc, rect.right/2+1, rect.bottom/2+k*step+1, digits[k], 2);
TextOut(hdc, rect.right/2+1, rect.bottom/2-k*step+1, digits[k], 2);
}
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HDC hDC;// Контекст дисплея
PAINTSTRUCT ps;// Структура для хранения информации о рисовании.
const BYTE net_step = 20;// шаг сетки
const BYTE dot_r = 2;// радиус окружности-засечки для отметки еденичных отрезков
const BYTE dots_num = 15;// количество отсечек на осях координат
static HPEN hPenDef,hPenRed4,hPenBlack2,hDASHPen,hDCPen;// Перья
static HBRUSH hBrush,hBrushDef;// Кисть
switch (msg)
{
case WM_CREATE:
hDCPen = (HPEN)GetStockObject(DC_PEN); // Стандартное DC перо
hDASHPen = CreatePen(PS_DASH, 1, RGB(0,0,0)); // Стандартное пунктирное перо
hPenRed4 = CreatePen(PS_SOLID, 4, RGB(255,0,0)); // Красное толстое толщиной 4 пиксела
hPenBlack2 = CreatePen(PS_SOLID, 2, RGB(0,0,0)); // Чёрное толстое толщиной 2 пиксела
hBrush = (HBRUSH)GetStockObject(DC_BRUSH);
return 0;
case WM_PAINT:
hDC = BeginPaint(hWnd, &ps); // Захватываем контекст дисплея
hPenDef = (HPEN)SelectObject(hDC, (HGDIOBJ)hDASHPen);// Для разметочных линий пунктирное перо
get_net(hDC, ps.rcPaint, net_step, dots_num);// Устанавливаем разметку на будущих осях координат
hBrushDef = (HBRUSH)SelectObject(hDC, (HGDIOBJ)hBrush);// Закраска засечек на координатных осях
SetDCBrushColor(hDC, RGB(0,0,0));// Выставляем черный цвет заливки
get_dots(hDC, ps.rcPaint, net_step, dots_num, 3);// Устанавливаем засечки на осях координат
get_digits(hDC, ps.rcPaint, net_step, 10);// Пишем цифры на осях координат
// Рисуем оси координат (поверх разметки)
SelectObject(hDC, (HGDIOBJ)hPenBlack2);// Используем черное перо 2 пиксела толщиной для осей
MoveToEx(hDC, ps.rcPaint.right/2, 0, NULL);
LineTo(hDC, ps.rcPaint.right/2, ps.rcPaint.bottom);
MoveToEx(hDC, 0, ps.rcPaint.bottom/2, NULL);
LineTo(hDC, ps.rcPaint.right, ps.rcPaint.bottom/2);
SelectObject(hDC, (HGDIOBJ)hPenDef);// Возвращаем перо по используемое по умолчанию
SelectObject(hDC, (HGDIOBJ)hBrushDef);// Возвращаем кисть по умолчанию
EndPaint(hWnd, &ps);
return 0;
case WM_CLOSE:
DeleteObject(hDCPen); // Удаляем заранее заготовленные перья
DeleteObject(hPenRed4);
DeleteObject(hPenBlack2);
DeleteObject(hDASHPen);
DeleteObject(hBrush); // Удаляем кисть
DestroyWindow(hWnd); // Уничтащаем окно переданное оконной процедуре
return 0;
case WM_DESTROY:
PostQuitMessage(0); // Посылаем системе сообщение о том, что всё готово к завершению работы программы
return 0;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
Разберись что такое PAINTSTRUCT.rcPaint и не ориентируйся на него больше.
Подскажите теперь как считывать нажатие клавиш ?
Задача нарисовать график Безье по n точкам и затем по нажатию клави перемещать, масштабировать. Как считывать клавиши и реагировать на них, то есть перерисовывать ? что то с WM_COMMAND ?
Задача - нарисовать кривую Безье по N точкам. Собственно я делаю это вот таким способом, но получается НЕ правильно, в конце кривая уходит не в конечную точку, а в ноль(в левый верхний угол). я в математике НОЛЬ, поэтому делал в лоб по формуле какой-то, кто может объяснить как сделать методом разбиения или в чём у меня ошибка - пожалуйста ...
DWORD fact(DWORD arg)
{
if(!arg)
return 1;
else
return arg*fact(arg-1);
}
...
double pow(double arg, BYTE n)
{
double tmp(1);
for(BYTE i(0); i<n; tmp*=arg, i++);
return tmp;
}
...
void draw_bezie_n(HDC hdc, const POINT pt[], BYTE num)
{
double x,y,b,c;
MoveToEx(hdc, pt[0].x, pt[0].y, NULL);
for(double t(0); t<=1.0; t+=0.00001)
{
x=0; y=0;
for(BYTE i(0); i<num; i++)
{
c = fact(num) / (fact(i)*fact(num-i));
b = c * ( pow(t,i) * pow(1-t ,num-i) );
x += pt.x * b;
y += pt.y * b;
}
LineTo(hdc, (int)x, (int)y);
}
}
Формулу взял в каких-то лекциях. Там на 4 странице внизу общая формула.
ты вроде парень с головой, а такие вопросы...
надо проецировать трехмерную поверхность на плоскость монитора. есть два основных вида проецирования: параллельная и центральная. подробней гугли. так же можешь почитать - Виктор Порев "Компьютерная графика. Учебное пособие". там есть и построение поверхностей, и проецирование, и много других интересных вещей.
да уже не уверен, что она, голова, у тебя имеется! если не любишь математику, то ты ошибся профессией, сильно ошибся. программирование это не только контролы на форме раскидывать, но и логика и математика. комп графика так вообще вся из математики состоит!