Как нарисовать треугольник winapi

КАК рисовать в Win32 API?

Обществом преподавателей информатики замечено, что очень многие, при изучении нового языка программирования, прежде всего интересуются его графическими возможностями. Видимо, ещё с детства в нас не остыл интерес к красивым разноцветным кружочкам и овалам. Как вы и думали, API даёт в этом плане огромнейшую свободу, ибо всё, что знает Windows о рисовании, она знает от API.
Разноцветные геометрические фигуры, которые можно заливать любым цветом, эллипсы, окружности, прямоугольники, линии. Во введении я уже говорил про кисти и перья. Эта важнейшие особенности API дают нам возможность заливать фигуры не только сплошным покровом, а линии делать пунктирными и штрих-пунктирными.
С текстом мы вроде как разобрались, теперь переходим к нашим любимым графическим примитивам. Так как в этой главе мы будем изучать почти все графические функции, предлагаю сделать заготовочку, в которую можно примерять новые изученные функции.

КАК и где вставлять графические функции?

Сам текст программы ничем не отличается от предыдущих. Рассмотрим лишь сообщение WM_PAINT. Я покажу куда рисовать.

case WM_PAINT :
hdc=BeginPaint(hWnd, &ps);
//здесь можно вставить какие-нибудь функции рисования
.

//обновляем окно
ValidateRect(hWnd, NULL);
//заканчиваем рисовать
EndPaint(hWnd, &ps);
break;

Графические функции GDI:
1. Вывод точки. SetPixel устанавливает заданный цвет в точке с указанными координатами:
COLORREF SetPixel(HDC hDC, int x, int y, COLORREF crColor);

Пример:
SetPixel(hDC, 10,10, RGB(0,0,0));
Функция GetPixel соответственно возвращает цвет в заданных координатах.
COLORREF Getpixel(hDC, int x, int y);

2. Рисование линий.
BOOL LineTo(hDC, int x, int y);
Функция рисует линию от текущей позиции до места, указанного в аргументах. Чтобы изменить тип линии (толщину, стиль)- меняется тип пера. Но об этом позже.

Так как в отличие от многих других подходов, в GDI нет функции рисования линии от одного указанного места до другого, её можно создать самому. Она будет соединять линией точки с координатами: x1,y1 и x2,y2.

BOOL Line(HDC hdc, int x1, int y1, int x2, int y2)
<
MoveToEx(hdc, x1, y1, NULL); //сделать текущими координаты x1, y1
return LineTo(hdc, x2, y2);
>

3. Дуга
BOOL Arc(hDC, int left, int top, int right, int bottom, int x1, int y1, int x2, int y2);
Первые четыре аргумента — левый верхний и правый нижний углы прямоугольника, в который вписан эллипс. Остальные значения — координаты точек, от которых будут проведены прямые к центру эллипса. В местах пересечения первой и второй прямой с радиусом эллипса, начинается и кончается дуга.

4. Прямоугольник. По умолчанию прозрачный, а вообще, тип его заливки определяется текущей кистью. По умолчанию она тоже прозрачная.
BOOL Rectangle(hDC, int left, int top, int right, int bottom); //аргументы — это коордианты левого верхнего и правого нижнего углов

5. Закруглённый прямоугольник. Его можно использовать, как импровизированную кнопку, если не лень возиться.
BOOL RoundRect(hDC, int left, int top, int right, int bottom, int width, int height);
Первые пять параметров совпадают с параметрами предыдущей фукнции. Далее width и height задают ширину и высоту эллипса, дуги которого ограничивают прямоугольник.

6. Кисти. Самое время познакомиться с кистями, так как фигуры, которые пойдут дальше выглядят лучше закрашенными. Мы уже немного затронули эту тему во вводной части. Теперь рассмотрим как задать свой стиль кисти. Как и setfillstyle() в DOS, кисть закрашивает какую-то область в какой-то цвет. В зависимости от кисти, она может делать это в полосочку, в клеточку, по диагонали.
Есть два способа объявить кисть. Первый — задать сплошную заливку, второй — указать стиль. Для этого существуют соответственно функции: CreateSoldBrush() и CreateHatchBrush().

Пример:
HBRUSH hBrush; //создаём объект-кисть
CreateSolidBrush(RGB(255,0,67)); //задаём сплошную кисть, закрашенную цветом RGB
SelectObject(hdc, hBrush); //делаем кисть активной

А вот как объявить не сплошную кисть:
CreateHatchBrush(int fnStyle, RGB(r,g,b));

Аргумент fnStyle принимает ряд константных значений:
HS_DIAGONAL — штрихует по диагонали
HS_CROSS — клеточка
HS_DIAGCROSS — диагональная сетка
HS_FDIAGONAL — по диагонали в другую сторону
HS_HORIZONTAL — горизонтальная «тельняшка»
HS_VERTICAL — вертикальный «забор»

HBRUSH hBrush1;
CreateHatchBrush(int fnStyle, RGB(r,g,b));
SelectObject(hdc, hBrush1); //делаем кисть активной

Вот махонький пример сообщения WM_PAINT с использованием кистей.

//сообщение рисования
case WM_PAINT :
//начинаем рисовать
hdc=BeginPaint(hWnd, &ps);
HBRUSH hBrush;
hBrush=CreateHatchBrush(HS_FDIAGONAL, RGB(255,0,0));
SelectObject(hdc,hBrush);

Ellipse(hdc, 100,100,200,300); //эллипс будет заштрихован

//заканчиваем рисовать
EndPaint(hWnd, &ps);
break;

7. Перья.
Они задают стиль линий, как и setlinestyle в DOS. Линия может быть жирной и тонкой, прерывистой и штрих-пунктирной. Всё предусмотрено. Это очень удобно для создания графиков функций, когда на график накладывается сетка, рисуются оси и выводится сама функция. Вы можете сказать, что это касается только математиков! Но почти любая фирма, что бы она не производила, иногда проводит презентации. На графике можно показать рост внешнего капитала, объём продаж и многое другое.

HPEN hPen; //Объявляется кисть
CreatePen(fnPenStyle, int width, RGB(r,g,b)); //Создаётся объект
SelectObject(hdc, hPen); //Объект делается текущим

fnStyle может принимать следующие значения:
PS_SOLD — сплошная
PS_DASH — состоящая из точек
PS_DOT — состоящая из тире
PS_DASHDOT — «точка-тире»
PS_DASHDOTDOT — «тире-точка-точка-тире»
PS_NULL — невидимая
PS_INSIDEFRAME — обводка замкнутых фигур

И пример будет такой:
//сообщение рисования
case WM_PAINT :
//начинаем рисовать
hdc=BeginPaint(hWnd, &ps);

HPEN hPen1, hPen2, hPen3; //объявляем сразу три объекта-пера
hPen1=CreatePen(PS_DASHDOT, 1, RGB(0,0,255)); //создаём всё три
hPen2=CreatePen(PS_DASH, 1, RGB(255,0,255));
hPen3=CreatePen(PS_DOT, 1, RGB(0,128,256));

SelectObject(hdc, hPen1); //но в одним момент времени может быть только 1
Rectangle(hdc, 10,10,100,100); //рисуем фигуру соответствующим пером

SelectObject(hdc, hPen2); //меняем перо
Ellipse(hdc, 100,100,200,300); //рисуем другим пером

SelectObject(hdc, hPen3);
LineTo(hdc, 200,100);

ValidateRect(hWnd, NULL);
//заканчиваем рисовать
EndPaint(hWnd, &ps);
break;

Важно понять, что можно создавать хоть 10 перьев с помощью CreatePen, но применить в данный момент времени можно только 1 из них. Для этого и нужен SelectObject, чтобы окно поняло какую кисть в настоящий момент мы достаём из этюдника GDI.

6. Закрашенный прямоугольник
int FillRect(HDC hDC, CONST RECT *lprc, HBRUSH hbr);
lprc — закрашиваемый прямоугольник типа RECT.
hbr — кисть

Вот пример-фрагмент WM_PAINT:

RECT r; //объявляем экзмепляр структуры RECT — координаты прямоугольника.
r.left=100; //левый верхний угол
r.top=100;
r.right=200; //правый нижний
r.right=300;

А вот и первый пример программы подоспел! Нарисуем что-то очень красивое — то, что мы уже умеем.

Как нарисовать треугольник winapi

В этом примере мы освежим методы работы со шрифтами, поменяв стандартный системный шрифт на свой, затем нарисуем прямоугольник, заполненный красным цветом, зелёный эллипс и прямоугольник с закруглёнными краями жёлтого цвета. Естественно, что для того, чтобы их заполнить нам понадобятся кисти, а для синей рамки на жёлтом прямоугольнике понадобятся перья. Всё, что есть в этой программе вы уже умеете. Мы просто закрепляем теорию на практике.

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
char szProgName[]=»Имя программы»;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
<
HWND hWnd;
MSG lpMsg;
WNDCLASS w;

w.lpszClassName=szProgName; //имя программы — объявлено выше
w.hInstance=hInstance; //идентификатор текущего приложения
w.lpfnWndProc=WndProc; //указатель на функцию окна
w.hCursor=LoadCursor(NULL, IDC_ARROW); //загружаем курсор
w.hIcon=0;
w.lpszMenuName=0;
w.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH); //цвет фона окна
w.style=CS_HREDRAW|CS_VREDRAW;
w.cbClsExtra=0;
w.cbWndExtra=0;

//Если не удалось зарегистрировать класс окна — выходим
if(!RegisterClass(&w))
return 0;

//Создадим окно в памяти, заполнив аргументы CreateWindow
hWnd=CreateWindow(szProgName, //Имя программы
«Грфические возможности Win32 API», //Заголовок окна
WS_OVERLAPPEDWINDOW, //Стиль окна — перекрывающееся
100, //положение окна на экране по х
100, //положение по у
500, //ширина
400, //высота
(HWND)NULL, //идентификатор родительского окна
(HMENU)NULL, //идентификатор меню
(HINSTANCE)hInstance, //идентификатор экземпляра программы
(HINSTANCE)NULL); //отсутствие дополнительных параметров

//Выводим окно из памяти на экран
ShowWindow(hWnd, nCmdShow);
//Обновим содержимое окна
UpdateWindow(hWnd);

//Цикл обработки сообщений

//Функция окна
LRESULT CALLBACK WndProc(HWND hWnd, UINT messg,
WPARAM wParam, LPARAM lParam)
<
HDC hdc; //создаём контекст устройства
PAINTSTRUCT ps; //создаём экземпляр структуры графического вывода
LOGFONT lf;
HFONT hFont;
RECT r;
HBRUSH hBrush;
HPEN hPen;

//Цикл обработки сообщений
switch(messg)
<
//сообщение рисования
case WM_PAINT :
hdc=BeginPaint(hWnd, &ps);

//Создаём свой шрифт
strcpy(lf.lfFaceName,»Times New Roman»); //копируем в строку название шрифта
lf.lfHeight=20;
lf.lfItalic=1;
lf.lfStrikeOut=0;
lf.lfUnderline=0;
lf.lfWidth=10;
lf.lfWeight=40;
lf.lfCharSet=DEFAULT_CHARSET; //значение по умолчанию
lf.lfPitchAndFamily=DEFAULT_PITCH; //значения по умолчанию
lf.lfEscapement=0;

hFont = CreateFontIndirect(&lf);
SelectObject(hdc, hFont);
SetTextColor(hdc, RGB(0,0,255));
TextOut(hdc, 80,40, «Красота спасёт мир!!», 20);

//рисуем зелёный эллипс
hBrush=CreateSolidBrush(RGB(10,200,100));
SelectObject(hdc, hBrush);
Ellipse(hdc, 20,100,200,200);

//рисуем закруглённый прямоугольник
hBrush=CreateSolidBrush(RGB(250,200,100));
SelectObject(hdc, hBrush);
hPen=CreatePen(2,2,RGB(0,0,255));
SelectObject(hdc, hPen);
RoundRect(hdc, 20, 250, 250, 350, 15, 15);


ValidateRect(hWnd, NULL);
EndPaint(hWnd, &ps);
break;

//сообщение выхода — разрушение окна
case WM_DESTROY:
PostQuitMessage(0); //Посылаем сообщение выхода с кодом 0 — нормальное завершение
DeleteObject(hPen);
DeleteObject(hBrush);
break;

default:
return(DefWindowProc(hWnd, messg, wParam, lParam)); //освобождаем очередь приложения от нераспознаных
>
return 0;
>

Правда, красиво? Наконец-то из этой API нам удалось выжать что-то стоящее. Продолжаем обзор.

7. Прямоугольная рамка — как видите, существует немало функций для работы с прямоугольниками:

int FrameRect(HDC hDC, CONST RECT *lprc, HBRUSH hbr);
Применение аналогично предыдущей.

8. Инверсия значения цветов точек в заданной области
BOOL InvertRect(HDC hDC, CONST RECT *lprc);

9. Эллипс
BOOL Ellipse(HDC hdc, int x1, int y1, int x2, int y2);
координаты — это прямоугольник, в который вписывается эллипс

10. Хорда (сегмент эллипса) — параметры аналогичны Arc
BOOL Chord(HDC hDC, int left, int top, int right, int bottom, int x1, int y1, int x2, int y2); Функция соединяет хордой точки начала и конца дуги эллипса и закрашивает выделенный сегмент текущей кистью.

11. Сектор эллипса
— аналог pieslice в DOS.
BOOL Pie(HDC hDC, int left, int top, int right, int bottom, int x1, int y1, int x2, int y2);

12. Многоугольник . Есть много функций рисования мноугольников. Мы рассмотрим две. Рисования от вершины к вершине и рисования отрезками:
PolyDraw оперирует вершинами:

POINT poly[8];
BYTE polytype[8];

poly[0].x=375; //координаты первой вершины
poly[0].y=375;

. //и так заполняем координаты всех восьми вершин

poly[7].x=400; //координаты восьмой вершины
poly[7].y =400;

. //другой массив содержит режим рисования

PolyDraw(hdc, poly, polytype, 8); //рисование многоугольника

Функция Polyline рисует набором отрезков:

Polyline(hdc, poly , 4);

КАК вывести график функции?

Всё это были фрагменты графических функций. Теперь рассмотрим настоящий полноценный пример. Это будет простейший график функции.

Как нарисовать треугольник winapi

Вообще графики очень часто используются в промышленных приложениях. А ведь промышленная автоматизация один из главныз заказчиков программиста. На деле никто не занимается программированием ради программирования. Обязательно придётся осваивать какую-то ещё предметную область. Будь то склад для программиста баз данных или производство упаковок для системного программиста. Так или иначе, программы, контроллирующие какой-нибудь физический парметр (температуру больного, давление в шахте, скорость двигателя, частоту оборотов, напряжённость магнитного поля) выводят оперативную информацию на грфик. Специальный человек — оператор следит за тем, чтобы значения графика не достигали предельных значений.
Как вы сами понимаете, мы не можем обойти столь важную тему. Именно для неё наша следующая программа.
Создайте пустой проект Win API и включите в него следующий текст:

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
//Процедура рисования линии
BOOL Line(HDC hdc, int x1, int y1, int x2, int y2);

char szProgName[]=»Имя программы»;

int i, xView, yView;
double y;
char Buf[2];

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
<
HWND hWnd;
MSG lpMsg;
WNDCLASS w;

w.lpszClassName=szProgName;
w.hInstance=hInstance;
w.lpfnWndProc=WndProc;
w.hCursor=LoadCursor(NULL, IDC_ARROW);
w.hIcon=0;
w.lpszMenuName=0;
w.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
w.style=CS_HREDRAW|CS_VREDRAW;
w.cbClsExtra=0;
w.cbWndExtra=0;

//Если не удалось зарегистрировать класс окна — выходим
if(!RegisterClass(&w))
return 0;

//Создадим окно в памяти, заполнив аргументы CreateWindow
hWnd=CreateWindow(szProgName,
«График функции»,
WS_OVERLAPPEDWINDOW,
100,
100,
500,
400,
(HWND)NULL,
(HMENU)NULL,
(HINSTANCE)hInstance,
(HINSTANCE)NULL);

//Выводим окно из памяти на экран
ShowWindow(hWnd, nCmdShow);
//Обновим содержимое окна
UpdateWindow(hWnd);

//Цикл обработки сообщений

while(GetMessage(&lpMsg, NULL, 0, 0)) < //Получаем сообщение из очереди
TranslateMessage(&lpMsg); //Преобразует сообщения клавиш в символы
DispatchMessage(&lpMsg); //Передаёт сообщение соответствующей функции окна
>
return(lpMsg.wParam);
>

//Функция окна
LRESULT CALLBACK WndProc(HWND hWnd, UINT messg,
WPARAM wParam, LPARAM lParam)
<
HDC hdc; //создаём контекст устройства
PAINTSTRUCT ps; //создаём экземпляр структуры графического вывода
HPEN hPen; //создаём перо
//Цикл обработки сообщений
switch(messg)
<

case WM_SIZE:
xView=LOWORD(lParam);
yView=HIWORD(lParam);

//сообщение рисования
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
SetMapMode(hdc, MM_ISOTROPIC); //логические единицы отображаем, как физические
SetWindowExtEx(hdc, 500,500, NULL); //Длина осей
SetViewportExtEx(hdc, xView, -yView, NULL); //Определяем облась вывода
SetViewportOrgEx(hdc, xView/6, yView/2, NULL); //Начало координат

//Рисуем оси координат
Line(hdc,0, 220,0,-220);//ось У
Line(hdc, -100,0,500,0);//ось Х
MoveToEx(hdc, 0,0,NULL); //перемещаемся в начало координат

//Создание красного пера
hPen=CreatePen(1,4,RGB(255,25,0));
SelectObject(hdc, hPen);

//Делаем перо снова чёрным
hPen=CreatePen(1,1,RGB(0,0,0));
SelectObject(hdc, hPen);

//Наносим деления
for(i=-100; i

//сообщение выхода — разрушение окна
case WM_DESTROY:
DeleteObject(hPen); //не забываем уничтожать перья
PostQuitMessage(0); //Посылаем сообщение выхода с кодом 0 — нормальное завершение
break;

default:
return(DefWindowProc(hWnd, messg, wParam, lParam)); //освобождаем очередь приложения от нераспознаных
>
return 0;
>

//Функция рисования линии
BOOL Line(HDC hdc, int x1, int y1, int x2, int y2)
<
MoveToEx(hdc, x1, y1, NULL); //сделать текущими координаты x1, y1
return LineTo(hdc, x2, y2); //нарисовать линию
>

Поскольку в данной программе большое внимание уделяется всяким украшательствам: делениям и надписям, обращу ваше внимание на главное — создавать графики совсем не сложно. И вот как это делается:

Мы знаем, что отсчёт координат задаётся от левого верхнего угла вниз и вправо. Как известно, значения на графике изменяются вверх и вправо. Вряд ли мы сможем объяснить пользователю, почему график «растёт вниз». На счастье, в Windows предусмотрена функция, преобразует координаты в нужном нам направлении.
Первым делом, мы узнаём размер окна. Для этого используется сообщение WM_SIZE. Параметр lParam содержит по этому сообщению размеры экрана. Переменные xView и yView будут содержать эти значения:

case WM_SIZE:
xView=LOWORD(lParam);
yView=HIWORD(lParam);

break;

Затем определим область вывода. Мы хотим, чтобы при увеличении координаты по у график рос вверх, а не вниз.

SetViewportExtEx(hdc, xView, -yView, NULL);

Обратите внимание: yView указан со знаком . Значит все координаты по у будут расти в обратную сторону — вверх.
Центр графика обычно где-нибудь посередине экрана. Координаты же увеличиваются из левого верхнего угла. Перенесём центр графика:

SetViewportOrgEx(hdc, xView/6, yView/2, NULL);

В точке, равной 1/6 максимального значения по х и 1/2 значения по этого значения по у будет центр.
Можно также задать длину осей — 500 и 500. Для этого применяется следующая функция:

SetWindowExtEx(hdc, 500,500, NULL);

Как вы уже знаете, окно имеет логические координаты и физические. Для того, чтобы логические координаты совпадали с физическими, а также, чтобы единица отложенная по х была равно единице отложенной по у, задаётся режим MM_ISOTROPIC. Его задаёт функция:

Вот, как будет выглядеть эта конструкция вцелом:

SetMapMode(hdc, MM_ISOTROPIC); //логические единицы отображаем, как физические
SetWindowExtEx(hdc, 500,500, NULL); //Длина осей
SetViewportExtEx(hdc, xView, -yView, NULL); //Определяем облась вывода
SetViewportOrgEx(hdc, xView/6, yView/2, NULL); //Начало координат

Дальше надо нарисовать оси. К нашей радости, точка 0, 0 сместилась на середину экрана, в левую его часть. Исходя из этого, рисуем оси, применяя самописную функцию Line:

Line(hdc,0, 220,0,-220);//ось У
Line(hdc, -100,0,500,0);//ось Х
MoveToEx(hdc, 0,0,NULL);

В выводе графика нет ничего примечательного. Переменная i меняется от 0 до 450. Подставляя i в формулу, мы получаем зависимость y от i. Рисуем линию до этой точки. Небольшие отрезки сольются в непрерывную линию.

Иногда, требуется хранить значения физических величин несколько дней. Тогда график выполняется с длиннющей горизонтальной полосой прокрутки. При желании, можно вернуться и посмотреть были ли изменения, и если да, то какие.
Другой выход из положения — вести историю, записывая её в файл, а на экран выводя только показания последних пяти минут.

Видео:КАК НАРИСОВАТЬ ТРЕУГОЛЬНИК В КОНСОЛИ C# | C# ДОМАШНИЕ ЗАДАНИЯ | #5Скачать

КАК НАРИСОВАТЬ ТРЕУГОЛЬНИК В КОНСОЛИ C# | C# ДОМАШНИЕ ЗАДАНИЯ | #5

Задания:

1. Добавьте в приложения с графиком стрелочки и подписи к осям.
2. Нарисуйте снеговика известными средствами GDI. Напомню, что снеговик состоит из трёх непрозрачных эллипсов грязно-белого цвета, на груди у него должны быть пуговки, в руке — метла, а на голове — ведро.
3. Нарисуйте красивый паровозик, клубы из трубы которого будут выводиться в цикле в виде синих эллипсов
4. Нарисуйте кораблик с жёлтой палубой и красными бортами, используя Polyline или Polydraw. Кораблик покоится на синих волнах, которые нарисованы дугами разной толщины. Попробуйте вывести дуги в цикле.
5. Нарисуйте с помощью одних только линий домик с забором

Видео:Как нарисовать пиксель в окне Windows. SetPixel. GDI. WinAPI.Скачать

Как нарисовать пиксель в окне Windows. SetPixel. GDI. WinAPI.

2D графика на основе WinApi C++ (2D graphics based on WinApi C++)

Для начала нам потребуется шаблон , на основе которого можно будет создавать дальнейшие приложения. Создайте в Visual Studio пустой проект (File → New → Project→ Other→ Empty). Cоздайте в нем main.cpp файл (Project → Add New Item…) и вставьте туда программный код (см. ниже).

Проследите, чтобы при создании проекта была установка x86 (32-разрядная программа):

Как нарисовать треугольник winapi

32-разрядные программы не могут загружать 64-разрядные библиотеки (DLL-файлы) и могут вылетать, если они попытаются загрузить определенный DLL-файл и обнаружат 64-битную версию вместо 32-разрядной

Если возникают ошибки из-за преобразования символов, то в окне свойств проекта установите «Использовать набор символов Юникода»:

Как нарисовать треугольник winapi

Можете также скачать с моего Google-диска заархивированный файл проекта по ссылке 2d_window.zip. Скачивание начинается после выбора кнопки меню в правом верхнем углу открывшегося окна

Как нарисовать треугольник winapi

Проект создан в версии Visual Studio 17. Для поздних версий (19 и выше), возможно, понадобиться пересобрать решение (кнопки меню Build>Rebuild Solution).

При запуске программы должно появиться окно, залитое синим цветом.

Как нарисовать треугольник winapi

Проект содержит файл main.cpp, ознакомьтесь с программным кодом.

Директива препроцессора #include открывает доступ к тысячам описаний констант, структур, типов данных и функций Windows.

WinAPI приложение является в своей основе процедурным приложением и содержит два основных модуля – функции WinMain и WndProc.

Функция WinMain составляет основу любого WinAPI приложения. Она служит как бы точкой входа в приложение и отвечает за следующее:

  • начальную инициализацию приложения;
  • создание и регистрацию объекта класса окна приложения;
  • создание и инициализацию цикла обработки событий.

Обработанное в бесконечном цикле событие переправляется (опосредовано через Windows) оконной функции WndProc. События в ней идентифицируются именем константы (WM_PAINT, WM_DESTROY и др.). Рисование осуществляется при помощи объектов типа HDC (дескриптор контекста устройства).

Видео:Рисуем линию. LineTo. GDI. WinAPI.Скачать

Рисуем линию. LineTo. GDI. WinAPI.

Рисуем треугольник в оконной СК

Нарисуем треугольник, задав координаты его вершин в оконной (физической) системе координат. Начало этой системы координат располагается в левом верхнем углу экрана. Ось X направлена слева направо, ось Y – сверху вниз. В качестве единицы длины в этой системе координат используется пиксель.

Как нарисовать треугольник winapi

Для этого создадим заголовочный файл draw.h:

Еще создадим файл draw.cpp:

В файле main.cpp подключим draw.h и закомментируем

А между заполнением области фоновым цветом и выводом изображения на основной контекст, появляется вызов процедуры рисования:

Рисуем мы на контексте-двойнике, а уже потом перекидываем изображение на основной контекст.

Видео:Язык Си - Как рисовать в консольном окне средствами WINAPI.Скачать

Язык Си - Как рисовать в консольном окне средствами WINAPI.

Логическая система координат

Оконная СК неудобна для пользователя из-за непривычного расположения осей (ось y направлена вниз), задания координат в пикселях и др. Устраним эти недостатки, используя логическую СК, в которой:

  • центр координат перенесен из левого верхнего угла в центр экрана;
  • направление оси Y меняется на противоположное;
  • координаты (от -1 до +1) соотнесены с шириной и высотой окна;
  • введен отступ от края окна (margin).

Как нарисовать треугольник winapi

Преобразования из логической системы координат в оконную осуществляются при помощи зависимостей:

X_Window = MARGIN + (1.0/2)*(X_Log + 1)*(Width — 2 * MARGIN);

Y_Window = MARGIN + (-1.0/2)*(Y_Log — 1)*(Height — 2 * MARGIN);

Ниже приводится новый код модуля draw.cpp:

В draw.h добавляется объявление новой функции:

В модуль main.cpp добавляем обработчик события WM_SIZE и WM_ERASEBKGND:

Теперь, при изменении размеров окна наш треугольник подстраивается под новые размеры. При изменении размеров окна происходит перерисовка изображения, поскольку событие WM_SIZE автоматически вызывает событие WM_PAINT.

Контрольные вопросы

  1. Чем обусловлен переход от оконной СК к логической?
  2. Какие задачи решают функции Tx(0.0), Ty(0.5)?
  3. Какие задачи решает функция SetWindowSize?

Видео:КАК НАРИСОВАТЬ ТРЕУГОЛЬНИК В ПРОГРАММЕ ADOBE ILLUSTRATOR.Скачать

КАК НАРИСОВАТЬ ТРЕУГОЛЬНИК В ПРОГРАММЕ ADOBE ILLUSTRATOR.

Поворот плоскости

Алгоритмическая часть

Мы нарисовали треугольник в логической системе координат. Теперь обеспечим возможность поворота треугольника при каждом нажатии клавиш ← или →.

Как нарисовать треугольник winapi

Для пересчета положения каждой вершины треугольника используем преобразование координат при повороте. Рассмотрим произвольный вектор r, задающий некоторую точку в системе координат ху:

Как нарисовать треугольник winapi

При повороте на угол φ координаты точки запишутся в виде:

Как нарисовать треугольник winapi

Выделим из этих уравнений 2 пары зависимостей – для пересчета координат точек в новой СК (1.1) и для обновления значений косинуса и синуса угла (1.2):

Как нарисовать треугольник winapi

В программе поворот реализуется пошагово. На каждом шаге в уравнение 1.1 вместо sin φ и cos φ подставляются sin (α+ φ) и cos (α+ φ), полученные из системы уравнений 1.2. При этом на следующем шаге в уравнении 1.2 вместо cos α и sin α в правую часть уравнения подставляются sin (α+ φ) и cos (α+ φ), которые были получены на предыдущем шаге.

В матричном представлении выражения (1.1) могут быть записаны в одном из следующих видов:

Как нарисовать треугольник winapi

Программная реализация

В программе модифицированы все модули (main.cpp, draw.h, draw.cpp) и добавлены новые (geometry.h, matrix.h, matrix.cpp). В файле main.cpp добавляются обработчики WM_CREATE и WM_KEYPRESSED, клавиши ← и → идентифицируются в программе константами VK_LEFT и VK_RIGHT, начальный поворот плоскости инициализируется вызовом функции InitRotation, текущий поворот – функцией AddRotation. Перерисовка изображения (вызов события WM_PAINT) обеспечивается функцией InvalidateRect.

В файлы draw.h, draw.cpp и main.cpp вносим изменения, которые обеспечивают рисование треугольника в зависимости от текущего значения угла. В определение функций InitRotation и AddRotation входит вызов функции SetRotationMatrix, которая осуществляет инициализацию массива из 4-х тригонометрических характеристик угла. Переменная массива объявляется типом Matrix. Обновление массива реализует функция MultiplyMatrices. В функции Draw каждая вершина треугольника объявляется типом _Point. При каждом запуске функции Draw вершины инициализируются начальными координатами точек, затем функция ApplyMatrixtoPoint обеспечивает пересчет координат в соответствии с текущими значениями массива тригонометрических характеристик угла. Поворот точек треугольника реализуется в логической системе координат, затем осуществляется преобразование в оконные координаты и рисование сторон треугольника.

По мере усложнения программы необходимо было осуществлять ее структуризацию. Очевидно, что по функциональному признаку все действия с массивами целесообразно описать в отдельном модуле.

Обратите внимание, что входными параметрами ряда функций есть не переменные, а ссылки на переменные, что обеспечивает возможность при вызове этих функций обновлять входные параметры.

Контрольные задания:

  1. Согласно системы уравнений (1.2) sin и cos суммы 2-х углов, определяются по двум аргументам – sin и cos этих углов. В функции MultiplyMatrices(Matrix &dest, Matrix &left, Matrix &right), которая реализует в программе систему уравнений (1.2), используются 3 параметра (один параметр лишний). Модифицируйте функцию MultiplyMatrices, чтобы она принимала 2 параметра. Упрощенный аналог решения этого задания: Fun (&A, B)
  2. Внесите изменения в программу, которые обеспечат поворот треугольника на угол (PI/10) при нажатии на левую кнопку мышки (событие WM_LBUTTONDOWN) и поворот на угол (-PI/10) при нажатии на правую кнопку (событие WM_RBUTTONDOWN).

Видео:СОЗДАЁМ ОКНО - C++ WINAPI ЧАСТЬ #1Скачать

СОЗДАЁМ ОКНО  -  C++ WINAPI ЧАСТЬ #1

Анимация изображения

Мы уже обеспечили возможность поворота треугольника при каждом нажатии клавиш ← или →. Теперь поставим задачу обеспечения непрерывного вращения треугольника. Для реализации задачи необходимо обеспечить изменение угла поворота треугольника с последующим обновлением изображения. Причем, это должно выполняться в цикле.

Рассмотрим и оценим два способа циклического обновления изображения:

  1. Бесконечный цикл обработки сообщений (в функции WinMain);
  2. Цикл, осуществляемый через обработку события WM_TIMER.

В первом случае в бесконечный цикл помещаются 2 функции:

При запуске приложения мы получим изменение угла поворота треугольника со скоростью, которая приблизительно соответствует скорости обработки сообщений. При этом отрегулировать скорость вращения можно за счет изменения входного параметра функции AddRotation.

Если для анимации использовать бесконечный цикл обработки сообщений, то добиться точной скорости обновления изображения сложно. В WinAPI для работы со временем обычно используют сообщение WM_TIMER:

Это сообщение будет посылаться вашей программе через интервал времени, который вы зададите при создании таймера:

Создание таймера необходимо предусматривать до его использования, например – в событии WM_CREATE. Кроме этого необходимо в заголовке файла определить идентификатор таймера:

Уничтожение таймера предусматривается, когда необходимо остановить анимацию:

Контрольные вопросы и задания

  1. Почему нецелесообразно использовать бесконечный цикл обработки сообщений для анимации изображения?
  2. Обеспечьте при запуске программы вращение треугольника как стрелки секундомера.
  3. Обеспечьте возможность запуска вращения треугольника при нажатии на клавишу P (пуск) и остановку – при нажатии на клавишу S (stop). Пример оформления события нажатия на клавишу приводится ниже:

Видео:[1] Уроки по WinAPI?! - Создание окна.Скачать

[1] Уроки по WinAPI?!  - Создание окна.

Аффинные преобразования треугольника

Рассматриваемое ниже приложение базируется на приложении, рассмотренном выше. Отличие лишь в том, что вместо матрицы поворота используется матрица аффинных преобразований в однородных координатах. При нажатии на клавишу «T» или перемещении мыши (при нажатой клавише CTRL) матрица инициализируется конкретными значениями, задавая соответствующие преобразования треугольника.

Как нарисовать треугольник winapi

Учитывая большое количество изменений по сравнению с приложением поворот плоскости мышью, предлагается создать новое приложение с соответствующими файлами:

Header Files

action.h
engine.h
geometry.h
matrix.h
vec.h
viewport.h

Source Files

main.cpp
action.cpp
engine.cpp
matrix.cpp
viewport.cpp

Если возникают ошибки из-за преобразования символов, то в окне свойств проекта установите «Использовать набор символов Юникода»:

Как нарисовать треугольник winapi

Можете также скачать с моего Google-диска заархивированный файл проекта по ссылке 2d.zip. Скачивание начинается после выбора кнопки меню в правом верхнем углу открывшегося окна

Как нарисовать треугольник winapi

Проект создан в версии Visual Studio 17. Для поздних версий (19 и выше), возможно, понадобиться пересобрать решение (кнопки меню Build>Rebuild Solution).

В файлах описаны классы, в которых сгруппированы функции и переменные для комплексного решения задачи аффинных преобразований на плоскости.

Класс Engine объединяет функцию формирования изображения (Draw) и объекты классов Action и Viewport, обеспечивающие действия по перестройке изображения, вызванной событиями нажатия на кнопки, перемещения мышки и изменения размеров окна.

Класс Action объединяет действия и данные, связанные с аффинными преобразованиями. Функция Rotate (Translate) устанавливает преобразования вращения (перемещения), опираясь на текущее и предыдущее положения мыши. Функция InitAction запоминает координаты курсора в объекте класса vec. Эти функции вызываются при событиях нажатия кнопки мышки и перемещения курсора. Функция Transform устанавливает преобразования через инициализацию коэффициентов матрицы

Класс Viewport объединяет действия и данные, связанные с изменением размеров окна, Кроме этого, в класс включена функция T, которая выполняет преобразование точек фигуры из экранных координат экрана, в логические координаты, а также, функция T_inv , которая преобразует координаты курсора из экранных координат в логические. Функция T_inv отсутствовала в приложении, рассмотренном ранее, поскольку в нем реализовывалось лишь преобразование вращения, а значение угла поворота не зависело от СК. В этом приложении с помощью курсора задается и преобразование перемещения, а оно зависит от используемой СК.

В класс Matrix была добавлена функция SetTranslationMatrix, в которой непосредственно инициализируются коэффициенты матрицы преобразований.
В классе Vec переопределяются операции с векторами. Эти операции используются в программе для определения преобразования перемещения, опираясь на текущее и предыдущее положения курсора при перемещении мыши.

Видео:OpenGL - Урок 2 - точка, линия, треугольник, кругСкачать

OpenGL - Урок 2 - точка, линия, треугольник, круг

Контрольные задания

Задание 1. Определите коэффициенты матрицы элементарных аффинных преобразований в соответствии с вариантом задания

Как нарисовать треугольник winapi

В каждый вариант задания включены по 3 преобразования

Как нарисовать треугольник winapi

Пример оформления задания показан ниже на рисунке

Как нарисовать треугольник winapi

Выполнить тестовые запуски приложения для проверки соответствия матриц заданным преобразованиям

Как нарисовать треугольник winapi

Коэффициенты матрицы преобразования инициализируются в функции Matrix::SetTranslationMatrix1, которая определена в файле matrix.cpp. Преобразование осуществляется при нажатии клавиши T.

Задание 2. Определить произведение матриц AB, (AB)C и C(AB) и протестировать композицию элементарных преобразований запуском приложения. Преобразование осуществлять нажатием клавиши T.

Как нарисовать треугольник winapi

Задание 3. Определить последовательность элементарных аффинных преобразований (преобразований не более 3-х), которые от начального положения треугольника приведут к заданному результату. Пример:

Как нарисовать треугольник winapi

Как нарисовать треугольник winapi

Как нарисовать треугольник winapi

Для тестирования добавьте в программу дополнительные реакции на нажатие клавиш 1, 2 и 3, которые обеспечивают соответствующее элементарное преобразование:

Объявления и определения функций action->Transform вместе с входящими функциями описываются по аналогии с описанием действий при нажатии на клавишу “T”.

Задание 4. Добавьте в приложение программный код, обеспечивающий возможность переключения преобразований из глобальной СК в локальную СК. Для тестирования приложения переместите треугольник и поверните его при помощи мышки на угол 135 градусов. Если включена глобальная СК, вращение будет происходить относительно центра окна, если локальная СК – относительно вершины прямого угла.

В соответствии с алгоритмом для выполнения этого задания необходимо поменять местами текущую матрицу и матрицу, переданную в качестве параметра для функции MultiplyMatrices(Matrix &right).

Видео:ЭЛЕМЕНТЫ УПРАВЛЕНИЯ - C++ WINAPI ЧАСТЬ #3Скачать

ЭЛЕМЕНТЫ УПРАВЛЕНИЯ  -  C++ WINAPI ЧАСТЬ #3

Как нарисовать треугольник winapi

Урок 2. Рисование треугольника

Итак, здравствуйте. Вы – один из тех, кого не напугал первый урок и кто готов двигаться дальше. Давайте на этот раз обойдемся без философских вступлений, тем более что работы у нас очень много.

Будем рисовать. Почему именно треугольник? Просто потому, что это базовая модель для трехмерной графики – примитив. Примитивами называют фигуры, из которых все и создается. Кроме треугольников можно рисовать линиями и точками, но кому нужен герой или монстр, состоящие из линий и живущие в мире точек? Поэтому обычно, когда говорят о примитивах, имеют в виду треугольники. Представьте, если удачно совместить два треугольника, получится квадрат. А если совместить 6 квадратов, можно создать уже куб. Но придавать нашим творениям объем будем немного позже.

Мы выяснили, для чего нам нужны треугольники. Теперь давайте подумаем, как можно нарисовать такой восхитительный объект. Тут ничего сложного: достаточно задать координаты трех точек и дать понять Direct 3 D , что рисовать мы хотим именно треугольник а не точки и не линии). Выглядит это примерно так:

Как нарисовать треугольник winapi

На практике все немного сложнее. Эти точки в трехмерной графике называются вершинами ( vertex ). Вершина может содержать информацию не только о своем положении в пространстве. Иначе мы могли бы создавать только серые однотонные миры. Обычно поступают так: создают структуру, которая будет описывать вершины в игре. Такая структура может содержать информацию о цвете вершины, координатах текстуры, о нормали (используется для вычисления освещения). В этом уроке наши вершины выглядят очень просто:

Нагоняющее ужас XMFLOAT 3 – это всего лишь структура из трех значений типа float, задающих координаты вершины по осям X, Y и Z. Координата Z не понадобится, потому что наш треугольник пока что плоский, как лепешка.

Разобравшись с форматом вершин, мы создадим буфер. Как несложно догадаться, мы создадим массив из трех вершин. Потом загрузим эти данные в буфер формата DirectX ID3D11Buffer.

Проблема в том, что DirectX не имеет представления о формате наших вершин. Как я уже говорил, для каждой вершины можно хранить много разной информации, следовательно, мы должно рассказать DirectX о содержимом структуры SimpleVertex. Здесь нам поможет функция CreateInputLayout(…) (Создать шаблон ввода), находящаяся в интерфейсе Device (устройство, ID3D11Device). Она создаст объект интерфейса Input Layout (шаблон ввода, ID3D11InputLayout), который мы подключим к устройству рисования:

g _ pImmediateContext -> IASetInputLayout ( g _ pVertexLayout );

Мы рассмотрим этот процесс подробно уже при написании программы.

Собственно говоря, в шейдерах нет ничего сложного. С другой стороны, именно шейдеры я так и не смог одолеть много лет назад, когда изучал DirectX8. Однако тогда они были лишь подающим надежды нововведением, теперь же шейдеры стали неотъемлемой частью трехмерного программирования. Шейдер – это всего лишь подпрограмма (функция), используемая для определения/изменения параметров любого объекта или изображения. Проще всего сравнить его с человечком, стоящим у конвейера. Скажем, по ленте Конвейера Отрисовки Треугольника несется наша вершина №1. Где-то ближе к концу лента замедляется, шейдер вносит небольшое изменение в вершину и отправляет ее дальше, прямо в функцию, рисующую вершину на экране. Такой же путь проделают вершины 2 и 3. В этом примере мы рассмотрели работу вершинных шейдеров. Существуют еще геометрические и пиксельные. Геометрические обрабатывают целый примитив (треугольник), а пиксельные, как легко догадаться, каждый пиксел. Например, для покрытия треугольника текстурой мы в пиксельном шейдере могли бы вычислить цвет текстуры в точке с координатами пикселя и вернуть этот цвет.

Как я уже сказал, шейдеры – это всего лишь функции. Вершинный шейдер возвращает объект, описывающий вершину (у нас – SimpleVertex ), а пиксельный шейдер возвращает цвет. Но пишутся шейдеры не на C++, а на похожем языке, который называется H L SL. Шейдеры будут храниться в отдельном файле Urok 2. fx , который не входит в программу. Мы загрузим и скомпилируем этот файл динамически в процессе загрузки программы (не бойтесь, DirectX выполнит основную работу за нас).

Вот пример пиксельного шейдера, который в нашей программе закрасит треугольник в желтый цвет:

float4 PS( float4 Pos : SV_POSITION ) : SV_Target

// Возвращаем желтый цвет, непрозрачный (альфа = 1)

return float4( 1.0f, 1.0f, 0.0f, 1.0f );

Функция рисования для каждой точки треугольника вызовет шейдер PS(…) ( аббревиатура от Pixel Shader ) и закрасит ее полученным цветом.

Я думаю, будет проще изучить сложные места уже по ходу написания программы.

Общая схема такая: Создаем окно a Создаем устройства DX a Загружаем шейдеры, создаем буфер вершин и формат вершин (объект Input Layout) a Готово! В цикле сообщений рисуем треугольник из нашего буфера.

Давайте опять создадим пустой объект и добавим в него файл Urok2.cpp и файл иконки с идентификатором ID I _ICON1. Сразу добавляем библиотеки в командную строку компоновщика. Значительная часть кода не будет отличаться от предыдущего урока, так что можно взять за основу старый проект.

// Урок 2. Рисование треугольника. Основан на примере из DX SDK (c) Microsoft Corp.

🔍 Видео

РАБОТА С ВИДЖЕТАМИ - C++ WINAPI ЧАСТЬ #5Скачать

РАБОТА С ВИДЖЕТАМИ  -  C++ WINAPI ЧАСТЬ #5

[2] Уроки по WinAPI?! - События и компоненты.Скачать

[2] Уроки по WinAPI?! - События и компоненты.

Сделал 3D в консоли с нуля | трассировка лучейСкачать

Сделал 3D в консоли с нуля | трассировка лучей

[3] Уроки по WinAPI?! - Практика...Скачать

[3] Уроки по WinAPI?! - Практика...

Уроки C# – WinAPI + свой контрол (2 в 1)Скачать

Уроки C# – WinAPI + свой контрол (2 в 1)

Треугольник - 3 точки?Скачать

Треугольник - 3 точки?
Поделиться или сохранить к себе: