ЛАБОРАТОРНАЯ РАБОТА № 2
Базовые  видовые  преобразования  двумерных  объектов
 
1.  Цель работы
 
1) Освоить программирование базовых видовых преобразований двумерных объектов с использованием методов матричного анализа.
2) Разработать программу в среде визуального проектирования C++Builder, позволяющую демонстрировать влияние различных параметров матрицы видовых преобразований на изменение местоположения, размеров и формы исходного объекта.
3) Освоить программирование конвейерной обработки заданного изображения с использованием интерактивного пользовательского интерфейса.
4) Освоить программирование анимации изображения.
 
2. Постановка задачи и порядок выполнения работы
 
2.1. В проект, разработанный для лабораторной работы №1 добавить новый модуль с формой Form3.
2.2. В графическом окне Form3 создать систему декартовых координат с сеткой.
2.3. В составе системы координат изобразить геометрическую фигуру, согласно варианту задания из таблицы 1. Фигура должна задаваться координатами вершин: A(x1,y1), B(x2,y2), C(x3,y3), и D(x4,y4) таким образом, чтобы она находилась в первом квадранте декартовой системы координат XOY, как показано на рисунке 1.
 
 
Рисунок 1 – Система координат и исходная фигура
 
                      Таблица 1

 Вар.
Тип фигуры
Цвет фигуры
Цвет осей
1
Квадрат
красный
черный
2
Прямоугольник
синий
голубой
3
Параллелограмм
зеленый
коричневый
4
Трапеция
коричневый
зеленый
5
Ромб
голубой
синий
6
Квадрат
красный
коричневый
7
Прямоугольник
синий
голубой
8
Параллелограмм
зеленый
черный
9
Трапеция
коричневый
синий
10
Ромб
голубой
зеленый
 

2.4. Задавая различные значения параметров видовых преобразований с помощью окон ввода, продемонстрировать следующие виды графических преобразований:
1) Перенос фигуры из 1-го во 2-й, 3-й и 4-й квадранты;
2) Сдвиг объекта вдоль каждой оси координат или вдоль обеих осей одновременно;
3) Изменение масштаба объекта по оси X или по оси Y, а также изменение его размера по обеим осям одновременно;
4) Отображение фигуры относительно каждой оси координат или относительно начала координат.
2.5. Преобразования типа "перенос" и "масштабирование" должны выполняться в режиме анимации. При этом необходимо принять меры к устранению эффекта «мерцания» изображения, возникающего в процессе этих преобразований.
2.6. Процесс создания программного кода модуля состоит из двух этапов.
Первый этап выполняется во время лабораторного занятия, в течение которого необходимо выполнить пункты 2.1 и 2.2. В разделах 6 и 7 подробно описан порядок выполнения этого этапа, в который входят:
- создание и подключение к проекту третьего модуля Form3;
- установка элементов управления на форму этого модуля в соответствии с рисунком 2;
- разработка функций прорисовки осей координат, сетки и их масштабирования.
Результаты предъявить преподавателю.
Второй этап (пункт 2.3) выполняется самостоятельно, дома, где необходимо доработать программу. Все необходимые пояснения для второго этапа приведены в подразделах 7.12 – 7.16.
2.7. Оформить отчет согласно правилам.
 
 
3.  Краткие теоретические сведения
 
Видовыми преобразованиями графических объектов называются преобразования, ведущие к изменению их местоположения, формы, размеров или ориентации. Видовые преобразования разделяются на базовые и комбинированные. Базовые преобразования отвечают только за какой либо один вид преобразования, а комбинированные – осуществляют сразу несколько видов преобразований. К базовым видовым преобразованиям относятся перенос, сдвиг, масштабирование, отображение, перспективное проектирование. К комбинированным преобразованиям относятся вращение, отображение относительно произвольной прямой и др.
В практической реализации видовые преобразования представляют собой операции перемножения двух матриц. Первая матрица - это матрица координат исходной фигуры Мисх. Вторая - матрица видового преобразования Тпреобр. (переноса, сдвига, масштабирования, отображения, или вращения). В результате получают результирующую матрицу координат нового положения объекта – Мрез.. Матричное уравнение видового преобразования исходной фигуры выглядит следующим образом:
 
                Мисх× Тпреобр = Мрез                                                           (1)
 
Здесь Тпреобр - матрица двумерного видового преобразования, третьего ранга, квадратная, способна преобразовывать двумерные объекты, выраженные в однородных координатах, имеет вид:
 
 
Каждый элемент этой матрицы отвечает за свой, и только свой вид преобразования:
- элементы  l  и  m   отвечают за перенос;
- элементы  b  и  d  отвечают за сдвиг;
- элементы  ae  и  s   отвечают за масштабирование и отображение.
Матрица исходных координат фигуры Мисх задается в однородной системе координат, т.е. каждая строка этой матрицы состоит из трех параметров: координаты одной из вершин фигуры по оси X, координаты той же вершины по оси Y и скалярного (масштабирующего) множителя h. Скалярный множитель добавляется для того, чтобы можно было выполнять перемножение двух матриц:  Мисх и  Тпреобр, и кроме этого, он позволяет дополнительно масштабировать координаты фигуры. Как правило, если значения координат фигуры, заданной в однородной системе соответствуют тем же значениям в декартовой системе, то скалярный множитель h равен единице. Тогда матрица исходных координат фигуры, состоящей из  k вершин, будет иметь следующий вид:


 
После подстановки  Мисх  и  Тпреобр  в уравнение (1) оно будет выглядеть следующим образом:


Здесь результирующая матрица координат М
рез определяет новое неотнормированное состояние фигуры в однородной системе координат после преобразования. Координаты неотнормированной матрицы Мрез в большинстве случаев в декартовой системе не применяют поэтому, прежде чем выполнить прорисовку объекта на картинной плоскости (т.е. в декартовой системе координат), необходимо их отнормировать. Для нормировки результирующих координат объекта параметры последнего столбца матрицы Мрез приводятся к единичному значению (в случае, если они не равны единице), для чего все значения каждой строки матрицы делятся на скалярный множитель hk данной строки и получается Мнорм т.е.:

Рассмотрим все виды базовых преобразований по отдельности.
 
3.1.  Перенос
 
Перенос – это такое видовое преобразование, при котором объект перемещается по экранной плоскости на заданное значение без изменения его формы, размера или ориентации.
Матрица двумерного переноса имеет вид:

 
где: l   – параметр переноса вдоль оси X;
mпараметр переноса вдоль оси Y.
Если параметры переноса «включены», то  l  и  mэто любые вещественные числа, положительные или отрицательные. Если они «выключены», то их значения равны 0.
Произвольная точка А(x, y) с помощью матрицы переноса преобразуется к виду:
 
 
Из соотношения (7) видно, что координаты преобразованной точки А'(x', y') представляют собой сумму, в которой к исходным координатам точки прибавляются соответствующие значения параметров  l  или  m. 
 
3.2. Сдвиг
 
Сдвиг – преобразование, приводящее к изменению формы и размеров объекта по аналогии с деформацией сдвига в «Сопротивлении материалов». Сдвиг может выполняться вдоль каждой из осей X, Y или вдоль обеих осей одновременно. Наиболее наглядно проявляется, если низ объекта «закреплен» на оси Х, а на верхнюю часть действует сила, заставляющая его перемещаться.
Матрица сдвига Тсдв имеет вид:
 
где: b  и  d   – параметры сдвига вдоль осей  Y  и  X  соответственно.
Если они «включены», то это любые вещественные числа, если «выключены», то равны 0.
Произвольная точка A(x, y) преобразуется с помощью этой матрицы к виду:


 
Из (9) видно, что к исходным координатам точки  А:  x  и  y,  в результате преобразования добавляются соответственно произведения  d×y   и   b×x .
 
3.3.  Масштабирование
 
Масштабирование – это преобразование, при котором объект изменяет свои размеры пропорционально заданным значениям.
Матрица масштабирования Тмасшт имеет вид:

 
где:    a – параметр масштабирования в направлении оси X;
e – параметр масштабирования в направлении оси Y;
s – параметр общего изменения масштаба (вдоль осей X и Y).
 
Если они «включены», то  a, e, s – любые положительные вещественные числа. Если они «выключены», то их значения равны 1.
Произвольная точка A(x, y) преобразуется с помощью матрицы (10) следующим образом:

 
 
Выражение (11) показывает, что исходные координаты точки  Аx  и  y  изменяются в  a  и  e  раз соответственно. Этим обеспечивается изменение масштабов вдоль осей X и Y. Если а  и  е  больше единицы, масштаб фигуры увеличивается, если они меньше единицы (но больше нуля) – уменьшается.
Параметр s, на который делится каждый элемент результирующей матрицы координат при нормировке (для перехода от однородных координат к декартовым) действует на масштаб фигуры в обратно-пропорциональном порядке. Если  s  больше единицы – масштаб вдоль осей  X и Y  уменьшается, если меньше единицы – увеличивается.
 
3.4.  Отображение
 
Отображение – это такое видовое преобразование при котором объект отображается (отражается) относительно оси Х или оси Y или относительно начала координат.
Матрица отображения Тотобр имеет вид:


 
где:  a – параметр отображения относительно оси Y;
e – параметр отображения относительно оси X;
s – параметр отображения относительно начала координат.
Если параметры отображения a, e, s   «включены», то они равны   –1, если «выключены», то – +1.
Матрица (12) обеспечивает «зеркальное» отображение, если численные значения параметров a, e, s - равны –1. Если параметры  a, e, s отличаются от -1  в большую или меньшую сторону (но не больше 0) они начинают работать одновременно и как параметры масштабирования, вследствие чего преобразованный объект оказывается не только отраженным, но и искаженным. Такое двойное преобразование объекта называется – эффект “кривого зеркала”.
 
3.5. Перспективное проектирование
 
Перспективное проектирование представляет собой видовое преобразование двумерных объектов, при котором объекты, расположенные на плоскости , вначале перед нормировкой переводятся в трехмерное пространство. Затем, в процессе нормировки пространственный объект проектируется на плоскость  из центра проекций, расположенного в начале координат. Матрица перспективного проектирования образуется из матрицы видового преобразования общего вида (1) следующим образом:
 
1. Элементы главной диагонали  a, e, s  приравниваются к единице;
2. Элементы  b, d, l, m  приравниваются к нулю.
После чего матрица перспективного проектирования будет иметь вид:


  
где  p и  q  - параметры перспективного проектирования.
Если они "включены", то могут принимать значения любых вещественных чисел, а если параметры "выключены", то они приравниваются к нулю.
Произвольная точка , расположенная на плоскости , преобразуется с помощью матрицы (13) к виду:


 
Из (14) следует, что координаты вершин исходных объектов изменяются в соответствии с законами перспективы: близкие к наблюдателю объекты кажутся большими по размеру, а чем они дальше от наблюдателя, тем их размеры становятся меньше.
 
4. Интерфейс программы
 
Работа программы лабораторной работы №2 начинается после нажатия указателем мыши на кнопку Lab2, расположенной на главной форме проекта Form1. После этого действия инициализируется дополнительная форма Form3, с установленными на ней графическим окном и элементами управления (см. рис. 2). Элементы управления установлены на двух панелях: на правой, и на нижней.
На правой панели Panel1 установлены:
- движок TrackBar,  предназначенный для масштабирования осей и фигуры от 0,5 до 2 раз;
- флажки CheckBox для переключения изображения сетки, обозначения вершин фигуры и анимации;
- кнопки "Запуск" и "Исходное", предназначенные для запуска видовых преобразований или возврата фигуры в исходное положение.
На нижней панели Panel2 установлены компоненты StringGrid1 - StringGrid3, предназначенные для демонстрации численных результатов видовых преобразований. В первое окно выводится матрица исходных координат фигуры MatrIsx[4][3], во второе окно выводится матрица видового преобразования Tpr[3][3]  и в третье – результирующая матрица координат фигуры MatrTek[4][3]. Свойства компонента StringGrid позволяют не только выводить данные, но и вводить их. Используем это качество для непосредственного ввода параметров видовых преобразований в ячейки компонента StringGrid2 и последующей демонстрации их действия на форму, положение и размеры исходной фигуры при нажатии на кнопку "Запуск". На рисунке 2 показаны результаты действия матрицы видового преобразования  Tpr[3][3] с включенными параметрами переноса: L=1 и m=1. Как видим, преобразованная фигура (красного цвета) сместилась вправо по оси X и вверх по оси Y по отношению к исходному положению (фигура серого цвета) на единицу.

 
Рисунок 2 – Окно формы Form3
 
 
5. Структура программы
 
Структура программы должна строится таким образом, чтобы можно было обеспечить оперативную перерисовку изображения, после каждого управляющего воздействия пользователя на движки, кнопки и флажки или в процессе анимационных преобразований фигуры.
Первичная прорисовка изображения должна выполняться сразу при запуске модуля, и включает в себя последовательное выполнение следующих действий:
- рисование осей координат;
- создание сетки;
- прорисовку фигуры в исходном месте;
- простановку обозначений вершин фигуры;
- вывод параметров матрицы исходных координат и матрицы видового преобразования.
Последующие перерисовки фигуры запускаются только по командам обработчиков событий при нажатии на кнопки, движки и флажки и отличаются от первичной прорисовки только тем, что в конвейере используется матрица видового преобразования Tpr[3][3] с измененными параметрами.
В связи с вышесказанным, все элементы структурной схемы программы можно разделить на три основные группы:
- функции исходной инициализации матриц и графического окна;
- функции конвейера обработки изображения (пересчета и нормировки матрицы координат и прорисовки осей, сетки, фигуры и обозначений вершин);
- функции обработки событий (управляющих воздействий пользователя).
На рисунке 3 изображена структурная схема работы программы. Она позволяет наглядно продемонстрировать работу конвейера обработки изображения и последовательность вызова различных функций, в зависимости от действий оператора.

Рисунок 3 – Структурная схема работы модуля Form3
 
Из схемы видно, что главной задачей любого обработчика событий является вызов функции FormPaint(), для перерисовки графического окна и запуска конвейера обработки изображения. Но прежде чем вызвать эту функцию, внутри обработчиков событий необходимо выполнить подготовительную работу: прочитать измененные значения параметров видового преобразования, значения параметров масштабирования осей, признаки включения флажков и т.п. Некоторые обработчики вообще ничего не делают, кроме вызова FormPaint(), например CheckBox1 и  CheckBox2. Зато такой обработчик как Button1 (кнопка "Запуск") работает в двух различных режимах:
- если флажок "Анимация" выключен, то в конвейере используются те параметры видового преобразования, которые непосредственно считаны из компонента StringGrid2;
- если флажок  "Анимация" включен, то в конвейере используются параметры преобразования, предварительно пересчитанные внутри обработчика кнопки "Запуск", и все это делается в цикле For;
 
 
6. СОЗДАНИЕ ДОПОЛНИТЕЛЬНОГО МОДУЛЯ ПРОЕКТА
 
6.1.  Открытие  проекта
 
1)  Загрузите свой проект в папку C:\Program Files\Borland\CBuilder\Projects\....
2) Запустите C++Builder (Пуск => Программы => Borland C++Builder 6).
3) Откройте свой проект: File => Open Project…=>  <имя папки проекта>  => <имя проекта>.bpr.
 
6.2.  Подключение дополнительного модуля Form3 к проекту
 
1) Добавление нового модуля.
Выполните: File => New => Form. Появится третья форма Form3.
 
2) Изменение заголовка формы.
В окне Object Inspector, в строке Caption вместо записи Form3  введите  Form3_Lab2.
 
3) Сохранение новой формы в папке проекта.
Выполните: File => Save All.
 
4) Перенастройка параметров инициализации формы.
Перенастройка выполняется аналогично пункту 4) раздела 6.3 лабораторной работы №1.
 
5) Сохранение проекта. Выполните: File => Save All.
 
6) Создание обработчика события на нажатие кнопки Lab2.
Откройте конструктор формы Form1 (через View => FormsView Form => Form3) и дважды щелкните по кнопке Lab2 (Button2). В окне редактора кода Unit1.cpp появится заголовок новой функции:
 
void __fastcall TForm1::Button2Click(TObject *Sender){}
 
Внутри фигурных скобок этой функции впишите команду инициализации формы Form3:
 
Application->CreateForm(__classid(TForm3), &Form3);
 
7) Добавление команды препроцессорной обработки.
В файле  Unit1.cpp  после записи  #include "Unit2.h"  допишите:  #include "Unit3.h".
 
8) Сохранение всех файлов проекта. Выполните: File => Save All.
 
9) Установка параметра видимости для формы Form3.
В окне Object Inspector, в строке Visible установите параметр true.
 
10) Сохраните проект, после чего запустите его на исполнение и убедитесь в том, что форма Form3 создается только после нажатия на кнопку Lab2.
 
6.3.  Компоновка формы Form3
 
Форма Form3 предназначена для демонстрации на её поле различных видовых преобразований над фигурой, форма которой задана в таблице 1.
Для доступа к конструктору Form3 надо выполнить: View => Forms В открывшемся окне View Form выберите Form3. Рассмотрим порядок заполнения формы Form3  элементами визуализации и управления в соответствии с рисунком 2.
 
1). Установка панели Panel1 на форму.
На вкладке Standard палитры компонентов выберите значок Panel, и установите панель на поле Form3, аналогично пункту 1) раздела 6.4 лабораторной работы №1.
Привяжите панель Panel1 к верхнему, правому и нижнему краю формы, для чего в окне Object Inspector переустановите свойство Anchors панели в состояние [akTop, akRight, akBottom].
В окне Object Inspector, в строке Caption удалите запись Panel1.
 
2). Установка панели Panel2 на форму.
Измените размеры Form3 в высоту на величину высоты панели Panel2 .
Установите вторую панель Panel2  так, как показано на рисунке 2 способом, аналогичным п.1).
Привяжите панель Panel2 к левому, правому и нижнему краю формы, для чего в окне Object Inspector переустановите свойство Anchors панели в состояние [akLeft, akRight, akBottom].
В окне Object Inspector, в строке Caption удалите запись Panel2.
 
3). Установка компонента TrackBar1 ("Масштабирование осей") на панель  Panel1.
На вкладке Win32 выберите компонент TrackBar  и установите его на поле панели Panel1.
Для компонента TrackBar1 в окне Object Inspector выполните следующие настройки:
- в строкеMax (максимальное количество отсчетов) установите значение:  20;
- в строкеMin (минимальное количество отсчетов) установите значение:  5;
- в строкеPosition (начальная позиция движка) установите значение: 10;
- в строкеTickStyle установите значение:  tsManual;
 
4). Установка компонентов CheckBox на панель управления  Panel1.
На вкладке Standard выберите компонент CheckBox  и установите его на поле панели Panel1.
В окне Object Inspector, в строке Caption удалите запись CheckBox1.
Всего, согласно рисунку 2, надо установить три таких компонента.
 
5). Установка компонентов Label на панель управления  Panel1.
Введите надписи "Масштабирование осей", "Сетка", "Обозначения" и "Анимация" на поле панели Panel1 в соответствии с рисунком 2.
 
6). Установка кнопок "Запуск" и "Исходное" на панель управления.
Установка кнопок выполняется аналогично пункту 4) раздела 6.4 лабораторной работы №1.
 
7). Установка компонентов StringGrid на панель управления  Panel2.
На вкладке Additional выберите компонент StringGrid  и установите его на поле панели Panel2.
Для компонента StringGrid1 в окне Object Inspector выполните следующие настройки:
- в строке Col Count (количество столбцов) установите значение: 3;
- в строке Row Count (количество строк) установите значение: 4;
- в строке Fixed Cols (число фиксир. столбцов) установите значение: 0;
- в строке Fixed Rows (число фиксир. строк) установите значение: 0;
- в строке ScrollBars выберите свойство ssNone (полосы прокрутки нет);
- в строке Options выберите – goEditing (редактировать),  для которого установите значение:  true.
Щелкните по компоненту StringGrid1 правой кнопкой мыши и в открывшемся меню выберите Copy. Щелкните правой кнопкой на поле панели Panel2 и в меню выберите Paste. На панели будет создана копия компонента - StringGrid2. Аналогичным образом создайте третий компонент - StringGrid3. Разместите все три компонента StringGrid на панели Panel2 согласно рисунку 2.
Для компонента StringGrid2 исправьте свойство Row Count  на значение:  3.
 
8). Установка компонентов Label на панель управления  Panel2:
Введите математические символы "Х" и "=" на поле панели Panel2  в соответствии с рисунком 2.
Установите названия матриц для компонентов StringGrid на панели Panel2 согласно рисунку 2.
 
На этом компоновка формы Form3 заканчивается.
Как видно из текста этого подраздела, здесь, в отличие от лабораторной работы №1, не устанавливается компонент PaintBox. В этом нет необходимости, потому что в данной работе мы будем рисовать прямо на форме, используя её свойство Canvas, о чем будет сказано далее. Однако, нужно оставить место на поле формы для выведения в эту область графического изображения, в соответствии с рисунком 2.
Сохраните проект и запустите его на исполнение. Проверьте, выполняется ли ввод и редактирование данных в окнах  StringGrid  формы  Form3.
 
7. РАЗРАБОТКА ФАЙЛА РЕАЛИЗАЦИИ Unit3.cpp
 
7.1. Предварительные пояснения
 
Как известно, разработка любой программы должна начинаться с построения её общей структурной схемы и блок-схем функций, входящих в состав программы.
Структурная схема для нашей программы составлена, как показано на рисунке 3. Что касается последовательности разработки самих функций, то здесь мы примем следующий порядок:
1) объявим прототипы всех разрабатываемых функций и глобальные переменные, используемые в этих функциях или в обработчиках событий;
2) выполним начальную инициализацию переменных;
3) разработаем главную функцию программы, из которой производится запуск конвейера изображения – это функция FormPaint – являющаяся обработчиком события OnPaint для формы Form3;
4) разработаем функцию графического конвейера GrafKonv(), входящую в состав FormPaint;
5) разработаем функции, входящие в состав графического конвейера;
6) создадим обработчики событий для управляющих элементов, установленных на форме при её компоновке;
7) разработаем вспомогательные функции.
 
Как видно из вышеприведенного алгоритма, разработка программы ведется по принципу "сверху-вниз", или же - от общего к частному. Сначала формируем главную функцию, потом разрабатываем всю её "начинку".

 
7.2. Объявление переменных и функций
 
Согласно требованиям к работе разрабатываемого проекта и в соответствии с рисунком 3, текст программного кода модуля Form3 должен содержать функции, ответственные за следующие действия:
- создание осей координат                                               – ShowOsi();
- изображение сетки на поле графического окна       – ShowStk();
- вывод матриц исходных и результирующих координат и матрицы преобразования;
- масштабирование осей координат и изображения в них;
- видовые преобразования исходной фигуры             – Preobr();
- нормировка координат преобразованной фигуры – Normir();
- прорисовка исходной и преобразованной фигуры – ShowFig(TColor col, float matrT[4][3]);
- прорисовка обозначений вершин фигуры                – Oboznath(float matrTo[4][3]);
- установка фигуры в исходное положение.
 
Запуск всех функций прорисовки объединим под одной общей функцией   - ShowAll(), а запуск всех функций преобразования и прорисовки изображения объединим под функцией - GrafKonv(), которая будет выполнять роль графического конвейера (см. рисунок 3).
Для таких действий как вывод матриц исходных и результирующих координат, масштабирование осей координат и установка фигуры в исходное положение отдельные функции создавать не будем, для их реализации достаточно использовать стандартные обработчики StringGridTrackBar1  и Button2.
Запуск функции GrafKonv(), как уже говорилось выше, будем осуществлять из обработчика FormPaint() , который должен вызываться при каждом обращении пользователя к элементам управления (кнопкам. движкам, флажкам и т.п.). Порядок программирования этих обработчиков будет рассмотрен ниже.
Объектами обработки для функций Preobr(), Normir(), ShowFig() и Oboznath() являются координаты фигуры ABCD, сгруппированные в матрицы. Координаты находятся на различных стадиях обработки, и поэтому они должны быть помещены в отдельные матрицы с различными именами, соответствующими этим стадиям, а именно:
matrIsx[4][3]     - матрица исходных координат;
matrIzm[4][3]    - матрица преобразованных (измененных) координат;
matrRez[4][3]    - матрица отнормированных координат;
matrEkr[4][2]    - матрица экранных координат.
 
Матрицу исходных координат мы создаем для того, чтобы обеспечить первоначальную прорисовку фигуры, сразу после запуска проекта или после нажатия на кнопку "Исходное".
Матрица измененных координат получается после преобразования исходных координат фигуры с помощью матрицы видового преобразования Tpr[3][3].
Матрицу результирующих координат мы получим после нормировки преобразованных координат.
Матрицу экранных координат получим после пересчета отнормированных координат в экранные.
Кроме этого, для преобразования координат фигуры необходима матрица видового преобразования - Tpr[3][3].
Поэтому, с учетом вышесказанного, в начальной части файла реализации объявим глобально все матрицы координат фигуры, матрицу преобразования фигуры и прототипы всех используемых функций преобразования и прорисовки изображения, а также все наиболее употребимые переменные в следующем виде:
 
Graphics::TBitmap * formBitmap;          // Описание указателя на буфер
 
static int W, H, nOs, h, hh, i, j, k;
static float a, b, d, e, l, m, p, q, s;
 
static float matrIsx[4][3];
static float matrIzm[4][3];
static float matrRez[4][3];
             int matrEkr[4][2];
 
static float Tpr[3][3];
 
static void GrafKonv(void);
static void Preobr(void);
static void Normir(void);
 
static void ShowAll(void);
static void ShowOsi(void);
static void ShowStk(void);
static void ShowFig(TColor col, float matrT[4][3]);
static void Oboznath(float matrTo[4][3]);

      Здесь, как видим, объявлен также указатель на графический буфер класса TBitmap.
Графический буфер – это битовая матрица, служащая для хранения в ней изображения (картинки) в формате .bmp, т.е. в пикселях.
Необходимость создания буфера в этой лабораторной работе определяется тем обстоятельством, что видовые преобразования фигуры должны выполняться в режиме анимации, осуществление которой потребует многократной перерисовки изображения. Если не применять отрисовку с буферизацией, то качество изображения будет очень низким из-за появления черных полос в области графического окна проекта.
Порядок использования графического буфера следующий:
- сначала забелим поверхность битмапа, т.е. графического буфера;
- после этого на его поле можно выполнять прорисовки с использованием команды:
formBitmap->Canvas-> <имя функции прорисовки>;
- затем содержимое буфера копируется в графическое окно формы с помощью команды:
Form3->Canvas->Draw(0, 0, formBitmap);
Здесь, параметр formBitmap - это имя буфера, его можно выбирать любым. Практический пример использования буфера рассмотрим далее, при описании функции FormPaint().
 
7.3. Разработка функции инициализации исходных данных FormCreate()
 
Обработчик события FormCreate  позволяет выполнять операцию предварительной инициализации данных сразу при запуске программы. Для его создания надо открыть вкладку Events окна Object Inspector для формы Form3 и щелкнуть по строке OnCreate, затем дважды щелкнуть по открывшемуся белому окошку. В окне редактора кода Unit3.cpp появится заголовок новой функции:
 
void __fastcall TForm1::FormCreate(TObject *Sender){}
 
Внутри фигурных скобок этой функции впишем команды обеспечивающие следующее:
- инициализацию графического буфера;
- инициализацию матрицы исходных координат   matrIsx[4][3];
- инициализацию матрицы видового преобразования  Tpr[3][3];
- инициализацию переменной  nOs.
 
Для инициализации графического буфера впишем следующие команды:
 
W=ClientWidth - (Panel1->Width) -10;                      // Задаем ширину графич. окна на форме
H=ClientHeight - (Panel2->Height) -10;                     // Задаем высоту графич. окна на форме
 
formBitmap=new Graphics::TBitmap();                      // Выделям динамич. память под буфер
formBitmap->Width = W;                                            // Задаем ширину битмапа
formBitmap->Height= H;                                              // Задаем высоту битмапа
formBitmap->Canvas->Brush->Color=clWhite;       // Задаем цвет закраски битмапа
 
Инициализацию матрицы исходных координат выполним следующим образом:
 
matrIsx[0][0]=1;   matrIsx[0][1]=1;   matrIsx[0][2]=1;
matrIsx[1][0]=2;   matrIsx[1][1]=3;   matrIsx[1][2]=1;
matrIsx[2][0]=4;   matrIsx[2][1]=3;   matrIsx[2][2]=1;
matrIsx[3][0]=5;   matrIsx[3][1]=2;   matrIsx[3][2]=1;
 
Понятно, что каждый студент здесь должен вписать другие численные значения. Координаты вершин фигуры можно выбирать любые, но так, чтобы форма фигуры соответствовала заданному варианту.
Далее, инициализируем матрицу видового преобразования. В начальный период она имеет вид единичной матрицы, так как ни один из её параметров еще не "включен":
 
Tpr[0][0]=1;   Tpr[0][1]=0;   Tpr[0][2]=0;
Tpr[1][0]=0;   Tpr[1][1]=1;   Tpr[1][2]=0;
Tpr[2][0]=0;   Tpr[2][1]=0;   Tpr[2][2]=1;
 
Затем выводим значения элементов матриц matrIsx[4][3] и Tpr[3][3] в соответствующие окна вывода StringGrid1 и StringGrid2. Пример заполнения первых двух строк для каждого из этих окон имеет вид:

StringGrid1->Cells[0][0]=matrIsx[0][0];
StringGrid1->Cells[1][0]=matrIsx[0][1];
StringGrid1->Cells[2][0]=matrIsx[0][2];
StringGrid1->Cells[0][1]=matrIsx[1][0];
StringGrid1->Cells[1][1]=matrIsx[1][1];
StringGrid1->Cells[2][1]=matrIsx[1][2];
…и т.д.  

  StringGrid2->Cells[0][0]= Tpr[0][0];
  StringGrid2->Cells[1][0]= Tpr[0][1];
  StringGrid2->Cells[2][0]= Tpr[0][2];
  StringGrid2->Cells[0][1]= Tpr[1][0];
  StringGrid2->Cells[1][1]= Tpr[1][1];
  StringGrid2->Cells[2][1]= Tpr[1][2];
…и т.д. 
 
 Остальные строки заполняются аналогично, самостоятельно.
 Обратите внимание на то, что порядок следования элементов в матрицах компонента StringGrid отличается от порядка следования элементов в матрицах языка Си.
В заключение зададим количество отсчетов на осях X и Y  с помощью переменной:     nOs = 10.
 
7.4. Создание  обработчика события  FormPaint
 
В отличие от лабораторной работы №1, где мы выполняли прорисовку изображения на поле объекта PaintBox1, в этой лабораторной работе предлагается выполнить прорисовку прямо на поле самой формы Form3,  используя свойство Canvas, которым обладает любая форма. Для этого откройте вкладку Events окна Object Inspector и щелкните по строке OnPaint. Для создания обработчика события  FormPaint  дважды щелкните по открывшемуся белому окошку.
Перейдите в окно Unit3.cpp с текстом программы. В нем появилась новая функция  FormPaint, внутри фигурных скобок которой надо вписать команды обработки графического буфера. После чего текст программного кода функции будет иметь следующий вид:
 
void __fastcall TForm3::FormPaint(TObject *Sender)
{
 formBitmap->Canvas->FillRect(Rect(0, 0, W, H)); // Забеливаем битмап
 GrafKonv();                                                               // Вызываем конвейер (рисуем на битмапе)
 Canvas->Draw(0, 0, formBitmap);                           // Перерисовываем битмап на форму
}
 
Из текста комментариев к командам понятны действия, выполняемые данным обработчиком.
Первая команда подготавливает поле графического буфера для рисования.
Вторая команда выполняет все необходимые прорисовки на битмапе (см. п. 7.5).
Третья команда копирует изображение битмапа в ту область формы, которая задана параметрами канвы.
 
Закомментируйте на период отладки вызов функции GrafKonv() и запустите проект на исполнение. Убедитесь, что в окне проекта формы Form3, в его левой части, появился белый прямоугольник графического окна.
 
7.5. Разработка функции графического конвейера GrafKonv()
 
В задачу функции GrafKonv() входит последовательный запуск функций, ответственных за видовые преобразования фигуры, нормировку преобразованных координат фигуры и полную прорисовку всего изображения. Текст функции будет иметь вид:
 
void GrafKonv(void)
{
  Preobr();           // Вызываем функцию преобразования фигуры
  Normir();          // Вызываем функцию нормировки преобразованных координат
  ShowAll();        // Рисуем на битмапе всё: сетку, оси, фигуру, обозначения
}
 
Если запустить на этом этапе программу, то компилятор выведет сообщение об ошибке, т.к. ни одна из функций, вызываемых здесь, еще не существует, их описание будем разрабатывать далее. А пока, чтобы можно было продолжать формирование текста программы и выполнять отладку её функций, надо закомментировать, т.е. заглушить вызовы не созданных функций Preobr(), Normir() и ShowAll(). Затем раскомментируйте вызов GrafKonv() в обработчике FormPaint и запустите программу на исполнение. После добавления в текст программы описания каждой из вызываемых функций, заглушки можно будет последовательно снимать.
 
7.6. Видовые преобразования координат фигуры. Разработка функции Preobr()
 
Для изменения местоположения, формы или размеров фигуры к ней необходимо применить видовые преобразования. Для преобразования матрицы координат фигуры с помощью матрицы видового преобразования создадим функцию  Preobr(). В её работу будет входить выполнение операции перемножения двух матриц: матрицы исходных координат matrIsx[4][3] и матрицы видового преобразования Tpr[3][3]. В результате этой операции мы получим преобразованную матрицу matrIzm[4][3] (в соответствии с выражением (1), представленным в разделе "Краткие теоретические сведения"). Эту матрицу затем необходимо отнормировать с помощью функции Normir(). Запуск функции преобразования Preobr(), а также функции нормировки фигуры осуществляется после нажатия на кнопку "Запуск". При этом вызывается процесс перерисовки графического окна и соответственно запуск графического конвейера GrafKonv(), в составе которого и находятся функции Preobr() и Normir().
Текст программного кода функции Preobr() имеет следующий вид:
 
void Preobr(void)
{
  for(i=0; i<4; i++)
  {for(j=0; j<3; j++)
     {float Sum=0;
       for(k=0; k<3; k++)
         Sum=Sum+matrIsx[i][k]* Tpr[k][j];
       matrIzm[i][j]=Sum;
      }
   }
}
Здесь как видим выполняется поэлементное перемножение двух матриц в соответствии с правилами перемножения матриц.
Раскомментируйте вызов функции Preobr(), находящийся в GrafKonv() и запустите проект на исполнение для обнаружения ошибок в программном коде.
 
7.7. Нормировка координат фигуры
 
Нормировку преобразованных координат фигуры, записанных в матрице matrIzm[4][3] необходимо выполнять для перевода этих координат из однородной системы координат в декартовую. Принцип нормировки координат результирующей матрицы определяется математическим выражением (5), представленным в разделе "Краткие теоретические сведения", данной лабораторной работы.
Создадим функцию Normir(), внутри которой организуем два вложенных цикла, необходимых для обработки матрицы координат фигуры и с их помощью выполним поэлементное деление параметров матрицы на последний элемент текущей строки, если он не равен единице. Кроме этого, надо проверить, не равен ли этот последний элемент нулю, потому что, понятно, на нуль делить нельзя. Если все-таки это случилось, то этому нулевому элементу надо присвоить какое-либо очень маленькое значение.
 
void Normir(void)
{
  for(int i=0; i<4; i++)
     for(int j=0; j<3; j++)
       {if(matrIzm[i][2]!=1)
           {if(matrIzm[i][2]==0)
                 matrIzm[i][2]=0,001;
             matrRez[i][j]=matrIzm[i][j]/matrIzm[i][2];
            }
          else matrRez[i][j]=matrIzm[i][j];
      }
}
Отнормированные координаты преобразованной фигуры matrRez[4][3] можно теперь использовать в функциях ShowFig() и Oboznath()  для прорисовки фигуры и простановки обозначений возле её вершин.
Раскомментируйте вызов функции Normir(), находящийся в GrafKonv() и запустите проект на исполнение для проверки.
 
7.8. Разработка функции прорисовки  изображения ShowAll()
 
В задачу функции ShowAll() входит последовательный запуск функций, ответственных за прорисовку конкретных элементов изображения: осей координат, сетки, фигуры и обозначения вершин фигуры. В обязанность этой функции также входит проверка состояния флажков "Сетка" и "Обозначения", установленных на панели Panel1.
 Порядок вызова функций прорисовки, входящих в состав ShowAll(), определяется тем, что каждое последующее изображение накладывается на предыдущее. Поэтому, чтобы не происходила зарисовка важной информации нужно определить, какое изображение является основным, а какое – второстепенным или фоновым. В нашем проекте основным является преобразованное изображение фигуры, а исходная фигура, оси координат, сетка, обозначения вершин – второстепенными. Поэтому, сначала надо вызывать второстепенные функции в порядке их нарастающей значимости, а затем основную. Тогда текст функции ShowAll() будет иметь вид:
 
void ShowAll(void)
{
 if(Form3->CheckBox1->Checked)    // Если флажок "Сетка" включен,
    ShowStk();                                      // рисуем сетку
 
 ShowOsi();                                        // Рисуем оси
 ShowFig(clSilver, matrIsx);               // Рисуем исходную фигуру
 
 if(Form3->CheckBox2->Checked)    // Если флажок "Обозначения"включен,
    Oboznath(matrIsx);                         // проставляем исходные обозначения
 
 ShowFig(clRed, matrRez);                 // Рисуем преобразованную фигуру
 
 if(Form3->CheckBox2->Checked)    // Если флажок "Обозначения" включен,
    Oboznath(matrRez);                       // проставляем преобразованные обозначения
}
 
Как видно из текста программы, функции прорисовки фигуры и обозначений вершин вызываем дважды: первый раз рисуется исходная фигура серым цветом, второй раз рисуется преобразованная фигура красным цветом.
Пока не созданы описания функций ShowStk()ShowOsi(), ShowFig() и Oboznath()   закомментируйте их вызовы в ShowAll(). Затем раскомментируйте вызов ShowAll() в функции GrafKonv() и запустите проект на исполнение для проверки.
 
7.9. Разработка функции построения осей координат ShowOsi()
 
Процесс создания функции ShowOsi() следующий:
а) В программный код файла реализации Unit3.cpp впишите заголовок этой функции. Его текст соответствует тексту объявленного прототипа этой функции. Далее, внутри фигурных скобок этой функции вписываются команды, обеспечивающие рисование на битмапе.
б) Задаем стиль и цвет линий, которыми будут прорисовываться оси:
 
FBC->Pen->Style=psSolid;         // Устанавливаем свойства пера
FBC->Pen->Color=clBlack;
 
Здесь - FBC  это идентификатор макроса для выражения formBitmap->Canvas, который мы введем в целях уменьшения длины этой цепочки в командах прорисовки изображения и настройки параметров рисования. Поэтому, в область объявления макросов необходимо ввести следующую запись:
 
#define FBC formBitmap->Canvas
 
в) Проведем горизонтальную ось X с помощью команд:
 
FBC->MoveTo(0, H/2);              // Рисуем ось Х
FBC->LineTo(W, H/2);
 
Уберите заглушку, установленную перед вызовом ShowOsi() в функции ShowAll() и запустите проект на исполнение для проверки правильности прорисовки горизонтальной оси.
г) Проведем вертикальную ось Y с помощью этих же команд с координатами, соответствующими положению оси (выполнить самостоятельно). Запустите проект на исполнение для проверки.
д) Формируем стрелки на концах осей, используя команды MoveTo и LineTo (самостоятельно).
е) Устанавливаем буквы ‘X’ и ‘Y’ возле осей:
 
FBC->TextOut(x1, y1, 'X');
FBC->TextOut(x2, y2, 'Y');
 
Здесь x1, y1, x2, y2 – координаты расположения символов X’ и Y’.
Реальные значения x1, y1, x2, y2  надо задать заранее, перед вызовом команд TextOut() (самостоятельно). Переменные x1, y1, x2, y2 объявите локально (внутри данной функции) как int.
ж) Прорисовываем отсчеты на осях  X и Y  в виде коротких отрезков (количество отсчетов на каждой полуоси равно nOs), с использованием цикла for. Внутри этого же цикла вводим команду, выполняющую простановку соответствующих цифр (от 1 до значения nOs) возле каждого отсчета на полуоси. Пример простановки отсчетов и обозначений на положительной полуоси имеет следующий вид:
 
h=W/(2*(nOs+1));
hh=h;
for (int i=1; i<nOs+1; i++)
 { FBC->MoveTo((W/2+hh), (H/2-3));         //Отсчеты на полуоси +Х
     FBC->LineTo((W/2+hh), (H/2+3));
     // Здесь самостоятельно вписываем команды для остальных полуосей
 
    FBC->TextOut((W/2+hh), (H/2+10), i);     //Оцифровка полуоси +Х
    // Здесь самостоятельно вписываем команды для остальных оцифровок
 
    hh=h*(i+1);
  } // цикл завершен
 
Здесь переменная  – цена делений (отсчетов), на которые разбиваются оси  X и Y. Она зависит от ширины окна и от количества отсчетов на осях, задаваемых переменной масштабирования осей  nOs.
     
Переменная  hh – отстояние конкретного отсчета от центра системы координат.
Переменная  nOs – это количество отсчетов на любой из полуосей. Начальное значение этой переменной задано в функции FormCreate() в виде: nOs =10. В дальнейшем её значение можно изменять с помощью компонента TrackBar1.
Запустите программу на исполнение и убедитесь в правильности прорисовки осей.
 
7.10. Разработка функции построения сетки ShowStk()
 
В программный код файла реализации Unit3.cpp впишем заголовок этой функции:
 
void ShowStk(void)
 
Затем, внутри фигурных скобок этой функции необходимо вписать команды рисования пунктирных горизонтальных и вертикальных линий. Процесс построения сетки аналогичен прорисовке отсчетов на осях в функции ShowOsi(). Здесь также необходимо использовать оператор цикла для прорисовки параллельных пунктирных линий, совпадающих с отсчетами на осях. Только длина этих линий будет равна не длине отсчетов, а длине каждой из осей координат соответственно.
Перед началом прорисовки необходимо установить стиль линий - пунктирный и цвет линий – серый:
 
FBC->Pen->Style=psDot;      // Устанавливаем стиль линий для сетки - пунктир
FBC->Pen->Color=clSilver;    // Задаем цвет линий сетки – серый
  // Здесь самостоятельно вписываем команды прорисовки горизонтальных и
   // вертикальных линий сетки в цикле
 
      После окончания прорисовки сетки свойства пера необходимо вернуть в исходное состояние (см. п. 7.9).
Как видно из текста программного кода функции ShowAll(),  прорисовка сетки осуществляется только в том случае, если включен флажок компонента CheckBox1.
Для того, чтобы при переключении состояния флажка выполнялась прорисовка сетки, или наоборот не выполнялась, необходимо создать обработчик события CheckBox1Click, с помощью которого можно вызывать команду FormPaint(0)  для перерисовки графического окна. При этом будет производиться вызов конвейера, с входящими в него командами проверки включения флажка и последующей прорисовки сетки. Порядок создания этого обработчика приведен в разделе 7.16.
Не забудьте убрать заглушку на вызове ShowStk() в функции ShowAll().
После этого можно запустить программу на исполнение и убедиться в том, что сетка прорисовывается при включении флажка компонента “Сетка”, и наоборот, исчезает при его выключении.
 
7.11. Масштабирование осей координат
 
Принцип масштабирования осей координат и объектов, расположенных в графическом окне формы основан на изменении значения переменной  nOs, которая отвечает за количество отсчетов на координатной полуоси. Эта переменная в свою очередь влияет на цену деления h для осей X и  Y,  а следовательно и на конкретные размеры осей и объектов. Изменение значения переменной nOs в программе осуществляется с помощью компонента TrackBar. Для его использования создадим функцию обработки события на перемещение движка TrackBar1, для чего необходимо дважды щелкнуть по этому компоненту на поле конструктора формы Form3. В окне редактора кода Unit3.cpp появится заголовок новой функции, внутри фигурных скобок которой впишем:
 
void __fastcall TForm3::TrackBar1Change(TObject *Sender)
{
 nOs=Form3->TrackBar1->Position;     // Читаем переметры TrackBar1
 FormPaint(0);                                         // Запускаем перерисовку графич. окна
}
 
Здесь, как видно из текста функции, осуществляется присвоение переменной nOs значения позиции движка - Position. Затем вызывается команда FormPaint(0)  для перерисовки графического окна с другими размерами осей и фигуры.
Запустите программу на исполнение и убедитесь в том, что перемещение движка компонента TrackBar1 вызывает изменение масштаба осей координат.
 
7.12. Разработка функции прорисовки фигуры  ShowFig()
 
Процесс прорисовки фигуры состоит из нескольких этапов. Сначала устанавливаем стиль пера и цвет фигуры. Затем преобразуем текущие отнормированные координаты фигуры в экранные. И только после этого выполняем собственно прорисовку: проводим линии от вершины A к вершине B, затем к вершине C, затем к вершине D и снова к A. После этого устанавливаем точки (маленькие окружности) в каждой из вершин фигуры. В заключение возвращаем свойствам пера исходные значения.
 
void ShowFig(TColorcol, float matrT[4][3])
{
 FBC->Pen->Color=col;                            // Устанавливаем цвет фигуры
 
 matrEkr[0][0]=W/2+int(h*matrT[0][0]); // Переводим координаты фигуры в экранные
 matrEkr[0][1]=H/2-int(h*matrT[0][1]);
 matrEkr[1][0]=W/2+int(h*matrT[1][0]);
 matrEkr[1][1]=H/2-int(h*matrT[1][1]);
//  и т.д., для всех восьми элементов матрицы matrEkr[4][2]
 
 FBC->MoveTo(matrEkr[0][0], matrEkr[0][1]);    // Устанавливаем перо в "А"
 FBC->LineTo(matrEkr[1][0], matrEkr[1][1]);       // Рисуем линию от "А" к "В"
 FBC->LineTo(matrEkr[2][0], matrEkr[2][1]);       // Рисуем линию от "В" к "С"...
// и т.д., для всех четырех вершин фигуры
 
 FBC->Ellipse(matrEkr[0][0]-2, matrEkr[0][1]-2,
                         matrEkr[0][0]+2, matrEkr[0][1]+2);  // Рисуем окружность в "А"
// и т.д., для всех четырех вершин фигуры
 
 FBC->Pen->Color=clBlack;            // Восстанавливаем исходные свойства пера
}
 
Снимаем заглушки с вызовов функций ShowFig() входящих в функцию ShowAll() и запускаем программу на исполнение. В графическом окне проекта должна прорисоваться исходная фигура в заданных исходных координатах с заданным цветом линий.
 
7.13. Разработка функции прорисовки обозначений  Oboznath()
 
Любая фигура, изображаемая на экране обладает, как минимум, двумя группами координат своих вершин. Первая группа – это её математические координаты, т.е. те, которые описывают местоположение фигуры в декартовой системе координат и хранятся в матрицах matrIsx[4][3] и matrRez[4][3] в виде вещественных чисел. Вторая группа координат – это экранные координаты фигуры, определяющие её физическое местоположение в графическом окне, и задаются в пикселях. Понятно, что математические координаты не совпадают с экранными, хотя они и описывают одно и то же место на экране. Поэтому, когда нам необходимо проставить буквенные обозначения с координатами возле каждой вершины мы должны придерживаться следующего правила: координаты возле вершин выводятся математические, а место, куда они проставляются, определяется в экранных координатах. Все эти особенности надо учитывать при заполнении списка фактических параметров для стандартной функции TextOut(), обеспечивающей вывод обозначений.
Пример вывода обозначения для вершины А фигуры в формате А(x, y) имеет следующий вид:
 
void Oboznath(float matrTo[4][3])
{
   FBC->TextOut(W/2+int(h*matrTo[0][0]), H/2-int(h*matrTo[0][1])-20,
                           "A("+FloatToStrF(matrTo[0][0], ffGeneral,2,1)+";"+
                            FloatToStrF(matrTo[0][1], ffGeneral,2,1)+")");
.//...и т.д., для остальных вершин: B, C, D
}
 
Как видно из текста программного кода функции ShowAll(),  прорисовка обозначений осуществляется только в том случае, если включен флажок компонента CheckBox2. Для того, чтобы при переключении состояния флажка выполнялась прорисовка обозначений, или наоборот удалялась, необходимо создать обработчик события CheckBox2Click, с помощью которого можно вызывать команду FormPaint(0) для перерисовки графического окна. Порядок создания этого обработчика приведен в разделе 7.16. 
Снимаем заглушки с вызовов функций Oboznath() входящих в функцию ShowAll() и запускаем программу на исполнение. Теперь можно убедиться в том, что обозначения прорисовываются возле каждой из вершин фигуры после включения флажка компонента “Обозначения” и наоборот, исчезают после его выключения.
 
7.14. Создание обработчика события для кнопки "Запуск"
 
Назначение обработчика события для кнопки "Запуск" (Button1)  заключается в том, чтобы с его помощью запускать процессы преобразования и нормировки координат фигуры, а затем и прорисовки фигуры. Но, как уже говорилось выше, все эти действия запускаются не сразу, а через вызов функции перерисовки графического окна FormPaint(), внутри которой прописана функция графического конвейера GrafKonv().
Учитывая то, что мы задались целью выполнять видовые преобразования фигуры в режиме анимации, нам необходимо в состав обработчика Button1 ввести оператор цикла For, из которого мы будем вызывать функцию FormPaint(0) много раз, в соответствии с требованиями анимации.
Вообще, под анимацией понимается такое преобразование, при котором создается эффект плавного, непрерывного изменения положения фигуры или её размеров. Для этого все перемещение разбивается на множество маленьких перемещений, или шагов, и затем в цикле выполняют перерисовку фигуры столько раз, на сколько разбито полное перемещение (в нашем случае на 100 шагов). Чтобы не происходило суммирование ошибок вычисления, неизбежных при любых математических операциях, каждое пошаговое преобразование будем производить не относительно предыдущего положения фигуры, а относительно некоторого неизменного начального положения. Для этого у нас имеется матрица исходных координат фигуры matrIsx[4][3]. Далее, пересчитаем параметры матрицы преобразованияTpr[3][3] для одного шага анимации фигуры, т.е для 1/100 части от заданного отрезка перемещения. После этого можно вызывать функции Preobr(), Normir() и ShowAll(), входящие в состав графического конвейера, для перерисовки фигуры в цикле.
Порядок создания функции обработки события Click для левой кнопки мыши аналогичен действиям, описанным в лаб. раб. №1. Необходимо дважды щелкнуть по кнопке "Пуск" (Button1) на конструкторе формы Form3. В окне редактора кода Unit3.cpp появится заголовок новой функции, внутри которой необходимо вписать команды, выполняющие следующие действия:
- чтение значений параметров видового преобразования из ячеек компонента StringGrid2;
- ввод этих значений в матрицу преобразования Tpr[3][3];
- формирование анимационного цикла, в котором выполняются следующие действия:
        - пересчет значений параметров матрицы преобразования Tpr[3][3]  для их 1/100 части;
        - вызов перерисовки графического окна, для запуска графического конвейера;
        - вызов функции временно́й задержки Sleep(n);
- вывод значений матрицы координат преобразованной фигуры в ячейки компонента StringGrid3.
 
С учетом вышесказанного, текст обработчика события для кнопки "Запуск" будет иметь вид:
 
void __fastcall TForm3::Button1Click(TObject *Sender)
{
 a=StrToFloat(Form3->StringGrid2->Cells[0][0]);
 b=StrToFloat(Form3->StringGrid2->Cells[1][0]);
 p=StrToFloat(Form3->StringGrid2->Cells[2][0]);
 d=StrToFloat(Form3->StringGrid2->Cells[0][1]);
 e=StrToFloat(Form3->StringGrid2->Cells[1][1]);
 q=StrToFloat(Form3->StringGrid2->Cells[2][1]);
 l=StrToFloat(Form3->StringGrid2->Cells[0][2]);
 m=StrToFloat(Form3->StringGrid2->Cells[1][2]);
 s=StrToFloat(Form3->StringGrid2->Cells[2][2]);
 
 Tpr[0][0]=a; Tpr[0][1]=b; Tpr[0][2]=p;
 Tpr[1][0]=d; Tpr[1][1]=e; Tpr[1][2]=q;
 Tpr[2][0]=l; Tpr[2][1]=m; Tpr[2][2]=s;
 
 if(Form3->CheckBox3->Checked)
   for(float i=0; i<100; i++)
   {Tpr[0][0]=1+(((a-1)*(i+1))/100);
    Tpr[1][1]=1+(((e-1)*(i+1))/100);
    Tpr[2][0]=(i+1)*(l/100);
    Tpr[2][1]=(i+1)*(m/100);
    Tpr[2][2]=1+(((s-1)*(i+1))/100);
    FormPaint(0);
    Sleep(10);
   }
 else FormPaint(0);
 
 StringGrid3->Cells[0][0]=matrRez[0][0];
 StringGrid3->Cells[1][0]=matrRez[0][1];
 StringGrid3->Cells[2][0]=matrRez[0][2];
 StringGrid3->Cells[0][1]=matrRez[1][0];
 StringGrid3->Cells[1][1]=matrRez[1][1];
 StringGrid3->Cells[2][1]=matrRez[1][2];
 //ит.д., для остальных шести ячеек матрицы  matrRez[4][3];
}
Как видно из текста этой программы, она позволяет выполнять анимацию только для переноса, масштабирования и отображения. Для осуществления анимации сдвига и перспективного проецирования необходимо разрабатывать гораздо более сложную функцию, так как там требуется пересчитывать каждую вершину по отдельности с использованием конвейера обработки изображения, и здесь мы это рассматривать не будем.
 
Сохраните и запустите проект на исполнение.
На рабочем поле открывшегося окна проекта, в ячейках компонента StringGrid2, установите параметры преобразования и щелкните мышью по кнопке "Пуск". В графическом окне, рядом с исходной фигурой ABCD, должна появиться вторая, преобразованная фигура. Местоположение и форма новой фигуры должны соответствовать заданным параметрам преобразования.
Если перед нажатием на кнопку "Пуск" включить флажок компонента "Анимация", то преобразование фигуры должно происходить плавно, в замедленном режиме.
 
7.15. Создание обработчика события для кнопки "Исходное"
 
Обработчик события кнопки "Исходное" (Button2)  предназначен для следующих действий:
- восстановления исходных значений матрицы видового преобразования Tpr[3][3];
- очистки всех ячеек компонентов StringGrid2  и StringGrid3;
- запуска функции перерисовки графического окна.
Тогда текст его программного кода будет иметь вид:
 
void __fastcall TForm3::Button2Click(TObject *Sender)
{
 Tpr[0][0]=1; Tpr[0][1]=0; Tpr[0][2]=0;
 Tpr[1][0]=0; Tpr[1][1]=1; Tpr[1][2]=0;
 Tpr[2][0]=0; Tpr[2][1]=0; Tpr[2][2]=1;
 
 StringGrid2->Cells[0][0]=Tpr[0][0];
 StringGrid2->Cells[1][0]=Tpr[0][1];
 StringGrid2->Cells[2][0]=Tpr[0][2];
 // и т.д., для остальных шести ячеек компонента StringGrid2
 
 StringGrid3->Cells[0][0]=' ';
 StringGrid3->Cells[1][0]=' ';
 StringGrid3->Cells[2][0]=' ';
 StringGrid3->Cells[0][1]=' ';
 StringGrid3->Cells[1][1]=' ';
 StringGrid3->Cells[2][1]=' ';
 // и т.д., для остальных шести ячеек компонента StringGrid3
 
 FormPaint(0);
}
Здесь заполнение ячеек компонентов StringGrid конечно можно выполнить в цикле, что более предпочтительно для тех, кто считает себя настоящим программистом.
 
7.16. Создание обработчиков событий  "Сетка" и "Обозначения"
 
Необходимость создания обработчиков событий для этих компонентов заключается в том, чтобы вызывать перерисовку всего изображения в случае манипуляции пользователя с этими компонентами. При этом должен происходить перезапуск функции FormPaint(TObject *Sender) с помощью команды FormPaint(0).
Для создания обработчика для флажка "Сетка" дважды щелкните по объекту CheckBox1 на конструкторе формы Form3. В окне редактора кода Unit3.cpp появится заголовок этой функции, внутри которой можно вписать команду вызова перерисовки графического окна:
 
void __fastcall TForm3::CheckBox1Click(TObject *Sender)
{
 FormPaint(0);
}
 
Аналогичный обработчик создайте для флажка "Обозначения" (CheckBox2).
 
На этом разработка файла реализации для лабораторной работы №2 заканчивается. Сохраните проект и запустите на исполнение для проверки его работоспособности при выполнении видовых преобразований над фигурой.
 
8.  Контрольные вопросы
 
1. Какие виды базовых видовых преобразований вы знаете, и в чем заключается их действие?
2, Как устроена матрица двумерного преобразования Тпреобр.?
3. Какие элементы матрицы Тпреобр. отвечают за перенос, сдвиг, масштабирование и отображение объекта?
4. Какой должна быть величина элемента общего масштабирования для увеличения объекта, для уменьшения, почему?
5. Как построить матрицу Тпреобр., реализующую «кривое зеркало»? Почему происходит искажение формы объектов?
6. Что такое однородные координаты и для чего они вводятся?
7. Как выполнить преобразование координат произвольной точки, расположенной на декартовой плоскости с помощью матрицы переноса?
8. Как выполнить преобразование координат произвольной точки с помощью матрицы сдвига?
9. Как выполнить преобразование координат произвольной точки с помощью матрицы масштабирования?
10. Каким образом можно получить анимационные эффекты при выполнении матричных преобразований над объектами?