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

Автор: | 05.02.2018

Знакомство с возможностями и структурой приложения
Контрольные задания
Исходные файлы проекта приложения

Знакомство с возможностями и структурой приложения

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

Для создания проекта используйте исходные файлы проекта приложения.

Проследите, чтобы при создании проекта была установка 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;
}

 

Полезные ссылки:

 

 

Автор: Николай Свирневский