МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ
НАЦІОНАЛЬНИЙ УНІВЕРСИТЕТ “ЛЬВІВСЬКА ПОЛІТЕХНІКА”
Кафедра КСА
Пояснювальна записка
до розрахункової роботи
з курсу "Програмування комп'ютерної графіки"
Перевірив:
старший викладач Іванюк О.О.
1.1 Завдання
Побудувати графік функції заданої таблично. Масштаб розмітки осей координат графіка повинен відповідати реальним результатам розрахунків. Параметри для побудови графіка визначені у таблиці 2.
Необхідно передбачити плаваючий центр координат – основну частину екрану мають займати ті чверті, в яких знаходиться графік функції.
ЗАВДАННЯ по графіку
Таблиця 1
№ варіанту
Функція f(x)
Діапазон зміни аргументу
3
z = tg(x)
[-0.49*π ; 0.49*π]
Таблиця 2
№ варіанту
Тип лінії для побудови
Товщина лінії
Колір лінії
3
4
Red
1.2 Завдання
Створити на екрані комп’ютера графічне вікно і сформувати в ньому рухоме зображення. Вікно розмістити у верхньому правому куті екрану. Навести межі вікна.
Параметри зображення задані в пікселях. Графік і вікно з рухомим зображенням повинні присутні на екрані одночасно.
ЗАВДАННЯ по анімації
№ варіанту
Структура зображення
Пояснення
23
Коло з чотирма спицями скочується по сходинках.
Параметри: а = 40, L=70, d = 18.
2.1 Розрахунок функціональних залежностей для побудови графіка
Оскільки реальні координати графіка функції, що виводиться на екран можуть бути або значно більшими, або значно меншими за машинні координати екрану монітору, не-
обхідно визначити значення масштабних коефіцієнтів стискання або розтягу для функції, що виводиться на екран.
Протабулюємо задану функцію на проміжку зміни аргументу з метою визначення максимального та мінімального значень функції і аргумента : xmax , xmin, zmax, zmin.
Знайдемо значення масштабуючих коефіцієнтів:
kx = (rectright - 50) / (xmax - xmin);
kz = (rect.bottom - 50) / (zmax - zmin);
де: rectright, rect.bottom – координати меж вікна.
Використовуючи обчислені коефіцієнти побудуємо графік функції і осі координат, нанесемо розмітку.
2.2 Розрахунок матриці перетворень для рухомого зображення
Для побудови зображень на екрані комп'ютера використовуються операції переносу, масптабування, повороту та їх композиції.
У своєму завданні я вирішив скористатись формулами розрахунку повороту в полярних координатах. Це передусім повязано з простотою їх використання.
Розрахунок повороту в полярних координатах можна виразити наступним чином :
x = rd * sin(angle) + Mx
y = -rd * cos(angle) + My
де:
angle – кут повороту;
М(х,у) – точка відносно якої відбувається операція повороту.
rd – радіус вектор.
У циклі програми, який реалізує рух обєкта прямолінійно я скористався цими ж формулами:
circleX = rd * sin(angle2) + rect.right - mx + step;
circleY = -rd * cos(angle2) + rect.top + my;
де:
(rect.right – mx, rect.top + my) – координати точки відносно якої відбувається рух.
Обчислення координати рухомої точки обчислюються при постійному значенні angle2 = 90 °:
sin(angle2) = cos(angle2) = 1
Тому точка не повертається, а рухається прямолінійно.
Координати вершин спиць, і координати цента при падінні обєкта обчислюються при змінному значенні кута angle. Тому в цьому випадку відбувається поворот точки на певний кут.
3. Список індетифікаторів програми
Побудова графіка функції:
rect – структура, в якій записані розміри екрана.
rect.bottom, rect.right – нижня та права сторона вікна, відповідає за розміри клієнтського вікна.
rect.top, rect.left – верхня та ліва сторона вікна, рівна 0.
rectright – зміння яка задає ширину області для побудови графіка функції.
str[192] – массив символів.
kx, kz – коефініент перетворення координат.
x0, z0 – мінімальне значення графіка в машинних координатах по осях x та y відповідно.
x – змінна, з інтервалом зміни від x1 до x2 з кроком h.
z – значення функції.
lx, lz – довжина осі по x та z відповідно.
sx, sz – ціле значення від розбиття осі на 10 частин по x та z відповідно.
tx, tz – змінна, значення якої відповідає довжині додатної осі по x та від’ємної по z відповідно.
xc, zc – центр графіка в точці x=0 по осях x та z відповідно. Плаваючий центр координат.
xx, zz – змінна, яка відповідає за розбиття графіка на 10 частин по осях x та z відповідно.
xmax , xmin, zmax, zmin – мінімальне та максимальне значення функції по осях x та z відповідно.
txp, txm, tzp, tzm – змінні, які визначають положення тексту розмітки осей у додатному та від’мному напрямках x та z відповідно.
Побудова анімації:
j, k – змінні які використовуються в циклах if() i for().
rd – радіус кола зі спицями.
h – крок.
аngle – кут, який використовується в формулі прямолінійного руху кола.
аngle2 – кут, який використовується в формулах які описують обертання спиць і падіння кола.
step – крок, з яким рухається коло.
circleX, circleY – координати центра кола.
dxc1, dyc1, dxc2, dyc2, dxc3, dyc3, dxc4, dyc4 – координати точок спиць при обертанні навколо центра кола, ці точки лежать на дузі кола.
mx, my – коефіцієнти, які разом із rect задають положення рухомого зображення. Використовуються в формулах прямолінійного руху кола.
gx, gy – коефіцієнти які разом із rect задають положення рухомого зображення. Використовуються в формулах падіння кола.
Команди і функції:
GetClientRect(&rect) – команда для запису розмірів екрана в структуру rect.
swprintf_s() – записує значення змінної в масив str.
TextOut() – виводить значення змінної по заданих координатах.
MoveTo(x, y) – переміщує поточну вершину, зміння x, y визначають координати нової поточнох вершини.
LineTo(x, y) – будує лінію з поточної вершини у точку з еоординатами x, y.
Rectangle(int x1, int y1, int x2, int y2) - малює прямокутник, x1, у1 задають координати верхнього лівого куга, a x2, у2 правого нижнього кута прямокутника.
Ellipse(int x1, int y1, int x2, int y2) - малює еліпс, x1, у1 задають координати верхнього лівого куга, a x2, у2 правого нижнього кута прямокутника який визначає розміри еліпса. При використанні функцій «перо» , колір контуру еліпса задається «пером».
Створює і задає перо з параметрами:
CPen «імя»;
«імя».CreatePen(«стиль пера», «товщина пера», «колір RGB»);
dc.SelectObject(&«імя»).
5. Текст програми
#include "stdafx.h"
#include "PKG_V2.0.h"
#include "ChildView.h"
#define _USE_MATH_DEFINES //для використання M_PI
#include <math.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
CChildView::CChildView()
{
}
CChildView::~CChildView()
{
}
BEGIN_MESSAGE_MAP(CChildView, CWnd)
ON_WM_PAINT()
END_MESSAGE_MAP()
BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs)
{
if (!CWnd::PreCreateWindow(cs))
return FALSE;
cs.dwExStyle |= WS_EX_CLIENTEDGE;
cs.style &= ~WS_BORDER;
cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS,
::LoadCursor(NULL, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1), NULL);
return TRUE;
}
void CChildView::OnPaint()
{
CPaintDC dc(this);
RECT rect;
GetClientRect(&rect);
////Побудова графіка функції////////////////////////////////////////////////////
wchar_t str[192];
double kx, kz, x0, z0, x, z, lx, lz, sx, sz, xc, zc, tx, tz, xx, zz, t;
double xmax = 0, xmin = 0, zmax = 0, zmin = 0, txp = 0, txm = 0, tzp = 0, tzm = 0;
double x1 = -0.49*M_PI, x2 = 0.49*M_PI, hh = 0.0005;
double rectright = rect.right - 270;
// Табулювання заданої функції на проміжку зміни аргументу з метою визначення максимального та мінімального значень функції
for (x = x1; x <= x2; x = x + hh)
{
z = tan(x);
if (zmin > -z)
{
zmin = -z;
}
else if (zmax<-z)
{
zmax = -z;
}
if (xmin>x)
{
xmin = x;
}
else if (xmax < x)
{
xmax = x;
}
}
//знаходимо значення функції в точці x=0
x = 0;
z = tan(x);
//знаходимо коефіцієнти і центр реальних координат
kx = (rectright - 50) / (xmax - xmin);
kz = (rect.bottom - 50) / (zmax - zmin);
x0 = xmin*kx - 25;
z0 = zmin*kz - 25;
xc = x*kx - x0;
zc = -z*kz - z0;
//Побудова осей
//побудова Z
dc.MoveTo(xc, rect.top + 10);
dc.LineTo(xc, rect.bottom - 10);
//побудова Х
dc.MoveTo(rect.left + 10, zc);
dc.LineTo(rectright - 10, zc);
dc.MoveTo(xc - 5, rect.top + 15); // стрілка осі Z
dc.LineTo(xc, rect.top + 10);
dc.LineTo(xc + 5, rect.top + 15);
dc.MoveTo(rectright - 15, zc - 5); // стрілка осі Х
dc.LineTo(rectright - 10, zc);
dc.LineTo(rectright - 15, zc + 5);
//розмітка осей
const CString Z("Z"), X("X"), O("0");
dc.TextOutW(xc - 20, rect.top + 5, Z);
dc.TextOutW(rectright - 20, zc + 5, X);
dc.TextOutW(xc + 5, zc + 5, O);
lx = (rectright - 10) - (rect.left + 10); //довжини осей
lz = (rect.bottom - 10) - (rect.top + 10);
sx = floor(lx / 15);
sz = floor(lz / 15);
tx = rectright - fabs(x0);
tz = rect.bottom - fabs(z0);
xx = (xmax - xmin) / 15;
zz = (zmax - zmin) / 15;
//розмітка осі Х
for (t = sx; t <= -x0 - 30; t = t + sx) //в відємному напрямі
{
txm = txm - xx;
dc.MoveTo(xc - t, zc - 5);
dc.LineTo(xc - t, zc + 5);
swprintf_s(str, 50, L"%.1f", txm);
dc.TextOut(xc - t - 10, zc + 5, str);
}
for (t = sx; t <= tx - 30; t = t + sx)//в додатньому напрямі
{
txp = txp + xx;
dc.MoveTo(xc + t, zc - 5);
dc.LineTo(xc + t, zc + 5);
swprintf_s(str, 50, L"%.1f", txp);
dc.TextOut(xc + t - 10, zc + 5, str);
}
//розмітка осі Z
for (t = sz; t <= -z0 - 15; t = t + sz)//в додатньому напрямі
{
tzp = tzp + zz;
dc.MoveTo(xc - 5, zc - t);
dc.LineTo(xc + 5, zc - t);
swprintf_s(str, 50, L"%.1f", tzp /57, 29578);
dc.TextOut(xc + 10, zc - t - 10, str);
}
for (t = sz; t <= tz - 15; t = t + sz)//в відємному напрямі
{
tzm = tzm - zz;
dc.MoveTo(xc - 5, zc + t);
dc.LineTo(xc + 5, zc + t);
swprintf_s(str, 50, L"%.1f", tzm / 57, 29578);
dc.TextOut(xc + 10, zc + t - 10, str);
}
CPen dRED;
dRED.CreatePen(PS_DASH, 1.9, RGB(255, 0, 0));
dc.SelectObject(&dRED);
//побудова графіка
for (x = x1; x <= x2; x = x + hh)
{
z = tan(x);
if (x == x1)
{
dc.MoveTo(x*kx - x0, -z*kz - z0);
}
dc.LineTo(x*kx - x0, -z*kz - z0);
}
//Побудова анімації/////////////////////////////////////////////////////////////
int j, k;
double rd = 9, h = 0.3;
double angle, step, circleX, circleY;
double dxc1, dyc1, dxc2, dyc2, dxc3, dyc3, dxc4, dyc4;
double mx, my, gx, gy;
double angle2 = 3.1415 / 2;
dc.SelectStockObject(BLACK_PEN);
dc.Rectangle(rect.right - 263, rect.top + 12, rect.right - 12, rect.top + 176);
dc.MoveTo(rect.right - 235, rect.top + 41);
dc.LineTo(rect.right - 215, rect.top + 41);
dc.LineTo(rect.right - 215, rect.top + 81);
dc.LineTo(rect.right - 145, rect.top + 81);
dc.LineTo(rect.right - 145, rect.top + 121);
dc.LineTo(rect.right - 75, rect.top + 121);
dc.LineTo(rect.right - 75, rect.top + 161);
dc.LineTo(rect.right - 39, rect.top + 161);
for (int g = 0; g < 7;g++)
{
for (j = 0; j <= 2; j++)
{
if (j == 0)
{
k = 1;
mx = 243.5;
my = 33;
gx = 208;
gy = 32;
}
if (j == 1)
{
k = 2;
mx = 200;
my = 73;
gx = 138;
gy = 72;
}
if (j == 2)
{
k = 2;
mx = 129.37;
my = 113;
gx = 68.73;
gy = 112;
}
//рух вправо
for (angle = 0; angle <= 3.1415 * k; angle += h / 8)
{
step = angle * 8;
circleX = rd * sin(angle2) + rect.right - mx + step;
circleY = -rd * cos(angle2) + rect.top + my;
dxc1 = rd * sin(angle) + circleX;
dyc1 = -rd * cos(angle) + circleY;
dxc2 = rd * sin(angle + 3.1415 / 2) + circleX;
dyc2 = -rd * cos(angle + 3.1415 / 2) + circleY;
dxc3 = rd * sin(angle + 3.1415) + circleX;
dyc3 = -rd * cos(angle + 3.1415) + circleY;
dxc4 = rd * sin(angle + 3.1415 + 3.1415 / 2) + circleX;
dyc4 = -rd * cos(angle + 3.1415 + 3.1415 / 2) + circleY;
dc.SelectStockObject(BLACK_PEN);
dc.Ellipse(circleX - rd, circleY - rd, circleX + rd, circleY + rd);
dc.MoveTo(circleX, circleY); dc.LineTo(dxc1, dyc1);
dc.MoveTo(circleX, circleY); dc.LineTo(dxc2, dyc2);
dc.MoveTo(circleX, circleY); dc.LineTo(dxc3, dyc3);
dc.MoveTo(circleX, circleY); dc.LineTo(dxc4, dyc4);
Sleep(10);
dc.SelectStockObject(WHITE_PEN);
dc.Ellipse(circleX - rd, circleY - rd, circleX + rd, circleY + rd);
dc.MoveTo(circleX, circleY); dc.LineTo(dxc1, dyc1);
dc.MoveTo(circleX, circleY); dc.LineTo(dxc2, dyc2);
dc.MoveTo(circleX, circleY); dc.LineTo(dxc3, dyc3);
dc.MoveTo(circleX, circleY); dc.LineTo(dxc4, dyc4);
}
//падіння
for (angle = 0; angle <= 3.1415 / 2; angle += h / 20)
{
step = angle * 8;
circleX = rd * sin(angle) + rect.right - gx + step / 1.5;
circleY = -rd * cos(angle) + rect.top + gy + rd + step*2.5;
dxc1 = rd * sin(angle - 3.1415 / 2) + circleX;
dyc1 = -rd * cos(angle - 3.1415 / 2) + circleY;
dxc2 = rd * sin(angle + 3.1415) + circleX;
dyc2 = -rd * cos(angle + 3.1415) + circleY;
dxc3 = rd * sin(angle + 3.1415 + 3.1415 + 3.1415 / 2) + circleX;
dyc3 = -rd * cos(angle + 3.1415 + 3.1415 + 3.1415 / 2) + circleY;
dxc4 = rd * sin(angle + 3.1415 * 2) + circleX;
dyc4 = -rd * cos(angle + 3.1415 * 2) + circleY;
dc.SelectStockObject(BLACK_PEN);
dc.Ellipse(circleX - rd, circleY - rd, circleX + rd, circleY + rd);
dc.MoveTo(circleX, circleY); dc.LineTo(dxc1, dyc1);
dc.MoveTo(circleX, circleY); dc.LineTo(dxc2, dyc2);
dc.MoveTo(circleX, circleY); dc.LineTo(dxc3, dyc3);
dc.MoveTo(circleX, circleY); dc.LineTo(dxc4, dyc4);
Sleep(10);
dc.SelectStockObject(WHITE_PEN);
dc.Ellipse(circleX - rd, circleY - rd, circleX + rd, circleY + rd);
dc.MoveTo(circleX, circleY); dc.LineTo(dxc1, dyc1);
dc.MoveTo(circleX, circleY); dc.LineTo(dxc2, dyc2);
dc.MoveTo(circleX, circleY); dc.LineTo(dxc3, dyc3);
dc.MoveTo(circleX, circleY); dc.LineTo(dxc4, dyc4);
}
}
}
}
6.Отриманий результат
7.ВИСНОВОК
Виконавши завдання даної роботи я набув практичних навиків в складанні програми для побудови зображень на екрані комп’ютера в середовищі Microsoft Visual Studio C++ 2008.
Ключовим моментом у програмуванні анімації є точне визначення матриць руху чи обертання реперних точок фігури та максимально точне обчислення координат подальшого положення фігури з метою уникнення похибок, які можуть суттєво погіршити результат. Суть анімації – зміна положення предмета з “затиранням” попереднього положення.
4. Блок-схема програми