Главная страницаОб авторахЗагрузка файловКонтакты
КОМПЬЮТЕРНАЯ 2D и 3D-ГРАФИКА
ПРАКТИЧЕСКИЙ КУРС ЛАБОРАТОРНЫХ РАБОТ. Учебное пособие
Часть 1 (2D-графика)
Лабораторная работа №1
Лабораторная работа №2
Лабораторная работа №3
Лабораторная работа №4
Лабораторная работа №5
Лабораторная работа №6
Часть 2 (3D-графика)
Лабораторная работа №7
Лабораторная работа №8
Лабораторная работа №9
Лабораторная работа №10
Лабораторная работа №11
Лабораторная работа №12
Главная страница
А. Ерошко, Л. Давиденко

ПРАКТИЧЕСКИЙ КУРС ЛАБОРАТОРНЫХ РАБОТ
ПО КОМПЬЮТЕРНОЙ ГРАФИКЕ
 
 
Настоящее учебное пособие, в двух частях (книгах), предназначено для самостоятельной работы студентов по закреплению теоретических знаний, полученных на лекционных занятиях по дисциплине "Математические основы компьютерной графики", а также для получения практических навыков по разработке алгоритмов и программ, рассматриваемых в данных лабораторных работах.
Приложения: демо-программы для части 1 и для части 2.

Севастополь, июнь 2012.

Примечание: Данный сайт находится в режиме корректировки, поэтому могут иметь место небольшие различия между текстом сайта и текстом архивного файла, предназначенного для скачивания, а также между версиями, корректироваными в различные периоды времени.
          Дата последней корректировки - 30.03.15.


 
СОДЕРЖАНИЕ
 
Часть 1
2D – ГРАФИКА

Предисловие
ВВЕДЕНИЕ
    1. Общие положения.
    2. Об уровнях сложности программ компьютерной графики и последовательных этапах её освоения.

 Структура программ компьютерной графики
    1. Основные принципы работы САУ.
    2. Конечные автоматы и их программная реализация.
    3. Звенья обратных связей.
    4. Структура проекта интерактивной графики.
    5. Графический конвейер.
    6. Графический конвейер в режиме текущих координат.
    7. Роль ООП и специальных графических библиотек в программировании КГ.
    8. Порядок разработки программ компьютерной графики.
ОСНОВНЫЕ ТЕОРЕТИЧЕСКИЕ СВЕДЕНИЯ ПО ДВУМЕРНОЙ ГРАФИКЕ
    1. Системы координат.
    2. Форматы описания графических объектов.
    3. Матричные видовые преобразования.
    4. Преобразование графических объектов с использованием результирующих матриц.
    5. Матричные операции.
Лабораторная работа № 1. Растровые и векторные примитивы. Алгоритмы генерации векторных примитивов.
Лабораторная работа № 2. Базовые видовые преобразования двумерных объектов.
Лабораторная работа № 3. Вращение двумерных объектов вокруг заданного центра.
Лабораторная работа № 4. Двумерные отсечения. Заполнения и заливки.
Лабораторная работа № 5. Аналитические кривые на двумерной плоскости. Управление графическими объектами с использованием конечных автоматов (КА).
Лабораторная работа № 6. Двумерные фракталы.
БИБЛИОГРАФИЯ


Часть 2
3D – ГРАФИКА
 
 
Основные теоретические сведения по трехмерной графике
     1. Системы координат.
    2. Форматы описания объектов. 
    3. Матричные видовые преобразования объектов.
    4. Векторные видовые преобразования объектов.
    5. Методы проецирования трехмерных объектов на картинную плоскость.
    6. Преобразование графических объектов с использованием результирующих матриц.
ЛАБОРАТОРНАЯ РАБОТА № 7. Ортографические и косоугольные проекции.
ЛАБОРАТОРНАЯ РАБОТА № 8. Аксонометрические проекции.
Лабораторная работа № 9. Центральные (перспективные) проекции. 
Лабораторная работа № 10. Трехмерное вращение вокруг произвольной оси. 
Лабораторная работа № 11. Методы удаления невидимых ребер и граней. 
Лабораторная работа № 12. Освещение и тени. Цветовые модели и эффекты. 
Библиография
 
 
ПРЕДИСЛОВИЕ
 

Программирование компьютерной графики является важнейшим направлением современных информационных технологий. В этой области программист должен обладать высоким уровнем подготовки по различным дисциплинам. Здесь надо быть не только профессионалом в области программирования как такового, но и хорошо знать физику, математику, автоматику, моделирование и другие предметы, чтобы со знанием дела разрабатывать графические функции, реализующие различные технические процессы, природные явления, игровые или научно-фантастические сюжеты на экране монитора.
Поэтому, целью создания данного учебного пособия является подробное описание процесса формирования алгоритмов различных программ по компьютерной графике и функций, используемых в их составе.
Примеры программ, приведенные здесь, создавались на базе пакета визуального проектирования C++Builder 6, и требуют от учащихся знаний языка  С++  в минимально необходимом объеме.
Книги рассчитаны на студентов и инженеров IT-технологий, желающих углубить свои познания в области математических основ компьютерной графики.
Книги базируются на курсе лекций доцента кафедры Информационных систем, кандидата технических наук Ерошко Алексея Антоновича по дисциплине «Математические основы компьютерной графики», прочитанном в период с 2003 по 2008 г. Программно-методическую часть работы, связанную с разработкой и описанием алгоритмов программ, изложением Предисловия, Введения и Структуры программ компьютерной графики, а также оформлением книг, выполнила Давиденко Людмила Федоровна, ассистент (2003-2011) кафедры ИС.
Демо-версии программ, представленных в качестве примеров в лабораторных работах, приведены в приложении к данному учебному пособию в виде ехе-файлов. Запустив эти файлы, можно наглядно увидеть, как сформированы оконные интерфейсы (т.е. внешнее оформление) проектов, а также самостоятельно проделать практические манипуляции и преобразования, заданные в них. Если студенты разработают свои программы в соответствии с вариантами заданий и изложенными алгоритмами в данных методических указаниях, то результаты будут полностью соответствовать работе демо-программ.
 
В первой части учебного пособия (2D-графика) содержатся шесть лабораторных работ по темам:
- Лаб.1: Растровые и векторные примитивы. Алгоритмы генерации векторных примитивов;
- Лаб.2: Базовые видовые преобразования двумерных объектов;
- Лаб.3: Вращение двумерных объектов вокруг заданного центра;
- Лаб.4: Двумерные отсечения. Заполнения и заливки;
- Лаб.5: Аналитические кривые на двумерной плоскости. Управление графическими объектами с использованием конечных автоматов;
- Лаб.6: Двумерные фракталы.
 
Во второй части учебного пособия (3D-графика) содержатся шесть лабораторных работ по темам:
- Лаб.7:  Ортографические и косоугольные проекции;
- Лаб.8:  Аксонометрические проекции;
- Лаб.9:  Центральные (перспективные) проекции;
- Лаб.10: Трехмерное вращение вокруг произвольной оси;
- Лаб.11: Методы удаления невидимых ребер и граней;
- Лаб.12: Освещение и тени. Цветовые модели и эффекты.
 
В третьей части пособия (OpenGL-графика) предполагается рассмотреть темы:
- Лаб.13: Трассировка лучей. Октантные деревья;
- Лаб.14: Криволинейные поверхности. Текстуры;
- Лаб.15: Симуляторы физических свойств графических объектов;
- Лаб.16: Модели сцены. Камера. Сценарии.
 
В этих работах подробно рассматриваются четыре базовых понятия, на основе которых должны формироваться программы компьютерной графики, а именно:
- структура программы;
- графический конвейер;
- математические основы преобразования координат;
- алгоритмы графических функций (генерация векторов, заливки, отсечения, удаления и т.п.).
 
Кроме этого, в каждой из лабораторных работ производится последовательное ознакомление студентов с возможностями пакета C++Builder (что также применимо и к Delphi).
Ввиду ограниченности в объеме, такие темы как «Заполнения и заливки», «Двумерные фракталы», «Удаление невидимых ребер и граней», «Освещение и тени» рассмотрены в обзорном режиме. 
В лабораторных работах №№ 1, 4 и 6 использованы теоретические сведения, находящиеся в свободном доступе в сети. Ссылки на источники приводятся.
Авторы выражают признательность рецензенту данного пособия – доценту кафедры Информационных систем, кандидату технических наук Щетинину Юрию Тимофеевичу.
Особую благодарность авторы выражают студентам кафедры ИС СевНТУ, Леванцову И.П. и Кузьменко Д.Е. за участие в формировании программных кодов демонстрационных проектов и разработке методических пояснений для лабораторных работ № 4, 5 и 10.
Отзывы, замечания и предложения просьба направлять по адресу: met_mokg@mail.ru


ВВЕДЕНИЕ
 
1. Общие  положения
  
Современная компьютерная графика (КГ) позволяет воспроизводить цветные, трехмерные, анимационные изображения на экране монитора в режиме интерактивного диалога и управления.
По функциональному назначению программные продукты компьютерной графики можно разделить на следующие категории:
    а) неподвижные художественные изображения;
    б) анимационные видеофильмы; 
    в) видеоигры, инженерное моделирование, учебные тренажеры;
    г) оконные интерфейсы с графическими объектами интерактивного диалога.
В данном пособии рассматриваются базовые, структурообразующие приемы по созданию программ компьютерной графики категорий б), в) и г). 
Без использования компьютерной графики не может работать ни одна из современных операционных систем. Вывод на экран картинок типа «Рабочий стол», различных окон системных программ и пользовательских пакетов осуществляется только благодаря компьютерной графике.
Не менее важное место занимает программирование различных игровых пакетов. Можно смело утверждать, что именно компьютерные игры и непрерывная конкурентная борьба фирм-разработчиков игр за потребителя явились главными движущими силами, обеспечивших бурное развитие технологий программирования графики и средств обработки графических данных в режиме on-line.
Термин "интерактивный диалог" подразумевает любой вид манипуляционного, управляющего воздействия пользователя на объекты графического окна рабочей программы в период её активной работы, к которому относится временной промежуток между её запуском и остановкой. Одним из необходимых условий для работы программы в режиме интерактивного диалога является перевод монитора компьютера в графический режим, в отличие от программ, в которых запросы ведутся в режиме командной строки, а монитор находится в текстовом режиме (как правило, это программы системного ПО и различных расчетно-математических задач). Что касается собственно технических средств манипуляционного взаимодействия, то здесь можно назвать такие, известные всем пользователям устройства, как клавиатура, мышь, джойстики, а также более изощренные: сенсорные экраны, интерактивные доски, системы телеуправления и т.п.
 Программы компьютерной графики и в частности анимационные игры в настоящее время можно создавать как посредством специальных программ-конструкторов, в режиме визуального моделирования игровой сцены и задания условий поведения её объектов, так и за счет непосредственного программирования, имея полное, осмысленное представление о структуре создаваемого проекта и методах преобразования графических объектов в нем. При этом не запрещается использовать вспомогательные средства визуального проектирования и функции различных графических библиотек. Иначе время программирования больших анимационных видеопродуктов затягивалось бы на годы и годы.

2.  Об  уровнях  сложности  программ  компьютерной  графики
и  последовательных  этапах  её  освоения
 
По методам программирования и степени сложности программы компьютерной графики условно можно разделить на несколько уровней.
Первый уровень – простейшее программирование на базе интегрированных сред языков высокого уровня, таких как Pascal, Borland C и др. С помощью этих пакетов можно переводить дисплей из текстового режима в графический, пользоваться стандартными графическими функциями библиотек BGI для прорисовки различных графических примитивов на экране. Здесь можно «вручную» (т.е. за счет непосредственного программирования) рисовать изображения кнопок на экране и выполнять распознавание положения графического указателя мыши, чтобы пользователь мог «нажимать» на эти кнопки. Можно создавать несложное меню, можно организовывать перемещение объектов в режиме анимации и т.п.. Так программировали в период где-то с 1985-го по 1997 год. Сейчас этот способ применяют в основном в ознакомительных целях при изучении Pascal и Borland C.
Второй уровень – средний, на базе пакетов визуального проектирования, таких как C++Builder,  Delphi, Visual Fortran, Visual Basic и др. Использование этих пакетов освободило программистов от необходимости «ручного» создания кнопок, движков, окошек ввода и т.п. Сам каркас программы, состоящий из нескольких модулей, также создается автоматически. Прилагается большой перечень разнообразных графических функций. Эти пакеты позволяют создавать удовлетворительную по качеству трехмерную графику, использовать классы объектно-ориентированного программирования и управлять её объектами в режиме интерактивного диалога. Этот метод программирования получил свое развитие после выпуска соответствующих программных продуктов, начиная с 1993 г. и применяется по настоящее время.
Третий уровень – высокий, основан на совместном использовании профессиональных пакетов типа Visual C и специальных графических библиотек, таких как  OpenGL, DirectX 3D, QuickDraw 3D, и т.п. Особенностью этих библиотек является то, что они не только содержат большой набор графических функций, но и выполняют обработку трехмерного изображения на всем пути его преобразования в составе так называемого графического конвейера. Это позволило эффективно использовать полигональную технологию и получить значительную экономию времени. На повышение скорости обработки изображений повлияли также применение кватернионных координат и шейдеров. На таком уровне программируются высокоскоростные трехмерные игры, компьютерные тренажеры, научное моделирование и т.п.
К недостаткам третьего уровня, вследствие его высокого профессионализма, можно отнести (условно конечно) нечитаемость программного кода создаваемых проектов. Простой программист средних способностей, не  умеющий работать с  OpenGL, DirectX 3D, и операторами шейдеров не сможет по настоящему разобраться в назначении команд такой программы. В современной литературе по программированию компьютерных игр на базе OpenGL или DirectX 3D обычно приводят в качестве примеров готовые шаблоны каких-либо программ или пошаговые алгоритмы их создания. Там конечно объясняется назначение каждой команды, но проблема в том, что большинство графических функций этих библиотек  на самом деле уже и не функции как таковые, а большие программы с обширными алгоритмами, объединяющие в своем составе множество подфункций неизвестного назначения для непосвященных.
Поэтому, для тех, кто хочет освоить программирование графики в режиме осмысленного понимания каждого шага, в данной книге приведены примеры создания программ среднего уровня сложности, на базе пакета C++Builder, без подключения функций OpenGL. Все примеры написаны открытым программным кодом, в учебном варианте. Это означает, что здесь (в 1-ой и 2-ой частях) принципиально не используются функции, в состав формальных параметров которых входили бы другие функции. Т. е., в целях наглядности отсутствует вложенность через параметры. А сам процесс формирования графического конвейера выделяется в отдельную функцию.
Необходимость таких упрощений на начальном этапе освоения программирования графики объясняется следующим. Ввиду громоздкости 3D-программ, важнейшим условием их корректной работы является, прежде всего, строгое отношение программиста к  структуре создаваемого продукта. Здесь абсолютно неуместна демонстрация доморощенных программных "находок", надо лишь четко следовать установленным правилам, так как в основе этих  правил лежат не быстроменяющиеся методики программирования, а более фундаментальные законы моделирования и автоматического управления.
Поэтому, освоение сути предмета надо начинать с изучения структуры современных интерактивных программ, основанных на использовании методов case-технологий. Для программистов, знающих языки высокого уровня в этом нет ничего нового. Однако не все знают, что в программах с использованием технических средств интерактивного диалога (мыши, джойстика и т.п.) использование case-структуры является обязательным условим. Далее, в следующем параграфе рассмотрены особенности формирования таких структур пакетами C++Builder  и Delphi.
После того, как станут понятны принципы формирования case-структур в программах КГ, можно переходить к содержимому этих структур и в первую очередь к устройству графического конвейера и функций, входящих в его состав. Здесь надо изучить порядок преобразования координат графических фигур в процессе их анимационного перемещения, масштабирования, вращения, проецирования и т.д. В параграфе 4 этого раздела конструкция графического конвейера и назначение его функций рассматриваются более подробно.
На следующем этапе изучаются алгоритмы функций, обеспечивающих прорисовку изображения. Эти функции также входят в состав графического конвейера и выполняют такие важные операции как генерацию линий и окружностей, закраску поверхностей, отсечение, удаление невидимых линий и т.п.
Далее можно переходить к изучению методов управления графическими объектами. Обычно для этого используются стандартные компоненты, предоставляемые средой визуального проектирования. К ним относятся кнопки, движки, окошки ввода и т.п. Здесь нужно будет разобраться, какие команды потребуется вводить в состав обработчиков управляющих событий, чтобы обеспечить корректную и надежную работу программы. А также, как придать объектам анимационные свойства.
Далее можно переходить к полигональному моделированию. Вот с этого места начинается освоение настоящего профессионального программирования графики. Когда графические фигуры строятся как совокупность из множества полигонов, уже можно применять классы объектно-ориентированного программирования, указатели и динамическое выделение памяти. Кроме обработки полигонов, выполняются моделирование трехмерных сцен и связанное с этим проецирование, отсечение, удаление невидимых линий. Важной частью современной графики является текстурирование поверхностей, применение различных визуальных эффектов. Сейчас уже никто не берется программировать такие элементы «вручную». Эти сложные задачи решаются только с использованием библиотеки OpenGL и аналогичных ей пакетов.
 
Такой порядок изучения компьютерной графики, основанный на принципах последовательного усложнения задачи, дает возможность освоить методы её программирования всем желающим, со средним и слабым уровнем подготовки. Кроме этого, для программистов, изучивших структуру графического конвейера не станет серьезным препятствием переход с одной библиотечной базы на другую.
Освоение методов профессионального программирования графики, конечно, не заканчивается на вышеперечисленных этапах. Сейчас в современных программах 3D-графики используют такие понятия как воксели, октантные деревья и т.д.. Описывать методы создания таких программ доступным языком – не простая задача. Но делать это надо, потому что разработка интерактивных программ с графическим интерфейсом на современном этапе становится обычным, рядовым видом производственной деятельности любого IT-специалиста.
 
СТРУКТУРА  ПРОГРАММ  КОМПЬЮТЕРНОЙ  ГРАФИКИ  
1.
 Основные  принципы  работы  САУ
 
Структура программы, интерфейс которой снабжен элементами интерактивного диалога (кнопки, движки, флажки и т.п.), должна быть построена таким образом, чтобы можно было обеспечить непрерывный опрос этих элементов в циклическом режиме с целью обнаружения управляющих воздействий со стороны пользователя. Результатом такого обнаружения должно быть формирование различных устойчивых сценарных состояний графических объектов, соответствующих управляющим воздействиям пользователя. Такие качества можно обеспечить только в том случае, если структура программы организована по аналогии с системами автоматического управления (САУ), использующими в своем составе конечные автоматы (КА). На рисунке 1 приведен пример одного из вариантов наиболее простой САУ с отрицательной обратной связью.


Рисунок 1 – Структурная схема простейшей САУ 

Принцип работы такой системы основан на том, что выходной сигнал объекта управления (ОУ), подается в цепь обратной связи (ОС), где подвергается необходимому преобразованию, откуда затем поступает на регулирующий элемент. Количество звеньев ОС может быть различным, это зависит от количества контролируемых параметров выходного сигнала. Если выходной сигнал начнет отклонятся от некоторого заданного значения, то сигнал рассогласования, создаваемый блоком регулирования под воздействием сигнала ОС, окажет необходимое восстанавливающее воздействие на объект управления. Существуют и другие схемы регулирования.
      В качестве примера можно привести систему регулирования напряжения и частоты в генераторах переменного тока. Там в качестве звеньев обратных связей используются датчики выходного напряжения и частоты, сигналы с которых влияют на регуляторы, установленные в цепях обмоток возбуждения генератора, что ведет к необходимому восстановлению регулируемых параметров.
     Методы управления поведением фигур в программах компьютерной графики ничем не отличаются от принципов регулирования в САУ. Поэтому, используя схему рисунка 1, можно по аналогии построить структурную схему для любой интерактивной программы (см. рис.2). 


Рисунок 2 – Структурная схема программы интерактивной графики

Главным элементом в этой схеме является графический конвейер (объект управления). В задачу конвейера входит расчет координат фигур по задаваемым параметрам изображения и последующая их прорисовка на экране монитора. Звенья управляющих элементов (обратных связей) включают в себя различные манипуляторы (мышь, клавиатуру и др.), предназначенные для управления фигурами на экране монитора, а также функции, фиксирующие критические отклонения координат фигур. Если обратные связи на рисунке 1, это, как правило - электрические сигналы, то обратные связи в схеме рисунка 2 реализуются другими способами – либо визуально, либо за счет отслеживания координат фигур. Визуальный контроль заключается в том, что пользователь наблюдает за перемещением фигур на экране монитора и, в зависимости от их поведения, предпринимает те или иные мотивированные управляющие воздействия с использованием мыши, клавиатуры и др. средств. Координатный контроль отслеживает определенные, критические положения фигур, значения которых должны регулироваться заданными условиям сценария, без вмешательства пользователя. К таким ситуациям можно отнести, например, столкновения фигур с препятствиями и дальнейший процесс их расхождения с неподвижными объектами, либо по законам физики, либо по другим правилам, установленными сценарием. Количество звеньев обратных связей соответствует количеству используемых в программе способов манипуляционного управления фигурами, а также количеству координатных и параметрических критериев, относительно которых прозводится управление графическими объектами программы.

В качестве регулятора в структуре рисунка 2  используется специальная функция, называемая конечным автоматом, в обязанности которого входит оценка управляющих воздействий, создаваемых звеньями обратных связей и выработка для каждого сценарного состояния соответствующих параметров изображения, используемых затем в работе графического конвейера.

Для воспроизведения анимационных видеофильмов применяется другой принцип управления САУ, основанный на использовании источника задающего сигнала. На рисунке 3 изображена структурная схема программы видеофильма, в которой функции источника задающего сигнала выполняет список сценарных состояний, содержащий таблицы покадровых параметров для всех объектов изображения.



Рисунок 3 – Структурная схема анимационного видеофильма

 
Основная работа такой программы заключается в том, что на конечный автомат последовательно подаются, например, номера сценарных состояний, согласно которым и производится прорисовка изображений (с использованием функций графического конвейера) на экране монитора. Пользователь является здесь всего лишь наблюдателем и влиять на сюжет программы не может. Обратные связи в такой программе необходимы только для запуска и остановки видеофильма кнопками Пуск и Стоп.
Вполне возможно создание программ смешанного типа, в которых будет иметь место как управление объектами с помощью манипуляторов, так и прорисовка отдельных этапов с использованием списков сценарных состояний или других методов неуправляемой анимации.
Главное назначение схем рисунков 2 и 3 заключается в том, что они позволяют разработчику определить базовый состав основных функциональных элементов программы, их приоритеты и порядок взаимодействия. К группе базовых функциональных элементов в них относятся: графический конвейер (ОУ), обратные связи (ОС), регулятор (КА) и список сценарных состояний (ССС). 
Далее последовательно рассмотрим работу всех составных частей этих схем.

2. Конечные автоматы и их программная реализация

Теория конечных автоматов первоначально была создана для проектирования систем автоматического регулирования механических и электротехнических устройств [12]. Затем конечные автоматы стали широко использовать в микропроцессорных логических схемах и в программах распознавания текстов в режиме лексического разбора.
     Гораздо меньше известно о возможностях применения конечных автоматов в обычных программах. Главным условием такого применения должно быть обязательное наличие цикла, обеспечивающего непрерывный обход кода реализации КА. Иными словами, произвольный циклический алгоритм всегда можно задать в виде графа переходов конечного автомата.
     На Западе методология использования конечных автоматов при создании программ с развитым пользовательским интерфейсом, под названием Case-технологий, активно развивалась, найдя свое воплощение в таких общеизвестных пакетах визуального проектирования как C++Builder и Delphi.
     На рисунке 4, в качестве примера, изображена блок-схема программы, работающей в режиме конечного автомата, имеющего четыре состояния: A, B, C, D. Главным структурным элементом здесь является конструкция типа while-switch-case, обеспечивающая две важнейшие функции:
     - создание непрерывного циклического режима с помощью оператора while, для своевременного обнаружения управляющих сигналов в цепях ОС;
      - смена сценарных состояний автомата оператором switch-case.

Каждому из состояний автомата: A, B, C  и т.д, в реальной программе соответствуют ситуации, наступающие после воздействия пользователя на какой-либо управляющий элемент: клавиатуру, кнопки, флажки и т.п. Поэтому, количество состояний конечного автомата (т.е. количество горизонтальных case-ветвей в блок-схеме) в пределе равно количеству установленных на форме проекта элементов управления, являющихся стандартными компонентами среды разработки, плюс количество состояний, определяемых логикой поведения графических объектов.

  Рассмотрим более подробно, как работает данный автомат.
После запуска программы производится инициализация переменных state и cycle (имена произвольные). Переменная state устанавливает состояние автомата, т.е. траекторию циклического прогона через заданную case-ветвь (для исходного состояния - это ветвь А). Переменная cycle предназначена для выхода из программы. Пока её значение равно 1, работа автомата будет продолжаться непрерывно.

  
 
Рисунок 4 – Блок–схема программы конечного автомата, имеющего четыре состояния A, B, C, D

Далее, программа переходит к оператору цикла while, но, прежде чем разбираться в том, что делает этот оператор, оговорим, какие цели должны достигаться при завершении каждого while-цикла.
        
Во-первых, должен осуществляться непрерывный контроль за значениями всех параметров (называемых параметрами перехода), отвечающих за смену сценарных состояний фигур в зоне графического окна.
         
Во-вторых, для обеспечения графической реализации текущего сценарного состояния должно производиться переприсваивание значений для всех переменных, влияющих на координатное положение и цветовое отображение объектов в этом состоянии.
          Пакет переменных, формируемый конечным автоматом для нового состояния, в соответствии с задаваемыми управляющими воздействиями, используется далее в функциях графического конвейера, для текущей перерисовки графического окна.
   Что такое сценарные состояния?
       Вся совокупность событий, происходящих в области графического окна должна быть разделена на последовательность стабильных периодов (состояний), а также должны быть обозначены действия (локальные события), отделяющие одно состояние от другого. Результаты этого разделения очень удобно изображать в виде графа переходов (см. рис.5). К стабильным состояниям можно отнести либо неподвижность, либо равномерные перемещения фигур, либо еще какие-нибудь спокойные процессы. Любые манипуляции с клавиатурой, мышью и др., ведущие к изменению характера движения фигур называются локальными событиями. Столкновения фигур, попадание в мишень и т.п. также являются локальными событиями, хотя здесь точками отсчета для перехода из одного состояние в другое являются не действия пользователя, а значения координат объектов, их соотношение и логическая взаимозависимость. Вот такие локальные события, либо в виде параметров (атрибутов) различных манипуляторов, либо в виде координат фигур и должен отслеживать (сканировать) конечный автомат.

На рисунке 5 изображен пример графа переходов, имеющего 4 состояния  A, B, C, D  и 7 переходов: AB, BA, BC, BD, CA, CD и  DA. Направление и количество переходов в графе может быть самым разным, это зависит от сценария. Самая важная информация, которая может быть почерпнута из графа переходов заключается в следующем:
      - количество горизонтальных case-ветвей в блок-схеме конечного автомата (рисунок 4) равно количеству состояний на его графе переходов;
     - количество параметров перехода, подлежащих сканированию в конкретном case-состоянии равно количеству исходящих стрелочек для этого состояния на графе переходов.
     
Например, для состояния А на рисунке 5 необходимо отслеживать только один параметр, регистрирующий локальное событие АВ, а для состояния В необходимо отслеживать параметры трех локальных событий: ВС, BD и ВА. Для состояния С надо контролировать два параметра: для событий CD и СА, а для состояния D – один параметр, для события DA.


Рисунок 5 – Пример графа переходов КА на 4 состояния

Задача программного сканирования значений параметров перехода для всех сценарных состояний в блок-схеме рисунка 4 реализуется с помощью двух операторов: while и switch.
     Оператор цикла while обеспечивает непрерывно-повторяющуюся прогонку программы по кругу: сначало через оператор выбора switch, потом вдоль одной из case-ветвей (в зависимости от того, в каком состоянии находится автомат, т.е. чему равна переменная case), а затем через команду break возвращает её на вход оператора while. И здесь весь циклический процесс повторяется заново, по той же траектории, до тех пор пока не произойдет какое-либо локальное событие, т.е. пока пользователь не нажмет на какую-нибудь кнопку (и т.п.) или координаты какой-либо фигуры не достигнут граничных значений. После этого автомат перейдет в другое состояние, которое будет отличаться от предыдущего тем, что циклические while-прогоны будут проходить уже через другую case-ветвь, соответствующую новому состоянию.

Каким образом автомат обнаруживает, что локальное событие произошло? Для этого в блоки "Действие на переходе" помещаются цепочки условных операторов if, причем, для каждой case-ветви набор операторов if будет разный, в соответствии с особенностями соответствующего case-состояния на графе переходов. Назначение этих операторов в том, чтобы последовательно проверять значения всех переменных, являющихся для данного состояния параметрами перехода, в виде конкретных атрибутов, принадлежащим тем или иным манипуляторам, или координатами фигур, влияющих на переход автомата из одного состояния в другое.

Таким образом, последовательность событий, предшествующих переходу автомата из одного состояния в другое состоит из двух этапов: сначала пользователь производит манипуляции с мышью или клавиатурой (или координаты фигур вступают в логическое противоречие), т.е. происходит локальное событие, а затем, на втором этапе параметры перехода, отображающие эти события, должны изменить свои значения так, чтобы операторы if, в соответствующих case-ветвях, могли обнаружить их изменения.

     Местом, где производится изменение параметров перехода в программе являются специальные функции, так называемые обработчики событий, входящие в состав цепей ОС (см. параграф 3 "Звенья обратных связей"). А далее, как уже говорилось выше, в соответствующих case-ветвях конечного автомата выполняется распознавание этих изменений.

Допустим, к примеру, что в одном из сценарных состояний рисунков 4 и 5, например в B, в зоне графического окна находится фигура, поведение которой можно изменять тремя различными способами, например двумя кнопками и одним координатным критерием, тогда развернутая структура case-ветви для этого В-состояния будет выглядеть следующим образом: 


Рисунок 6 – Блок-схема развернутой case-ветви состояния "B"

Как видим, здесь, в блок «Действие на переходе» для состояния B вставлены три условных оператора if (по количеству стрелочек, исходящих из "B" на графе рисунка 5). Первые два проверяют срабатывание кнопок Button1 и Button2 (стандартных компонентов C++Builderа, являющихся изображениями кнопок, устанавливаемые на поле формы проекта), третий – критическое значение координат фигуры.

Если значения параметров перехода kn1 и kn2 равны false, т.е. нажатия на кнопки не произошло, то программа проходит эти операторы по огибающей, без захода в их вертикальные веточки и переходит к проверке параметра koord, в составе третьего оператора if. Если значение и этого параметра не достигло критического значения, сигнализирующего о наступлении локального события, то программа таким же образом огибает третий оператор, запускает графический конвейер для перерисовки графического окна в режиме ранее установленного состояния В, после чего выходит на команду break. Такая траектория прохода программы называется режимом холостого ожидания.

В случае, когда происходит нажатие на одну из кнопок, то в обработчике этой кнопки (в стандартных функциях Button1 или Button2) производится изменение значения kn1 (или kn2), а именно, вместо false этим переменным присваивается true, что и дает возможность автомату своевременно, на текущем прогоне, обнаружить управляющее воздействие пользователя. Операторы if, обнаружив изменения своих логических условий, перераспределяют направление обработки команд вниз, в вертикальные ветви. В составе этих ветвей производятся модификационные действия над параметрами изображения объектов, после которых функция графического конвейера прорисует измененную картинку. А автомат, сменив свое состояние, на следующем while-прогоне продолжит свою сканирующую работу уже по другой case-ветви (в соответствии с выполненными командами state=‘С’, state=‘В’  и т.д).

На схеме рисунка 6 показан пример формирования набора обязательных команд, обеспечивающих процесс переключения автомата в следующее case-состояние, в соответствии с манипуляциями пользователя или заданными сценарными условиями. К этим командам относятся:
      - цепочка операторов if, тестирующих все параметры перехода, задействованные в данном состоянии;
     
- команды смены состояния, типа state=‘С’;
      - команды инициализации параметров видового преобразования и цветового отображения объектов, входящие в блоки "Модификация".

Блок-схемы для других case-ветвей строятся аналогичным способом, на основе анализа структуры графа переходов.

Блоки "Модификация" включают различный, более свободный набор команд, содержимое которых сводится к задаче присвоения определенных значений тем переменным, которые отвечают за характер перемещения и отображения фигур согласно заданному сценарному состоянию.

Пакеты визуального проектирования, такие как C++Builder и др. позволяют имитировать работу конечных автоматов более простым способом, чем тот, что изображен на рисунке 4.
      Во-первых, благодаря возможности использовать системный таймер, можно обойтись без while-циклов, потому что с помощью таймера можно в непрерывно-последовательном режиме запускать КА, для обеспечения сканирования элементов управления.
      Во-вторых, переходы между состояниями можно организовать за счет использования операторов if (а не case), если количество состояний КА не более 2х или 3х;
      В-третьих, для контроля состояния манипуляторов (мыши, клавиатуры и др.) и соответственно значений параметров перехода, можно использовать стандартные функции среды разработки, так называемые обработчики событий, что очень удобно. Далее, в текстах лабораторных работ будет показано, как эти обработчики используются.

В обязанности автомата, работающего под управлением таймера не входит прорисовка изображения. Здесь задача решается по-иному: пакет визуального проектирования C++Builder предоставляет такой стандартный компонент как "графическое окно" (типа PaintBox, Image и т.п.), который программист может установить на поле своего проекта методом перетаскивания (Drag and Drop). Для того, чтобы разработчик мог самостоятельно программировать процесс прорисовки изображения в этом окне, C++Builder автоматически, по команде разработчика, создает стандартную функцию "обработчик графического окна". Внутри фигурных скобок этой функции можно вписывать команды инициализации параметров окна, команды вызова функций КА, графического конвейера и др. А таймер обеспечит непрерывные перезапуски графического окна и соответственно непрерывные анимационные перерисовки изображения каждые 0,2-0,01 секунд. Своевременно подготовить (к моменту текущей перерисовки) необходимые данные, в соответствии с задаваемыми командами – вот задача КА.

На рисунке 7 изображена структурная схема интерактивной программы, созданной в среде визуального проектирования с установленным таймером и набором стандартных обработчиков событий. Пакеты C++Builder и Delphi предоставляют большой перечень разнообразных обработчиков на всевозможные виды событий: одинарные щелчки мыши, двойные, по левой клавише, по правой, на нажатие, на отпускание и т.д. и т.п.. Все это описано в соответствующих справочниках.

Здесь, как видим, таймер инициирует непрерывные анимационные перезапуски графического окна. Конечный автомат, входящий в его состав, на каждом запуске запрашивает значения параметров перехода, которые предварительно подготавливаются в составе обработчиков событий, в виде такой команды как, например, kn=true, (см. рис. 9). Если результаты запросов положительны, то автомат переходит в другое состояние и меняет параметры изображения. После этого запускается графический конвейер и выполняет перерисовку графического окна с новыми параметрами. Затем повторяется следующий перезапуск окна и т.д.


Рисунок 7 – Работа конечного автомата с таймером

 На этом рисунке красным цветом обозначены те функции, внутреннее содержимое которых программирует разработчик. Сами функции могут быть как стандартные (графическое окно, обработчики), так и самодельные (конечный автомат, графический конвейер).

На рисунке 8 изображена блок-схема несложного КА на 3 состояния, работающего под управлением таймера, без использования связки операторов типа while-switch-case. Назовем такой простой автомат – if-автоматом.

В состав блоков "Действия на переходе" входят те же команды, что и на рисунке 6, а именно: цепочки if-операторов проверки параметров перехода, операторы присваивания типа state=‘С’ и блоки команд "Модификация".


 
Рисунок 8 – Структура конечного if-автомата, работающего под управлением таймера в составе
 
графического окна

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

Принципиальных структурных различий между главными КА и локальными КА нет, за исключением того, что главные КА проектов формируются автоматически средой разработки, а локальные КА создают сами программисты.

Одним из достоинств пакета C++Builder (и аналогичных ему) является возможность создавать многооконные программы. Трудности по созданию окон в процессе разработки сведены к минимуму. Программисту достаточно в меню рабочего окна пакета выбрать команды: File=>New=>Form, а остальное среда разработки выполнит сама: создаст модуль для новой формы и встроит его в общую схему главного конечного автомата программы, согласно рис.4 или рис.8. Понятно, что количество case-ветвей в главном автомате проекта (т.е. количество его состояний) будет равно числу созданных окон.

Для каждого окна (формы) пакетом C++Builder также создается свой собственный главный КА. Он также строится по схемам рисунков 4 или 8, а количество состояний для него равно количеству установленных программистом на его поле кнопок, движков, флажков и др. стандартных компонентов управления, предоставляемых средой разработки.

Пакет C++Builder позволяет устанавливать на поле формы не только простые элементы управления, но и осуществлять вызов таких объектов, которые сами имеют структуру конечных автоматов, например, различные выпадающие меню, окошки, радиокнопки и т.п.. Таким образом, имеется возможность создавать древовидную структуру взаимоподчиненных конечных автоматов, начиная от главного КА проекта, затем главных КА форм проекта, затем КА компонентов форм и ниже. И все это формируется автоматически, в режиме визуального проектирования, т.е. путем простого перетаскивания подчиненных объектов указателем мышки на поле каждой формы.

Что касается различных фигур, изображаемых в зоне графического окна конкретной формы проекта, то для управления ими, в соответствии с задуманным сценарием, необходимо будет создать функции, воспроизводящие качества конечных автоматов (локальные КА) по структуре рис.4 или 8. Условиями перехода в этих автоматах из одного состояния в другое будут как манипуляции с кнопками, так и различные логические и "физические" события, такие например, как: сближение двух фигур, достижение ими каких-либо ограничивающих поверхностей, окончание какого-то сценарного этапа и т.п.

Функции, реализующие работу локальных КА встраиваются в состав функции "графическое окно", ответственной за прорисовку фигур. Количество локальных КА для каждого графического окна может быть самым разным: это будет зависеть от количества движущихся фигур и степени их самостоятельности.

 В лабораторной работе №5 изложены основные теоретические сведения по КА, а также рассмотрен практический пример построения графа переходов, структурной схемы и программы для локального конечного автомата, управляющего построением графиков.
Программы, создаваемые для управления сенсорными экранами и интерактивными досками также строятся по вышеприведенным принципам, с конечным автоматом в своем составе.

3. Звенья обратных связей

Обратные связи – важнейшие элементы любой интерактивной программы, предназначенные для управления графическими объектами в зоне рабочего окна проекта.
     
К ним относятся:
     - визуальные ОС, обеспечивающие управление фигурами с помощью различных манипуляторов;

     - компонентные ОС, для обслуживания элементов оконного интерфейса;

     - координатные ОС, продназначенные для предотвращения разного рода столкновений и др.;
    - параметрические ОС позволяют своевремменно влиять на события, в зависимости от тех или иных параметров. Например, если у фигуры имеется несколько "жизней", то понадобится создавать счетчик жизней, а конечному автомату отслеживать момент его обнуления.

Кроме перечисленных видов ОС программист может создавать и другие виды ОС.

3.1. Визуальная ОС
     Визуальная обратная связь (рис.9) состоит из четырех функциональных частей:
     - первая часть – это собственно датчик: мышь, клавиатура, джойстик и т.п.;
     - вторая часть – функция распознавания локального события, т.е. сканирование параметров датчика;
     - третья часть – сигнальная, она содержит оператор типа kn=true, т.е команду фиксации локального события в содержимом переменной, являющейся параметром перехода для конкретного управляющего воздействия;
    - ч
етвертая часть – команда Formpaint(), предназначена для вызова функции "графическое окно", а следовательно, для запуска конечного автомата и графического конвейера. Команда Formpaint() – это стандартная функция C++Builderа, её можно не использовать, если таймер работает в программе непрерывно.

Вторую часть ОС, т.е. функцию сканирования параметров датчиков (мыши или клавиатуры) программист не создает. Её формирует среда разработки с использованием собственных методов. Эти методы заключаются в том, что для каждой формы проекта автоматически создается конечный автомат, в обязанности которого входит сканирование состояния всех стандартных компонентов, установленных на форме. Поэтому, блок "Локальное событие" обозначен зеленым цветом.
Грамотные программисты, умеющие работать с элементами конфигурации компьютера на уровне языков низкого уровня, а также с использованием стандартных функций обработки потоков ввода-вывода различных интегрированных сред, таких как C++, Pascal и др., а также самих операционых систем, могут самостоятельно расписывать программный код блока "Локальное событие".
Грамотные программисты, хорошо знающие функции пакета C++Builder, для фиксации локального события не создают отдельные переменные (типа kn), а используют уже имеющиеся средства стандартных компонентов, а именно, принадлежащие им стандартные атрибуты и параметры, отображающие различные управляющие воздействия на мышь и клавиатуру.


Рисунок 9 – Структура визуальной ОС 

 
Как видно из рисунка 9, блоки "Модификация", входящие в состав КА обязательно должны содержать операторы деинициализации параметров перехода, т.е. команды типа kn=false;. Это необходимо для приведения обратных связей в исходное состояние, позволяющее им отвечать на следующие управляющие воздействия.
Пунктирная стрелочка, идущая от блока "kn=true" в сторону блока "if-операторы" предназначена для того, чтобы показать, что значение этой переменной проверяется в цепях if-операторов соответствующих case-ветвей автомата и является спусковым сигналом для перехода автомата из одного состояния в другое (см. рис. 4 и 6). 

3.2. Компонентная ОС
    
Если программа разрабатывается в виде набора форм, работающих в режиме оконных интерфейсов, без графических окон, но с большим количеством стандартных компонентов среды, т.е. различных кнопок, окошек ввода, флажков, выпадающих меню и т.п. (так например оформляются различные базы данных), то в ней обработка всех манипуляционных событий изначально возложена на главные автоматы каждой формы. Соответственно, все модификационные действия на переходах из одного состояния в другое выполняет главный КА формы. При этом программисту предоставлена возможность вносить команды преобразования различных расчетных и списочных данных в состав стандартных обработчиков конкретных компонентов, контролируемых этим автоматом.
     Главные автоматы форм управляют прорисовкой рабочих окон самих форм по собственным командам проекта, без участия каких либо команд программиста. А если програмист не устанавливает на форме графическое окно, то нет надобности программировать все, что входит в состав его обработчика, а именно: локальный КА, графический конвейер прорисовки фигур и т.п. Иными словами, компонентная ОС – наиболее стандартизована и автоматизирована и требует от программиста наименьших усилий по её созданию.
    
В структурных схемах программ к лабораторным работам данного учебного пособия мы не будем показывать ни главные автоматы форм, ни рабочие окна форм, ни графические конвейеры форм проектов. Присутствие этих элементов будет только подразумеваться. Единственно, что необходимо показывать – это обработчики стандартных компонентов, установленных на формах, так как их содержимое заполняет сам программист, если возникает такая необходимость.
   
Когда пользователь наводит указатель мыши на изображения стандартных компонентов C++Builderа, и производит щелчок левой клавишей мыши, главный автомат формы самостоятельно обнаруживает это событие и запускает работу стандартных блоков "Действия на переходе". Далее, вызывается графический конвейер рабочего окна формы. Выполняется прорисовка рабочего окна формы и всех стандартных компонентов управления, установленных на его поле.
    
На рисунке 10, для примера, изображена структурная схема программы для оконной формы, имеющей две ОС компонентного типа. Здесь (а также на рисунках 11 и 13) специально показано присутствие тех частей программы, которые программист не разрабатывает, это: главный КА, рабочее окно формы и графический конвейер рабочего окна формы.
    
На рисунке 10 звенья ОС обрабатывают локальные события типа OnClick1, означающие одинарные нажатия на левую кнопку мыши при наведении на какой либо стандартный компонент, например на кнопку Button. Аналогично формируются цепи ОС для других компонентов среды C++Builder: флажков (CheckBox), выпадающих меню (ListBox), окошек ввода-вывода (Edit), и т.п.
                

 

Рисунок 10 – Структура программы с двумя обратными связями компонентного типа


Надо сказать, что в большинстве случаев интерактивные программы представляют комбинированную структуру, т.е. содержат несколько различных видов управляемых объектов, требующих формирования  различных по внутреннему составу цепей ОС.
Наиболее часто комбинируются такие ОС как визуальная и компонентная.
 
Вообще, если формально отнестись к применению термина «визуальная», то компонентная ОС тоже работает по визуальным командам. Отличие заключается в том, что для «визуальных ОС» объектами управления являются фигуры графического окна, а для «компонентных ОС» объектами управления являются текстовые или численные данные, выводимые (или вводимые) в окошки стандартных компонентов, расположенных на поле рабочего окна формы. Общее между ними в том, что воздействия на управляемые объекты ведутся через стандартные компоненты среды проектирования.

   На рисунке 11 изображена структура программы с визуально-компонентными ОС. Здесь зеленым цветом обозначены все элементы компонентных ОС, а красным цветом обозначены элементы визуальных ОС. В качестве управяющего средства принята мышь, однако возможны к использованию и другие виды манипуляторов.



 

Рисунок 11 – Структура программы с обратными связями визуально-компонентного типа

Блоки "Модификация параметров изображения" обведены штриховой линией, потому, что их можно помещать и в другое место, а именно, в состав локального КА. Разницы никакой нет.
Если в программе установлен постоянно действующий таймер, то писать команду Formpaint() не потребуется, поэтому этот блок также обведен штриховой линией.

3.3. Координатная и параметрическая ОС
Структурная схема координатной ОС представлена на рис. 12.



Рисунок 12 – Структура координатной ОС
 

Как видим, в этой ОС вообще отсутствуют составные элементы, так как параметрами регулирования в ней являются числовые данные, которые не требуют какого-либо преобразования. Координатные значения фигур непосредственно используются в роли параметров перехода в локальном КА.  

Структурные схемы параметрических ОС – такие же как и координатные, единственное отличие: в блоке "Модификация" локального КА необходимо установить счетчик параметрических данных, если таковая необходимость имеется.

4. Структура проекта интерактивной графики

Проекты, разрабатываемые в  C++Builder  или  Delphi  на первом шаге их создания представляют собой совокупность стандартных модулей следующего назначения [11]:
      
- project1.cpp - головной файл проекта;
       - project1.bpr - файл опций проекта;
       - project1.rez - файл ресурсов проекта;
       - Unit1.h - заголовочный файл модуля главной формы Form1;
       - Unit1.dfm - файл параметров внешнего вида формы Form1.
       - Unit1.cpp - файл реализации модуля главной формы Form1;
 
Все остальные файлы разрабатываемого проекта (их более 15) среда C++Builder создает автоматически в процессе компиляции и отладки проекта.
 Главной частью проекта является головной файл project1.cpp с функцией WinMain, с которой начинается выполнение программы и инициализация всех её модулей. Этот файл создается и корректируется автоматически.
 В заголовочном файле Unit1.h хранится объявление класса формы данного модуля. Его основной текст формируется автоматически.
 В файле Unit1.dfm хранится информация о внешнем виде формы модуля, её размерах, местоположении и т.п. Он также создается автоматически, хотя значения параметров формы программист может потом изменять по своему усмотрению. 
 Файл реализации модуля Unit1.cpp - это основной файл, с которым работает программист. В нем хранится программный код, обеспечивающий непосредственную работу данного модуля проекта.
 Текст программного кода файла Unit1.cpp, за исключением заголовка и подключаемых библиотечных модулей, программист разрабатывает самостоятельно. По содержанию, текст файла представляет собой набор следующих друг за другом описаний функций, которые можно разделить на два основных типа:
  - функций создания графического изображения; 
  - функций обработки событий.
 
 Все функции, служащие для создания графического изображения на экране монитора, составляют в своей совокупности графический конвейер. Для реализации анимационного эффекта изображение графического окна должно перерисовываться с определенной частотой, либо с помощью таймера, либо с помощью while-автомата.
       Функции, входящие в конвейер обеспечивают не только перерисовку самого изображения, но и предварительный пересчет координат графических объектов для каждого нового положения (см. следующий параграф). Для пересчета координат в компьютерной графике используются специальные методы матричного или векторного анализа. В лабораторной работе №2 эти методы рассмотрены более подробно.

       Функции обработки событий, в свою очередь, можно также разделить на две основные группы:
      - функции-обработчики стандартных компонентов среды проектирования: кнопок, флажков и т. п., входящие в состав обратных связей структуры формы проекта;
       - функции обработки "физических" и логических событий между графическими объектами.

      Процесс создания функций-обработчиков событий стандартных компонентов максимально упрощен в средах визуального проектирования. Сами компоненты устанавливаются на форме простым перетаскиванием указателем мыши. Затем в окне конструктора формы надо дважды щелкнуть по компоненту, для которого программист намеревается создать обработчик события. После чего в окне файла реализации Unit.cpp автоматически появляется заголовок этой функции. Разработчику остается только вписать в фигурные скобки этой функции команды, которые программа должна выполнить при воздействии пользователя на данный компонент.

     Процесс создания функций обработки физических или логических событий, а также разных вспомогательных функций, полностью лежит на разработчике. Ему самому необходимо придумать название функции, определить состав переменных, входящих в список формальных параметров (если он имеется), объявить прототип функции в начальной части файла реализации Unit.cpp, и далее, в теле этой программы создать описание функции, с заголовком и содержанием, соответствующем сути функции. Обычно, под понятием "физические события" понимаются события связанные, например, с взаимодействиями движущихся объектов в графическом окне, согласно их физической сути: массе, прочности и т.п.. К логическим событиям относятся различные игровые условности между объектами и они, как правило, работают в режиме координатных или параметрических ОС.
Функции физических событий еще называют "симуляторами" или "физическими движками".

     Порядок следования описаний функций в файле реализации, как стандартных обработчиков, так и разработанных программистом может быть произвольным. Обработчик графического окна, входящий в этот файл в любом случае инициализируется сразу, при запуске формы или её перерисовке, и все команды, входящие в его состав также обрабатываются в первую очередь. Остальные функции запускаются только по вызову из других функций и прежде всего из функции графического конвейера. Обработчики кнопок, флажков, окон ввода запускаются только после того, как пользователь будет нажимать на эти кнопки и др., для управления объектами, ввода параметров переменных, включения-выключения и т.д.

     Главной функцией файла Unit.cpp является обработчик графического окна, стандартный компонент C++Builderа. Внутри фигурных скобок этой функции разработчик вписывает команды инициализации параметров окна, команды вызова функций КА, графического конвейера и др.

     Описания функции "Локальный конечный автомат" и функции "Графический конвейер" программист разрабатывает самостоятельно и вписывает их в Unit.cpp в любом месте.

     Описания функций, входящих в состав графического конвейера, программист разрабатывает самостоятельно и вписывает их в Unit.cpp в любом месте.

     Обработчики управляющих (локальных) событий - это также стандартные функции среды разработки. Они помещаются в файле Unit.cpp по указанию разработчика для всех кнопок, флажков, движков и т.п., установленных на рабочем поле проекта. Тексты необходимых команд в составе этих обработчиков программист вписывает самостоятельно.

      Подробности будут рассмотрены в конкретных лабораторных работах. Здесь же необходимо выделить следующее:
     Первое - все объекты (стандартные компоненты), устанавливаемые программистом на форме проекта – являются равноправными элементами case-структуры, осуществляя функции звеньев обратных связей.
     Второе - исходной функцией, откуда начинается заполнение файла реализации текстом программного кода, является стандартная функция "обработчик графического окна". (А не обработчик запускающей кнопки Button, как иногда пишут в учебниках, и что допустимо только для очень простых, неанимационных программ).
     Третье - основной (т.е. обязательной) командой, помещаемой в функцию обработчика графического окна является команда вызова функции графического конвейера.
     Четвертое – основной командой, помещаемой в стандартные функции обработчиков событий является команда Formpaint() (а также Repaint() и др.) выполняющая вызов функции-обработчика графического окна.

    Это - четыре основных правила формирования структуры программ визуального проектирования. Они обеспечивают автоматическую перерисовку графического окна формы приложения (с перезапуском графического конвейера) при каждом обращении пользователя к стандартным элементам управления, расположенным на форме или по задаваемым таймерным отсчетам. Без соблюдения этих условий сложные графические программы превращаются в огромные свалки из нечитаемых функций, непригодных для корректировки и модификации.

      Учитывая вышесказанное, можно изобразить полную структурную схему интерактивной программы согласно рисунку 13.
      Здесь разными цветами обозначены три вида объектов схемы.
      Черным цветом прорисованы объекты конфигурации компьютера: монитор, манипуляторы.
     Зеленым цветом обозначены те элементы разрабатываемой программы, которые среда разработки C++Builder формирует самостоятельно, без участия программиста.
     Красным цветом обозначены модули, функции и переменные, создаваемые самим программистом.

 
 
 
 Рисунок 13 – Cтруктурная схема проекта интерактивной графики
 

Работа программы основана на функционировании двух принципиальных структур:
      - сканирующих запросов, формируемых конечными автоматами;
      - обратных связей: визуальной ОС, координатной ОС и др..

Главный конечный автомат формы отслеживают такие команды пользователя как «свернуть (или развернуть) окно проекта (или формы)», «закрыть окно», «работа в режиме конструктора» и другие системные команды, выполняемые с использованием мыши, клавиатуры и т.п. Если окон (форм) проекта несколько, то и автоматов такого ранга будет несколько.

Локальный конечный автомат (один или несколько) в свою очередь отслеживает управляющие воздействия визуальных, координатных и др. обратных связей.

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

Так как управляющие действия пользователя носят эпизодический характер, в задачу конечных автоматов входит обеспечение непрерывного сканирования состояния стандартных компонентов с использованием  таймера. Если события в зоне графического окна развиваются самостоятельно, по собственному, независимому сценарию, то здесь отслеживать прийдется не только манипуляционные команды пользователя но и текущие координаты фигур (через координатную ОС). Для этого необходимо разработать специальные функции для оценки их местоположения и принятия решений в отношении дальнейших действий.

Таймер графического окна – это стандартный компонент среды разработки C++Builder, обработчик которого может быть установлен программистом в файле Unit.cpp в случае необходимости (наличие анимации). В состав этого обработчика вводятся команды запуска или остановки таймера. Таймер является источником непрерывных, последовательных команд на перерисовку графического окна. Частота перерисовки устанавливается программистом. Чем выше частота перерисовки, тем лучше качество анимации, но здесь надо помнить, что возможности компьютера не беспредельны, поэтому оптимальная частота не должна превышать 100 Гц, т. е. 100 перерисовок в секунду.

Локальный КА на каждой перерисовке запрашивает значения текущих координат фигур, которые являются одновременно атрибутами объектов сцены, описывающих эти фигуры в составе Модуля описания классов. В случае достижения этими координатами каких-то граничных значений, автомат осуществляет переход сценарного сюжета в новое состояние, которое обеспечивает необходимые изменения в траектории движения или внешнем отображении формы и размеров фигур.

Графический конвейер производит непрерывный перерасчет текущих координат фигур и их прорисовку, независимо от того произошел переход конечного автомата в новое состояние или нет. Если, например по условию сценария одно из состояний автомата предполагает равномерное прямолинейное перемещение фигуры от одной стены до другой, то каждая перерисовка по таймерным отсчетам обеспечивает анимационное изменение текущих координат фигуры в пределах всего этого отрезка. Когда фигура достигнет противоположной стены, наступает локальное событие, потому что конечный автомат обнаруживает, что параметр, который он непрерывно сканирует, наконец-то достиг критического значения. После чего автомат переходит в другое case-состояние, ведущее к изменению траектории движения фигуры в режиме дальнейших непрерывных анимационных перерисовок. И так до следующего локального события. При этом, для выполнения различных преобразований и прорисовок всех задействованных объектов сцены в составе графического конвейера используются функции, являющиеся одновременно методами классов, что показано пунктирными стрелочками на рисунке 13.

Как уже говорилось ранее, графических конвейеров может быть несколько, каждый из которых будет отслеживать перемещение и поведение своих собственных фигур, т.е. свой набор текущих координат и любых других параметров.

Реальная структурная схема любого проекта, созданного в среде визуального проектирования, конечно, гораздо больше по размеру, чем та, что изображена на рисунке 13. Здесь же показаны только те программные блоки, которые имеют непосредственное отношение к управлению поведением графических объектов, т.е. то, что среда проектирования оставляет на проработку самому программисту.

Автоматный подход позволяет строить простые и надежные алгоритмы, с соблюдением высокой культуры программирования. Автоматное программирование применимо при создании программ, где необходимо обеспечивать независимое функционирование нескольких объектов в соответствии с заданными моделями их поведения или управления. К ним относятся игровые системы, программы физического моделирования, программы с развитым интерактивным интерфейсом и т.п. А также различные пакеты профессиональной обработки изображений, текстов, чертежей, создания игровых конструкторов и т.п., что под силу только большим коллективам программистов.

Следует отметить, что библиотека OpenGL (или DirectX), используемая совместно с пакетом Visual C++ формирует программы только в режиме конечных автоматов. Именно этот тандем используют профессиональные программисты компьютерной графики. Мы же начнем применять эти пакеты позднее, только в 3-ей части данного пособия (пока отсутствующей).
 
5. Графический  конвейер
  
Графический конвейер представляет собой последовательность специальных функций, предназначенных для выполнения анимационных преобразований координат графических объектов и их прорисовки на экране монитора в режиме реалистичного проецирования, свето и цветоотображения.
На рисунке 14 приведен пример структурной схемы типичного графического конвейера. При необходимости, программист вправе добавлять сюда любые другие преобразования (или наоборот, удалять).


 
Рисунок 14 – Структурная схема типичного графического конвейера
 
Эта схема отображает графический конвейер в его «классическом» варианте, в то время как в современных программах, особенно там, где используются ООП и OpenGL–технологии отдельные элементы этой цепочки разбросаны по разным модулям. Поэтому в таких программах трудно отследить реальную структуру этой конструкции. Кроме этого, может быть изменен и порядок следования некоторых блоков. Например, такую операцию как «Определение видимости фигур» в насыщенных трехмерных программах лучше делать до «Преобразования координат», иначе никаких ресурсов не хватит на вычисление координат, которые потом все равно не будут использоваться.
          Как видно из рисунка, графический конвейер состоит из трех основных частей:
А) Блок формирования результирующей матрицы видового преобразования Тпр;
Б) Блок преобразования координат фигуры (с использованием Тпр);
В) Блок прорисовки фигуры (с использованием преобразованных координат фигуры).
Рассмотрим назначение каждого блока по отдельности, но начнем с блока Б. Это главный элемент не только графического конвейера, но и всей программы. Потому что именно он выдает на выход те координаты, которые соответствуют текущему положению графических объектов на данный конкретный момент времени и используются для их прорисовки.
Конкретно, работа блока Б заключается в следующем. На его вход поступают две переменные: матрица исходных координат Мисх и матрица видового преобразования Тпр. В блоке производится перемножение этих матриц, в результате чего получается матрица преобразованных (измененных) координат Мизм. Матричное уравнение этого преобразования имеет вид:
 
Мисх × Тпр = Мизм
 
Или в развернутом виде:


Здесь приведен пример двумерного матричного преобразования. Для трехмерных систем отличия будут не значительны (см. Часть 2). Параметры  x1, y1, x2, y2  и т.д. – это координаты вершин (узлов), из которых формируется контур любой фигуры. Матрица видового двумерного преобразования Тпр здесь также изображена в обобщенном виде. В конкретных ситуациях обычно задействована только часть её параметров. Остальные равны нулю или единице (см. раздел «Основные теоретические сведения»). Каждый из параметров матрицы Тпр отвечает за свой вид преобразования поэтому, в зависимости от того, какие из этих параметров будут «включены», такой вид перемещения и произойдет.
В целях сокращения количества операций, существуют более упрощенные (нематричные) способы обработки координат. Обычно, такие методы применяют там, где не надо создавать локальную систему координат с независимой оцифровкой её осей, а координаты фигур задаются в пикселях, а не в относительных единицах. В таких случаях вместо перемножения матриц применяют попарные перемножения отдельных элементов этих матриц. Например, матричное преобразование координат фигуры, осуществляющее её вращение относительно начала координат против часовой стрелки, будет иметь вид: 

         
Тогда как в упрощенном, нематричном варианте пересчет координат будет выполняться по формулам:
 
x1' = x1cosθ – y1sinθ;                y1' = x1sinθ + y1cosθ
x2' = x2cosθ – y2sinθ;                y2' = x2sinθ + y2cosθ
…                                           …
xk' = xkcosθ – yksinθ;                 yk' = xksinθ + ykcosθ
 
 
   Если преобразования афинные – этого будет достаточно для получения окончательного значения, а вот для различного рода проецирований (в 3D), а также для масштабирования по всем осям одновременно потребуется дополнительно еще и отнормировать полученные координаты.
Для учебных целей упрощенные способы не подходят, потому что не позволяют учащимся осознать в полной мере универсальность метода матричных преобразований, единого для всех видов изменения местоположения и формы графических объектов.
 
Функция «Проецирование фигуры» блока Б применяется только в трехмерных изображениях для выполнения принятого для данной программы вида проецирования на картинную (двумерную) плоскость: аксонометрического, косоугольного или перспективного.
Функция «Нормирование» устраняет искажение координат, возникающее в результате матричных преобразований.
Если для какой-либо фигуры применяется несколько видов базовых преобразований, то здесь возможны два различных способа обработки координат:
- с использованием результирующей матрицы видового преобразования Трез вместо обычной Тпр;
- с использованием раздельного преобразования.
Для случая с использованием Трез производятся подготовительные операции (в составе блока А), заключающиеся в перемножении всех базовых матриц видовых преобразований, задаваемых для конкретной фигуры. Пример расчета результирующей матрицы для фигуры, подвергающейся таким изменениям как «перенос», «масштабирование» и «вращение относительно произвольного центра» имеет вид:
 
Тпер × Тмасшт × Твр = Трез

         Или в развернутом виде:

 

      
Здесь не показан конкретный результат матрицы Трез – он очень громоздкий, на этой странице не поместится. Если кто-то интересуется её видом, он может самостоятельно перемножить эти три матрицы. Но обычно программисты не занимаются такой работой, так как программа сама выполняет перемножение и сама использует результат, достаточно только указать имя вычисленной матрицы.
         Разъясним элементный состав этих матриц: параметры первой матрицы  l1  и m1 - это параметры переноса фигуры по осям X и Y соответственно, параметры второй матрицы  a, e, s - коэффициенты масштабирования по осям X, Y и по обеим осям одновременно, параметры третьей матрицы  l2  и  m2  - координаты отстояния произвольного центра вращения фигуры от начала координат. Параметр θ (tetta) - угол поворота фигуры.        
Если в какой-то момент времени одно из действий этой цепочки не должно выполняться, то удалять его из формулы не надо. Потому что, при отключенных параметрах данной конкретной матрицы она превращается в единичную матрицу и никакого влияния на результат не оказывает. Единственный недостаток – затрачивается время на пустую операцию.
Работа по введению параметров в матрицы видовых преобразований обычно выполняется в составе стандартных обработчиков для различного вида окон, выпадающих меню, движков и т.п. Как уже говорилось ранее, эти операции программирует сам разработчик. Далее, в описаниях лабораторных работ будет показано, как это делается.
Для случая с раздельным преобразованием результирующая матрица Трез не создается, а матрица координат фигуры последовательно перемножается с каждой из базовых матриц видового преобразования по отдельности. Понятно, что здесь блок А не нужен.
Назначение блока В можно определить по наименованиям функций, входящих в его состав.
Далее, в разделе 3 Основных теоретических положений, а также в лаб. раб. № 2, 3, 4 можно подробно узнать о методах матричных преобразований, о нормировке, прорисовке, заливке и т.п.
Анализируя изложенную информацию, можно сделать следующий вывод: графический конвейер – это совокупность двух преобразующих цепочек. Первая и главная состоит из последовательности функций (на рисунке 14 это функции 2, 3…7), предназначенных для пошаговой обработки матрицы координат М и последующей прорисовки фигур по этим координатам. Вторая цепочка – вложенная. Она входит в блок А и представляет собой последовательность операций по перемножению матриц видовых преобразований, составляющих в результате матрицу Трез.
       
Из всего вышесказанного видно, что понятие "графический конвейер" – не является какой-то строго фиксированной структурой. В каждом конкретном случае, в зависимости от поставленных задач, от типа используемых графических библиотек, от типа установленных аппаратных средств поддержки графики эта цепочка будет скомпонована различным способом и из различных элементов. Количество и содержание этих элементов определяется самим программистом. Здесь он должен самостоятельно определять, какие элементы конвейера ему надо реализовывать программным способом, а какие – аппаратно, с использова-нием микропрограммной логики, жестко "зашитой" в составе видеокарт, например в шейдерах.
В учебниках по компьютерной графике, выпущенных в последние годы с описанием программ, использующих профессиональные графические библиотеки, практически не встречаются упоминания о графическом конвейере  и тем более о необходимости формировать его программно.  Это связано с тем, что библиотеки, типа OpenGL, взяли на себя все заботы по формированию конвейера. Кроме этого, специфика современных графических технологий требует разделения конвейерной цепочки на отдельные блоки с последующей их обработкой с помощью специальных функций. Состав этих функций является закрытой информацией фирм-разработчиков графических библиотек.
В данном цикле лабораторных работ представлена методика программирования графики в учебном режиме, с обязательным формированием графического конвейера «вручную», без использования специальных библиотечных функций, укрывающих математическую суть графических преобразований над изображениями.
 
6. Графический конвейер в режиме текущих координат
 
В лабораторных работах данного учебного пособия рассматриваются два режима представления координат графических объектов:
- режим исходных координат;
- режим текущих координат.
Режим исходных координат заключается в том, что для расчета с помощью преобразующих матриц каждого нового положения фигуры используются её исходные, неизменные координаты.
Достоинства этого метода - простота и точность вычисления новых координат. Здесь исключается накопление погрешности, возникающей в процессе видовых преобразований.
Недостатки: необходимость запоминать всю последовательность предыдущих перемещений, что неприемлемо для программ с произвольной анимацией. 
Поэтому, этот метод используется в основном в учебно-демонстрационных целях, а конкретно здесь - в лабораторных работах № 2, 7, 8, 9.
Режим текущих координат заключается в том, что каждое новое положение фигуры рассчитывается относительно её предыдущего положения. В этом режиме значения матриц текущих координат  Mtek  постоянно перезаписываются на каждом шаге таймерного отсчета, т.е. при каждой перерисовке графического окна.
На рисунке 15 изображена структура графического конвейера в 2D-программах в режиме текущих координат.


 
Рисунок 15 – Работа графического конвейера в режиме текущих 2D-координат (вар.1)
 
 Как видно из схемы, при использовании режима текущих координат в графический конвейер после функции преобразования координат необходимо добавить оператор присваивания вида:  Mtek = Mizm;
       Где Mtek – это матрица текущих координат фигуры, а Mizm – матрица тех же координат, после их обработки с помощью Tpr - матрицы видового преобразования.
       Тогда, при следующей перерисовке графического окна по команде таймера или каких-либо обработчиков управляющих событий в функцию преобразования координат будет введено предыдущее, зафиксированное этим оператором значение матрицы текущих координат. 
       Если программист не хочет создавать несколько матриц для хранения промежуточных значений координат фигуры (а это будет иметь существенное значение при большом количестве различных объектов в программе), то можно составить другой вариант структурной схемы, такой, как на рисунке 16.
 
 
 
 

 
Рисунок 16 – Структурная схема графического конвейера в режиме текущих 2D-координат (вар. 2)
 
Здесь, как видим, потребуется добавлять команды переприсваивания типа  Mtek=:Mizm  во все обработчики событий и функцию таймера.
Первый вариант более предпочтителен для учебных целей, он позволяет просматривать все промежуточные значения матриц координат в режиме отладки программы. 
Второй вариант используется для рабочего программирования.
        
На самом деле структура рабочих программ еще сложнее и эта сложность выражается прежде всего в том, что для сокращения количества операций и переменных в них активно используется вложенность одних функций в состав формальных параметров других функций. Такие программы похожи на матрешек, глубина внутренних закладок в них может достигать несколько уровней. С такими методами мы также предполагаем ознакомить читателей в третьей части данного пособия (пока отсутствующей). Но, чтобы грамотно формировать всю скрытую структуру вложенных функций, необходимо изучить полный, развернутый набор всех рабочих блоков программы, возможность чего здесь и предоставляется.
 
Структура графического конвейера в режиме текущих 3D-координат представлена на рисунке 17.
Сравнивая эту схему с рисунком 15 видно, что здесь добавлены еще две функции: «Проецирование в локальной системе координат» и «Проецирование в глобальной системе координат».
Особенностью этих двух функций является то, что они, несмотря на то, что также выполняют преобразования  координат фигур, тем не менее не могут быть включены в состав результирующей матрицы видового преобразования Трез (обозначенной здесь как Tpr), реализующей такие действия, как перенос, масштабирование, вращение и др.
         Здесь необходимо создавать разрыв в общей цепочке преобразующих матриц, чтобы «выудить» из этого места промежуточные координаты фигуры, запомнить их в отдельной переменной и затем использовать как «текущие» при последующей перерисовке. А уже после этого разрыва преобразования, относящиеся к различного рода проецированию, можно продолжить, чтобы потом выполнить вывод изображения фигуры на экран монитора.


 
 Рисунок 17 – Структурная схема графического конвейера в режиме текущих 3D-координат

Эту схему также можно представить во втором варианте, аналогично рисунку 16.
 Подробности, касающиеся методов проецирования в локальной и глобальной системе координат приводятся в лабораторных работах № 7-10. Но, если коротко обозначить их особенности, то можно сказать следующее.
Локальная система координат – это трехмерное декартовое пространство (сцена), где помещаются все графические фигуры действующего сценария. Эта система определяет истинные координаты фигур (либо в относительных единицах либо в пикселях) и только с этими координатами производятся различные видовые проеобразования, обеспечивающие перемещение, вращение и масштабирование фигур внутри сцены.
Глобальная система координат – это пространство реального существования графических объектов локальной сцены, отображающее их «видимое» состояние для конкретного наблюдателя. Координаты «видимого» изображения отличаются от истинных координат одного и того же объекта тем, что в глобальной системе производится их дополнительная обработка, соответствующая перспективному преобразованию. Для обеспечения такого преобразования глобальная система содержит два важных элемента: центр проецирования (камеру) и картинную плоскость, т.е. двумерную поверхность на которую выполняется проецирование. В качестве картинной плоскости используется, как правило, одна из координатных плоскостей глобальной системы координат: XOY, ZOX или  YOZ. Локальная система координат располагается между камерой и картинной плоскостью, т.е. внутри глобальной системы. При такой компоновке лучи проецирования, испускаемые из центра проецирования в направлении трехмерных объектов сцены, проходят сквозь них, затем движутся далее, до пересечения с картинной плоскостью глобальной системы координат, выполняя при этом проецирование всех объектов сцены на картинную плоскость. Таким образом получаем проекции трехмерных объектов на двумерной плоскости, которые используются  далее при их прорисовке на экране монитора. Положение камеры можно менять произвольным образом, что будет соответствовать таким действиям, как круговой «облёт» сцены, приближение, удаление и т.п. Математически, такие действия будут соответствовать поворотам, перемещениям и масштабированию локальной системы координат и всех её объектов внутри глобальной системы, что потребует введения дополнительных видовых преобразований одновременно над всеми участниками сцены и самой локальной системой координат. Обращаем ваше внимание на эти два, одновременно и различных и в то же время однотипных, вида преобразований фигур: одно выполняется внутри локальной системы и определяет их местоположение и перемещения в области сцены, другое выполняется внутри глобальной системы и определяет положение камеры относительно локальной системы и картинной плоскости.
Окончательное действие, которое нужно будет выполнить, это перевести глобальные координаты фигур (т.е. перспективно-преобразованные истинные координаты) в экранные координаты, которые уже можно использовать для непосредственных прорисовок на экране монитора. Для экранных координат используется только двумерная часть (x,y) из трехмерного комплекта (x,y,z) глобальных координат, описывающих каждый узел (вершину) графических объектов. А также, при необходимости, производится перевод численных значений координат из относительных величин в пиксели.
На рисунке 17 присутствуют два вида проецирований: локальное и глобальное. В реальной программе локальное проецирование применяется не всегда и  связано с необходимостью изображения проекций трехмерных объектов на координатные плоскости локальной системы в учебных целях, или с проецированием теней на различные двумерные плоскости, окружающие фигуры.
В одной и той же программе можно одновременно применять и режим исходных координат и режим текущих координат. Это будет зависеть от степени активности конкретной фигуры. Для малоподвижных и малоуправляемых объектов можно использовать первый способ, а для динамичных фигур – второй (см. л.р. №10).
          Если в какой-либо программе фигуры описываются в режиме ООП - это никак не повлияет на структуру графического конвейера. При этом, все матрицы координат (или матрицы полигонов) будут объектами классов, а функции видовых преобразований, прорисовок и т.п. - методами классов.
 
 
7.  Роль ООП и специальных графических библиотек в программировании КГ
 
ООП, конечные автоматы и специальные графические библиотеки – три кита, на которых базируется современная компьютерная графика. ООП формирует стройную структуру описания свойств объектов с использованием классов, конечные автоматы обеспечивают надежную систему управления состояниями этих объектов, а библиотечные функции позволяют обеспечить высочайший уровень качества и скорости отображения графических объектов.
Объектно-ориентированное программирование (ООП) является непосредственным детищем компьютерной графики. Если простые фигуры вполне возможно изображать и без ООП, то для отображения трехмерных объектов, задаваемых сотнями полигонов, с тенями, текстурами, световыми эффектами, без ООП не обойтись.
Не всякий молодой программист способен, по причине недостаточной профессиональной подготовки использовать методы ООП. А из тех, что используют, не все хорошо понимают суть этой технологии. В дебрях атрибутов и методов объектов ООП не легко разглядеть цепочки функций, реализующих графический конвейер. Поэтому, на начальном этапе изучения математических основ графических проебразований крайне не желательно использовать ООП и библиотеки OpenGL в учебных программах, чтобы не отвлекать внимание на такие важные понятия, как структура программы, графический конвейер и алгоритмы графических функций. Всегда надо помнить, для чего предназначена технология ООП - для облегчения описания свойств большого количества графических объектов, и не более того. И описания эти достаточно специфичны, чтобы сбить с толку любого начинающего соискателя. Только после освоения методов формирования структуры программ и принципов работы преобразующих функций можно переходить к рассмотрению особенностей использования ООП. К этим особенностям относятся, например, такие факторы:
1) Способ именования переменных и функций, являющихся одновременно атрибутами и методами классов ООП. Если например в обычной программе матрица координат какой-нибудь фигуры будет именоваться как fig1, то в ООП-программе эта же фигура будет именоваться как Figura.fig1. Здесь первая часть имени, т.е. термин Figura является именем класса фигур, к которому принадлежит объект fig1. Аналогично формируются и имена функций, служащих для обработки данной фигуры и являющихся методами её класса. 
2) Способ формирования структуры программы. В её состав необходимо будет ввести несколько модулей:
- для описаний классов ООП;
- для стандартных обработчиков управляющих событий;
- для собственных библиотечных функций и т.п.
Можно, конечно, обходиться и без модулей, но стиль ООП-программирования предполагает именно такое разделение, что несомненно является правильным подходом. Что касается основной части файла реализации, то она останется практически без изменения. В ней по-прежнему будут присутствовать стандартная функция-обработчик графического окна и описание функции графического конвейера.
         3) Использование методов "наследования" и "инкапсуляции". Эти два принципа, наряду с использованием понятия "классы" для обозначения принадлежности графических объектов к тем или иным группам - являются основополагающими факторами ООП. Наследование позволяет соподчиненным объектам использовать функции (методы), принадлежащие главным по отношению к ним объектам. Например, если надо изобразить человека и движение всех его конечностей, то в перемещении кистей рук принцип наследования позволяет учитывать перемещение костей предплечья, а в перемещении предплечья - перемещение плеча, а в перемещении плеча - перемещение всего корпуса тела, и так для каждого элемента скелета человека. Из вышесказанного понятно, что наследование значительно сокращает объем разрабатываемых функций (методов) и обеспечивает надежное соединение соподчиненных объектов. Инкапсуляция в свою очередь позволяет использовать одни и те же функции (методы) для  обработки изображений разных объектов, незначительно отличающихся друг от друга, особенно когда этих объектов много. Здесь программа, при выполнении прорисовки множества одинаковых фигур не должна "перепутать", какой объект и с какими параметрами рисовать. Инкапсуляция позволяет четко привязать конкретный обрабатываемый объект к конкретной функции (методу) с конкретными параметрами. И защитить данный конкретный объект от преобразований, предназначенных для другого объекта. Из описаний сути наследования и инкапсуляции видно, что эти методы нет смысла применять для прорисовок простых, контурных изображений, они относятся к "тяжелой артиллерии" компьютерной графики и применимы в больших, насыщенных  программах.   
К использованию библиотек типа OpenGL в процессе обучения надо переходить в последнюю очередь. Эти библиотеки можно использовать с любыми пакетами: с  C++Builder, с Visual C  и др.  
На этом этапе структура программы опять поменяется. Причем достаточно принципиально.
Главное отличие структуры OpenGL-программ от классического варианта заключается в том, что в них не используются результирующие матрицы видового преобразования. Базовые и комбинированные используются, а результирующие нет. Имеется библиотека базовых матриц видового преобразования, поэтому в графическом конвейере отсутствует функция №1 (см. рис. 14). Структура конвейера также изменится. Если в классическом варианте сначала все видовые преобразования объединяются в результирующую матрицу, затем исходные координаты фигур обрабатываются с помощью этой общей матрицы, а затем выполняется нормировка полученных координат перед их прорисовкой, то в этом варианте способ обработки координат сведен к единой, универсальной схеме. Каждый полигон (или вершина) обрабатывается своим собственным конвейером, начиная с заданного определенной командой видового преобразования и заканчивая нормировкой. Поэтому, программист, использующий функции OpenGL практически и не подозревает о тех дополнительных математических действиях, которые выполняет за него эта библиотека. За это ей можно сказать большое спасибо, но настоящий программист, тем не менее, должен знать всю «кухню».
В данном учебном пособии методы ООП использованы только в четвертой лабораторной работе. Там, по сути задания в зоне графического окна одновременно перемещаются более 20 независимых объектов. В остальных лабораторных работах задействовано не более 1, 2-х объектов, поэтому и необходимости в применении ООП не было.
В лабораторных работах, рассматриваемых во второй части пособия (в №№ 7, 8, 9 и 10) методы ООП также не используются, хотя по объему разрабатываемых программ с трехмерными объектами, по их громоздкости уже видна настоятельная необходимость в представлении объектов в полигональном режиме и использовании классов ООП для их описания. Но здесь, как уже говорилось выше, мы намеренно не использовали эти методы, в целях обеспечения лучшего понимания и сохранения наглядности графических преобразований в составе графического конвейера. Важность такого решения будет понятна, когда читатель ознакомится с процессом создания результирующих матриц видового преобразования для трехмерных графических объектов в этих лабораторных работах. Только здесь представлена возможность увидеть всю цепочку графических преобразований целиком, в составе функции графического конвейера, и изучить влияние каждого из преобразований на результирующее изображение. 
           В 3-ей части пособия использование ООП будет обязательным, наряду с применением OpenGL.
 
8. Порядок разработки программ компьютерной графики
 
Все, кто берется разрабатывать программы компьютерной графики, должны четко осознавать разницу между такими понятиями как «алгоритм программного кода» и «алгоритм создания алгоритма программного кода».
Большие программы, особенно такие как в КГ, размером от 10 до 500 страниц печатного текста, невозможно записывать в порядке сплошного последовательного набора команд, сверху вниз. Они должны разрабатываться по спирали, по этапам, по модулям, для каждой фигуры отдельно, сначала в статическом режиме, потом в динамике (анимации), потом с добавлением реалистических эффектов, с полной отладкой каждого конкретного этапа и каждой конкретной фигуры. Отлаживать программы с графикой достаточно удобно, потому что все ошибки сразу видны на получаемом изображении.
Здесь, в этом параграфе будет описан алгоритм создания учебных программ среднего уровня сложности на базе пакета C++Builder.
Итак, первое, что надо сделать – это взять три листа бумаги, формата А4 и разложить их перед собой на столе. Затем, на первом листе нарисовать рабочее окно разрабатываемого проекта. Изобразить в нем графическое окно со всеми действующими фигурами и панель управления со всеми манипуляторами, кнопками, флажками и т.п. Если окон несколько, то аналогично описать все остальные.
Для тех кто не знает, поясним: рабочее окно проекта – это обычное, стандартное, windows-подобное окно разрабатываемой программы, которое C++Builder формирует самостоятельно, а графическое окно – это прямоугольная область, создаваемая программистом в зоне рабочего окна проекта, для изображения в нем своих анимационных сюжетов.
Возле каждой фигуры в графическом окне надо написать имя (идентификатор), обозначающее её в программе. Имена фигур - произвольные, по собственному усмотрению. Для каждого имени определить формат описания. Например, для полигональной фигуры надо описать матрицы координат вершин полигонов этой фигуры, для спрайтовой картинки – растровую матрицу и т.д., для каждого объекта.
 
На панели управления рабочего окна проекта, возле каждой кнопки, флажка и др. прописать стандартное название этого элемента в С++Builder, область действия (какими фигурами управляет) и род действия. Здесь важно определить, надо ли вызывать перерисовку всей картинки при обращении пользователя к данному управляющему элементу, или нет. Т.е. какова его роль: активная или пассивная. Если элемент активный, то в его обработчик надо обязательно вписывать в самом его конце команды перерисовки – Repaint()  или  Formpaint(), в зависимости от типа установленного графического окна. Если элемент пассивный, то такую команду писать не надо. Стандартные обработчики для пассивных элементов создаются только в тех случаях, когда потребуется дополнительно что-то делать с данными, введенными пользоватетем. Само считывание данных, введенных через эти компоненты, можно производить напрямую, из списков их атрибутов, по мере необходимости.
    Далее, на втором листе бумаги необходимо нарисовать графический конвейер, который будет обеспечивать пересчет координат и прорисовку объектов графического окна. Каждой функции, входящей в конвейер надо присвоить имя и определить для них списки формальных параметров. Т.е. имена тех переменных, которые потребуется вводить в эти функции для обработки. Не исключено, что для некоторых объектов прийдется создавать свои собственные конвейеры.
    На третий лист заносится содержимое матриц видового преобразования, с помощью которых будут осуществляться графические преобразования для каждой из фигур в составе функций графического конвейера. Тогда будет понятно, какие параметры, входящие в эти матрицы подлежат регулированию и с помощью каких манипуляторов или функций. Результирующие матрицы формируются в виде цепочек из нескольких, последовательно перемножаемых базовых матриц. В зависимости от того, какой набор действий надо будет выполнить над конкретной фигурой, такие матрицы преобразования и войдут в состав результирующей матрицы. В четвертом параграфе раздела «Основные теоретические сведения по двумерной графике» рассмотрены  методы формирования результирующих матриц.
После заполнения этих трех листов программист будет иметь перед глазами:
         Во-первых, полный перечень всех переменных, то есть:
- имена матриц координат фигур - исходных и текущих;
         Во-вторых, все матрицы преобразования, обрабатывающие фигуры, а именно:
- имена матриц базовых и результирующих видовых преобразований;
- имена переменных – параметров, входящих в матрицы видового преобразования.
В-третьих, полный перечень функций:
- имена обрабатывающих функций графического конвейера;
- имена дополнительных и вспомогательных функций.
И в-четвертых, перечень всех стандартных компонентов, для которых надо будет создать функции-обработчики управляющих событий.
Теперь можно перйти к составлению общей структурной схемы для функций, входящих в Unit.cpp  и предварительному построению блок-схем для отдельных функций, входящих в цепочку графического конвейера. Опять берем несколько листов бумаги и начинаем рисовать схемы.
Функции и операторы, из которых будет строиться общая структурная схема, по функциональному назначению можно разделить на следующие группы:
- команды инициализации исходного представления фигур;
- команды инициализации параметров графического окна;
- функции графического конвейера;
- дополнительные функции;
- функции – обработчики событий стандартных компонентов формы проекта.
Далее, в составе представленных лабораторных работ приведены структурные схемы разрабатываемых программ.
И последнее что надо сделать, перед тем как сесть за клавиатуру, это определить функции и переменные, которые желательно поместить в отдельные модули. Обычно к ним относятся функции общего пользования, так называемые библиотечные функции. В отдельные модули также помещают описания классов объектов, если в программе используют элементы ООП. Чем больше различных перемещающихся объектов будет в программе, тем больше потребуется модулей.
Кроме этого, необходимо определить объекты, создаваемые в режиме динамического выделения памяти, так как для этого случая также придется создавать отдельные функции.
Все эти предварительные списки переменных и обрабатывающих функций в процессе создания программы еще много раз могут корректироваться. Но при всем при этом первые три листа («три А4») с перечнем переменных и схем их преобразования, являются основой, базой, к которой программист постоянно будет обращаться во время работы. Потому что держать в памяти список из нескольких десятков имен переменных и функций мало кто способен, да в этом и нет особой необходимости. Позже, когда будет сформирован раздел переменных и описаний прототипов функций в самой программе, от этих листков можно будет частично отказаться.
Итак, подготовительная часть закончена, можно запускать компьютер и создавать исходный шаблон проекта в среде  C++Builder. Порядок его создания, первичного сохранения и формирования рабочего окна со всеми управляющими компонентами в режиме конструктора подробно описан ниже, в лабороторной работе №1. Это самая простая часть работы. Далее действия следующие:
а) заполняется раздел описаний переменных и прототипов функций в начальной части файла реализации Unit.cpp. Для этого используются данные из «трех А4»;
б) создаются модули для описаний классов ООП, для библиотечных функций, по необходимости. Эти этапы обычно выполняется частично, с последующими добавлениями по ходу программирования;
в) создается стандартная функция исходной инициализации переменных FormCreate(). Её содержимое также будет пополняться в процессе работы;
г) создается обработчик графического окна. Для этого используются стандартные компоненты среды разработки, обладающие свойством Canvas. К таким объектам в С++Builder относятся компоненты  Image,  PaintBox,  Bitmap,  FormPaint и др. Вообще, стандартный обработчик для какого-либо стандатртного компонента – это просто обыкновенная стандартная функция, создаваемая самой средой разработки по команде программиста. Стоит только ему щелкнуть мышкой в окне конструктора проекта по какой-либо кнопке или другому управляющему объекту, установленному на поле формы, как сразу же в окне файла реализации появляется стандартная функция для этого компонента. Пока еще пустая, только один заголовок. Создадим аналогичным образом обработчик для графического окна. Впишем в его состав группу команд, обеспечивающих функционирование двойной буферизации изображения (если есть анимация) и вызов функции, запускающей графический конвейер. Описание функции еще не создано, а мы уже записываем её вызов. Имя этой функции графического конвейера нами уже объявлено в разделе описания прототипов функций, а чтобы программа при компиляции не ругалась на отсутствие описания этой функции, временно закомментируем (заглушим) её вызов.
Если читателю не совсем понятно, почему вызовы функций записываются раньше, чем их описания, то это можно объяснить следующим. Программист сначала создает «скелет» программы, состоящий только из названий функций (т.е. команд вызова функций). На начальном этапе создания программы важны только четыре вещи: перечень объектов - участников изображения, перечень функций, обрабатывающих эти объекты, последовательность выполнения этих функций и их вложенность, т.е., иными словами, кто, кого, для чего и откуда вызывает. Пока этого достаточно. Поэтому, программирование ведется «сверху вниз», сначала описываются главные запускающие функции (а именно функция графического окна), потом – помельче, но тоже пока еще управляющего типа. И только после этого можно разрабатывать описания рабочих, т.е. собственно преобразующих функций. Можно, конечно, создать, к примеру, описание функции прорисовки какого-либо объекта в первую очередь, но откуда вы её будете запускать и в какой области прорисовывать, вот вопрос? Потому что для каждой функции надо сначала подготовить "зону обитания", т.е. функцию, откуда она будет вызываться, и только потом разрабатывать её описание, но не наоборот. Иначе говоря, каждая функция в программе имеет свое, строго фиксированное место (с точки зрения вложенности) и свой номер следования, с точки зрения порядка разработки. Только очень хорошие программисты, способные держать в памяти всю структуру создаваемой программы целиком, могут позволить себе нарушать эту последовательность. 
        Функция графического конвейера относится к управляющим функциям. В ней нет ничего, кроме вызовов других функций - функций обработки изображения. При небрежном составлении программы её вообще можно не создавать, а просто накидать все вызовы обрабатывающих функций прямо в обработчике графического окна. Но, как уже говорилось выше, такие свалки делать не хорошо.
д) далее, создаем собственно описание функции графического конвейера. Эта функция не относится к группе стандартных функций. Она относится к группе «самодельных» функций. Поэтому процесс её создания от начала и до конца выполняется самим программистом. В файле Unit.cpp, сразу после обработчика графического окна, созданного нами перед этим, впишем заголовок нашей функции в соответствии с объявленным прототипом. После заголовка пишем фигурные скобки, внутри которых заносим команды вызовов функций, составляющих цепочку графических преобразований и прорисовок. Вызовы этих функций также временно закомментируем, потому что их описания еще не разработаны. Зато уже можно снять ограничения на вызов функции графического конвейера в обработчике графического окна и снова запустить проект на компиляцию. Эти запуски программы необходимо делать после каждой, вновь созданной функции для своевременного выявления ошибок в программном коде, пусть даже пока и нет никакого изображения, кроме пустого белого квадрата графического окна.
е) выполняем последовательную разработку функций, входящих в состав графического конвейера. Первая функция, к которой надо приступить – это функция №6 (см. рис. 14), функция прорисовки контура для одной из фигур, любой. Здесь главное – получить работающий алгоритм, пригодный для прорисовки первой фигуры, а затем и остальных фигур. На этом этапе полученное изображение будет статическим, наподвижным. Координаты фигуры надо задать в функции FormCreate() во временном режиме. Позднее, при установке таймера и обработчиков для окон ввода координат фигур эту временную инициализацию можно будет удалить из FormCreate(). Теперь, при разработке функций № 1, 2, 3 и 4 все ошибки их программирования сразу будут выявляться на изображаемой фигуре. Чтобы понимать, какие команды надо писать в функциях преобразования координат, проецирования и нормировки (т.е. в № 1, 2, 3 и 4) надо прочитать теорию, содержащуюся в параграфах 3 и 4 раздела «Основные теоретические положения» и теоретические сведения в самих лабораторных работах.
Если в графическом окне изображено несколько графических фигур, каждая из которых должна перемещаться по своей независимой траектории, то для каждой из них необходимо создать свой конвейер. Или наоборот, создать один общий, но при этом во всех функциях преобразования и прорисовки последовательно обрабатывать весь «списочный состав». Это уже определяется из конкретных условий.
ж) реальная работа функций преобразования координат и их нормировки начнется только тогда, когда фигурам будут добавлены анимационные качества. Первое, что надо будет сделать для этого – установить системный таймер и разработать функции, ответственные за его запуск и останов (или же сформировать цикл For, как в лаб. раб. № 2).  На втором шаге – создадим обработчики для стандартных элементов управления нашими фигурами: для кнопок, для движков и т.п. После этого можно удалить временные команды инициализации исходных координат фигур из функции FormCreate(), снять комментарии на вызовы этих функций, а проект запустить на исполнение. На этом этапе все фигуры уже должны двигаться и подчиняться управляющим воздействиям.
з) на последнем этапе программирования разрабатываются второстепенные функции, например очистки окна, работа флажков переключения, радиокнопок и т.п. Разрабатываются здесь также функции заливки, текстурирования и т.п.

           Подводя итог сказанному, изложим вышеописанный алгоритм в более сжатом виде:
1) Рисуем предварительное эскизное изображение рабочего окна проекта со всеми графическими объектами и элементами управления;
2) Составляем предварительные списки переменных, матриц видовых преобразований и преобразующих функций;
3) Составляем структурную схему для функций файла реализации;
4) Составляем структурную схему для графического конвейера;
5) На базе принятого пакета визуального проектирования создаем исходный шаблон проекта;
6) В тексте файла реализации заполняем раздел описаний переменных и прототипов функций;
7) Создаем модули классов ООП, модули библиотечных функций и др.;
8) Разрабатываем функцию - обработчик графического окна;
9) Разрабатываем функцию графического конвейера;
10) Разрабатываем функции, входящие в графический конвейер;
11) Разрабатываем функции, входящие в библиотечный модуль (дополнительные функции);
12) Создаем обработчики стандартных управляющих элементов;
13) Создаем обработчик для таймера;
14) Создаем функции закраски, текстурирования и др., а также вспомогательные функции.
Если говорить о «спиралевидной» методике разработки программ КГ, то из текста приведенного алгоритма просматриваются четыре «витка».
На первом витке выполняются пункты с 1-го по 9-й. При этом в описании функции графического конвейера активируется вызов только одной функции - функции прорисовки какой-либо фигуры. После чего разрабатывается описание этой функции. Получаем неподвижную картинку первой фигуры.
На втором витке выполняются пункты с 10-го по 13-й, а именно: разрабатываются функции преобразования координат, входящие в графический конвейер, устанавливается таймер и создаются обработчики событий для управления фигурой. Получаем анимационную картинку первой фигуры.
На третьем витке повторям аналогичные операции для остальных фигур. Получаем контурное изображение всех фигур в режиме анимации.
И на четвертом витке реализуем 14-й пункт, т.е. закраски, текстуры и эффекты для всех фигур.
 
Как видим, главное назначение спиралевидной методики заключается в том, чтобы сначала получить исходное, статическое, контурное изображение фигуры, а потом, имея возможность визуально контролировать результаты программирования, последовательно добавлять фигуре остальные качества: анимацию, закраски, текстуру и эффекты.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


 
Главная страницаОб авторахЗагрузка файловКонтакты