Федеральное агентство по образованию РФ
Государственное образовательное учреждение высшего профессионального образования
Волгоградский государственный технический университет
(ВолгГТУ)
Кафедра "ЭВМ и сети"
Лабораторная работа №3
по дисциплине:
"Компьютерная графика"
Выполнил: студент группы ВМ-09.2
Тимофеев Е.К.
Проверил: Земцов А.Н.
Оценка работы __________ баллов
Волгоград 2010
1 Задание
Написать программу, выполняющую вращение перспективного изображения куба (или более сложной фигуры, например, икосаэдра или додекаэдра) вокруг произвольного вектора или вращение со случайными параметрами.
2 Листинг программы
Для решения поставленной задачи был создан стандартный проект MFC Dialog на С++ в Microsoft Visial Studio 2008.
В проекте для реализации управлением геометрическими преобразованиями были использованы кнопки блока стрелок клавиатуры. Для чего была перегружена стандартная функция обработки сообщений в проекте. Описание добавлено в Lab3_PatrakeevDS_EVM-09.2Dlg.h:
public:
afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
BOOL PreTranslateMessage(MSG* pMsg); //Перегрузка функции
Реализация перегруженной функции связано с тем, что кнопки стрелок вправо и влево вращают октаэдр вокруг случайного вектора, задаваемой левой клавишей мыши. В свою очередь кнопки вверх и вниз вращают мировые координаты по часовой и против часовой стрелки. Щелчок правой клавишей сбрасывает значения всех параметров к нулевым. Реализация функции находится в Lab3_PatrakeevDS_EVM-09.2Dlg.cpp:
BOOL CLab3_PatrakeevDS_EVM092Dlg::PreTranslateMessage(MSG* pMsg)
{
if(pMsg->message == WM_KEYDOWN)
{
if(pMsg->wParam == VK_LEFT)//поворачиваем октаэдр
{
alfa+=0.0157;//поворачиваем октаэдр вокруг вектора
this->Invalidate();//очищаем клиентскую область
OnPaint();//перерисовываем клиентскую область
}
if(pMsg->wParam == VK_RIGHT)//поворачиваем октаэдр
{
alfa-=0.0157;//поворачиваем октаэдр вокруг вектора
this->Invalidate();//очищаем клиентскую область
OnPaint();//перерисовываем клиентскую область
}
if(pMsg->wParam == VK_UP)//поворачиваем мировые координаты
{
nu+=0.0157;
this->Invalidate();//очищаем клиентскую область
OnPaint();//перерисовываем клиентскую область
}
if(pMsg->wParam == VK_DOWN)//поворачиваем мировые координаты
{
nu-=0.0157;
this->Invalidate();//очищаем клиентскую область
OnPaint();//перерисовываем клиентскую область
}
}
return CDialog::PreTranslateMessage(pMsg);
}
К основной форме проекта было добавлено следующее событие, возникающее при клике левой кнопкой мыши, создающее вектор со случайными параметрами, относительно которого будет происходить вращение октаэдра:
void CLab3_PatrakeevDS_EVM092Dlg::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
this->Invalidate();//очищаем клиентскую область
//зададим случайным образом параметры вектора вращения
srand((unsigned)time(NULL));
xyzvv.x = rand()%30-30;
xyzvv.y = rand()%30-30;
xyzvv.z = rand()%30-30;
nuv = (rand()%314-314)/100.0;
fiv = (rand()%314-314)/100.0;
OnPaint();
CDialog::OnLButtonDown(nFlags, point);
}
Аналогично вызываются действия при клике правой кнопкой мыши, обнуляющие все параметры:
void CLab3_PatrakeevDS_EVM092Dlg::OnRButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
this->Invalidate();//очищаем клиентскую область
//обнуляем параметры вектора вращения
xyzvv.x = 0;
xyzvv.y = 0;
xyzvv.z = 0;
nuv = 0;
fiv = 0;
alfa = 0;
nu = 0.314;
//обнуляем массив точек октаэдра в мировом пространстве с длиной ребра 20 ед.
octm.t1.x = 0;
octm.t1.y = 0;
octm.t1.z = 14;
octm.t2.x = -10;
octm.t2.y = 10;
octm.t2.z = 0;
octm.t3.x = 10;
octm.t3.y = 10;
octm.t3.z = 0;
octm.t4.x = 10;
octm.t4.y = -10;
octm.t4.z = 0;
octm.t5.x = -10;
octm.t5.y = -10;
octm.t5.z = 0;
octm.t6.x = 0;
octm.t6.y = 0;
octm.t6.z = -14;
OnPaint();
CDialog::OnRButtonDown(nFlags, point);
}
Предварительно были объявлены нужные для работы библиотеки и глобальные переменные:
#include "func.h"
#include "math.h"
#include "time.h"
OctaedrXYZ octm = //задаём массив точек октаэдра в мировом пространстве с длиной ребра 20 ед.
{
{0,0,14},
{-10,10,0},
{10,10,0},
{10,-10,0},
{-10,-10,0},
{0,0,-14}
};
int p = 750;//расстояние от глаза до объекта
float nu = 0.314, fi = 0.785;//углы определяющие наклон вектора до глаза в мировых координатах
int d = 600;//расстояние до экрана
double nuv = 0, fiv = 0, alfa = 0;//характеристики вектора вращения и угла поворота
XYZ xyzvv = {0,0,0};//начальная точка вектора вращения определяемого в сферических координатах
Рисование на форме осуществляется модифицированной функцией OnPaint():
void CLab3_PatrakeevDS_EVM092Dlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
//----------------------------------------------------------------------------------
//листинг лабораторной работы
CDC* pDC = this->GetDC();//получение контекста устройства
CRect rect;
this->GetClientRect(&rect); //получаем размеры области вывода
//устанавливаем систему координат
pDC->SetMapMode(MM_ISOTROPIC);
pDC->SetWindowExt(100,100);
pDC->SetViewportExt(rect.Width(),-rect.Height());
pDC->SetViewportOrg(rect.Width()/2,rect.Height()/2);
//устанавливаем инструменты рисования
CBrush brush(RGB(255,0,0));
CPen pen(PS_SOLID,1,RGB(0,0,0));
CBrush* old_brush = pDC->SelectObject(&brush);
CPen* old_pen = pDC->SelectObject(&pen);
//рисуем оси
Osi(p,nu,fi,d,pDC);
//рисуем вектор вращения
Vector(xyzvv,p,nuv,fiv,d,pDC);
//рисуем октаэдр
OctaedrXYZ octv;
PreobIsMirVVid(octm.t1,&octv.t1,p,nu,fi);
PreobIsMirVVid(octm.t2,&octv.t2,p,nu,fi);
PreobIsMirVVid(octm.t3,&octv.t3,p,nu,fi);
PreobIsMirVVid(octm.t4,&octv.t4,p,nu,fi);
PreobIsMirVVid(octm.t5,&octv.t5,p,nu,fi);
PreobIsMirVVid(octm.t6,&octv.t6,p,nu,fi);
OctaedrXY octe;
Perspect(octv.t1,d,&octe.t1);
Perspect(octv.t2,d,&octe.t2);
Perspect(octv.t3,d,&octe.t3);
Perspect(octv.t4,d,&octe.t4);
Perspect(octv.t5,d,&octe.t5);
Perspect(octv.t6,d,&octe.t6);
Transform(&octm.t1,nuv,fiv,xyzvv,alfa);
Transform(&octm.t2,nuv,fiv,xyzvv,alfa);
Transform(&octm.t3,nuv,fiv,xyzvv,alfa);
Transform(&octm.t4,nuv,fiv,xyzvv,alfa);
Transform(&octm.t5,nuv,fiv,xyzvv,alfa);
Transform(&octm.t6,nuv,fiv,xyzvv,alfa);
Octaedr(octe,pDC);
}
}
Реализация пользовательских функций осуществлено в func.h:
#include "stdafx.h"
#include "math.h"
struct XYZ//объявляем структуру типа координат точки
{
int x;
int y;
int z;
};
struct XY//координаты точки в плоскости экрана
{
int x;
int y;
};
struct OctaedrXYZ//структура хранит координаты вершин октаэдра в мировом пространстве
{
XYZ t1;
XYZ t2;
XYZ t3;
XYZ t4;
XYZ t5;
XYZ t6;
};
struct OctaedrXY//структура хранит координаты вершин октаэдра в плоскости экрана
{
XY t1;
XY t2;
XY t3;
XY t4;
XY t5;
XY t6;
};
void Osi(int p, float nu, float fi, int d, CDC* pole)//рисование осей мировых координат
{
CBrush brush(RGB(255,0,0));
CPen pen(PS_SOLID,1,RGB(255,0,0));
CBrush* old_brush = pole->SelectObject(&brush);
CPen* old_pen = pole->SelectObject(&pen);
XYZ OXYZm[4] = //задаём точки для построения осей
{
{0,0,0},
{50,0,0},
{0,50,0},
{0,0,50}
};
XYZ OXYZv[4];
for (int i=0;i<=3;i++)//преобразуем координаты из мировых координат в видовые
{
int mir[4] = {OXYZm[i].x,OXYZm[i].y,OXYZm[i].z,1};
float V[4][4] =
{
{-sin(nu),-cos(fi)*cos(nu),-sin(fi)*cos(nu),0},
{cos(nu),-cos(fi)*sin(nu),-sin(fi)*sin(nu),0},
{0,sin(fi),-cos(fi),0},
{0,0,p,1}
};
float vidV [4] =
{
mir[0]*V[0][0]+mir[1]*V[1][0]+mir[2]*V[2][0]+mir[3]*V[3][0],
mir[0]*V[0][1]+mir[1]*V[1][1]+mir[2]*V[2][1]+mir[3]*V[3][1],
mir[0]*V[0][2]+mir[1]*V[1][2]+mir[2]*V[2][2]+mir[3]*V[3][2],
mir[0]*V[0][3]+mir[1]*V[1][3]+mir[2]*V[2][3]+mir[3]*V[3][3]
};
OXYZv[i].x = vidV[0];
OXYZv[i].y = vidV[1];
OXYZv[i].z = vidV[2];
}
XY OXYZ[4];
for (int j=0;j<=3;j++)//производим перспективные преобразования
{
//Perspect(OXYZv[j],d,&OXYZ[j]);
OXYZ[j].x = d*OXYZv[j].x/OXYZv[j].z;
OXYZ[j].y = d*OXYZv[j].y/OXYZv[j].z;
}
for (int k=1;k<=3;k++)
{
pole->MoveTo(OXYZ[0].x,OXYZ[0].y);
pole->LineTo(OXYZ[k].x,OXYZ[k].y);
}
pole->SelectObject(&old_brush);
pole->SelectObject(&old_pen);
}
void Vector(XYZ xyzvn,int p, float nuv, float fiv, int d, CDC* pole)//рисование вектора вращения
{
CBrush brush(RGB(255,0,0));
CPen pen(PS_SOLID,1,RGB(255,0,0));
CBrush* old_brush = pole->SelectObject(&brush);
CPen* old_pen = pole->SelectObject(&pen);
XYZ xyzvk;
xyzvk.x = 50*sin(fiv)*cos(nuv);
xyzvk.y = 50*sin(fiv)*sin(nuv);
xyzvk.z = 50*cos(fiv);
XYZ OXYZm[2] = //задаём точки для построения вектора
{
xyzvn,
xyzvk
};
XYZ OXYZv[2];
for (int i=0;i<=1;i++)//преобразуем координаты из мировых координат в видовые
{
int mir[4] = {OXYZm[i].x,OXYZm[i].y,OXYZm[i].z,1};
float V[4][4] =
{
{-sin(nuv),-cos(fiv)*cos(nuv),-sin(fiv)*cos(nuv),0},
{cos(nuv),-cos(fiv)*sin(nuv),-sin(fiv)*sin(nuv),0},
{0,sin(fiv),-cos(fiv),0},
{0,0,p,1}
};
float vidV [4] =
{
mir[0]*V[0][0]+mir[1]*V[1][0]+mir[2]*V[2][0]+mir[3]*V[3][0],
mir[0]*V[0][1]+mir[1]*V[1][1]+mir[2]*V[2][1]+mir[3]*V[3][1],
mir[0]*V[0][2]+mir[1]*V[1][2]+mir[2]*V[2][2]+mir[3]*V[3][2],
mir[0]*V[0][3]+mir[1]*V[1][3]+mir[2]*V[2][3]+mir[3]*V[3][3]
};
OXYZv[i].x = vidV[0];
OXYZv[i].y = vidV[1];
OXYZv[i].z = vidV[2];
}
XY OXYZ[2];
for (int j=0;j<=1;j++)//производим перспективные преобразования
{
OXYZ[j].x = d*OXYZv[j].x/OXYZv[j].z;
OXYZ[j].y = d*OXYZv[j].y/OXYZv[j].z;
}
pole->MoveTo(OXYZ[0].x,OXYZ[0].y);
pole->LineTo(OXYZ[1].x,OXYZ[1].y);
pole->SelectObject(&old_brush);
pole->SelectObject(&old_pen);
}
void Octaedr(OctaedrXY octe, CDC* pole)//рисование октаэдра
{
CBrush brush(RGB(255,255,255));
CPen pen(PS_SOLID,1,RGB(0,255,0));
CBrush* old_brush = pole->SelectObject(&brush);
CPen* old_pen = pole->SelectObject(&pen);
POINT mass[6];
mass[0].x=octe.t1.x;
mass[0].y=octe.t1.y;
mass[1].x=octe.t2.x;
mass[1].y=octe.t2.y;
mass[2].x=octe.t3.x;
mass[2].y=octe.t3.y;
mass[3].x=octe.t4.x;
mass[3].y=octe.t4.y;
mass[4].x=octe.t5.x;
mass[4].y=octe.t5.y;
mass[5].x=octe.t6.x;
mass[5].y=octe.t6.y;
POINT m1[3] = {mass[0],mass[1],mass[2]};
POINT m2[3] = {mass[5],mass[1],mass[2]};
POINT m3[3] = {mass[0],mass[2],mass[3]};
POINT m4[3] = {mass[5],mass[2],mass[3]};
POINT m5[3] = {mass[0],mass[3],mass[4]};
POINT m6[3] = {mass[5],mass[3],mass[4]};
POINT m7[3] = {mass[0],mass[4],mass[1]};
POINT m8[3] = {mass[5],mass[4],mass[1]};
pole->Polygon(m1,3);
pole->Polygon(m2,3);
pole->Polygon(m3,3);
pole->Polygon(m4,3);
pole->Polygon(m5,3);
pole->Polygon(m6,3);
pole->Polygon(m7,3);
pole->Polygon(m8,3);
pole->Polyline(m1,3);
pole->Polyline(m2,3);
pole->Polyline(m3,3);
pole->Polyline(m4,3);
pole->Polyline(m5,3);
pole->Polyline(m6,3);
pole->Polyline(m7,3);
pole->Polyline(m8,3);
pole->SelectObject(&old_brush);
pole->SelectObject(&old_pen);
}
void Transform(XYZ* xyz, double nu, double fi, XYZ xyzvn, double alfa)//вращение точки вокруг вектора с входными параметрами
{
double r11 = cos(nu)*(cos(nu)*pow(sin(fi),2)+cos(fi)*(sin(nu)*sin(alfa)+cos(alfa)*cos(nu)*cos(fi)))+
sin(nu)*(cos(alfa)*sin(nu)-cos(nu)*cos(fi)*sin(alfa));//
double r12 = sin(nu)*(cos(nu)*pow(sin(fi),2)+cos(fi)*(sin(alfa)*sin(nu)+cos(alfa)*cos(nu)*cos(fi)))-
cos(nu)*(cos(alfa)*sin(nu)-cos(nu)*cos(fi)*sin(alfa));//
double r13 = cos(nu)*cos(fi)*sin(fi)-sin(fi)*(sin(alfa)*sin(nu)+cos(alfa)*cos(nu)*cos(fi));//
double r21 = cos(nu)*(sin(nu)*pow(sin(fi),2)-cos(fi)*(cos(nu)*sin(alfa)-cos(alfa)*sin(nu)*cos(fi)))-
sin(nu)*(cos(alfa)*cos(nu)+sin(nu)*cos(fi)*sin(alfa));//
double r22 = sin(nu)*(sin(nu)*pow(sin(fi),2)-cos(fi)*(sin(alfa)*cos(nu)-cos(alfa)*sin(nu)*cos(fi)))+
cos(nu)*(cos(alfa)*cos(nu)+sin(nu)*cos(fi)*sin(alfa));//
double r23 = cos(fi)*sin(nu)*sin(fi)+sin(fi)*(sin(alfa)*cos(nu)-cos(alfa)*sin(nu)*cos(fi));//
double r31 = cos(nu)*(cos(fi)*sin(fi)-sin(fi)*cos(alfa)*cos(fi))+sin(fi)*sin(alfa)*sin(nu);//
double r32 = sin(nu)*(cos(fi)*sin(fi)-cos(alfa)*cos(fi)*sin(fi))-cos(nu)*sin(alfa)*sin(fi);//
double r33 = pow(sin(fi),2)*cos(alfa)+pow(cos(fi),2);//
xyz->x = r11*xyz->x+r21*xyz->y+r31*xyz->z-r11*xyzvn.x-r21*xyzvn.y-r31*xyzvn.z+xyzvn.x;
xyz->y = r12*xyz->x+r22*xyz->y+r32*xyz->z-r12*xyzvn.x-r22*xyzvn.y-r32*xyzvn.z+xyzvn.y;
xyz->z = r13*xyz->x+r23*xyz->y+r33*xyz->z-r13*xyzvn.x-r23*xyzvn.y-r33*xyzvn.z+xyzvn.z;
}
void PreobIsMirVVid(XYZ Mir, XYZ* Vid, int p, float nu, float fi)
{
int mir[4] = {Mir.x,Mir.y,Mir.z,1};
float V[4][4] =
{
{-sin(nu),-cos(fi)*cos(nu),-sin(fi)*cos(nu),0},
{cos(nu),-cos(fi)*sin(nu),-sin(fi)*sin(nu),0},
{0,sin(fi),-cos(fi),0},
{0,0,p,1}
};
float vidV [4] =
{
mir[0]*V[0][0]+mir[1]*V[1][0]+mir[2]*V[2][0]+mir[3]*V[3][0],
mir[0]*V[0][1]+mir[1]*V[1][1]+mir[2]*V[2][1]+mir[3]*V[3][1],
mir[0]*V[0][2]+mir[1]*V[1][2]+mir[2]*V[2][2]+mir[3]*V[3][2],
mir[0]*V[0][3]+mir[1]*V[1][3]+mir[2]*V[2][3]+mir[3]*V[3][3]
};
Vid->x = vidV[0];
Vid->y = vidV[1];
Vid->z = vidV[2];
}
void Perspect(XYZ Vid, int d ,XY* xy)
{
xy->x = d*Vid.x/Vid.z;
xy->y = d*Vid.y/Vid.z;
}