Лабораторная работа № 10
Трехмерное вращение вокруг произвольной оси
 
1. Цель работы
 
1) Освоить программирование трехмерного вращения объектов относительно произвольной оси с использованием методов матричного анализа.
2) Разработать программу в среде  C++Builder, для демонстрации вращения заданной трехмерной фигуры относительно произвольной оси в режиме перспективного проецирования.
3) Освоить методы анимационного изображения графических объектов с использованием матрицы "текущих координат".
 
2.  Постановка задачи
 
2.1. В проект, разработанный для лабораторных работ по 3D-графике добавить новый модуль с формой Form5 (см. рис.2).
2.2. В графическом окне Form5 создать локальную систему трехмерных декартовых координат.
2.3. В составе системы координат изобразить прямоугольный параллелепипед. Размеры его сторон указаны в таблице 1 в лабораторной работе №7. Фигура должна задаваться координатами своих вершин: A(x1,y1,z1), B(x2,y2,z2), C(x3,y3,z3), D(x4,y4,z4), E(x5,y5,z5) и т.д. Исходное положение фигуры - в начале системы координат. Вид проецирования – перспективный, 3х-точечный.
2.3. В составе системы координат изобразить произвольную пространственную ось вращения с координатами вершин: M(x1,y1,z1) и  N(x2,y2,z2). Исходное положение оси вращения - произвольное.
2.7. Построить лучи схода, связывающие вершины фигуры с точками схода Xсх,  Yсх и Zсх.
2.8. Вершины фигуры, ось вращения и координатные оси снабдить буквенными обозначениями.
2.9. Используя средства управления, расположенные на панели, продемонстрировать следующее:
1) Повороты осей координат и всех фигур, расположенных в этой системе, в двух плоскостях;
2) Изменение масштаба фигуры ABCDEFGH;
3) Изменение положения (перенос) фигуры вдоль осей X, Y и Z;
4) Изменение положения оси вращения MN;
5) Вращение фигуры вокруг оси вращения MN при нажатии на кнопку "Пуск";
6) Прекращение вращения фигуры при нажатии на кнопку "Стоп";
7) Возврат фигуры в начало координат при нажатии на кнопку "Исходное";
8) Изменение направления и скорости вращения фигуры.
 
3.  Краткие теоретические сведения
 
Вращение вокруг произвольной пространственной оси нельзя выполнить с помощью базовых вращающих матриц, потому что они позволяют вращать объекты только вокруг главных осей координат. Поэтому произвольную пространственную ось сначала необходимо совместить с какой-либо главной осью координат, выполнить поворот на заданный угол и вернуть ось в исходное положение. Вместе с осью в переносе и вращении участвуют все объекты, подлежащие повороту. Из этого следует, что задача становится многоходовой и для ее решения необходимо получить результирующую матрицу. Для случая вывода результатов вращения на плоскость Z=0 с помощью объемной одноточечной перспективной проекции, она выводится по схеме:
 
 
Первая и третья матрицы в соотношении (1) - матрицы, которые обеспечивают трехмерный перенос пространственной оси вращения так, чтобы сначала она проходила через начало координат, а после поворота возвратилась в исходное положение. Вторая матрица обеспечивает поворот объектов вокруг произвольной пространственной оси, проходящей через начало координат, на заданный угол θ, по или против часовой стрелки. Она имеет достаточно сложный вид (2). Ее вывод приведен в курсе лекций по компьютерной графике.
 
 
Направляющие косинусы. В матрицу (2) входят элементы n1, n2, n3, которые представляют собой направляющие косинусы пространственной оси вращения, проходящей через начало координат. На рисунке 1 показана схема, поясняющая физический смысл направляющих косинусов.
 
 
 
Рисунок 1 - Построение направляющих косинусов для произвольного пространственного вектора  N 
 
Пусть в трехмерной системе координат проведен через начало координат произвольный пространственный вектор  N (реально являющийся осью вращения), наклоненный к главным осям координат X,Y,Z под углами  α, β, γ  соответственно. Образуем от него единичный вектор n. Спроектируем полученный единичный вектор на каждую ось координат. На рисунке 1 маленькими четырехугольниками показаны прямые углы, возникающие при этом проектировании. Проекции единичного вектора на главных осях координат X,Y,Z  обозначены через   n1, n2, n3 соответственно. Единичный вектор  n от вектора N, проходящего через начало координат определяется следующим соотношением:
 
                                                                      (3)
 
где xn, yn, zn - координаты конца вектора N. Компоненты единичного вектора  n1, n2, n3 находят из (3) по формулам:
 
 
Почему эти компоненты называются "направляющими косинусами"? Если из прямоугольных треугольников  OAB, OBC, ODB  на рисунке 1 определить косинусы углов наклона вектора  N  и совпадающего с ним по направлению единичного вектора n, то получим следующие равенства, которые и дают ответ на этот вопрос:
 
 
В общем случае, когда произвольный пространственный вектор не проходит через начало координат, он имеет начало и конец. Пусть, например, им будет вектор MN. В этом случае расчет направляющих косинусов выполняется по тем же формулам (3) ÷ (6) с той лишь разницей, что компоненты вектора   MN  будут определяться разностью одноименных координат его конца и начала:
 
 
Компоненты единичного вектора  n1, n2, n3  находят из (10) по формулам:
 
  
Вывод результирующей матрицы для вращения трехмерных объектов вокруг произвольной пространственной оси.
Чтобы упростить написание матрицы (2), введем новые обозначения ее элементов:
 
 
Перепишем матрицу (2) с учетом введенных обозначений:
 
 
В соотношении (1) произведение трех последних матриц представляет собой результирующую матрицу одноточечной перспективной проекции из центра, установленного на оси Zна плоскость Z=0.
 
 
С учетом (16) перепишем (1) в развернутом виде:
 
 
Матрица (17) обеспечивает вращение трехмерных объектов вокруг произвольной пространственной оси с выдачей графического результата на картинную плоскость  Z = 0  при помощи одноточечной перспективной проекции. На самом деле эта проекция будет трехточечной. Почему? Потому что вращение объектов вокруг произвольной пространственной оси можно рассматривать как комплексное вращение, являющееся результатом комбинации одновременных поворотов вокруг всех трех осей сразу. А это приводит к автоматическому возникновению трехточечной перспективной проекции [12].
В данном случае действует второй вариант перспективной трехточечной проекции - подготовительным видовым преобразованием в котором служит преобразование трехмерного поворота вокруг одной из главных осей координат X, Y, Z, за которым следует второй вариант двухточечной перспективной проекции - один поворот и одноточечное перспективное проецирование.

4.  Интерфейс программы
 
Работа лабораторной работы №10 начинается после нажатия на кнопку Lab10, расположенной на главной форме проекта Form1. После чего открывается дополнительная форма Form5, с установленными на ней графическим окном и элементами управления (см. рис. 2).
На панели управления формы расположены следующие элементы регулирования:
- движок TrackBar1  для масштабирования фигуры;
- движки TrackBar2TrackBar4 для поворота локальной системы координат и объектов, находящихся в ней, относительно глобальных осей  Y, X и Z;
- движки  TrackBar5, TrackBar6 и TrackBar7 для переноса фигуры вдоль локальных осей X, Y и Z;
- скроллинговые окошки CSpinEdit1 - CSpinEdit6 для ввода координат оси вращения MN;
- окошки Edit1,  Edit2 и Edit3 для вывода параметров перспективного проецирования  p, q, r;
- флажок CheckBox1 для прорисовки лучей схода и точек схода Xсх, Yсх, и  Xсх;
- флажок CheckBox2 для выполнения заливки поверхностей фигуры (по желанию);
- кнопка Batton1 ("Пуск") служит для запуска вращения фигуры;
- кнопка Batton2 ("Стоп") служит для остановки вращения фигуры;
- кнопка Batton3 ("Исходное") служит для возврата фигуры, оси вращения и локальных осей в исходное положение;
- движок TrackBar8  для изменения направления и скорости вращения фигуры.
- таймер Timer1 - предназначен для перерисовки изображения в режиме анимации.
 
 
 
Рисунок 2 – Исходное изображение объектов на форме Form5
 
 
 
Рисунок 3 – Изображение объектов на форме Form5 после разворота осей координат и запуска
вращения фигуры
 
На рисунке 2 показана форма Form5, в графическом окне которой созданы локальная трехмерная система координат с осями  X,  Y  и  Z , кубическая видимая (перспективно-преобразованная) фигура, произвольная ось вращения MN и лучи схода с точками схода.
На рисунке 3 выполнен разворот локальной системы координат и объектов, входящих в её состав.
 
5. Структура программы
 
Общая структура программы имеет следующий вид:
 
 
 
Рисунок 4 – Структурная схема программы
 
Как видно из схемы, структура программы выполнена аналогично предыдущим лабораторным работам. В обработчик для графического окна FormPaint() модуля Form5 помещается вызов функции графического конвейера GrafKonv(), в состав которого в свою очередь включены функции обработки координат всех объектов: осей координат – IzmOsi(), оси вращения MN – IzmMN(), фигуры – IzmVid(), точек схода – IzmToht(), а также функция прорисовки этих объектов – ShowAll().
Все компоненты управления, такие как движки масштабирования фигуры, поворота осей координат, переноса фигуры, изменения скорости вращения, скроллинговые окошки изменения положения оси вращения MN, кнопки "Пуск", "Стоп", "Исходное" и т.д. – все они имеют свои обработчики событий, каждый из которых обязательно оканчивается командой FormPaint(). Здесь в схеме они не показаны в целях сокращения места. Далее, в программе можно ознакомиться с их содержимым.
Из схемы также видно, что прежде чем выполнить пересчет координат осей и фигуры в таких функциях как IzmOsi() и IzmVid(), необходимо предварительно рассчитать параметры результирующих матриц преобразования: осей координат – TrezOs[4][4] и фигуры – TrezVid[4][4].
Вычисление этих матриц выполняется в соответствии со следующими схемами:
 
TrezOs =: TvrYOs * TvrXOs * TortZ
 
TrezVid =: Tper * Tmsb * Tvr
 
Если говорить о полном преобразовании фигуры, то оно выполняется по схеме:
 
TVid =: Tper * Tmsb * Tvr * Tpsp * TrezOs
 
Здесь первая часть цепочки (синий цвет) вычисляется в составе TrezVid(), а вторая часть (зеленый цвет) вычисляется в составе IzmVid(). Такое разделение диктуется особенностями использования метода текущих координат, при котором текущие координаты сначала подвергаются базовым видовым преобразованиям (перенос, масштабирование, вращение вокруг оси MN), затем они перезапоминаются снова как текущие, и только после этого подвергаются проекционным преобразованиям, таким как перспективное и аксонометрическое проецирование совместно с преобразованиями осей координат.
Преобразование осей локальной системы координат выполняется в режиме исходных координат (без использования текущих координат), так как на положение осей по условию работы программы не оказывает влияние ни один из имеющихся компонентов управления, кроме их собственных движков для разворота в двух (или в трех) плоскостях.
 
В функциях IzmMN() и IzmToht() вычисления результирующих матриц видовых преобразований не производятся, так как конкретные (в составе локальной системы) значения текущих координат для оси вращения MN считываются непосредственно из скроллинговых окошек, а для точек схода вычисляются в функции Tvr().
В функции Tvr() вычисляются все параметры матрицы вращения фигуры относительно произвольной оси MN. Компоненты этой матрицы p, q и  r, являющиеся параметрами перспективного проецирования, позволяют определить координаты точек схода для каждого конкретного положения этих точек в процессе вращения фигуры (см. формулы (2) в лабораторной работе №9).
Для оси вращения MN в составе функции IzmMN() выполняются только аксонометрические проекционные преобразования, совместно с поворотами осей координат по схеме:
 
TMN =:TrezOs
 
Для точек схода, кроме проекционных преобразований добавляется еще вращение вокруг оси MN вместе с фигурой (если включен таймер, т.е. нажата кнопка "Пуск"):
 
TToht =:Tvr * TrezOs
 
6. СОЗДАНИЕ ДОПОЛНИТЕЛЬНОГО МОДУЛЯ ПРОЕКТА
 
Создайте форму Form5 аналогично предыдущим работам, и выполните её компоновку в соответствии с рисунком 2.
 
7. РАЗРАБОТКА ФАЙЛА РЕАЛИЗАЦИИ Unit5.cpp
 
7.1. Предварительные пояснения
 
Порядок разработки файла реализации состоит из следующих этапов:
1-й этап – подготовительный;
2-й этап – разработка управляющих функции;
3-й этап – построение осей координат;
4-й этап – построение произвольной оси вращения;
5-й этап – построение видимой фигуры;
6-й этап – реализация вращения видимой фигуры вокруг произвольной оси MN;
7-й этап – построение лучей схода и точек схода;
8-й этап – разработка вспомогательных функций.
В отличие от лабораторных работ №7, 8, и 9 где мы выполняли прорисовку изображения на поле объекта PaintBox1, в этой лабораторной работе будем выполнять прорисовку на поле формы Form5, с использованием графического буфера, аналогично тому, как это делалось в лабораторных работах № 2, 3, 4 и 5. Это связано с тем, что здесь необходимо реализовать вращение фигуры в режиме анимации, с использованием таймера.
1-й ЭТАП
7.2. Объявление переменных и функций
 
Перечень глобальных переменных будет иметь вид:
 
Graphics::TBitmap * formBitmap;             // Описание указателя на буфер
 
static int W,H,Z,h,i,j,k,nOs;
static float l,m,n,s,km,p,q,r,Cx,Cy,Cz;
static float Q,Q1,Q2,F1,F2,G1,G2,k1,k2,Sum;
static float oldl,oldm,oldn,olds,oldQ2,oldF2,oldG2;
static float Mx,My,Mz,Nx,Ny,Nz;
 
    // Объявления матриц координат объектов
static floatm IsxOs[6][4];          // матрица исходных координат осей
static floatm IzmOs[6][4];          // матр. коорд. осей после аксоном. преобраз.
static floatm RezOs[6][4];          // матр. преобраз. коорд. осей после нормировки
 
static floatm IsxMN[2][4];          // матрица исходных координат оси вращения MN
static floatm TekMN[2][4];          // матрица текущих координат оси вращения MN
static floatm IzmMN[2][4];          // матр. коорд. оси вращения после преобраз.
static floatm RezMN[2][4];          // матр. коорд. оси вращения после нормировки
 
static floatm IsxFg[8][4];          // матрица исходных координат фигуры
static floatm TekVid[8][4];         // матрица текущих координат фигуры
static floatm IzmVid[8][4];         // матр. коорд. фигуры после преобразования
static floatm RezVid[8][4];         // матр. коорд. фигуры после нормировки
 
static floatm IsxToht[3][4];        // матрица исходных координат точек схода
static floatm TekToht[3][4];        // матрица текущих координат точек схода
static floatm IzmToht[3][4];        // матр. коорд. точек схода после преобраз.
static floatm RezToht[3][4];        // матр. коорд. точек схода после нормировки
 
    // Объявления матриц преобразования
static float TortX[4][4]={{0,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}};
static float TortY[4][4]={{1,0,0,0},{0,0,0,0},{0,0,1,0},{0,0,0,1}};
static float TortZ[4][4]={{1,0,0,0},{0,1,0,0},{0,0,0,0},{0,0,0,1}};
 
static float Ted[4][4]={{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}};
static float Tper[4][4];            // матрица видового преобразования «перенос»
static float Tmsb[4][4];            // матрица видового преобраз. «масштабирование»
static float Tpsp[4][4];            // матрица перспективного преобразования
 
static float TvrYOs[4][4];            // матрица поворота осей вокруг Y
static float TvrXOs[4][4];            // матрица поворота осей вокруг X
static float TvrZOs[4][4];            // матрица поворота осей вокруг Z
static float TvrOs[4][4];             // матрица поворота вокруг всех осей
static float Tvr[4][4];               // матрица вращения фигуры вокруг отрезка MN
 
static float TrezOs[4][4];        // результирующая матрица преобраз. осей
static float TrezMN[4][4];          // результирующая матрица преобраз. отрезка MN
static float TrezVid[4][4];         // результирующая матрица преобраз. фигуры
static float TrezToht[4][4];        // результирующая матр. преобраз. точек схода
 
    // Объявления функций преобразования объектов
static void GrafKonv(void);         // функция графического конвейера
static void IzmOsi(void);           // функция преобразования осей
static void IzmMN(void);            // функция преобразования отрезка MN
static void IzmVid(void);           // функция преобразования фигуры
static void IzmToht(void);          // функция преобразования точек схода
 
    // Объявления функций вычисления результирующих матриц
static void RezMatrOs(void);
static void RezMatrVid(void);
static void RezMatrToht(void);
 
    // Объявленияфункцийвычислениякоординатобъектов
static void MultMatr(float Matr1[4][4],float Matr2[4][4],float Matr3[4][4]);
static void Preobr(int rows,float MatrIsx[][4],float MatrRez[4][4],float MatrIzm[][4]);
static void Normir(int rows, float MatrIzm[][4],float MatrTek[][4]);
static void Povorot3(void);      // функция поворота фигуры на 1о вокруг отрезка MN
 
    // Объявления функций прорисовки изображения
static void ShowAll(void);
static void ShowOsi(void);
static void ShowMN(void);
static void ShowFig(TColor col, float matrFg[8][4]);
static void ShowLuthi(float XToht, float YToht, float matrFg[8][4]);
static void ShowToht(float XToht, float YToht, char text[3]);
static void Oboznath(void);
 
Здесь синим цветом обозначены те переменные, матрицы и прототипы функций, которых не было в предыдущих работах: они описывают произвольную ось вращения MN и необходимые преобразования для вращения фигуры вокруг неё. Все остальные переменные, служащие для прорисовки осей координат и фигуры, не изменились.
Здесь также добавлены переменные и матрицы, обеспечивающие режим "текущих координат", при котором требуется запоминать в отдельных переменных каждое предыдущее положение фигуры и оси вращения.
 
7.3. Функция инициализации исходных данных - FormCreate()
 
Создайте обработчик события FormCreate для формы  Form5  и впишите в него команды, обеспечивающие инициализацию необходимых переменных. Перечислим эти переменные:
- графический буфер formBitmap (см. лаб. раб. №2);
- переменные, используемые для прорисовки осей: nOs и h;
- параметры переноса и масштабирования фигуры l, m, n, s;
- коэффициент  k2 пересчета градусов в радианы;
- значение коэффициента масштабирования  km;
- четыре матрицы исходных координат для осей, отрезка MN, фигуры и точек схода: mIsxOs[6][4], mIsxMN[2][4], mIsxFg[8][4], mIsxToht[3][4] 
- четыре матрицы текущих координат: mTekOs[6][4], mTekMN[2][4], mTekFg[8][4], mTekToht[3][4];
- три матрицы видовых преобразований: переноса Tper[4][4], масштабирования Tmsb[4][4] и перспективного проецирования Tpsp[4][4];
- присвоение нулевых значений углам поворота локальных осей   Q2, F2 и  G2;
- выведение значений Q2, F2 и  G2 в соответствующие окна Edit;
- инициализация матриц вращения осей координат TvrYOs[4][4], TvrXOs[4][4] и TvrZOs[4][4].
 
formBitmap=new Graphics::TBitmap();
 
 W=ClientWidth - Panel1->Width;
 H=ClientHeight;
 
 nOs=10;
 k2=3.141592653589793/180;
 km=0.1;
 l=0;
 m=0;
 n=0;
 h=W/(2*(nOs+1));
 s=km*(Form4->TrackBar1->Position);
 Q=1;
 
 mIsxOs[0][0]=-nOs; mIsxOs[0][1]=0;    mIsxOs[0][2]=0;    mIsxOs[0][3]=1;
 mIsxOs[1][0]= nOs; mIsxOs[1][1]=0;    mIsxOs[1][2]=0;    mIsxOs[1][3]=1;
 mIsxOs[2][0]=0;    mIsxOs[2][1]=-nOs; mIsxOs[2][2]=0;    mIsxOs[2][3]=1;
 mIsxOs[3][0]=0;    mIsxOs[3][1]= nOs; mIsxOs[3][2]=0;    mIsxOs[3][3]=1;
 mIsxOs[4][0]=0;    mIsxOs[4][1]=0;    mIsxOs[4][2]=-nOs; mIsxOs[4][3]=1;
 mIsxOs[5][0]=0;    mIsxOs[5][1]=0;    mIsxOs[5][2]= nOs; mIsxOs[5][3]=1;
 
 Mx=-2;  My=-1;  Mz=1;
 Nx=6;   Ny=8;   Nz=4;
 
 mIsxMN[0][0]=Mx; mIsxMN[0][1]=My; mIsxMN[0][2]=Mz; mIsxMN[0][3]=1;
 mIsxMN[1][0]=Nx; mIsxMN[1][1]=Ny; mIsxMN[1][2]=Nz; mIsxMN[1][3]=1;
 
 for(int i=0;i<2;i++)
    for(int j=0;j<4;j++)
        mTekMN[i][j]=mIsxMN[i][j];
 
 Form5->CSpinEdit1->Value=Mx;
 Form5->CSpinEdit2->Value=My;
 Form5->CSpinEdit3->Value=Mz;
 Form5->CSpinEdit4->Value=Nx;
 Form5->CSpinEdit5->Value=Ny;
 Form5->CSpinEdit6->Value=Nz;
 
 mIsxFg[0][0]=0; mIsxFg[0][1]=0; mIsxFg[0][2]=0; mIsxFg[0][3]=1;
 mIsxFg[1][0]=0; mIsxFg[1][1]=4; mIsxFg[1][2]=0; mIsxFg[1][3]=1;
 mIsxFg[2][0]=4; mIsxFg[2][1]=4; mIsxFg[2][2]=0; mIsxFg[2][3]=1;
 mIsxFg[3][0]=4; mIsxFg[3][1]=0; mIsxFg[3][2]=0; mIsxFg[3][3]=1;
 mIsxFg[4][0]=0; mIsxFg[4][1]=0; mIsxFg[4][2]=4; mIsxFg[4][3]=1;
 mIsxFg[5][0]=0; mIsxFg[5][1]=4; mIsxFg[5][2]=4; mIsxFg[5][3]=1;
 mIsxFg[6][0]=4; mIsxFg[6][1]=4; mIsxFg[6][2]=4; mIsxFg[6][3]=1;
 mIsxFg[7][0]=4; mIsxFg[7][1]=0; mIsxFg[7][2]=4; mIsxFg[7][3]=1;
 
 for(int i=0;i<8;i++)
    for(int j=0;j<4;j++)
        mTekVid[i][j]=mIsxFg[i][j];
 
 for(int i=0;i<4;i++)
    for(int j=0;j<4;j++)
       {
        Tper[i][j]=Ted[i][j];
        Tmsb[i][j]=Ted[i][j];
        Tvr[i][j]=Ted[i][j];
       }
 
 Cx=50; Cy=50; Cz=50;
 p=-1/Cx;
 q=-1/Cy;
 r=-1/Cz;
 
mIsxToht[0][0]=-Cx;mIsxToht[0][1]=0; mIsxToht[0][2]=0; mIsxToht[0][3]=1;
mIsxToht[1][0]=0; mIsxToht[1][1]=-Cy;mIsxToht[1][2]=0; mIsxToht[1][3]=1;
mIsxToht[2][0]=0; mIsxToht[2][1]=0; mIsxToht[2][2]=-Cz;mIsxToht[2][3]=1;
 
 for(int i=0;i<3;i++)
    for(int j=0;j<4;j++)
       mTekToht[i][j]=mIsxToht[i][j];
 
 Form5->Edit8->Text=FloatToStr(p);
 Form5->Edit9->Text=FloatToStr(q);
 Form5->Edit10->Text=FloatToStr(r);
 
 Tpsp[0][0]=1; Tpsp[0][1]=0; Tpsp[0][2]=0; Tpsp[0][3]=p;
 Tpsp[1][0]=0; Tpsp[1][1]=1; Tpsp[1][2]=0; Tpsp[1][3]=q;
 Tpsp[2][0]=0; Tpsp[2][1]=0; Tpsp[2][2]=1; Tpsp[2][3]=r;
 Tpsp[3][0]=0; Tpsp[3][1]=0; Tpsp[3][2]=0; Tpsp[3][3]=1;
 
 Q2=0;
 F2=0;
 G2=0;
 Form5->Edit2->Text=FloatToStr(Q2);
 Form5->Edit3->Text=FloatToStr(F2);
 Form5->Edit11->Text=FloatToStr(G2);
 
 TvrYOs[0][0]=cos(k2*Q2);TvrYOs[0][1]=0; TvrYOs[0][2]=-sin(k2*Q2);TvrYOs[0][3]=0;
 TvrYOs[1][0]=0;         TvrYOs[1][1]=1; TvrYOs[1][2]=0;          TvrYOs[1][3]=0;
 TvrYOs[2][0]=sin(k2*Q2);TvrYOs[2][1]=0; TvrYOs[2][2]= cos(k2*Q2);TvrYOs[2][3]=0;
 TvrYOs[3][0]=0;         TvrYOs[3][1]=0; TvrYOs[3][2]=0;          TvrYOs[3][3]=1;
 
 TvrXOs[0][0]=1;TvrXOs[0][1]= 0;         TvrXOs[0][2]=0;          TvrXOs[0][3]=0;
 TvrXOs[1][0]=0;TvrXOs[1][1]= cos(k2*F2);TvrXOs[1][2]=sin(k2*F2); TvrXOs[1][3]=0;
 TvrXOs[2][0]=0;TvrXOs[2][1]=-sin(k2*F2);TvrXOs[2][2]=cos(k2*F2); TvrXOs[2][3]=0;
 TvrXOs[3][0]=0;TvrXOs[3][1]= 0;         TvrXOs[3][2]=0;          TvrXOs[3][3]=1;
 
 TvrZOs[0][0]= cos(k2*G2);TvrZOs[0][1]=sin(k2*G2); TvrZOs[0][2]=0; TvrZOs[0][3]=0;
 TvrZOs[1][0]=-sin(k2*G2);TvrZOs[1][1]=cos(k2*G2); TvrZOs[1][2]=0; TvrZOs[1][3]=0;
 TvrZOs[2][0]= 0;         TvrZOs[2][1]=0;          TvrZOs[2][2]=1; TvrZOs[2][3]=0;
 TvrZOs[3][0]= 0;         TvrZOs[3][1]=0;          TvrZOs[3][2]=0; TvrZOs[3][3]=1;
 
 formBitmap->Width= W;
 formBitmap->Height=H;
 formBitmap->Canvas->Brush->Color=clWhite;
 
7.4. Создание обработчика события FormPaint
 
Процесс создания такого обработчика для этой программы приведен в лабораторной работе №2.
В содержимое функции FormPaint должны входить всего три команды:
 
void __fastcall TForm5::FormPaint(TObject *Sender)
{
 formBitmap->Canvas->FillRect(Rect(0,0,W,H));      //Забеливаем битмап
 GrafKonv();                                       //Рисуем на битмапе
 Canvas->Draw(0,0,formBitmap);              // Копируем битмап на форму
}
 
2-й ЭТАП
7.5. Разработка управляющих функций
 
В состав этого этапа входит разработка функции GrafKonv() и функции ShowAll().
 
7.6. Разработка графического конвейера GrafKonv()
 
Текст этой функции отличается от предыдущей работы только тем, что здесь добавлен вызов функции преобразования произвольной оси вращения MN:
 
void GrafKonv(void)
{
 IzmOsi();
 IzmMN();
 IzmVid();
 IzmToht();
 ShowAll();
}
Добавленная функция выделена синим цветом.
 
7.7. Функция запуска прорисовок  всех объектов - ShowAll()
 
В функции ShowAll() должны быть запущены функции, ответственные за прорисовку следующих объектов:
- трехмерной локальной системы координат;
- произвольной оси вращения MN;
- видимой (перспективно-преобразованной) фигуры;
- точек схода Tсх.zTсх.x  и Tсх.y;
- лучей схода, проходящих от точек схода к вершинам видимой фигуры;
 
С учетом этого функция будет иметь вид:
 
void ShowAll(void)
{
 ShowOsi();
 ShowMN();
 ShowFig(clBlue,mRezVid);              // Рисуем видимую фигуру
 
 if(Form5->CheckBox1->Checked)         // Если 1-й флажок включен,
    {ShowLuthi(mRezToht[2][0],          //   рисуем лучи схода к точке Tz,
               mRezToht[2][1],mRezVid);
      ShowToht(mRezToht[2][0],          //   рисуем точку Tcx на оси Z
               mRezToht[2][1],"Tz");
 
     ShowLuthi(mRezToht[0][0],          //   рисуем лучи схода к точке Tx,
               mRezToht[0][1],mRezVid);
      ShowToht(mRezToht[0][0],          //   рисуем точку Tcx на оси X
               mRezToht[0][1],"Tx");
 
     ShowLuthi(mRezToht[1][0],          //   рисуем лучи схода к точке Ty,
               mRezToht[1][1],mRezVid);
      ShowToht(mRezToht[1][0],          //   рисуем точку Tcx на оси Y
               mRezToht[1][1],"Ty");
    }
}
3-й ЭТАП
7.8. Построение осей координат
 
В работу третьего этапа входит разработка функций IzmOsi(), RezMatrOs(), Preobr(), Normir(), MultMatr() и ShowOsi(). Содержимое этих функций полностью соответствует аналогичным функциям из лаб. раб. 7. Не забудьте заменить имя макроса FPC на FBC во всех командах, где он был применен, а также объявить сам макрос в начальной части программы (см. лаб. раб. 2). Это связано, как вы помните, с тем, что в лабораторных работах №7, 8, 9 для реализации графического окна использовался компонент PaintBox1, а в этой лабораторной работе – FormPaint.
Кроме вышеперечисленных функций, необходимо создать обработчики для компонентов  TrackBar2TrackBar3  и  TrackBar4, с помощью которых будут вводиться углы поворотов осей Q2, F2 и G2.
Порядок создания этих обработчиков описан в лаб. раб. 7.
 
4-й ЭТАП
7.9. Порядок построения  произвольной  оси  вращения
 
Отличительной особенностью при построении произвольной оси вращения будет то, что здесь мы не станем создавать отдельную результирующую матрицу её преобразования. Все преобразования над координатами вершин оси MN будем выполнять путем прямого внесения изменений в матрицу её координат при манипуляциях с компонентами CSpinEdit1CSpinEdit6.
Единственно, какую матрицу для преобразования будем использовать, так это матрицу TrezOs[4][4], обеспечивающую разворот прямой MN в составе локальных осей X, Y, Z.
Другой особенностью, которую мы здесь впервые реализуем, будет применение так называемых текущих координат. Если раньше, в предыдущих работах любое новое положение объектов получалось путем суммарного преобразования относительно исходных координат, то здесь новые значения координат точек M или N мы будем заносить в матрицу текущих координат и использовать их в роли исходных для последующих преобразований. Более наглядно суть этого метода будет понятна из текста функций, разрабатываемых для видимой фигуры, которые будут приведены далее.
Порядок построения произвольной оси вращения следующий:
- создадим запускающую функцию IzmMN();
- поместим вызов функции IzmMN() в главный конвейер (что уже сделано в п. 7.6.);
- создадим функцию ShowMN() для прорисовки оси вращения MN  с использованием отнормированных текущих координат фигуры;
- создадим обработчики для компонентов CSpinEdit1CSpinEdit6.
 
7.10. Создание функции IzmMN()
 
Текст программного кода функции IzmMN() имеет вид:
 
void IzmMN(void)
{
 Preobr(2,mTekMN,TrezOs,mIzmMN);   // Преобразование координат оси вращения
 Normir(2,mIzmMN,mRezMN);          // Нормировка координат оси вращения
 }
7.11. Создание функции ShowMN()
 
В этой функции необходимо установить параметры компонента "перо": толщину линии MN и её цвет, затем провести саму линию, изображающую ось вращения, проставить обозначения концов линии – символы "M" и "N", после чего вернуть параметры пера в исходное состояние.
 
void ShowMN(void)
{
 FBC->Pen->Width=3;
 FBC->Pen->Color=clRed;
 
 FBC->MoveTo(W/2+int(h* mRezMN[0][0]), H/2-int(h* mRezMN[0][1]));
 FBC->LineTo(W/2+int(h* mRezMN[1][0]), H/2-int(h* mRezMN[1][1]));
 
 FBC->TextOut(W/2+int(h* mRezMN[0][0])-10, H/2-int(h* mRezMN[0][1])-10,"M");
 FBC->TextOut(W/2+int(h* mRezMN[1][0])+10, H/2-int(h* mRezMN[1][1])-10,"N");
 
 FBC->Pen->Width=1;
 FBC->Pen->Color=clBlack;
}
 
7.12. Создание обработчиков для скроллинговых окошек
CSpinEdit1CSpinEdit6
 
В составе этих обработчиков необходимо сначала прочитать значения координат точек M или N в переменные Mx, Nx, My, Ny, Mz, Nz, затем внести эти значения в матрицу текущих координат оси вращения, после чего вызвать команду перерисовки графического окна. Ниже приводится пример программного кода для CSpinEdit1, остальные пять обработчиков создайте самостоятельно.
 
void __fastcall TForm5::CSpinEdit1Change(TObject *Sender)
{
 Mx=(Form5->CSpinEdit1->Value);
 mTekMN[0][0]=Mx;
 FormPaint(0);
}
На этом создание произвольной оси вращения завершается. Запустите программу на исполнение и проверьте правильность работы окон CSpinEdit при установке концов прямой MN в задаваемые координаты.
 
5-й ЭТАП
7.13. Порядок построения видимой фигуры
 
Видимая фигура отличается от истинной фигуры тем, что она, кроме обычных видовых преобразований, таких как перенос, масштабирование, повороты, подвергается ещё и перспективному проецированию. Процесс построения видимой фигуры почти полностью совпадает с построением видимой фигуры в лабораторной работе №9, за исключением того, что здесь добавляется еще и такое преобразование как вращение относительно произвольной оси MN с использованием матрицы Tvr[4][4]. Для вычисления параметров матрицы вращения разработаем функцию Povorot3().
Здесь мы также будем использовать текущие координаты. Это связано с тем, что в ситуации, когда объект вращается в двух различных системах, а в нашем случае это вращение в составе локальных координатных осей и вращение относительно произвольной оси MN, положение фигуры на поверхности сферы вращения может принимать несколько различных значений, в зависимости от последовательности поворотов координатных осей. Использование текущих координат позволяет избежать сложностей при определении истинного местоположения объекта в трехмерном пространстве.
В работу пятого этапа входят следующие действия:
- создадим запускающую функцию  IzmVid();
- поместим вызов функции IzmVid() в функцию  GrafKonv();
- создадим функцию вычисления результирующей матрицы  RezMatrVid();
- создадим функцию ShowFig(),  её содержимое почти полностью соответствует аналогичной функции из лаб. раб. 9.
Также как и в л.р. №7, здесь необходимо создать обработчики событий для компонентов управления:
- TrackBar1 – для масштабирования фигуры;
- TrackBar5 – для переноса фигуры вдоль оси X;
- TrackBar6 – для переноса фигуры вдоль оси Y;
- TrackBar7 – для переноса фигуры вдоль оси Z;
 
7.14. Функция обработки  координат видимой фигуры IzmVid()
 
Обработка координат видимой фигуры заключается в последовательном запуске следующих функций:
- функции вычисления результирующей матрицы локального преобразования  RezMatrVid();
- функции преобразования координат фигуры Preobr() с использованием матрицы текущих координат mTekVid и матрицы TrezVid;
- функции преобразования координат фигуры Preobr() с использованием матрицы Tpsp;
- функции преобразования координат фигуры Preobr() с использованием матрицы TrezOs;
- функции нормировки координат фигуры Normir():
 
void IzmVid(void)
{
 float M1[8][4],M2[8][4];
 
 RezMatrVid();                     // Вычисляемрез.матрицулок.преобраз. фиг.
 Preobr(8,mTekVid,TrezVid,M1);     // Преобраз.текущ.координатфигуры
                                   //    в локальной системе координат
   for(int i=0;i<8;i++)
    for(int j=0;j<4;j++)
        mTekVid[i][j]=M1[i][j];     // Запоминаем текущие координаты после
                                    //    очередного преобразования
 Preobr(8,M1,Tpsp,M2);              // Перспективное проецирование
 Preobr(8,M2,TrezOs,mIzmVid);       // Преобраз. координат фигуры
                                    //    в глобальной системе координат
 Normir(8,mIzmVid,mRezVid);         // Нормировка координат фигуры
}
 
Как видно из текста программного кода, здесь, после выполнения вращения фигуры с использованием матрицы локальных преобразований TrezVid[4][4], в том числе и вращения относительно MN, необходимо запомнить полученные координаты в матрице текущих координат. А все дальнейшие преобразования, относящиеся к проецированию и вращению в глобальной системе координат с использованием матриц Tpsp и TrezOs, будут использоваться только для последующей прорисовки фигуры.
Запоминание координат фигуры в матрице текущих координат позволяет выполнять различные преобразования фигуры (перенос, масштабирование, изменение положения оси вращения, развороты осей) с использованием стандартных управляющих компонентов  C++Builderа  даже в процессе вращения фигуры вокруг оси MN.
 
7.15. Разработка функции вычисления результирующей матрицы
видимой фигуры RezMatrVid()
 
Результирующая матрица локального преобразования видимой фигуры для случая, когда запущен процесс вращения фигуры, вычисляется по схеме:
 
TrezVid = Tper * Tmsb * Tvr
 
Как видим, она отличается от аналогичной матрицы в лаб. раб. 9 тем, что здесь не включены матрицы Tpsp и TrezOs, они будут задействованы только после того, как выполнится запоминание преобразованных координат в матрице текущих координат. Это реализовано в функции IzmVid().
Для случая, когда таймер не включен, схема вычисления имеет вид:
 
TrezVid = Tper * Tmsb
 
С учетом этого программный код функции будет иметь вид:
 
void RezMatrVid(void)
{
 float T1[4][4];
 
 if(Form5->Timer1->Enabled==true)
    {Povorot3();                      // Вычисл. парам. матрицы поворота Tvr
     MultMatr(Tper, Tmsb, T1);        // 1-я промежуточн.матр.преобр.фиг.
     MultMatr(T1, Tvr, TrezVid);      // Вычисл.результир.матрицу преобр.фиг.
    }                                 //   для вращения относит. произв. оси
 else
     MultMatr(Tper, Tmsb, TrezVid);   // Вычисл.результир.матрицу преобр.фиг.
                                      //   без вращения
}
О том, как работает функция Povorot3() смотри пункт 7.19.
 
7.16. Создание обработчика событий для движка TrackBar1
 
Программный код для обработчика TrackBar1  в основном совпадает с аналогичным кодом в лаб. раб.7, за исключением отличия, связанного с использованием текущих координат и имеет вид:
 
void __fastcall TForm5::TrackBar1Change(TObject *Sender)
{
 s=km*(Form5->TrackBar1->Position);
 Form5->Edit1->Text=FloatToStr(s);
 Tmsb[3][3]=olds/s;
 
 FormPaint(0);
}
 
Порядок текущей инициализации переменной olds описан в п. 7.18.
 
 
7.17. Создание обработчиков событий для движков
TrackBarTrackBar6 и TrackBar7
 
Программный код для обработчиков TrackBar5,  TrackBar6  и TrackBar7, служащих для переноса фигуры на значения l, m и n, в основном совпадает с аналогичными обработчиками в лаб. раб.7, за исключением отличий, связанных с использованием текущих координат и для TrackBar5 имеет вид:
 
//--------------Перенос фигуры по X-------------------------------------------
void __fastcall TForm5::TrackBar5Change(TObject *Sender)
{
 l=(Form5->TrackBar5->Position);
 Form5->Edit5->Text=FloatToStr(l);
 Tper[3][0]=l-oldl;
 
 FormPaint(0);
}
 
Обработчики для TrackBar6  и TrackBar7 создайте самостоятельно с необходимыми отличиями.
 
7.18. Функция прорисовки фигуры ShowFig()
 
Функция ShowFig()  незначительно отличается от аналогичной функции из лаб. раб. №9. Можно использовать её текст, но в конце необходимо добавить следующие команды:
 
 oldl=Form5->TrackBar5->Position;
 oldm=Form5->TrackBar6->Position;
 oldn=Form5->TrackBar7->Position;
 olds=km*(Form5->TrackBar1->Position);
 
   for(int i=0;i<4;i++)
    for(int j=0;j<4;j++)
       {Tper[i][j]=Ted[i][j];
        Tmsb[i][j]=Ted[i][j];
       }
Эти команды необходимы для правильного функционирования режима текущих координат, т.к. на преобразование фигуры оказывают влияние не конкретные параметры, а их изменения, т.е. приращения. Если же изменения в каком либо преобразовании нет на момент перерисовки графического окна, то и сама матрица данного преобразования должна быть "выключена", т.е. приведена к единичной.
В принципе, эти команды можно поместить и в функциях GrafKonv() или ShowAll(), главное, чтобы они выполнялись после завершения всех перерасчетов и прорисовок, осуществляемых за один проход графического конвейера.
 
6-й ЭТАП
7.19. Реализация вращения видимой фигуры относительно оси MN
 
Для вращения видимой фигуры относительно произвольной оси MN необходимо выполнять последовательные повороты фигуры вокруг оси на 1 градус до тех пор, пока суммарное число этих поворотов не сравняется с заданным углом поворота. После каждого поворота на 1 градус необходимо перерисовывать графическое окно, в результате чего получим анимационный эффект вращения фигуры.
Запуск вращения будем осуществлять по нажатию на кнопку "Пуск", в обработчике которой надо поместить команду включения таймера:
 
void __fastcall TForm5::Button1Click(TObject *Sender)
{
 Form5->Timer1->Enabled=true;               // включаемтаймер
}
Затем создадим обработчик для таймера и поместим в него команду перерисовки графического окна:
 
void __fastcall TForm5::Timer1Timer(TObject *Sender)
{
 FormPaint(0);
}
Для прекращения вращения фигуры необходимо остановить таймер, поэтому в обработчике кнопки "Стоп" поместим команду:
 
void __fastcall TForm5::Button2Click(TObject *Sender)
{
  Form5->Timer1->Enabled=false;             // выключаемтаймер
}
При каждой перерисовке графического окна, запускаемой при помощи таймера, происходит перезапуск графического конвейера, а с ним и пересчет координат фигуры в процессе её вращения.
Из приведенного примера функции  RezMatrVid() (см. п. 7.15), в которой производится вычисление результирующей матрицы преобразования видимой фигуры  TrezVid[4][4] можно увидеть, что там, прежде чем выполняются преобразования координат, запускается функция Povorot3(), в которой производится вычисление матрицы поворота фигуры Tvr[4][4]:
 
void Povorot3(void)
{
 float n1,n2,n3;
 float a11,a12,a13,a14,a21,a22,a23,a24,a31,a32,a33,a34,a41,a42,a43,a44;
 
 float sumXYZ=(Nx-Mx)*(Nx-Mx)+(Ny-My)*(Ny-My)+(Nz-Mz)*(Nz-Mz);
 
 n1=(Nx-Mx)/sqrt(sumXYZ);
 n2=(Ny-My)/sqrt(sumXYZ);
 n3=(Nz-Mz)/sqrt(sumXYZ);
 
 a11=n1*n1+(1-n1*n1)*cos(k2*Q);
 a12=n1*n2*(1-cos(k2*Q))+ n3*sin(k2*Q);
 a13=n1*n3*(1-cos(k2*Q))- n2*sin(k2*Q);
 p=a11*p+a12*q+a13*r;                                   // a14=p
 
 a21=n1*n2*(1-cos(k2*Q))- n3*sin(k2*Q);
 a22=n2*n2+(1-n2*n2)*cos(k2*Q);
 a23=n2*n3*(1-cos(k2*Q))+ n1*sin(k2*Q);
 q=a21*p+a22*q+a23*r;                                   // a24=q
 
 a31=n1*n3*(1-cos(k2*Q))+ n2*sin(k2*Q);
 a32=n2*n3*(1-cos(k2*Q))- n1*sin(k2*Q);
 a33=n3*n3+(1-n3*n3)*cos(k2*Q);
 r=a31*p+a32*q+a33*r;                                   // a34=r
 
 a41=-Mx*a11-My*a21-Mz*a31+Mx;
 a42=-Mx*a12-My*a22-Mz*a32+My;
 a43=-Mx*a13-My*a23-Mz*a33+Mz;
 
 Tpsp[0][3]=p;
 Tpsp[1][3]=q;
 Tpsp[2][3]=r;
 
 Tvr[0][0]=a11; Tvr[0][1]=a12; Tvr[0][2]=a13; Tvr[0][3]=0;
 Tvr[1][0]=a21; Tvr[1][1]=a22; Tvr[1][2]=a23; Tvr[1][3]=0;
 Tvr[2][0]=a31; Tvr[2][1]=a32; Tvr[2][2]=a33; Tvr[2][3]=0;
 Tvr[3][0]=a41; Tvr[3][1]=a42; Tvr[3][2]=a43; Tvr[3][3]=1;
}
В этой функции сначала вычисляются направляющие косинусы (см. формулы (11), (12), (13)), а затем параметры матрицы вращения a11, a12, a13 и т.д. После чего вычисленные значения вводятся в саму матрицу Tvr[4][4].
Переменные a14, a24, a34  в виде параметров  p, q, r  будут задействованы позже, в составе матрицы Tpsp  при реализации завершающего перспективного проецирования. Если эти параметры включить сейчас в матрицу преобразования, произойдет эффект умножения перспективного преобразования на каждом таймерном проходе, в результате чего изображение свернется в точку.
 
7-й ЭТАП
7.20. Порядок построения точек схода и лучей схода
 
В работу седьмого этапа входят следующие действия:
- создадим запускающую функцию  IzmToht();
- поместим вызов функции IzmToht() в функцию  GrafKonv();
- создадим функцию ShowToht().
 
7.21. Функция обработки  координат точек схода IzmToht()
 
В отличие от предыдущей лабораторной работы, здесь точки схода будут вращаться вместе с фигурой вокруг произвольной оси  MN. Поэтому для точек схода Xсх, Yсх и Zсх придется все преобразования вести в режиме текущих координат. А в качестве матрицы преобразования будем использовать матрицу вращения фигуры Tvr.
С учетом этого, содержимое функции   IzmToht() будет иметь вид:
 
void IzmToht(void)
{
 static float M1[3][4];
 
 if(Form5->Timer1->Enabled==true)
   {Preobr(3,mTekToht,Tvr,M1);
    for(int i=0;i<3;i++)
     for(int j=0;j<4;j++)
         mTekToht[i][j]=M1[i][j];
    Preobr(3,M1,TrezOs,mIzmToht);
   }
 else
    Preobr(3,mTekToht,TrezOs,mIzmToht);
 Normir(3,mIzmToht,mRezToht);
}
 
7.22. Функция прорисовки точек схода ShowToht()
 
Функция прорисовки точек схода почти полностью идентична такой же функции в лаб. раб. №9 с той лишь разницей, что здесь используется макрос с именем "FBC".
 
7.23. Функция прорисовки лучей схода ShowLuthi()
 
Для прорисовки лучей схода достаточно иметь координаты точек схода и координаты видимой фигуры. Текст программного кода этой функции полностью соответствует лаб. раб. №9.
Для включения или выключения прорисовки лучей схода и точек схода необходимо создать обработчик для компонента CheckBox1  с одной командой:   Repaint().
 
8-й ЭТАП
7.24. Разработка вспомогательных функций
 
Обработчики для кнопки "Исходное" и движка "Скорость вращения" создайте самостоятельно, используя объяснения из предыдущих лабораторных работ.
 
 
 
6. Контрольные вопросы
 
1. Напишите схему вычисления результирующей матрицы вращения вокруг произвольной оси.
2. Каково назначение матриц переноса в схеме вычисления результирующей матрицы ?
3. Напишите матрицу вращения трехмерных объектов относительно начала координат.
4. Чему равны направляющие косинусы  n1, n2, n3 главных осей координат X, Y, Z?
5. Нарисуйте чертеж, поясняющий физический смысл направляющих косинусов.
6. Какой вариант перспективной проекции возникает при вращении трехмерных объектов вокруг произвольной пространственной оси ?
7. Какой вариант перспективной проекции возникает при вращении трехмерных объектов вокруг осей, параллельных главным осям координат X, Y, Z?
8. Какой вариант перспективной проекции возникает при вращении трехмерных объектов вокруг главных осей координат X, Y, Z?
9. Какой вид проекции выбран Вами для выполнения этой лабораторной работы? Почему?
10. В процессе вращения трехмерных объектов вокруг произвольной пространственной оси или вокруг осей, параллельных главным осям координат возникают ситуации, когда картинная плоскость (экран ПК) весь испещрен пересекающимися линиями, цвет которых совпадает с цветом ребер исходного объекта. От чего возникает этот эффект? Как от него избавиться?