Задачи и порядок выполнения работы В работе требуется создать абстрактный класс «геометрическая фигура на экране», который содержит ЧВФ для рисования фигуры (какая фигура заранее неизвестно). На основе этого абстрактного класса необходимо создать производные классы, определяющие конкретные геометрические фигуры. При работе с объектами этих классов необходимо использовать массив указателей, имеющих тип абстрактного класса, что позволит для работы с объектами разных классов использовать один цикл.
Условие задачи:
Создать абстрактный класс- «геометрическая фигура» (на экране). Класс содержит следующие поля: координаты геометрического центра фигуры на экране; поле, задающее размер фигуры (например, расстояние от центра о вершины или радиус окружности в пикселях); поле, задающее угловое положение фигуры; поле, задающее угловую скорость вращения фигуры; поле, определяющее направление движения (возможно два варианта: движение по вертикали и движение по горизонтали); поле, определяющее скорость движения; поле, определяющее цвет фигуры; поле, содержащее хэндл окна для рисования; при необходимости можно включить другие поля. Класс включает: конструктор для инициализации полей, функцию, изменяющую угловое положение фигуры и положение на экране во время движения за один такт времени, и чистую виртуальную функцию (или функции) для рисования и стирания фигуры на экране.
На основе абстрактного класса «фигура» разработать программу, содержащую описание трех графических классов: правильный многоугольник, отрезок (линия), половина окружности. Создать несколько объектов каждого из трех классов (не менее трех) для этого использовать один массив указателей типа базового класса «фигура». Реализуя механизм полиморфизма, привести объекты классов в одновременное вращение вокруг их геометрических центров с различными угловыми скоростями и в движение с отскоком от краев окна в заданном режиме (по горизонтали или по вертикали). При этом использовать обработку сообщений от таймера, таймер периодически с интервалом несколько миллисекунд генерирует сообщение, при обработке сообщения стирается старая фигура и рисуется новая на новом месте.
Возможный внешний вид окна приложения с движущимися фигурами представлен на рисунке 3.
Рисунок 3 – Внешний вид окна приложения
Пример выполнения работы
В работе создается приложение Windows с графическим интерфейсом пользователя (Проект Win32 или Win32 Project для нерусифицированной версии).
В листинге, представленном ниже, приведен только код файла MyGrahics.cpp. В среде Microsoft Visual Studio 2013 было создано стандартное оконное приложение (Проект Win32 или Win32 Project для нерусифицированной версии) с именем MyGrahics (имя может быть другим), все остальные файлы: stdafx.h, MyGrahics.h и другие – стандартные, сгенерированы автоматически при создании проекта. В файл stdafx.h необходимо в конце добавить 2 строчки:
// TODO: Установите здесь ссылки на дополнительные заголовки, требующиеся для программы
#define _USE_MATH_DEFINES
#include <math.h>
Листинг файла MyGraphics.cpp с комментариями:
// MyGraphics.cpp: определяет точку входа для приложения.
//
#include "stdafx.h"
#include "MyGraphics.h"
#define MAX_LOADSTRING 100
// Глобальные переменные:
HINSTANCE hInst; // текущий экземпляр
TCHAR szTitle[MAX_LOADSTRING]; // Текст строки заголовка
TCHAR szWindowClass[MAX_LOADSTRING]; // имя класса главного окна
// Отправить объявления функций, включенных в этот модуль кода:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: разместите код здесь.
MSG msg;
HACCEL hAccelTable;
// Инициализация глобальных строк
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_MYGRAPHICS, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Выполнить инициализацию приложения:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MYGRAPHICS));
// Цикл основного сообщения:
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
//
// ФУНКЦИЯ: MyRegisterClass()
//
// НАЗНАЧЕНИЕ: регистрирует класс окна.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
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.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MYGRAPHICS));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_MYGRAPHICS);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassEx(&wcex);
}
//
// ФУНКЦИЯ: InitInstance(HINSTANCE, int)
//
// НАЗНАЧЕНИЕ: сохраняет обработку экземпляра и создает главное окно.
//
// КОММЕНТАРИИ:
//
// В данной функции дескриптор экземпляра сохраняется в глобальной переменной, а также
// создается и выводится на экран главное окно программы.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
hInst = hInstance; // Сохранить дескриптор экземпляра в глобальной переменной
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
// Абстрактный класс Фигура
class Figure
{
protected: // Поля наследуются должен быть доступ в производном классе
int x, y; // Координаты геометр центра
int R; // Расстояние от центра до вершины
int Ang; // Угловое положение в градусах
int VAng; // Угловая скорость (градусов за интервал срабатывания таймера)
int V; // Скорость (пикселей за интервал таймера)
int Napr; // Направление движения 0 - вертикально 1- горизонтально
COLORREF col; // Цвет фигуры
HWND hWnd; // Хэндел окна, где рисуется фигура
int N_Reg; // Направление перемещение
// (1 - вправо или вниз; -1 - влево или вверх)
public:
// Конструктор для инициализации всех параметров
Figure(int R, int VAng, int V, int Napr, COLORREF col, HWND hWnd);
// Метод меняет положение фигура за 1 такт таймера
virtual void step();
// Чистая виртуальная функция для рисования (стирания) фигуры
virtual void draw(int Reg) = 0;
};
// Определение конструктора и функций за пределами класса
Figure::Figure(int R, int VAng, int V, int Napr, COLORREF col, HWND hWnd)
{
this->R = R;
this->VAng = VAng;
this->V = V;
this->Napr = Napr;
this->col = col;
this->hWnd = hWnd;
Ang = 0;
N_Reg = 1; // Координаты увеличиваются
RECT rect; // Размеры окна
GetClientRect(hWnd, &rect); // Получаем размеры окна
// Начальное положение фигуры в центре окна
x = rect.right / 2;
y = rect.bottom / 2;
}
// Определение метода меняющего положение фигуры за 1 такт таймера
void Figure::step()
{
// Меняем угловое положение фигуры
Ang += VAng;
if (Ang >= 360) Ang -= 360;
// Получаем размеры окна
RECT rect;
GetClientRect(hWnd, &rect);
// Меняем положение центра фигуры
if (Napr == 1) // Движение горизонтально меняется x
{
x += V*N_Reg;
if (N_Reg == 1) // Движемся вправо
{
if (x + R >= rect.right) // Достигли правой границы окна
N_Reg = -1; // Меняем направление движения
}
else // Движемся влево
{
if (x - R <= 0) // Достигли левой границы
N_Reg = 1; // Меняем направление движения
}
}
else // Движение вертикально меняется y
{
y += V*N_Reg;
if (N_Reg == 1) // Движемся вниз
{
if (y + R >= rect.bottom) // Достигли нижней границы окна
N_Reg = -1; // Меняем направление движения
}
else // Движемся вверх
{
if (y - R <= 0) // Достигли верхней границы
N_Reg = 1; // Меняем направление движения
}
}
}
// Класс многоугольник
class MyPolygon : public Figure
{
protected:
int N; // Число вершин
POINT *p; // Массив координат вершин
public:
// Заголовок конструктора
MyPolygon(int R, int VAng, int V, int Napr, COLORREF col, HWND hWnd, int N);
void step(); // Метод дополнительно считает новые координаты вершин
void draw(int Reg); // Метод рисования фигуры
};
// Определение конструктора
MyPolygon::MyPolygon(int R, int VAng, int V, int Napr, COLORREF col, HWND hWnd, int N) :
// Вызов конструктора базового класса
Figure(R, VAng, V, Napr, col, hWnd)
{
this->N = N;
p = new POINT[N]; // Создаем массив координат вершин
// Расчет координат вершин
double A = Ang*M_PI / 180; // Угол в градусах переводим в радианы
double A1 = 2 * M_PI / N; // Угол между направлениями на соседние вершины из
// центра фигуры
for (int i = 0; i<N; i++, A += A1)
{
p[i].x = x + R*cos(A);
p[i].y = y - R*sin(A);
}
}
void MyPolygon::step() // Метод дополнительно считает новые координаты вершин
{
Figure::step(); // Вызов метода базового класса
// Расчет координат вершин многоугольника
double A = Ang*M_PI / 180; // Угол в градусах переводим в радианы
double A1 = 2 * M_PI / N; // Угол между направлениями на соседние вершины из
// центра фигуры
for (int i = 0; i<N; i++, A += A1)
{
p[i].x = x + R*cos(A);
p[i].y = y - R*sin(A);
}
}
void MyPolygon::draw(int Reg) // Метод рисования фигуры
{
HPEN pen;
if (Reg == 1) // Режим рисования фигуры
pen = CreatePen(PS_SOLID, 1, col);
else // Режим стирания (белое перо)
pen = CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
HDC hdc;
// Получаем контекст устройства
hdc = GetDC(hWnd);
SelectObject(hdc, pen); // Загрузка пера в контекст устройства
MoveToEx(hdc, p[0].x, p[0].y, 0); // Графический курсор в первую вершину
for (int i = 1; i<N; i++)
LineTo(hdc, p[i].x, p[i].y);
LineTo(hdc, p[0].x, p[0].y); // Последнею вершину соединяем с первой
ReleaseDC(hWnd, hdc);
DeleteObject(pen); // Удаляем перо
}
// Класс отрезок
class MyOtrezok : public Figure
{
protected:
int x1, y1, x2, y2; // Координаты концов отрезка
public:
// Заголовок конструктора
MyOtrezok(int R, int VAng, int V, int Napr, COLORREF col, HWND hWnd);
void step(); // Метод дополнительно считает новые координаты концов отрезка
void draw(int Reg); // Метод рисования фигуры
};
// Определение конструктора
MyOtrezok::MyOtrezok(int R, int VAng, int V, int Napr, COLORREF col, HWND hWnd) :
// Вызов конструктора базового класса
Figure(R, VAng, V, Napr, col, hWnd)
{
// Расчет координат вершин
double A = Ang*M_PI / 180; // Угол в градусах переводим в радианы
x1 = x + R*cos(A);
y1 = y - R*sin(A);
x2 = x - R*cos(A);
y2 = y + R*sin(A);
}
void MyOtrezok::step() // Метод дополнительно считает новые координаты вершин
{
Figure::step(); // Вызов метода базового класса
// Расчет координат вершин многоугольника
double A = Ang*M_PI / 180; // Угол в градусах переводим в радианы
x1 = x + R*cos(A);
y1 = y - R*sin(A);
x2 = x - R*cos(A);
y2 = y + R*sin(A);
}
void MyOtrezok::draw(int Reg) // Метод рисования фигуры
{
HPEN pen;
if (Reg == 1) // Режим рисования фигуры
pen = CreatePen(PS_SOLID, 1, col);
else // Режим стирания (белое перо)
pen = CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
HDC hdc;
// Получаем контекст устройства
hdc = GetDC(hWnd);
SelectObject(hdc, pen); // Загрузка пера в контекст устройства
MoveToEx(hdc, x1, y1, 0); // Графический курсор в первую вершину
LineTo(hdc, x2, y2); // Последнею вершину соединяем с первой
ReleaseDC(hWnd, hdc);
DeleteObject(pen); // Удаляем перо
}
// Класс половина круга
class MyPoluKrug : public Figure
{
public:
// Заголовок конструктора
MyPoluKrug(int R, int VAng, int V, int Napr, COLORREF col, HWND hWnd);
void draw(int Reg); // Метод рисования фигуры
};
// Определение конструктора
MyPoluKrug::MyPoluKrug(int R, int VAng, int V, int Napr, COLORREF col, HWND hWnd) :
// Вызов конструктора базового класса
Figure(R, VAng, V, Napr, col, hWnd)
{
}
void MyPoluKrug::draw(int Reg) // Метод рисования фигуры
{
HPEN pen;
if (Reg == 1) // Режим рисования фигуры
pen = CreatePen(PS_SOLID, 1, col);
else // Режим стирания (белое перо)
pen = CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
HDC hdc;
// Получаем контекст устройства
hdc = GetDC(hWnd);
SelectObject(hdc, pen); // Загрузка пера в контекст устройства
MoveToEx(hdc, x, y, 0); // Курсор помещаем в центр круга
AngleArc(hdc, x, y, R, Ang, 180); // Рисуем полуокружность
LineTo(hdc, x, y); // Соединяем полуокружность с центром
ReleaseDC(hWnd, hdc);
DeleteObject(pen); // Удаляем перо
}
Figure *pF[9]; // Будет создано 9 объектов
//
// ФУНКЦИЯ: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// НАЗНАЧЕНИЕ: обрабатывает сообщения в главном окне.
//
// WM_COMMAND - обработка меню приложения
// WM_PAINT -Закрасить главное окно
// WM_DESTROY - ввести сообщение о выходе и вернуться.
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_CREATE: // Создание окна
SetTimer(hWnd, 1, 20, 0); // Таймер с номером 1 срабатывает через 10 мс
// Создаем объекты
pF[0] = new MyPolygon(100, 1, 10, 0, RGB(255, 0, 0), hWnd, 3);
pF[1] = new MyPolygon(150, 2, 5, 0, RGB(0, 255, 0), hWnd, 3);
pF[2] = new MyPolygon(70, 3, 3, 1, RGB(0, 0, 255), hWnd, 3);
pF[3] = new MyOtrezok(50, 4, 2, 1, RGB(255, 0, 255), hWnd);
pF[4] = new MyOtrezok(100, 2, 1, 1, RGB(0, 255, 255), hWnd);
pF[5] = new MyOtrezok(150, 1, 2, 0, RGB(0, 0, 255), hWnd);
pF[6] = new MyPoluKrug(50, 2, 2, 0, RGB(255, 0, 255), hWnd);
pF[7] = new MyPoluKrug(100, 1, 3, 0, RGB(0, 255, 255), hWnd);
pF[8] = new MyPoluKrug(150, 3, 4, 1, RGB(0, 0, 255), hWnd);
break;
case WM_TIMER: // Сообщение от таймера
for (int i = 0; i<9; i++)
{
pF[i]->draw(0); // Стираем старую фигуру
pF[i]->step(); // Меняем положение фигуры
pF[i]->draw(1); // Рисуем фигуру на новом месте
}
break;
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Разобрать выбор в меню:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
KillTimer(hWnd, 1); // Удаляем таймер
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: добавьте любой код отрисовки...
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
KillTimer(hWnd, 1); // Удаляем таймер
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Обработчик сообщений для окна "О программе".
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
|