Знакомство с возможностями и структурой приложения
Контрольные задания
Исходные файлы проекта приложения
Знакомство с возможностями и структурой приложения
Приложение, демонстрирующее аффинное преобразование треугольника в плоскости, было модифицировано в приложение, демонстрирующее аффинное преобразование пространства. Здесь, вместо плоской фигуры (треугольника) создается и подвергается преобразованиям пространственный объект (рупорная антенна).
Для создания проекта используйте исходные файлы проекта приложения.
Проследите, чтобы при создании проекта была установка x86 (32-разрядная программа):
Если возникают ошибки из-за преобразования символов, то в окне свойств проекта установите «Использовать набор символов Юникода»:
Можете также скачать с моего Googl-диска заархивированный файл проекта по ссылке 3d.zip . Скачивание начинается после выбора кнопки меню в правом верхнем углу открывшегося окна.
Проект создан в версии Visual Studio 17. Для поздних версий (19 и выше), возможно, понадобиться пересобрать решение (кнопки меню Build>Rebuild Solution).
При запуске программы плоскость внешнего раствора рупора совпадает картинной плоскостью.
В коде программы предусмотрена возможность при нажатии на клавиши (0,1,2,3,4,5) обеспечить геометрические преобразования объекта путем непосредственного введения значений коэффициентов матрицы преобразований.
case WM_KEYDOWN: int KeyPressed; KeyPressed = int(wParam); if (KeyPressed == int('0')) { action->Transform_0(); } if (KeyPressed == int('1')) { action->Transform_1(); } if (KeyPressed == int('2')) { action->Transform_2(); } .... InvalidateRect(hWnd, NULL, FALSE); break;
При перемещении курсора мышки происходит вращение системы координат относительно оси Z, опираясь на текущее и предыдущее положения мыши. Если, при перемещении курсора нажата клавиша Ctrl, то изменение координат курсора мышки трансформируется в изменение значений коэффициентов матрицы преобразований, которые определяют перемещение и масштабирование.
case WM_MOUSEMOVE: if (UINT(wParam) & MK_LBUTTON) { _Point mouse_point; mouse_point.x = LOWORD(lParam); // Координаты в системе окна mouse_point.y = HIWORD(lParam); // Логические координаты mouse_point = engine->viewport.T_inv(mouse_point); if (UINT(wParam) & MK_CONTROL) { //Преобразует с.к., опираясь на текущее и пред. положения мыши action->Translate(mouse_point.x, mouse_point.y); } else { action->Rotate(mouse_point.x, mouse_point.y); } InvalidateRect(hWnd, NULL, FALSE); } break;
Контрольные задания
Задание 1. Определите матрицы ортогональных проекций (видов) рупорной антенны (фронтальную и профильную) и протестируйте их запуском программы. При последовательном нажатии на клавиши 1, 2 должна обеспечиваться композиция базовых преобразований, определяемая выражением T=T1T2. Результаты нажатия на клавиши 1 и 2 представлены на рисунке
Задание 2. По аналогии с предыдущим заданием обеспечить при последовательном нажатии на клавиши 1…5, композицию преобразований T=T1T2T3T4T5 в соответствии с вариантом задания
Варианты задания:
При решении аналогичной задачи на изменение положения любого объекта через последовательность элементарных преобразований прежде всего у Вас должна быть пространственная схема с определением положения объекта в пространстве через параметры (см. рис выше).Затем Вы определяете последовательность элементарных преобразований T=T1T2T3T4T5, при которой параметры не искажаются (см. GIF анимацию):
- T1 — сдвиг вдоль оси Z (параметр h)
- T2 — поворот вокруг оси Z (угол gamma)
- T3 — поворот вокруг оси X (угол beta)
- T4 — поворот вокруг оси Y (угол alpha)
- T5 — сдвиг вдоль оси Y (параметр p)
При последовательности T=T2T1T3T4T5, параметры тоже не искажаютсяОднако, при последовательности T=T1T2T4T3T5 угол beta искажается — попробуйте это представить, используя свое пространственное воображение.Важным при определении последовательности преобразований является еще и то, относительно какой системы координат происходит каждое последующее преобразование — неподвижной глобальной или текущей локальной. В задаче, которая рассмотрена в этой статье, все преобразования задаются относительно глобальной системы координат (так определено алгоритмом на основе математики).Для той же самой задачи с рупорной антенной при использовании библиотеки OpenGL (см. https://api-2d3d-cad.com/opengl_c/#3 ) последовательность преобразований другая — поскольку каждое последующее преобразование задается относительно локальной системы координат (так определено в библиотеке OpenGL).Углы Эйлера (см. раздел Матрицы поворота и углы Эйлера) также определятся относительно локальной системы координат. Здесь же демонстрируется возможность их определения относительно глобальной системы координат.
Задание 3. Обеспечить возможность вращения антенны вокруг оси Y при движении курсора мышки.
Задание 4. Обеспечить возможность создания ортогональной проекции объекта при нажатии на клавишу 0. Матрица ортогональной проекции приводится ниже:
Проверьте создание проекции путем последовательного нажатия на клавиши 1,2,3,4,5,0 (преобразований T=T1T2T3T4T5T0 ) и, затем, вращения антенны вокруг оси Y при движении курсора мышки. В этом случае (в отличие от задания 3, см. рисунок выше) будет вращаться не 3D модель антенны, а 2D проекция антенны.
Задание 5. Обеспечьте возможность переключения из глобальной системе координат в локальную нажатием на клавишу W (см. алгоритм к заданию 4 из темы «2D графика»). После переключения в локальную систему координат при движении мышки антенна должна вращаться относительно собственной оси Y.
Обеспечьте при движении мышки вращение антенны вокруг собственной оси Z
Проверьте, какая реакция при нажатии на клавишу 0 в случае, когда включена локальная система координат
Задание 6. Клавишу 0 использовать для создания центральной проекции. Матрица центральной проекции отличается от матрицы ортогональной проекции наличием коэффициента r (вместо 0). Этот коэффициент влияет на значения координат x и y, ставит их в зависимость от координаты z.
Проверить эффект от создания центральной проекции путем последовательного нажатия на клавиши 1,2,3,4,5,0 (преобразований T=T1T2T3T4T5T0 ). На рисунке показан эффект от нажатия на клавишу 0.
Задание 7. В предыдущих заданиях использовался способ создания видов (проекций), при котором вектор проецирования и плоскость проекций были неподвижны, а объект перемещался.
В этом задании реализуйте 2-й способ определения проекции – объект неподвижен, вектор проецирования перемещается.
Указанные способы получения видов (проекций) можно сравнить с работой фотографа и фотомодели. В одном случае фотограф неподвижен, а модель движется, а во втором — наоборот.
Для решения задачи используем новую СК, положение которой определяется 2-я углами.
В матричном представлении это выражается как результат перемножение 2-х матриц:
Но нас интересует не положение новой СК, а описание точек объекта в этой системе координат. Для представления точек в новой СК необходимо выполнить перемножение координат точек объекта на обратную последовательность двух преобразований с отрицательным углами:
Почему знаки угла и последовательность поворота меняются на противоположные? Объяснение этому простое. Движение относительно. Абстрагируемся и представим, что СК XYZ меняет положение относительно неподвижной антенны. При этом вектор проецирования по прежнему направлен вдоль оси Z.
Преобразования обеспечить последовательным нажатием клавиш 6 и 7.
Задание 8. В предыдущем задании перемещение вектора проецирования обеспечивалось последовательным нажатием клавиш 6 и 7. В этом задании необходимо обеспечить возможность изменения углов вектора проецирования с помощью курсора мышки. При движении вверх (вниз) меняется угол вращения вокруг оси X, при движении вправо (влево) — угол вращения вокруг оси Y.
Для решения этой задачи необходимо получить композицию двух обратных матриц:
В программе значения косинусов и синусов в матрице преобразований должны быть заменены на соответствующие значения перемещений курсора мышки (ty и tx):
Фрагменты программной реализации приводятся ниже. При движении мышки с нажатой клавишей Ctrl (файл main.cpp) управление передается функции:
action->Translate(mouse_point.x, mouse_point.y);
Определение этой функции (файл action.cpp) содержит вызов функции:
Tr.SetTranslationMatrix(delta.x, delta.y);
которой передаются перемещения курсора мышки (ty и tx). В соответствии с принятыми обозначениями в определении этой функции (в файле matrix.cpp) инициализируются коэффициенты матрицы:
void Matrix::SetTranslationMatrix(double tx, double ty){ SetUnit(); double txx = tx*tx; double txy = tx*ty; double tyy = ty*ty; double tsx = sqrt (1 - txx); double tsy = sqrt (1 - tyy); data[0][0] = tsx; // a // a c p 0 // data[0][1] = -txy; // c // b d q 0 // data[0][2] = tx*tsy; // p // h f r 0 // //data[0][3] = 1.0; // 0 // m n l 1 // //data[1][0] = 0.0; // b data[1][1] = tsy; // d data[1][2] = ty; // q //data[1][3] = 0.0; // 0 data[2][0] = -tx; // h data[2][1] = -tsx*ty; // f data[2][2] = tsx*tsy; // r //data[2][3] = 0.0; // 0 //data[3][0] = 0.25; // m //data[3][1] = 0.25; // n //data[3][2] = 1.0; // l //data[3][3] = 1.0; // 1 }
Исходные файлы проекта приложения
Header Files
action.h
#include "matrix.h" #include "vec.h" class Action { public: vec old_mouse; Matrix CurrentMatrix; void InitAction(double x, double y); void Rotate(double x, double y); void Translate(double x, double y); void Transform_0(); void Transform_1(); void Transform_2(); void Transform_3(); void Transform_4(); void Transform_5(); };
engine.h
#include "action.h" #include "viewport.h" class Engine{ //Matrix current_rot; Action *action; public: Viewport viewport; void Draw(HDC hdc); void SetAction(Action *_action); };
geometry.h
#ifndef _POINT struct _Point { double x, y, z; }; #define _POINT #endif
matrix.h
#include "geometry.h" #ifndef _MATRIX class Matrix{ double data[4][4]; public: Matrix(); void SetUnit(); void SetRotationMatrix(double alpha); void SetRotationMatrixbySinCos(double sinalpha, double cosalpha); void SetTranslationMatrix(double tx, double ty); void SetTranslationMatrix_0(); void SetTranslationMatrix_1(); void SetTranslationMatrix_2(); void SetTranslationMatrix_3(); void SetTranslationMatrix_4(); void SetTranslationMatrix_5(); void MultiplyMatrices(Matrix &right); void ApplyMatrixtoPoint(_Point &point); }; #define _MATRIX #endif
vec.h
#include "math.h" typedef double vec_float; class vec { public: vec_float x, y; vec() {} vec(vec_float xx, vec_float yy) { x = xx; y = yy; } vec(const vec& vector){ x = vector.x; y = vector.y; } inline void set(vec_float xx, vec_float yy) { x = xx; y = yy; } inline vec operator + (vec t) // сложение { return vec(x + t.x, y + t.y); } inline vec operator - (vec t) // вычитание { return vec(x - t.x, y - t.y); } inline vec operator * (vec_float t) // произведение на число { return vec(x * t, y * t); } inline vec_float operator * (vec t) // скалярное произведение { return x * t.x + y * t.y; } inline vec_float operator ^ (vec t) // длина результата векторного произведения с учетом направления { return x * t.y - y * t.x; } inline vec_float length() // длина вектора { return sqrt(x * x + y * y); //return x * x + y * y; } inline vec unit() // нормализация вектора { vec_float l = length(); if (l == 0.0f) return vec(0.0f, 0.0f); return vec(x / l, y / l); } inline bool zero() // определяет нулевой ли вектор { return (x == 0.0f) && (y == 0.0f); } inline bool equals(vec t) // проверяет вектора на точное совпадение { return (x == t.x) && (y == t.y); } };
viewport.h
#include "geometry.h" #ifndef _VIEWPORT class Viewport{ int Margin; int Height, Width; public: Viewport(); void SetWindowSize(int _Width, int _Height); _Point T(_Point point); _Point T_inv(_Point point); void SetMargin(int _Margin = 10); }; #define _VIEWPORT #endif
Source Files
main.cpp
#include <windows.h> #include "engine.h" const double PI = 3.141592653; LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); char szClassName[] = "CG6"; char szWindowCaption[] = "CG #6 Mouse Tracking and Rotation"; //////////////////////////////////////////////////////////////////////////////////////////////////// int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { HWND hWnd; MSG lpMsg; WNDCLASS wc; // Заполняем структуру класса окна wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = NULL; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = NULL; wc.lpszClassName = (LPCWSTR)szClassName; // Регистрируем класс окна if (!RegisterClass(&wc)) { MessageBox(NULL, L"Cannot register class", L"Error", MB_OK); return 0; } // Создаем основное окно приложения hWnd = CreateWindow( (LPCWSTR)szClassName, // Имя класса L"Шаблон WinAPI приложения", // Текст заголовка WS_OVERLAPPEDWINDOW, // Стиль окна 50, 50, // Позиция левого верхнего угла 600, 600, // Ширина и высота окна (HWND) NULL, // Указатель на родительское окно NULL (HMENU) NULL, // Используется меню класса окна (HINSTANCE)hInstance, // Указатель на текущее приложение NULL ); // Передается в качестве lParam в событие WM_CREATE if (!hWnd) { MessageBox(NULL, L"Cannot create main window", L"Error", MB_OK); return 0; } // Показываем наше окно 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) { PAINTSTRUCT ps; static RECT Rect; HDC hdc, hCmpDC; HBITMAP hBmp; static Action *action; static Engine *engine; //static bool x = true; switch (messg) { case WM_CREATE: engine = new Engine(); action = new Action(); //вызыв. констр. Matrix и иниц. матрица (станов. единичной) engine->SetAction(action); // иниц.закрытой перем engine->action = action; break; case WM_PAINT: //x = x; GetClientRect(hWnd, &Rect); hdc = BeginPaint(hWnd, &ps); SetBkColor(hdc, 0xEECCCC); // Создание нового контекста для двойной буфферизации hCmpDC = CreateCompatibleDC(hdc); hBmp = CreateCompatibleBitmap(hdc, Rect.right - Rect.left, Rect.bottom - Rect.top); SelectObject(hCmpDC, hBmp); // Закраска фоновым цветом LOGBRUSH br; br.lbStyle = BS_SOLID; br.lbColor = 0xEECCCC; HBRUSH brush; brush = CreateBrushIndirect(&br); FillRect(hCmpDC, &Rect, brush); DeleteObject(brush); // Рисование engine->Draw(hCmpDC); // Вывод на экран SetStretchBltMode(hdc, COLORONCOLOR); BitBlt(hdc, 0, 0, Rect.right - Rect.left, Rect.bottom - Rect.top, hCmpDC, 0, 0, SRCCOPY); DeleteDC(hCmpDC); DeleteObject(hBmp); hCmpDC = NULL; EndPaint(hWnd, &ps); break; case WM_SIZE: // какое раньше событие WM_PAINT или WM_SIZE GetClientRect(hWnd, &Rect); engine->viewport.SetWindowSize(Rect.right - Rect.left, Rect.bottom - Rect.top); // x = false; break; //определение закрытых перем. viewport.Height, viewport.Width; case WM_ERASEBKGND: return 1; break; case WM_LBUTTONDOWN: _Point mouse_point; mouse_point.x = LOWORD(lParam); //Сохраним координаты курсора мыши в системе окна mouse_point.y = HIWORD(lParam); //преобразуем координаты курсора в логические mouse_point = engine->viewport.T_inv(mouse_point); // запоминаем координаты курсора в объекте old_mouse action->InitAction(mouse_point.x, mouse_point.y); //InvalidateRect(hWnd, NULL, FALSE); break; case WM_MOUSEMOVE: if (UINT(wParam) & MK_LBUTTON) { _Point mouse_point; mouse_point.x = LOWORD(lParam); // Координаты в системе окна mouse_point.y = HIWORD(lParam); mouse_point = engine->viewport.T_inv(mouse_point);// Логические координаты if (UINT(wParam) & MK_CONTROL) { //Перемещает систему координат, опираясь на текущее и предыдущее полощения мыши action->Translate(mouse_point.x, mouse_point.y); } else { action->Rotate(mouse_point.x, mouse_point.y); } InvalidateRect(hWnd, NULL, FALSE); } break; case WM_KEYDOWN: int KeyPressed; KeyPressed = int(wParam); if (KeyPressed == int('0')) { action->Transform_0(); } if (KeyPressed == int('1')) { action->Transform_1(); } if (KeyPressed == int('2')) { action->Transform_2(); } if (KeyPressed == int('3')) { action->Transform_3(); } if (KeyPressed == int('4')) { action->Transform_4(); } if (KeyPressed == int('5')) { action->Transform_5(); } InvalidateRect(hWnd, NULL, FALSE); break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, messg, wParam, lParam)); } return (0); }
action.cpp
#include "action.h" /* Сигнализирует о начале некоторого действия */ void Action::InitAction(double x, double y){ old_mouse.set(x, y); } /* Поворачивает систему координат, опираясь на текущее и предыдущее полощения мыши */ void Action::Rotate(double x, double y){ vec new_mouse(x, y); vec_float sina, cosa; sina = old_mouse.unit() ^ new_mouse.unit(); cosa = old_mouse.unit() * new_mouse.unit(); Matrix Rot; Rot.SetRotationMatrixbySinCos(sina, cosa); CurrentMatrix.MultiplyMatrices(Rot); old_mouse = new_mouse; } /* Перемещает систему координат, опираясь на текущее и предыдущее положения мыши */ void Action::Translate(double x, double y){ vec new_mouse(x, y); vec delta = new_mouse - old_mouse; Matrix Tr; Tr.SetTranslationMatrix(delta.x, delta.y); CurrentMatrix.MultiplyMatrices(Tr); old_mouse = new_mouse; } void Action::Transform_0(){ Matrix Tr; Tr.SetTranslationMatrix_0(); CurrentMatrix.MultiplyMatrices(Tr); } void Action::Transform_1(){ Matrix Tr; Tr.SetTranslationMatrix_1(); CurrentMatrix.MultiplyMatrices(Tr); } void Action::Transform_2(){ Matrix Tr; Tr.SetTranslationMatrix_2(); CurrentMatrix.MultiplyMatrices(Tr); } void Action::Transform_3(){ Matrix Tr; Tr.SetTranslationMatrix_3(); CurrentMatrix.MultiplyMatrices(Tr); } void Action::Transform_4(){ Matrix Tr; Tr.SetTranslationMatrix_4(); CurrentMatrix.MultiplyMatrices(Tr); } void Action::Transform_5(){ Matrix Tr; Tr.SetTranslationMatrix_5(); CurrentMatrix.MultiplyMatrices(Tr); }
engine.cpp
#include <windows.h> #include "geometry.h" #include "matrix.h" #include "engine.h" /* Привязываем к движку объект, который отвечает за действия пользователя */ void Engine::SetAction(Action *_action) { action = _action; } /* Выводит графику на контекст hdc */ void Engine::Draw(HDC hdc){ _Point rupor[20]; double z=0.0; rupor[1].x = 0.2; rupor[1].y = 0.1; rupor[1].z = z; rupor[2].x = 0.2; rupor[2].y = -0.1; rupor[2].z = z; // 0.4 = 1 rupor[3].x = -0.2; rupor[3].y = -0.1; rupor[3].z = z; rupor[4].x = -0.2; rupor[4].y = 0.1; rupor[4].z = z; z=-0.4; rupor[5].x = 0.1; rupor[5].y = 0.05; rupor[5].z = z; rupor[6].x = 0.1; rupor[6].y = -0.05; rupor[6].z = z; rupor[7].x = -0.1; rupor[7].y = -0.05; rupor[7].z = z; rupor[8].x = -0.1; rupor[8].y = 0.05; rupor[8].z = z; z=-0.8; rupor[9].x = 0.1; rupor[9].y = 0.05; rupor[9].z = z; rupor[10].x = 0.1; rupor[10].y = -0.05; rupor[10].z = z; rupor[11].x = -0.1; rupor[11].y = -0.05; rupor[11].z = z; rupor[12].x = -0.1; rupor[12].y = 0.05; rupor[12].z = z; z=0.0; // оси координат антенны rupor[13].x = 0.0; rupor[13].y = 0.0; rupor[13].z = z; rupor[14].x = 0.4; rupor[14].y = 0.0; rupor[14].z = z; rupor[15].x = 0.0; rupor[15].y = 0.4; rupor[15].z = z; rupor[16].x = 0.0; rupor[16].y = 0.0; rupor[16].z = 0.4; z=0.0; // глобальная система координат rupor[17].x = 0.0; rupor[17].y = 0.0; //rupor[17].z = 0.0; rupor[18].x = 1.0; rupor[18].y = 0.0; rupor[18].z = 0.0; rupor[19].x = 0.0; rupor[19].y = 1.0; rupor[19].z = 0.0; for (int i = 1; i <17; i++){ //Вращение и перемещение осуществляется в логических координатах action->CurrentMatrix.ApplyMatrixtoPoint(rupor[i]); // Переход из логических в оконные координаты. rupor[i] = viewport.T(rupor[i]); } for (int i = 17; i <20; i++){ //Переход из логических в оконные координаты. rupor[i] = viewport.T(rupor[i]); } // прямоугольник в плоскости z=0 for (int i = 1; i <= 4; i++){ if (i == 1){ MoveToEx(hdc, rupor[i].x, rupor[i].y, NULL); } else { LineTo(hdc, rupor[i].x, rupor[i].y); } } LineTo(hdc, rupor[1].x, rupor[1].y); // прямоугольник в плоскости z=-0.2 for (int i = 5; i <= 8; i++){ if (i == 5){ MoveToEx(hdc, rupor[i].x, rupor[i].y, NULL); } else { LineTo(hdc, rupor[i].x, rupor[i].y); } } LineTo(hdc, rupor[5].x, rupor[5].y); // прямоугольник в плоскости z=-0.6 for (int i = 9; i <= 12; i++){ if (i == 9){ MoveToEx(hdc, rupor[i].x, rupor[i].y, NULL); } else { LineTo(hdc, rupor[i].x, rupor[i].y); } } LineTo(hdc, rupor[9].x, rupor[9].y); // ребра, параллельные оси z for (int i = 1; i <= 4; i++){ MoveToEx(hdc, rupor[i].x, rupor[i].y, NULL); LineTo(hdc, rupor[i+4].x, rupor[i+4].y); } for (int i = 5; i <= 8; i++){ MoveToEx(hdc, rupor[i].x, rupor[i].y, NULL); LineTo(hdc, rupor[i+4].x, rupor[i+4].y); } // оси координат антенны int i = 13; for (int j = 1; j <= 3; j++){ MoveToEx(hdc, rupor[i].x, rupor[i].y, NULL); LineTo(hdc, rupor[i+j].x, rupor[i+j].y); } // глобальные оси координат i = 17; for (int j = 1; j <= 2; j++){ //rupor[i+j] = viewport.T(rupor[i+j]); MoveToEx(hdc, rupor[i].x, rupor[i].y, NULL); LineTo(hdc, rupor[i+j].x, rupor[i+j].y); } }
matrix.cpp
#include <math.h> #include <memory.h> #include "matrix.h" #include "geometry.h" /* Конструктор, инициализирует матрицу единичной (матрица тождественного преобразования) */ Matrix::Matrix(){ SetUnit(); // data[1][1] = 0.866; //cos30; //data[2][1] = 0.5; //sin30; //data[1][2] = -0.5; //sin(-30); //data[2][2] = 0.866; //cos30; //data[1][1] = 0.966; //cos15; //data[2][1] = 0.259; //sin15; //data[1][2] = -0.259; //sin(-15); //data[2][2] = 0.866; //cos15; } /* Текущая матрица становится единичной */ void Matrix::SetUnit(){ //memset(data, sizeof(data), 0); for (int i = 0; i < 4; i++){ for(int j = 0; j < 4; j++){ data[i][j] = 0.0; } } for (int i = 0; i < 4; i++){ data[i][i] = 1.0; } } /* Устанавливает текущую матрицу в качестве матрицы вращения на угол alpha, заданный косинусом и синусом */ void Matrix::SetRotationMatrixbySinCos(double sinalpha, double cosalpha){ SetUnit(); data[0][0] = cosalpha; // a // a c p 0 // data[0][1] = sinalpha; // c // b d q 0 // //data[0][2] = 0.0; // p // h f r 0 // //data[0][3] = 1.0; // 0 // m n l 1 // data[1][0] = -sinalpha; // b data[1][1] = cosalpha; // d //data[1][2] = 0.1; // q //data[1][3] = 0.1; // 0 //data[2][0] = 0.0; // h //data[2][1] = 0.25; // f //data[2][2] = 0.0; // r //data[2][3] = 1.0; // 0 //data[3][0] = 0.25; // m //data[3][1] = 0.25; // n //data[3][2] = 1.0; // l //data[3][3] = 1.0; // 1 } /* Устанавливает текущую матрицу в качестве матрицы вращения на угол alpha */ void Matrix::SetRotationMatrix(double alpha){ SetRotationMatrixbySinCos(sin(alpha), cos(alpha)); } /* Устанавливает текущую матрицу в качестве матрицы параллельного переноса на вектор (tx, ty) */ void Matrix::SetTranslationMatrix(double tx, double ty){ SetUnit(); // data[0][0] = 1 - tx; data[1][1] = 1 - ty; data[3][0] = tx; data[3][1] = ty; } void Matrix::SetTranslationMatrix_0(){ SetUnit(); //data[0][0] = 0.0; // a // a c p 0 // //data[0][1] = 1.0; // c // b d q 0 // //data[0][2] = 0.0; // p // h f r 0// //data[0][3] = 1.0; // 0 // m n l 1 // //data[1][0] = 0.1; // b //data[1][1] = 0.0; // d //data[1][2] = 0.1; // q //data[1][3] = 0.1; // 0 //data[2][0] = 0.5; // h //data[2][1] = 0.25; // f //data[2][2] = 0.0; // r //data[2][3] = -1.0; // 0 //data[3][0] = 0.25; // m //data[3][1] = 0.25; // n //data[3][2] = 1.0; // l //data[3][3] = 1.0; // 1 } void Matrix::SetTranslationMatrix_1(){ SetUnit(); //data[0][0] = 0.0; // a // a c p 0 // //data[0][1] = 1.0; // c // b d q 0 // //data[0][2] = 0.0; // p // h f r 0// //data[0][3] = 1.0; // 0 // m n l 1 // //data[1][0] = 0.1; // b //data[1][1] = 0.0; // d //data[1][2] = -1.0; // q //data[1][3] = 0.1; // 0 //data[2][0] = 0.5; // h //data[2][1] = 1.0; // f //data[2][2] = 0.0; // r //data[2][3] = 0.0; // 0 //data[3][0] = 0.25; // m //data[3][1] = 0.25; // n //data[3][2] = 1.0; // l //data[3][3] = 1.0; // 1 } void Matrix::SetTranslationMatrix_2(){ SetUnit(); //data[0][0] = 0.766; // a // a c p 0 // //data[0][1] = 0.642; // c // b d q 0 // //data[0][2] = 1.0; // p // h f r 0 // //data[0][3] = 1.0; // 0 // m n l 1 // //data[1][0] = -0.642; // b //data[1][1] = 0.766; // d //data[1][2] = -1.0; // q //data[1][3] = 0.1; // 0 //data[2][0] = -1.; // h //data[2][1] = 1.0; // f //data[2][2] = 0.0; // r //data[2][3] = 0.0; // 0 //data[3][0] = 0.25; // m //data[3][1] = 0.25; // n //data[3][2] = 1.0; // l //data[3][3] = 1.0; // 1 } void Matrix::SetTranslationMatrix_3(){ SetUnit(); //data[0][0] = 0.0; // a // a c p 0 // //data[0][1] = 1.0; // c // b d q 0 // //data[0][2] = 0.0; // p // h f r 0// //data[0][3] = 1.0; // 0 // m n l 1 // //data[1][0] = 0.1; // b //data[1][1] = 0.866; // d //data[1][2] = -0.5; // q //data[1][3] = 0.1; // 0 //data[2][0] = 0.5; // h //data[2][1] = 0.5; // f //data[2][2] = 0.866; // r //data[2][3] = 0.0; // 0 //data[3][0] = 0.25; // m //data[3][1] = 0.25; // n //data[3][2] = 1.0; // l //data[3][3] = 1.0; // 1 } void Matrix::SetTranslationMatrix_4(){ SetUnit(); //data[0][0] = 0.94; // a // a c p 0 // //data[0][1] = 1.0; // c // b d q 0 // //data[0][2] = -0.342; // p // h f r 0// //data[0][3] = 1.0; // 0 // m n l 1 // //data[1][0] = 0.1; // b //data[1][1] = 0.0; // d //data[1][2] = -1.0; // q //data[1][3] = 0.1; // 0 //data[2][0] = 0.342; // h //data[2][1] = 1.0; // f //data[2][2] = 0.94; // r //data[2][3] = 0.0; // 0 //data[3][0] = 0.25; // m //data[3][1] = 0.25; // n //data[3][2] = 1.0; // l //data[3][3] = 1.0; // 1 } void Matrix::SetTranslationMatrix_5(){ SetUnit(); //data[0][0] = 0.0; // a // a c p 0 // //data[0][1] = 1.0; // c // b d q 0 // //data[0][2] = 0.0; // p // h f r 0// //data[0][3] = 1.0; // 0 // m n l 1 // //data[1][0] = 0.1; // b //data[1][1] = 0.0; // d //data[1][2] = -1.0; // q //data[1][3] = 0.1; // 0 //data[2][0] = 0.5; // h //data[2][1] = 1.0; // f //data[2][2] = 0.0; // r //data[2][3] = 0.0; // 0 //data[3][0] = 0.45; // m //data[3][1] = 0.2; // n //data[3][2] = 1.0; // l //data[3][3] = 1.0; // 1 } /* Умножает текущую матрицу на матрицу, переданную в качестве параметра */ void Matrix::MultiplyMatrices(Matrix &right){ double temp[4][4]; double val; memcpy(temp, data, sizeof(data)); for (int i = 0; i < 4; i++){ for (int j = 0; j < 4; j++){ val = 0; for (int v = 0; v < 4; v++){ val += temp[i][v] * right.data[v][j]; } data[i][j] = val; } } } /* Перемножает точку, переданную в качестве параметра на текущую матрицу. При этом последний столбец матрицы не учитывается */ void Matrix::ApplyMatrixtoPoint(_Point &point){ double _x, _y, _z, w; _x = point.x; _y = point.y; _z = point.z; point.x = _x * data[0][0] + _y * data[1][0]+ _z * data[2][0] + data[3][0]; point.y = _x * data[0][1] + _y * data[1][1]+ _z * data[2][1] + data[3][1]; point.z = _x * data[0][2] + _y * data[1][2]+ _z * data[2][2] + data[3][2]; w= data[2][3] * _z + 1; point.x = point.x/w; point.y = point.y/w; }
viewport.cpp
#include "viewport.h" /* Задает размер окна */ void Viewport::SetWindowSize(int _Width, int _Height){ Width = _Width; Height = _Height; } /* Выполняет преобразование из координат [-1, 1] x [-1, 1] в координаты экрана, с учетом отступов */ _Point Viewport::T(_Point point){ _Point TPoint; TPoint.x = Margin + (1.0 / 2) * (point.x + 1) * (Width - 2 * Margin); TPoint.y = Margin + (-1.0 / 2) * (point.y - 1) * (Height - 2 * Margin); return TPoint; } /* Выполняет преобразование из координат экрана, в координаты [-1, 1] x [-1, 1] с учетом отступов */ _Point Viewport::T_inv(_Point point){ _Point TPoint; TPoint.x = double(point.x - Margin) / (1.0 / 2) / (Width - 2 * Margin) - 1; TPoint.y = double(point.y - Margin) / (-1.0 / 2) / (Height - 2 * Margin) + 1; return TPoint; } /* Конструктор, выставляет отступы по умолчанию */ Viewport::Viewport(){ SetMargin(); } /* Устанавливает отступ по краям экрана */ void Viewport::SetMargin(int _Margin){ Margin = _Margin; }
Полезные ссылки:
Автор: Николай Свирневский