Автор работы: Пользователь скрыл имя, 20 Мая 2012 в 14:18, методичка
Технология разработки больших программных комплексов. Основные принципы модульного программирования. Объектно-ориентированная методология разработки программ. Примеры (этапы и особенности) проектирования программ с использованием данной технологии: на примере задачи качения квадрата по некоторой поверхности с отображением этого процесса на экране; задачи на создание движущейся модели шагающего человека.
38
Министерство народного образования Российской Федерации
ИжГТУ
Кафедра вычислительной техники
Методические указания для выполнения курсовых и лабораторных работ по теме «Объектно-ориентированное программирование»
для студентов 1-2 курсов спец. 2201.
Разработал к.т.н., доцент кафедры ВТ Гафаров Р.М.
Ижевск 2002
1. Краткие теоретические сведения
1.1 Технология разработки больших программных комплексов
К началу 70-х годов уже было очевидно, что программные проекты стали слишком сложными для успешного проектирования, кодирования и отладки в приемлемые сроки. Кроме того, программисты, решающие сложные задачи, столкнулись с проблемой разрастания количества и размера программ до такой степени, что дальнейший процесс разработки становился практически неуправляемым, и никто из разработчиков не мог с уверенностью сказать, что созданный программный продукт всегда выполняет то, что требуется, и что он не выполняет ничего такого, что не требуется. Таким образом, стала проблема коренного изменения подходов к созданию больших программных комплексов. Исходя из этих проблем, были разработаны строгие правила ведения проектов, которые получили название структурной методологии, а программирование по ней – модульного программирования.
Объектно-ориентированная методология так же, как и структурная методология, была создана с целью дисциплинировать процесс разработки больших программных комплексов и тем самым снизить их сложность и стоимость. Она преследует те же цели, что и структурная, но решает их с другой отправной точки и в большинстве случаев позволяет управлять более сложными проектами, чем структурная методология. Одним из принципов управления сложностью проекта является декомпозиция (разделение на части). Выделяют две разновидности декомпозиции: алгоритмическую (поддерживаемыми структурными методами) и объектно-ориентированную.
Алгоритмическая декомпозиция учитывает в большей степени структуру взаимосвязей между частями сложной проблемы, а объектно-ориентированная декомпозиция уделяет больше внимания характеру взаимосвязей.
На практике рекомендуется применять обе разновидности декомпозиции: при создании крупных проектов целесообразно сначала применять объектно-ориентированный подход для создания общей иерархии объектов, отражающих сущность программируемой задачи, а затем использовать алгоритмическую декомпозицию на модули для упрощения разработки и сопровождения разрабатываемого программного комплекса.
1.2. Основные принципы модульного программирования
Модульное программирование - это организация программы как совокупности небольших независимых блоков, называемых модулями, структура и поведение которых подчиняются определенным правилам. Использование модульного программирования позволяет упростить тестирование программы и обнаружение ошибок, упрощается процесс повышения эффективности программ, так как критичные по времени модули могут быть написаны на языках низкого уровня (ассемблерах) и многократно переделываться независимо от других. Кроме того, модульные программы могут разрабатываться параллельно во времени многими исполнителями, что сокращает сроки их разработки и испытаний, их значительно легче понимать, а модули могут использоваться как строительные блоки в других программах. Современные технологии программирования основываются на нескольких базовых принципах, среди которых основное место занимают следующие[1].
Принцип абстракции – вообразить требуемое решение проблемы без сиюминутного учета множества деталей. На принципе абстракции основываются многие структурные методы, например, восходящая и нисходящая стратегии программирования.
Принцип формальности – предполагает строгий методический подход и является базой для превращения программирования из импровизации в инженерную дисциплину. Структурный подход использует формальность для придания процессу творчества определенной дисциплины и строгости, что позволяет ускорять принятие решений и избегать множества ошибок.
Принцип независимости – является методом решения трудных проблем путем разделения проблемы на множество мелких независимых, которые легче понимать и решать. При разработке программного обеспечения этот принцип означает разделение программы на отдельные фрагменты (модули), которые просты по управлению и допускают независимую отладку и тестирование. Он позволяет разработчику программ спокойно работать над отдельной частью большой системы, не заботясь об огромном количестве деталей, охватывающих всю систему.
Принцип иерархического упорядочивания – структурирование связей между модулями программного комплекса, что облегчает достижение целей структурного программирования.
Форма модульной программы.
1. Программа должна разделяться на независимые части, называемые модулями.
2. Модуль - это независимый блок, код которого физически и логически отделен от кода других модулей.
3. Модуль выполняет только одну логическую функцию.
4. Размер модуля не должен превышать 100 операторов.
5. Модуль имеет одну входную и одну выходную точку.
6. Взаимосвязи между модулями устанавливаются по иерархической структуре.
7. Каждый модуль должен начинаться с комментария, объясняющего его назначение, назначение переменных, передаваемых в модуль и из него, модулей, которые его вызывают, и модулей, которые вызываются из него.
8. Избегать ненужных меток и использовать оператор goto только для переходов на входную или выходную точку модуля, или вообще не использовать.
9. Идентификаторы всех переменных и модулей должны быть смысловыми.
10. Родственные группы идентификаторов должны начинаться с одинакового префикса.
11. Использовать только стандартные управляющие конструкции (выбор, цикл, выход, блок).
12. В одной строке записывать не более одного оператора. Если для записи оператора требуется больше, чем одна строка, то все последующие строки записываются с отступами.
13. Не допускать вложенности операторов If более 3-х уровней.
14. Избегать использования языковых конструкций с неочевидной семантикой и программистских трюков.
1.3 Объектно-ориентированная методология разработки программ
Объектно-ориентированное программирование (ООП) является более структурным, чем все предыдущие разработки, касающиеся структурного программирования. Оно также является более модульным и более абстрактным, чем предыдущие попытки абстрагирования данных и переноса деталей программирования на внутренний уровень. Объектно-ориентированное программирование – это методология программирования, которая основана на представлении программы в виде совокупности объектов, каждый из которых является реализацией определенного класса (типа особого вида), а классы образуют иерархию на принципах наследуемости. В основе ООП лежит идея объединения в одной структуре данных и действий, которые производятся над ними. Объект – структура, компонентами которой являются взаимосвязанные данные различных типов и использующие эти данные процедуры и функции. Данные называют полями объекта, а процедуры и функции объекта его методами. Логическая структура объекта приведена на рис. 1.
a : T1 |
b : T2 |
………. |
d : Tn |
адр. процедуры Р1 |
адр. функции F2 |
…………………….. |
адр. процедуры Рm |
Рис. 1. Логическая структура объекта.
Описание объектного типа выглядит следующим образом:
Type TO = Object
а : T1;
b : T2;
……
d : Tn;
Procedure P1 ( …….. );
Function F2 (………) : Tk;
……………………………
Procedure Pm(………);
End;
Var O : TO; {О - переменная объектного типа называется экземпляром}
Основные преимущества ООП:
1. В максимальной степени изолируется объект от внешнего окружения, что обеспечивает его независимость и скрытие информации внутри него. Отметим, что существующие ранее в языках программирования только такие синтаксические конструкции, как процедура и функция, не могли обеспечить надежного скрытия информации, поскольку подвержены влиянию глобальных переменных, поведение которых в сложных программах зачастую бывает трудно предсказуемым.
2. Повышается надежность программы, так как обмен данными с другими объектами и программой тщательно контролируется.
3. Модификация алгоритмов и данных объекта не влечет за собой плохо прослеживаемых связей.
ООП базируется на 3-х основных понятиях:
- инкапсуляция;
- наследование;
- полиморфизм.
Инкапсуляция – объединение информационных полей и методов, которые выполняют над ними действия, в единый объект. При этом данные и процедуры их обработки теряют самостоятельное значение.
Наследование – такое отношение между объектами, когда один объект повторяет структуру и поведение другого. Используется для иерархического упорядочения объектов. В модульном подходе сложный объект просто делится на части. Например, иерархическое описание такого сложного объекта как животное может быть осуществлено после его декомпозиции на части ( рис. 2). При объектно-ориентированной методологии декомпозиция может быть осуществлена по принципу наследования свойств объекта (рис.3).
Правила наследования.
1. Доступ к О-полям и О-методам родительских типов точно такой же, как если бы они были описаны в самом потомке.
2. Идентификаторы О-полей потомка не должны совпадать с идентификаторами О-полей родительских типов. Если в потомке описывается О-метод с тем же именем, что и у родителя, то он перекрывает (подавляет) его.
3. Любое изменение текста в родительском О-методе автоматически оказывает влияние на все О-методы порожденных типов, которые его вызывают.
Полиморфизм – свойство родственных объектов решать схожие по смыслу проблемы разными способами. Например, если абстрактный объект Фигура (рис. 5) обладает методом Draw прорисовки её на экране монитора, то все потомки типа Фигура могут иметь собственные методы Draw, перекрывающие аналогичный метод родителя.
Каждый
из этих
объектов
имеет свой
метод Draw
Таким образом, изменяя алгоритм О-метода в потомках можно придавать этим потомкам специфические свойства, отсутствующие у родителя. В результате в О-родителе и О-потомке будут действовать два одноименных О-метода, имеющие разную алгоритмическую основу – следовательно придающие объектам разные свойства – это и есть полиморфизм.
Переопределение методов родителя может осуществляться с помощью статических методов и динамических методов. Рассмотрим их отличие на примере простой ООП-программы.
Program OOP_Primer;
Uses CRT, Graph;
Type TFigure = Object
x, y :Integer; { координаты фигуры}
color :Word; { и её цвет}
Procedure Init ( ax, ay :Integer; col :Word );
Procedure Draw ( col :Word );
Procedure Show;
Procedure Hide;
Procedure MoveTo ( dx, dy :Integer );
End;
TPoint = Object ( TFigure ) { TFigure – имя родительского типа }
Procedure Draw ( col :Word );
End;
TCircle = Object ( TPoint )
R :Integer; { радиус окружности }
Procedure Init (ax,ay,aR :Integer; col :Word );
Procedure Draw ( col :Word );
End;
{ реализация методов TFigure }
Procedure TFigure. Init ( ax, ay :Integer; col :Word ); { инициализация фигуры }
Begin x := ax; y := ay; color := col; End;
Procedure TFigure. Draw ( col :Word ); { рисование фигуры }
Begin {ничего не делает, потомки должны перекрывать этот метод} End;
Procedure TFigure. Show; { рисование фигуры цветом color}
Begin Draw ( Color ); End;
Procedure TFigure. Hide; {стирание фигуры }
Begin Draw ( GetBkColor ); End;
Procedure TFigure. MoveTo ( dx, dy :Integer ); { перемещение фигуры }
Begin Hide; x := x+dx; y := y+dy; Show; End;
{ реализация методов TPoint }
Procedure TPoint. Draw ( col :Word ); { рисование точки }
Begin PutPixel ( x, y, col ); End;
{ реализация методов TCircle }
Procedure TCircle. Init (ax,ay,aR :Integer; col :Word ); { инициализация окружности }
Begin Inherited Init ( ax, ay, col ); R := aR; End;
procedure TCircle. Draw ( col :Word ); { рисование окружности }
Begin SetColor ( col ); Circle ( x, y, R ); End;
{ $I GrInit }
Var VCircle : TCircle;
Begin
GrInit;
VCircle. Init ( 320, 240, 100, 12 );
VCircle. Show;
ReadKey;
CloseGraph;
End.
Хотя эта программа составлена синтаксически правильно и должна нарисовать окружность на экране, в действительности этого не будет. Дело в том, что вызовы методов и их связи установлены статически на этапе трансляции и в дальнейшем не изменяются. Поэтому, при выполнении метода VCircle. Show будет вызываться метод TFigure. Draw, а не метод TCircle. Draw. Связи между методами в этой программе показаны на рис. 6.
Головная программа TFigure. Show TFigure. Draw
TPoint. Draw
T
Статическая связь TCircle. Draw
Динамическая связь –
необходимая для правильного
выполнения программы.
Рис. 6. Пример раннего ( во время трансляции ) связывания.
При использовании статических методов транслятор выполняет следующие действия по связыванию О-методов:
1. Устанавливает тип объекта, вызывающий данный О-метод.
2. Производит поиск О-метода в рамках данного типа. Если О-метод найден, то назначается вызов этого О-метода, иначе выполняется п.3.
3. Продолжает поиск О-метода в прародительских типах вверх по иерархии. Если поиск успешен, то назначается вызов, иначе – ошибка.
Из этого следует вывод: если О-метод прародителя вызывает другие методы, то они будут методами прародителя, даже если потомки имеют собственные подобные методы.
Объект VСircle имеет собственный метод Draw, однако его унаследованный метод Show жестко (статически) связан с методом Draw объектного типа TFigure, который ничего не рисует. Конечно, можно снабдить всех потомков собственными статическими методами Show и Hide, однако в объектно-ориентированном программировании реализуется более эффективный подход – механизм позднего связывания и виртуальные методы.
Таким образом, для реализации полиморфизма в языке программирования требуются не статическое, а динамическое (во время выполнения программы) связывание экземпляров объектов с методами, которые поддерживают полиморфические действия.
Виртуальные методы ( V-методы).
1. Реализуют свойство полиморфизма объектов.
2. Если О-метод объявлен виртуальным, то все потомки должны описываться виртуальными ( т.е. статический метод никогда не может подавить виртуальный).
3. V-метод в заголовке содержит директиву Virtual, например:
Procedure P1 (……………..); Virtual;
4. Если V-метод переопределяется у потомков, то заголовок (имя, порядок, тип и количество параметров) не должны изменяться. Иначе у статических методов – у них можно изменять формальные параметры.
5. Каждый О-тип, имеющий V-методы, обязан иметь Constructor инициализирующий экземпляр объекта. Constructor – специальный тип процедуры, который выполняет начальные установки для работы механизма V-методов. Каждый отдельный экземпляр объекта должен быть инициализирован отдельным вызовом Constructor’a. При этом Constructor устанавливает связь экземпляра с таблицей виртуальных методов VMT. Constructor всегда вызывается до первого вызова V-метода. Сам Constructor статический, хотя может содержать вызовы V-методов.
6. Каждый О-тип, содержащий V-методы, имеет свою таблицу VMT. Структура VMT и ее взаимодействие с объектами и V-методами приведена на рис. 7.
Constructor VMT
Размер типа |
Адрес Р1 |
Адрес Р2 |
…………… |
Адрес Рn |
Рис. 7. Таблица виртуальных методов VMT.
Таблица VMT служит для передачи управления тому методу, который соответствует вызову определенного объекта. По сути VMT является таблицей адресов для косвенного перехода. При этом каждый из методов не является какой-то независимой процедурой или функцией Паскаля – любой из методов может вызываться только из какого-либо объекта, причем при каждом таком вызове в метод всегда передается невидимый адрес (SELF) вызывающего объекта, поэтому метод будет работать с полями того объекта, который его вызвал. VMT создается одна для каждого О-типа (но не экземпляра). При передаче в процедуру или функцию полиморфного объекта, имеющего V-методы, передается и таблица VMT. Это гарантирует, что сработают именно те методы, которые подразумевались при вызове.
Рассмотрим работу программы рисования различных фигур после придания ей полиморфических свойств.
Program OOP_Primer;
Uses CRT, Graph;
Type TFigure = Object
x, y :Integer; { координаты фигуры}
color :Word; { и её цвет}
Constructor Init ( ax, ay :Integer; col :Word );
Procedure Draw ( col :Word ); Virtual;
Procedure Show;
Procedure Hide;
Procedure MoveTo ( dx, dy :Integer );
End;
TPoint = Object ( TFigure ) { TFigure – имя родительского типа }
Procedure Draw ( col :Word ); Virtual;
End;
TCircle = Object ( TPoint )
R :Integer; { радиус окружности }
Constructor Init (ax,ay,aR :Integer; col :Word );
Procedure Draw ( col :Word ); Virtual;
End;
{ реализация методов TFigure }
Constructor TFigure. Init ( ax, ay :Integer; col :Word );
Begin x := ax; y := ay; color := col; End;
Procedure TFigure. Draw( col :Word );
Begin {ничего не делает, потомки должны перекрывать этот метод} End;
Procedure TFigure. Show;
Begin Draw ( Color ); End;
Procedure TFigure. Hide;
Begin Draw ( GetBkColor ); End;
Procedure TFigure. MoveTo ( dx, dy :Integer ):
Begin Hide; x := x+dx; y := y+dy; Show; End;
{ реализация методов TPoint }
Procedure TPoint. Draw( col :Word );
Begin PutPixel ( x, y, col ); End;
{ реализация методов TCircle }
Constructor TCircle. Init;
Begin Inherited Init ( ax, ay, col ); R := aR; End;
Procedure TCircle. Draw( col :Word );
Begin SetColor ( col ); Circle ( x, y, R ); End;
{ $I GrInit }
Var VCircle : TCircle;
Begin
GrInit;
VCircle. Init ( 320, 240, 100, 12 );
VCircle. Show;
ReadKey;
CloseGraph;
End.
В этом варианте программа нарисует на экране окружность. Действительно, при работе метода VCircle. Init Constructor создаст таблицу VMT для типа TСircle и экземпляр VCircle установит с ней связь. При вызове метода Show из объекта VCircle последует передача управления унаследованному методу TFigure. Show, причем следует иметь в ввиду два момента:
1. Связь с методом TFigure. Show из головной программы установлена статически (во время трансляции).
2. При вызове этого метода в качестве невидимого фактического параметра предается адрес объекта VCircle, который в свою очередь имеет связь с VMT типа TCircle.
Связь с виртуальным методом TCircle.Draw осуществляется путем косвенного перехода через таблицу VMT объектного типа TCircle, которая и рисует окружность. Аналогично будет работать механизм связи с виртуальными методами, если в описанную иерархию фигур добавлять новые фигуры со своими виртуальными методами Draw.
Все приведенные до сих пор экземпляры объектов были статического типа (не путать со статическими методами!), они описывались зарезервированным словом Var, им присваивались имена, и сами объекты размещались в сегменте данных или в стеке. Но точно так же, как и любые типы данных в Паскале, объекты можно размещать в динамической памяти и работать с ними, применяя указатели. Паскаль включает несколько усовершенствованных процедур для размещения и удаления объектов из памяти наиболее эффективными способами. Подробное их рассмотрение выходит за рамки данных методических указаний и предлагается изучить самостоятельно, например, по работе [1,2]. Отметим только, что Паскаль предоставляет специальный тип метода Destructor, называемый «сборщиком мусора» или деструктором, для очистки и удаления динамических методов. Деструктор объединяет этап удаления объекта с другими действиями, необходимыми для данного типа объекта. Деструктор размещается вместе с другими методами объекта в определении типа объекта. В приведенных ниже примерах используются только статические объекты, поэтому непосредственной необходимости в деструкторах нет. Тем не менее, они присутствуют в методах объектов для полноты картины и выполняют некоторые завершающие действия в программе.
3. Пример1 объектно-ориентированного программирования
3.1. Формулировка задачи. Рассмотрим процессы проектирования программы с использованием ООП - технологии на примере задачи качения квадрата по некоторой поверхности с отображением этого процесса на экране. Начнем с наиболее простого варианта решения этой задачи, а затем постепенно ее усложним. Сначала выберем в качестве поверхности качения плоскость, которая в боковой проекции на экране будет выглядеть как прямая линия. Общий вид сцены на экране будет выглядеть так, как на рис.8:
4 2
G1
Рис. 8. Качение квадрата по прямой линии G1-G2. (т.1 – текущая ось вращения; as – длина стороны квадрата ).
Основными структурными элементами (объектами) этого изображения являются точки и линии, и из них может быть построен квадрат и скомпонована вся сцена на экране, причем эти элементы образуют следующую иерархию:
Опишем эти объекты в соответствии с положениями ООП-программирования.
Const
kv = 4;
speed = 1.5;
one = pi/180; { 1 градус в радианах }
step = one*speed; { приращение угла поворота }
ms = 2000; { величина задержки при анимации }
Type TPoint = Object { О-тип - точка }
x,y :Real; { координаты точки }
Pcolor :Byte; { ее цвет }
Constructor Init ( xx,yy :Real; col :Byte );
Procedure Rotate ( xOs,yOs :Integer ); Virtual;
Procedure Show ( col :Byte ); Virtual;
Destructor Done;
End;
TLine = Object ( TPoint ) { О-тип - отрезок прямой }
pn, pk :TPoint; { нач. и кон. точки прямой }
Lcolor :Byte; { ее цвет }
Constructor Init ( x1,y1,x2,y2 :Real; col :Byte );
Procedure Rotate ( xOs,yOs :Integer ); Virtual;
Procedure Show ( col :Byte ); Virtual;
Destructor Done;
End;
TSides = Array [ 0..kv-1 ] Of TLine; {тип для описания сторон квадрата}
TSquare = Object ( TLine ) { О-тип - квадрат }
as :Byte; { размер стороны квадрата }
Sides :TSides; { стороны квадрата }
Scolor :Byte; { цвет квадрата }
Constructor Init ( aa, colK :Byte );
Procedure Rotate ( xOs,yOs :Integer ); Virtual;
Procedure Show ( col :Byte ); Virtual;
Destructor Done;
End;
TScreen = Object ( TSquare ) { О-тип - сцена }
Gdisp :Integer; { эффективное Y-смещение поверхности качения}
Gcolor :Byte; { цвет поверхности}
angle :Real; { угла поворота квадрата}
OsX,OsY :Integer; { текущее значения координат оси вращения }
Constructor Init ( aa, colK, colG :Byte; dG :Integer );
Procedure GraphInit; Virtual;
Function ShiftOsXY :Boolean; Virtual;
Procedure Go; Virtual;
Procedure DrawGround; Virtual;
Destructor Done;
End;
3.2. Проектирование структуры объектов.
Объект типа TPoint является точкой с координатами x,y вещественного типа и цветом Pcolor. Вещественный тип координат точки определяется тем , что этот объект предназначен для описания вершин квадрата, а при вращении они могут принимать вещественные значения. Кроме очевидных необходимых методов Init, Show и Destructor добавим в их число и виртуальный метод Rotate реализующий плоское вращение точки вокруг заданной оси. Координаты оси вращения имеют целый тип и задаются извне (являются входными параметрами метода). Целые значения координат оси обусловлены тем, что размер as квадрата в дальнейшем будем выбирать среди целых значений.
Объект типа TLine наследует все поля и методы TPoint, но перекрывает своими виртуальными методами Show и Rotate соответствующие методы родителя. Отметим, что перекрываются и другие одноименные методы (например, Constructor Init, Destructor), но как указывалось ранее, в программе с ними будет установлена статическая связь и они останутся вне рамок, обеспечивающих полиморфизм объектов. Обратите внимание, что заголовки виртуальных методов должны оставаться неизменными. Тип TLine, кроме унаследованных полей x и y, содержит два поля pn и pk типа TPoint, которые являются объектами и описывают две точки, задающих отрезок прямой на плоскости – будущую сторону квадрата. Виртуальные методы Show и Rotate позволяют отображать этот отрезок на экране цветом Lcolor и поворачивать его вокруг заданной оси. На первый взгляд, непонятна роль унаследованных полей x и y. Тем более, что можно было в типе TLine обойтись только одним полем pn, а координаты конца отрезка поместить в x и y. Однако, такой подход привел бы к разным синтаксическим конструкциям при обращении к начальной и конечной точкам отрезка, что повлияло бы на единообразие стилистики программы и затруднило бы ее восприятие. Кроме того, потомки данного типа могут использовать эти поля для своих специфических нужд, например, помещая в них координаты середины отрезка.
Объектный тип TSquare (квадрат) конструируется из 4-х отрезков типа ТLine, длины стороны квадрата as и переменной Scolor для задания его цвета, а виртуальные методы Show и Rotate обеспечивают его полиморфические свойства отображения и поворота.
Тип TScreen, находясь на самом нижнем уровне иерархии описывает самый сложный объект – экран , со всеми его «действующими лицами»: катящимся квадратом и поверхностью качения. Кроме квадрата, унаследованного из родительского типа, в данном типе имеются О-поля для задания положения и цвета поверхности качения (поля Gdisp и Gcolor) и соответствующий метод DrawGround ее прорисовки на экране, а также поля OsX,OsY и angle для текущих координат положения оси вращения и текущего значения угла поворота квадрата соответственно. Метод GraphInit инициализирует графический режим монитора VGAHi и сделан виртуальным, так как тип TScreen сам, в дальнейшем, может использоваться как родительский для создания других объектов, использующих другие графические режимы. Метод ShiftOsXY отвечает за своевременное перемещение оси вращения квадрата при его качении по поверхности и тоже является виртуальным, поскольку условия перемещения оси вращения квадрата зависят от рельефа качения и в потомках может возникнуть необходимость его модификации. Вообще, во всех случаях, когда возникают сомнения относительно того, сделать метод статическим или виртуальным, лучше сделать выбор в пользу виртуальности, поскольку «все течет – все изменяется» и нельзя исключать, что в дальнейшем данный объект, уже в другой Вашей программе, может пригодиться как прародительский и сэкономит Ваши время и усилия. Метод Go реализует основные действия при качении квадрата: анимацию, регенерацию сцены при достижении квадратом края экрана, реакцию на завершение программы и т.п.
3.3. Проектирование методов объектов.
Методы TPoint достаточно простые: Constructor Init заполняет О-поля объекта; процедура Rotate вычисляет новые координаты точки по формулам геометрического поворота вокруг заданной оси на угол step, величина которого зависит от требуемой скорости вращения объекта и процедура Show скорее служит прототипом для дальнейшего наследования, чем для самостоятельного использования. Роль Destructor’a обсуждалась ранее.
В Constructor’e TLine обратите внимание, что объекты pn и pk инициализируются отдельно, поскольку каждый из них должен быть связан с таблицей VMT типа TPoint, а в методе Show значение входного параметра col определяет рисование или стирание линии.
Аналогично осуществляется инициализация сторон квадрата в TSquare и
перекрытие виртуальных методов Show и Rotate собственными. После инициализации положение квадрата соответствует верхнему левому углу экрана.
(******** Методы TPoint ********************)
Constructor TPoint .Init ( xx, yy :Real; col :Byte );
Begin x:=xx; y:=yy; Pcolor := col; End;
{-----------------------------
Procedure TPoint .Rotate ( xOs,yOs :Integer );
Var xx, yy :Real;
Begin xx := (x - xOs)*Cos(step) - (y - yOs)*Sin(step) + xOs;
yy := (x - xOs)*Sin (step) + (y - yOs)*Cos(step) + yOs;
x :=xx; y:=yy;
End;
{-----------------------------
Procedure TPoint .Show ( col :Byte );
Begin PutPixel ( Round(x), Round(y), Pcolor ); End;
{-----------------------------
Destructor TPoint .Done;
Begin End;
(******** Методы TLine ********************)
Constructor TLine .Init ( x1,y1,x2,y2 :Real; col :Byte );
Begin pn.Init(x1,y1,col); pk.Init(x2,y2,col); Lcolor:=col; End;
{-----------------------------
Procedure TLine .Rotate ( xOs,yOs :Integer );
Begin pn.Rotate( xOs,yOs ); pk.Rotate( xOs,yOs ); End;
{-----------------------------
Procedure TLine .Show ( col :Byte );
Begin If col=0 Then SetColor ( col ) Else SetColor ( Lcolor ) ;
Line(Round(pn.x),Round(pn.y),
End;
{-----------------------------
Destructor TLine .Done;
Begin End;
(***************** Методы TSquare ****************************)
Constructor TSquare .Init ( aa, colK :Byte );
Begin
as := aa;
Sides[0]. Init ( as, as, 0, as, colK ); { инициализация сторон квадрата }
Sides[1]. Init ( 0, as, 0, 0, colK );
Sides[2]. Init ( 0, 0, as, 0, colK );
Sides[3]. Init ( as, 0, as, as, colK );
Scolor := colK;
End;
{-----------------------------
Procedure TSquare .Rotate ( xOs, yOs :Integer );
{ реализует вращение квадрата путем поворота каждой из его сторон вокруг оси }
Var i :Byte;
Begin For i:=0 To kv-1 Do Sides[i] .Rotate ( xOs,yOs ); End;
{-----------------------------
Procedure TSquare .Show( col :Byte ); { рисует(стирает) изображение квадрата }
Var i :Byte;
Begin For i := 0 To kv-1 Do Sides[i].Show ( col ); End;
{-----------------------------
Destructor TSquare .Done;
Begin End;
(***************** Методы TScreen ******************************
Constructor TScreen .Init ( aa, colK, colG :Byte; dG :Integer );
Var i :Byte;
Begin
GraphInit;
Inherited Init ( aa, colK ); { инициализация родителя }
Gdisp := dG; { задание Y-смещения поверхности качения }
For i := 0 To kv-1 Do With Sides[i] Do Begin {перенос квадрата на поверхность}
pn.y := pn.y + Gdisp - as;
pk.y := pk.y + Gdisp - as;
End;
Gcolor := colG; { задание цвета поверхности качения }
OsX := as; OsY := Gdisp; { задание начальных координат оси вращения }
angle := 0; { задание начального значения угла поворота }
DrawGround;
End;
{-----------------------------
Procedure TScreen .GraphInit; { инициализация графического режима VGAHi }
Var gd, gm, ErrorCode :Integer;
Begin gd := Detect;
InitGraph ( gd, gm, '');
ErrorCode := GraphResult;
If ErrorCode <> grOk Then Begin
Writeln('Ошибка графики:', GraphErrorMsg ( ErrorCode ) );
Halt(1);
End;
End;
{-----------------------------
Procedure TScreen .DrawGround; { рисование поверхности качения }
Begin SetColor ( Gcolor );
Line ( 0, Round( Gdisp + 1 ), GetMaxX, Round( Gdisp + 1 ) );
End;
{-----------------------------
Function TScreen .ShiftOsXY :Boolean;
{ определяет момент и реализует смещение оси вращения квадрата по оси X}
Begin If angle > pi/2 { если наступил момент переноса оси поворота, }
Then Begin OsX := OsX + as; ShiftOsXY := True; End { то сместить ось по X на as}
Else ShiftOsXY := False;
End;
{-----------------------------
Procedure TScreen .Go; {реализует движение квадрата и анимацию его изображения}
Begin
Repeat { цикл возобновления сцены }
Repeat { цикл качения по поверхности и анимации }
angle := angle + step; { накопление угла поворота}
If ShiftOsXY { если была смена оси вращения, }
Then Begin angle:=0; Continue; End; { то пропустить вращение и анимацию }
Rotate ( OsX, OsY ); { вращение квадрата вокруг текущей оси }
Show(Scolor); { рисует изображение квадрата }
Delay( ms ); { задержка }
Show(0);
If KeyPressed Then Exit; { если клавиша нажата, то выход из процедуры}
Until OsX > GetMaxX; { если квадрат достиг правого края экрана, то }
Init ( as, Scolor, Gcolor, Gdisp ); { возобновление сцены }
DrawGround;
Until False;
End;
{-----------------------------
Destructor TScreen .Done;
Begin CloseGraph; End; {закрытие графического режима }
{-----------------------------
Более сложные действия выполняются в Constructor’e TScreen. После перехода в графический режим осуществляется инициализация квадрата оператором Inherited Init ( aa, colK ); , где ключевое слово Inherited означает – унаследованный, то есть происходит вызов Constructor’a типа TSquare. Отметим, что вызов унаследованного Constructor’a при проектировании Constructor’ов дочерних типов считается хорошим стилем в ООП-программировании. Далее квадрат «устанавливается» на поверхность качения путем коррекции координат Y его вершин на величину смещения этой поверхности на экране. Обратите внимание на оператор With, который, как и при обработке записей, позволяет вынести имя объекта в заголовок и избавляет от необходимости писать длинные составные имена. После этого назначаются координаты оси вращения OsX, OsY, совпадающие с координатами вершины 1 квадрата (см. рис. 8). Поскольку рисунок рельефа является статической частью сцены и, при движении квадрата остается неизменной, в конце процедуры следует прорисовка поверхности виртуальным методом DrawGround. Виртуальность метода обусловлена возможностью изменения формы (а значит и метода прорисовки) поверхности качения у потомков. По этой же причине виртуальны метод ShifOsXY( контролирующий положение оси вращения по мере движения квадрата по поверхности) и метод Go. В последнем реализуются основные действия, происходящие на экране. Метод состоит из двух вложенных циклов: во внутреннем моделируется качение квадрата начиная с левого края до правого края экрана и анимация его изображения, во внешнем – обновление всей сцены и повторение указанных действий. Остальные подробности алгоритмов описанных методов даны в приведенных их текстах.
3.4. Реализация программы.
Описание объектов удобно поместить в отдельный модуль SQUnit, который может быть подключен к разрабатываемой программе.
INTERFACE
Const
kv = 4; { кол-во вершин квадрата }
speed = 1.5; { коэфф. скорости перемещения квадрата }
one = pi/180; { 1 градус в радианах }
step = one*speed; { приращение угла поворота }
ms = 2000; { величина задержки при анимации }
Type TPoint = Object
x,y :Real; { координаты точки }
Pcolor :Byte; { ее цвет }
Constructor Init ( xx,yy :Real; col :Byte );
Procedure Rotate ( xOs,yOs :Integer ); Virtual;
Procedure Show ( col :Byte ); Virtual;
Destructor Done;
End;
TLine = Object ( TPoint )
pn, pk :TPoint; { нач. и кон. точки прямой }
Lcolor :Byte; { ее цвет }
Constructor Init ( x1,y1,x2,y2 :Real; col :Byte );
Procedure Rotate ( xOs,yOs :Integer ); Virtual;
Procedure Show ( col :Byte ); Virtual;
Destructor Done;
End;
TSides = Array [ 0..kv-1 ] Of TLine; {тип для описания сторон квадрата}
TSquare = Object ( TLine )
as :Byte; { размер стороны квадрата }
Sides :TSides; { стороны квадрата }
Scolor :Byte; { цвет квадрата }
Constructor Init ( aa, colK :Byte );
Procedure Rotate ( xOs,yOs :Integer ); Virtual;
Procedure Show ( col :Byte ); Virtual;
Destructor Done;
End;
TScreen = Object ( TSquare ) { О-тип - сцена };
Gdisp :Integer; { эффективное Y-смещение поверхности качения}
Gcolor :Byte; { цвет поверхности}
angle :Real; { угол поворота квадрата}
OsX,OsY :Integer; { текущее значения координат оси вращения }
Constructor Init ( aa, colK, colG :Byte; dG :Integer );
Procedure GraphInit; Virtual;
Function ShiftOsXY :Boolean; Virtual;
Procedure Go; Virtual;
Procedure DrawGround; Virtual;
Destructor Done;
End;
Uses Crt, Graph;
(******** Методы TPoint ********************)
Constructor TPoint .Init ( xx, yy :Real; col :Byte );
Begin x:=xx; y:=yy; Pcolor := col; End;
{-----------------------------
Procedure TPoint .Rotate ( xOs,yOs :Integer );
Var xx, yy :Real;
Begin xx := (x - xOs)*Cos(step) - (y - yOs)*Sin(step) + xOs;
yy := (x - xOs)*Sin (step) + (y - yOs)*Cos(step) + yOs;
x :=xx; y:=yy;
End;
{-----------------------------
Procedure TPoint .Show ( col :Byte );
Begin PutPixel ( Round(x), Round(y), Pcolor ); End;
{-----------------------------
Destructor TPoint .Done;
Begin End;
(******** Методы TLine ********************)
Constructor TLine .Init ( x1,y1,x2,y2 :Real; col :Byte );
Begin pn.Init(x1,y1,col); pk.Init(x2,y2,col); Lcolor:=col; End;
{-----------------------------
Procedure TLine .Rotate ( xOs,yOs :Integer );
Begin pn.Rotate( xOs,yOs ); pk.Rotate( xOs,yOs ); End;
{-----------------------------
Procedure TLine .Show ( col :Byte );
Begin If col=0 Then SetColor ( col ) Else SetColor ( Lcolor ) ;
Line(Round(pn.x),Round(pn.y),
End;
{-----------------------------
Destructor TLine .Done;
Begin End;
(***************** Методы TSquare ****************************)
Constructor TSquare .Init ( aa, colK :Byte );
Begin
as := aa;
Sides[0]. Init ( as, as, 0, as, colK ); { инициализация сторон квадрата }
Sides[1]. Init ( 0, as, 0, 0, colK );
Sides[2]. Init ( 0, 0, as, 0, colK );
Sides[3]. Init ( as, 0, as, as, colK );
Scolor := colK;
End;
{-----------------------------
Procedure TSquare .Rotate ( xOs, yOs :Integer );
{ реализует вращение квадрата путем поворота каждой из его сторон}
Var i :Byte;
Begin For i:=0 To kv-1 Do Sides[i] .Rotate ( xOs,yOs ); End;
{-----------------------------
Procedure TSquare .Show( col :Byte ); { рисует(стирает) изображение }
Var i :Byte;
Begin For i := 0 To kv-1 Do Sides[i].Show ( col ); End;
{-----------------------------
Destructor TSquare .Done;
Begin End;
(***************** Методы TScreen ******************************
Constructor TScreen .Init ( aa, colK, colG :Byte; dG :Integer );
Var i :Byte;
Begin
GraphInit;
Inherited Init ( aa, colK ); { инициализация родителя }
Gdisp := dG; { задание Y-смещения поверхности качения }
For i := 0 To kv-1 Do With Sides[i] Do Begin {перенос квадрата на
pn.y := pn.y + Gdisp - as;
pk.y := pk.y + Gdisp - as;
End;
Gcolor := colG; { задание цвета поверхности качения }
OsX := as; OsY := Gdisp; { задание начальных координат оси вращения }
angle := 0; { задание начального значения угла поворота }
DrawGround; { рисование поверхности качения }
End;
{-----------------------------
Procedure TScreen .GraphInit;{ инициализация графич. режима VGAHi }
Var gd, gm, ErrorCode :Integer;
Begin
If GetGraphMode = 2 Then Exit;{ если графич. режим вкл., то выход }
gd := Detect;
InitGraph ( gd, gm, '');
ErrorCode := GraphResult;
If ErrorCode <> grOk Then Begin
Writeln('Ошибка графики:', GraphErrorMsg ( ErrorCode ) );
Halt(1);
End;
End;
{-----------------------------
Procedure TScreen .DrawGround; { рисование поверхности качения }
Begin SetColor ( Gcolor );
Line ( 0, Round( Gdisp + 1 ), GetMaxX, Round( Gdisp + 1 ) );
End;
{-----------------------------
Function TScreen .ShiftOsXY :Boolean;
{опред-т момент и реализует смещение оси вращения квадрата по оси X}
Begin
If angle > pi/2 { если наступил момент переноса оси поворота, }
Then Begin OsX := OsX + as; { то сместить ось по X на as}
ShiftOsXY := True; End
Else ShiftOsXY := False;
End;
{-----------------------------
Procedure TScreen .Go; {реализует движение квадрата и анимацию его}
Begin
Repeat { цикл возобновления сцены }
Repeat { цикл качения по поверхности и анимации }
angle := angle + step; { накопление угла поворота}
If ShiftOsXY { если была смена оси вращения, то пропустить }
Then Begin angle:=0; Continue; End; { вращение и анимацию }
Rotate ( OsX, OsY ); { вращение квадрата вокруг текущей оси }
Show(Scolor);
Delay( ms );
Show(0); { стирает изображение квадрата }
If KeyPressed Then Exit; { если клавиша нажата, то выход из процедуры}
Until OsX > GetMaxX; { если квадрат достиг правого края экрана, то }
Init ( as, Scolor, Gcolor, Gdisp ); { возобновление сцены }
DrawGround;
Until False; { повторение работы до нажатия любой клавиши }
End;
{-----------------------------
Destructor TScreen .Done;
Begin CloseGraph; End; {закрытие графического режима }
{-----------------------------
End. {конец модуля SQUnit}
{*********** Головная программа ***************************}
Program Primer_OOP;
Uses SqUnit; { подключение модуля с описанием объектов }
Const sizeSq = 80; { размер квадрата }
colorSq = 12; { и его цвет }
colorG = 2; { цвет поверхности качения }
deltaG = 400; { смещение по Y поверхности качения на экране}
Var Screen :TScreen;
Begin
Screen .Init ( sizeSq, colorSq, colorG, deltaG ); {инициализация}
Screen .DrawGround; { прорисовка поверхности качения }
Screen .Go;
Screen .Done;
End.
{*****************************
Головная программа содержит единственную переменную-объект Screen, обладающая всеми свойствами, необходимыми для решения поставленной задачи. Таким образом, демонстрируется стиль ООП-программирования, исключающий использование многих глобальных переменных и строгую локализацию данных и действий рамками отдельных объектов, что является основой высокой надежности программы.
3.5. Модификация программы.
Следующий пример демонстрирует возможности ООП-программирования при разработке новых программ на основе ранее созданных. Свойства наследования и полиморфизма позволяют существенно экономить трудозатраты за счет использования ранее созданных объектов. Пусть ставится задача моделирования качения квадрата по некоторой, достаточно гладкой, криволинейной поверхности (рельефу)(рис. 10). Новый объект TScreen1, удовлетворяющий условиям задачи, может быть получен наследованием из объекта TScreen. Он содержит дополнительное поле Ground в виде целочисленного массива ординат рельефа и поле Sides0 для промежуточного хранения предыдущих координат сторон квадрата в процессе качения. Очевидно, что должны быть переопределены методы Init, DrawGround, ShiftOsXY и Go. Кроме того, понадобятся новые методы CalcABC и Dist для реализации подалгоритмов переопределяемых методов.
2
G1
1
Рис. 10. Качение квадрата по криволинейной поверхности.
Далее приведен текст программы, полученный указанным способом.
{*********** Качение квадрата по заданному рельефу ************}
Program PrimerМ_OOP;
Uses SqUnit, Crt, Graph;
Const sizeSq = 80; colorSq = 12; colorG = 2; deltaG = 400; n=1000;
Type TScreen1 = Object( TScreen )
Ground :Array [ 0..n-1 ] Of Integer; { массив для рельефа}
Sides0 :TSides; { буфер сохранения квадрата }
Constructor Init ( aa, colK, colG :Byte; dG :Integer );
Procedure DrawGround; Virtual;
Procedure CalcABC ( Var S1,S2 :TLine; Var A,B,C :Real );
Function Dist( A, B, C, xx, yy :Real) :Real; Virtual;
Function ShiftOsXY :Boolean; Virtual;
Procedure Go; Virtual;
Destructor Done;
End;
Var Screen1 :TScreen1;
(***************** Методы TScreen1 *****************************)
{-----------------------------
Procedure TScreen1 .DrawGround; { рисование рельефа на экране }
Var i :Integer;
Begin
SetColor(colorG); ClearDevice;
For i:=0 To 640 Do Begin MoveTo(i,Ground[i]); LineTo(i,GetMaxY); End;
End;
{-----------------------------
Constructor TScreen1 .Init ( aa,colK,colG :Byte; dG :Integer );
Var i, j, A1, T1, D1, A2, T2, D2: Integer;
Begin
Randomize; { генерация амплитуды, частоты и фазы гармоник }
A1:=Random(45)+5; T1:=Random(40)+20; D1:=Random(T1);
A2:=Random(25)+5; T2:=Random(40)+20; D2:=Random(T2);
For i:=0 To n-1 Do { цикл заполнения ординат рельефа, dG-смещение }
Ground[i] := dG + Round(A1 * Sin( i / T1 + D1) + A2 * Sin( i / T2 + D2 ));
Gdisp := MaxInt;
For i := 0 To aa Do { поиск места начальной установки квадрата }
If Ground[i] < Gdisp Then Begin Gdisp := Ground[i]; j:=i; End;
Inherited Init ( aa, colK, colG, Gdisp-1 ); { инициализация квадрата }
OsX:=j;
DrawGround; { нарисовать рельеф }
End;
{-----------------------------
Function TScreen1 .Dist( A,B,C, xx,yy :Real) :Real;
{ ф-ция расстояния между прямой Ax+By+C=0 и точкой(xx,yy) }
Begin Dist := Abs((A*xx+B*yy+C) / Sqrt(A*A+B*B)); End;
{-----------------------------
Procedure TScreen1 .CalcABC( Var S1,S2 :TLine; Var A,B,C :Real );
{ вычисление параметров A,B,C уравнения прямой, проходящей через центр } { квадрата параллельно двум его противоположным сторонам }
Var xn,yn,xk,yk :Real;
Begin xn := (S1.pn.x+S2.pk.x)/2; yn := (S1.pn.y+S2.pk.y)/2;
xk := (S1.pk.x+S2.pn.x)/2; yk := (S1.pk.y+S2.pn.y)/2;
A := yk - yn; B := xn - xk; C := xk * yn - xn * yk;
End;
{-----------------------------
Function TScreen1 .ShiftOsXY :Boolean;
{ Если в процессе качения какая-либо точка квадрата переходит границу } { рельефа, то функция смещает ось вращения и возвращает True }
Var Ax, Bx, Cx, Ay, By, Cy, xx, yy :Real; i :Integer;
Begin
ShiftOsXY := False;
{ вычисление пар-ров прямых – осей локальной системы координат x10y1 }
CalcABC( Sides[1], Sides[3], Ax, Bx, Cx );
CalcABC( Sides[0], Sides[2], Ay, By, Cy );
For i := OsX + 1 To OsX + 3*as Div 2 Do { перебор точек рельефа }
Begin
yy := Dist( Ay, By, Cy, i, Ground[i] ); { координаты i-ой точки рельефа }
xx := Dist( Ax, Bx, Cx, i, Ground[i] ); { в лок-ой системе x10y1 }
If ( xx <= as Div 2 + 1 ) And ( yy <= as Div 2 + 1 ) { если точка рельефа }
Then Begin
Sides := Sides0; { восст-ть предыд. положение квадрата }
OsX := i; OsY := Ground[i]; {и сместить ось вращения }
ShiftOsXY := True;
Exit;
End;
End;
End;
{-----------------------------
Procedure TScreen1 .Go; { моделирует движение квадрата }
Begin
Repeat
Repeat
Repeat
Sides0 := Sides; { запоминание текущих коорд-т квадрата в буфере}
Rotate ( OsX, OsY ); { вращение квадрата вокруг текущей оси }
Until Not ShiftOsXY; { если была смена оси вращения, то пропустить }
Show ( Scolor ); { рисует изображение квадрата }
Delay ( ms ); { задержка }
Show ( 0 ); { стирает изображение квадрата }
If KeyPressed Then Exit; { если клавиша нажата, то выход из процедуры}
Until OsX > GetMaxX; { если квадрат достиг правого края экрана, то }
Init ( as, Scolor, Gcolor, deltaG );
Until False; { повторение работы до нажатия любой клавиши }
End;
{-----------------------------
Destructor TScreen1 .Done;
Begin Inherited Done; End;
{-----------------------------
{*********** Головная программа ****************************}
Begin
With Screen1 Do Begin
Init ( sizeSq, colorSq, colorG, deltaG );
Go;
Done;
End;
End.
{*****************************
В constructor’е Init в начале генерируются точки рельефа путем сложения нескольких гармоник со случайными параметрами: амплитудой, частотой и фазой. Затем квадрат устанавливается в свое начальное положение. Для этого, среди первых аа точек (аа - размер стороны квадрата) массива Ground, находится минимальное значение Gdisp, которое определяет положение нижней стороны квадрата. По ней вычисляются положения остальных сторон вызовом constructor’а Init родительского типа. Значение минимума Gdisp и его смещение в массиве Ground определяют положение точки контакта квадрата с поверхностью качения, следовательно и начальные координаты OsX, OsY оси вращения. После прорисовки рельефа на экране constructor Init завершает свою работу. Следующим важным для работы программы является метод ShiftOsXY, который контролирует контакт с рельефом и своевременно смещает ось вращения в новую позицию. Для определения момента пересечения квадрата поверхности рельефа после очередного поворота его на небольшой угол step проводятся испытания всех точек рельефа в диапазоне от OsX+1 до OsX + 1.5*as на предмет попадания их во внутрь квадрата. С этой целью вычисляются координаты каждой из этих точек в локальной системе координат X10Y1 квадрата (см. рис.10). Указанный алгоритм реализуется методами CalcABC и Dist. В методе CalcABC вычисляются параметры прямой Ax+By+C=0 – одной из осей локальной системы координат X10Y1, а в методе Dist вычисляется расстояние (координата) точки до соответствующей оси по формуле:
Dist=,
Если каждая из полученных координат по абсолютной величине меньше половины длины стороны квадрата, то данная точка рельефа попадает во внутрь квадрата, поэтому данное положение квадрата не отображается на экране, а восстанавливаются предыдущие координаты квадрата (оператор Sides:=Sides0) и ось вращения перемещается в отмеченную ранее точку. Факт смены оси вращения отмечается в результате функции ShiftOsXY значением True. Метод Go обеспечивает перемещение квадрата и его отображение на экране, а алгоритм практически не отличается от прототипа в TScreen. Как видим, получение новой версии программы не потребовало существенных трудозатрат за счет использования преимуществ объектно-ориентированного программирования. Возможны дальнейшие модификации программы, например, перевести ее в режим двухстраничного отображения, что устранит мелькания изображения на экране. Для более углубленного изучения ОО-программирования предлагается еще один более сложный пример.
4. Пример2 объектно-ориентированного программирования
4.1. Задача. Создать движущуюся модель шагающего человека.
Рис.11. Движущаяся модель шагающего человека.
Создание программы начнем с разработки иерархии объектов для данной задачи.
1. Формально наиболее динамичной и универсальной «деталью» фигуры человека являются ее конечность, поэтому в качестве базового объекта выберем объект «Noga».
2. Запрограммируем с помощью соответствующих виртуальных методов ( SETXY, ROTATE, POVOROT и др. ) характерные движения таких конечностей как ноги (1-я и 2-я) и руки (1-я и 2-я).
3. Продолжая наследование получим голову, туловище и, наконец, общую сцену на экране с движущимся человеком.
4. Обратите внимание на то, что описательная часть программы содержит только константы, описание объектных типов и одну единственную глобальную переменную SCREEN.
Unit Men;
Interface
Const ksn = 0.01; { коэф-т сгибания ноги }
ksr = 0.005; { коэф-т сгибания руки }
kc = 2; { коэф-т скорости 1..2 }
kb = 16; { коэф-ты
cb = 360 Div kb; { аппроксимации эллипса-головы }
one = kc*pi/180; { один градус - шаг поворота конечности }
yst = 200; { высота картинки }
k = 0.775; { масш. коэф-т по Y для режима VGAMed}
Type TNoga = Object { базовый тип для конечностей }
num :Byte; { номер конечности }
Bedro :Real; { размер бедра-задает масштаб фигуры }
xn, yn, xp, yp, xk, yk :Real; { коорд-ты носка,пятки и колена }
m :Real; {текущий коэф-т сгибания ноги }
Fip, Fiz, Alf :Real; {углов. полож. ноги (пер.,зад.)и стопы}
Constructor Init ( nnum :Byte; xxt, yyt, Bdr :Real);
Procedure SetXY ( xxt, yyt :Real ); Virtual;
Procedure Show ( col :Byte; xxt, yyt :Real ); Virtual;
Procedure Rotate ( ug, x, y, OsX, OsY :Real; Var xx, yy :Real ); Virtual;
Procedure Povorot( fi: Real; Var xxt, yyt, ddx, ddy :Real );Virtual;
End;
TRuka = Object ( TNoga )
Constructor Init ( nnum :Byte; plx, ply, fp, fz, Bdr :Real);
Procedure SetXY ( plx, ply :Real ); Virtual;
Procedure Show ( col :Byte; plx, ply :Real ); Virtual;
Procedure Povorot ( fi: Real; Var plx, ply, ddx, ddy :Real); Virtual;
End;
TBashka = Object ( TNoga ) { голова - эллипс }
Rot :Byte; { фазовая стадия рта }
El :Array[ 0..cb-1 ] Of Record x,y :Real; End; { овал головы }
Constructor Init ( plx, ply, Bdr :Real );
Procedure Show ( col: Byte; plx, ply :Real ); Virtual;
End;
TTelo = Object ( TRuka ) { объект - тело }
Noga1, Noga2 :TNoga;
Ruka1, Ruka2 :TRuka;
Bashka :TBashka;
faza :Byte; { фаза движения }
ik :Byte; { конеч. угол поворота конечностей}
xt, yt, xpl, ypl :Real; { коорд-ты таза и плеча }
dx, dy :Real;{ приращения коорд-т таза при движении}
ExitCode :( Start, Stop); { код завершения программы }
Constructor Init ( xtaz, ytaz :Integer; Bdr :Real );
Procedure Show ( col : Byte; x, y: Real ); Virtual;
Procedure Dvig_Akt ( i :Integer );
Procedure TipTop ( Var Tek, Old :TTelo );
End;
TScreen = Object { объект - сцена }
Telo, TeloOld :TTelo;{ экз-ры тела для двух видеостраниц }
Constructor Init;
Procedure SetRatio ( Var Ratio :Real );
Procedure Activation;
Procedure Done;
End;
Implementation
Uses Graph, CRT;
{$I GrInit}
{******************** методы TNoga **************************}
Constructor TNoga. Init ( nnum :Byte; xxt, yyt, Bdr :Real);
{ nnum - номер ноги; xxt, yyt - коорд. таза; Bdr - фактич. размер бедра }
Var xnn, ynn, r, a, si, co :Real;
Begin num := nnum;
Bedro := Bdr;
m := 0; { m - коэф-т сгиба }
xp := xxt + 0.86*Bedro; { задаем положение пятки по коорд-м таза }
yp := yyt + 1.72*Bedro; { 1.72*Bedro=h }
SetXY( xxt, yyt ) ; { устанавливаем коорд-ты колена и носка }
Fip :=pi/3; { крайнее угловое положение передней ноги }
Alf := ArcTan( (yp - yn)/(xn - xp) ); { нач-е угловое положение стопы }
r := Sqrt ( Sqr(xn - xxt) + Sqr(yn - yyt) );
xnn:= xxt - Sqrt ( r*r - 1.72*Bedro*1.72*Bedro );{ нач. коорд-ты носка}
ynn:= yp; { задней ноги }
a := Sqrt ( Sqr(xn - xnn) + Sqr(yn - ynn) ); { вычисление }
si := a/(2*r); { крайнего углового положения задней ноги }
co := Sqrt( 1 - si*si );
Fiz := Fip + 2*ArcTan(si/co);
If num = 2{ полож-е зад. ноги получ. поворотом перед. на угол Fiz-Fip}
Then Begin Rotate( Fiz-Fip, xp, yp, xxt, yyt, xp, yp );
SetXY( xxt, yyt ); End;
End;
Procedure TNoga. Rotate ( ug, x, y, OsX, OsY :Real; Var xx, yy :Real );
{ поворот на угол ug т.(х,у) вокруг заданной оси (OsX,OsY), рез-т т.(хх,уу) }
Begin xx := (x - OsX)*Cos(ug) - (y - OsY)*Sin(ug) + OsX;
yy := (x - OsX)*Sin(ug) + (y - OsY)*Cos(ug) + OsY;
End;
Procedure TNoga. SetXY ( xxt, yyt :Real ); { определение точек конечности }
{выч-е коорд-т колена и носка по коорд-м таза и пятки с учетом коэф-та сгиба}
Var d, hh, Stopa :Real;
Begin Stopa := Bedro/3.027;
xp := xp + kc*m*( xxt - xp ); {коррекция положения пятки}
yp := yp + kc*m*( yyt - yp ); { xxt, yyt - коорд-ты таза }
d := Sqrt( Sqr(xp-xxt) + Sqr(yp-yyt) );
hh := Sqrt( Bedro*Bedro - d * d / 4 );
xk := xxt + ( xp - xxt)/2 + hh*( yp - yyt )/d;
yk := yyt + ( yp - yyt)/2 - hh*( xp - xxt )/d;
xn := ( Bedro*xp + Stopa*( yp - yk ) )/ Bedro;
yn := ( Bedro*yp - Stopa*( xp - xk ) )/ Bedro;
End;
Procedure TNoga. Povorot ( fi: Real; Var xxt, yyt, ddx, ddy :Real );
{ поворот ноги вокруг пятки или носка на угол fi с учетом перемещ-я таза }
{ xxt, yyt - текущ. коорд. таза; ddx, ddy - смещение таза после поворота }
Begin
If fi <= Alf
Then Begin ddx := xxt; ddy := yyt; { поворот вокруг пятки }
Rotate ( one, xxt, yyt, xp, yp, xxt, yyt );
ddx := xxt - ddx; ddy := yyt - ddy;
SetXY( xxt, yyt ); End
Else If fi < Fiz - Fip { поворот вокруг носка }
Then Begin Rotate( one, xp, yp, xn, yn, xp, yp );
ddx := xxt; ddy := yyt;
Rotate( one, xxt, yyt, xn, yn, xxt, yyt );
ddx := xxt - ddx; ddy := yyt - ddy;
SetXY( xxt, yyt ); End
Else Begin { поворот задней ноги }
fi := fi - ( Fiz - Fip ) - (kc mod 2)*one;
If fi <= ( Fiz - Fip )/2
Then m := ksn { подъем ноги}
Else m := -ksn; { опускание ноги}
xp := xp + ddx; yp := yp + ddy;
Rotate(-one, xp, yp, xxt, yyt, xp, yp );
SetXY( xxt, yyt ); End;
End;
Procedure TNoga. Show( col: Byte; xxt, yyt :Real ); { рисование ноги }
Begin
SetColor(col);
If col <> 0 Then If num = 1 Then SetColor(14) Else SetColor(10);
MoveTo( Round(xxt), Round(yyt*k) );
LineTo( Round(xk), Round(yk *k) );
LineTo( Round(xp), Round(yp *k) );
LineTo( Round(xn), Round(yn *k) );
Circle( Round(xxt), Round(yyt*k), Round(Bedro/20) );
Circle( Round(xk), Round(yk *k), Round(Bedro/20) );
Circle( Round(xp), Round(yp *k), Round(Bedro/20) );
End;
{************************ методы TRuka ************************}
Constructor TRuka.Init ( nnum :Byte; plx, ply, fp, fz, Bdr :Real);
{ nnum - номер руки; plx,ply - коорд. плеча; fp,fz - углов. полож. рук;
Bdr - фактич. размер бедра, причем размеры локтя совп. с разм. бедра }
Begin num := nnum;
Bedro := Bdr;
Fip := fp; Fiz := fz;
m := 0;
xp := plx + 0.86*Bedro;
yp := ply + 1.72*Bedro/1.7;
SetXY( plx, ply ) ;
If num = 2 Then Begin Rotate( Fiz-Fip, xp, yp, plx, ply, xp, yp );
m := -ksr*( Fiz - Fip ) *180/pi;
SetXY( plx, ply );
End;
End;
Procedure TRuka. SetXY ( plx, ply :Real ); { определение точек конечности }
{ plx, ply - коорд. плеча; xp,yp - коорд. запястья; xk,yk - коорд. локтя }
{ Bedro = Lokot }
Var d, hh :Real;
Begin xp := xp + m*( plx - xp );
yp := yp - m*( yp - ply );
d := Sqrt( Sqr(xp-plx) + Sqr(yp-ply) );
hh := Sqrt( Bedro*Bedro - d * d / 4 );
xk := plx + ( xp - plx)/2 - hh*( yp - ply )/d;
yk := ply + ( yp - ply)/2 + hh*( xp - plx )/d;
End;
Procedure TRuka. Show ( col: Byte; plx, ply :Real ); { рисование руки }
Var x1, y1, x2, y2 :Real;
Begin
SetColor(col);
If col <> 0 Then If num = 2 Then SetColor(14) Else SetColor(10);
MoveTo( Round(plx), Round(ply*k) );
LineTo( Round(xk), Round(yk*k) );
LineTo( Round(xp), Round(yp*k) );
Circle( Round(plx), Round(ply*k), Round(Bedro/20) );
Circle( Round(xk), Round(yk*k), Round(Bedro/20) );
Circle( Round(xp), Round(yp*k), Round(Bedro/20) );
If num=2 Then Begin SetColor(col); { рисуем палку }
Rotate ( pi*3/4,xk,yk,xp,yp,x1,y1);
Rotate (-pi/4, xk,yk,xp,yp,x2,y2);
Line( Round(x1), Round(y1*k), Round(x2), Round(y2*k));
End;
End;
Procedure TRuka. Povorot( fi: Real; Var plx, ply, ddx, ddy :Real );
{ поворот руки вокруг плеча на угол fi }
Begin If fi < Fiz - Fip
Then Begin m := -ksr; Rotate( one, xp, yp, plx, ply, xp, yp ); End
Else Begin m := ksr; Rotate(-one, xp, yp, plx, ply, xp, yp ); End;
SetXY( plx, ply );
End;
{************************ методы TBashka ********************}
Constructor TBashka.Init ( plx, ply, Bdr :Real );
{plx, ply - коорд. плеча; Bdr - размер бедра (для масштаба) }
Var a, b, c, t :Real; i :Integer;
Begin Bedro := Bdr;
a := Bedro/2; b := a/2; { оси эллипса }
xp := plx + 1.4*Bedro/8.0; {1.4*Bedro=Spina}
yp := ply - 1.4*Bedro/6.0;
c := Sqrt( a*a - b*b );
For i := 0 To cb-1 Do With El[i] Do Begin { получаем эллипс }
t := kb*i*pi/180;
x := xp + a*Cos(t) + c/2 ;
y := yp + b*Sin(t) - b;
Rotate( pi/4, x, y, xp, yp, x, y ); { поворот эллипса }
End;
End;
Procedure TBashka. Show ( col: Byte; plx, ply :Real ); { рисование головы }
Var i :Integer;
Begin SetColor(col); If col<>0 Then SetColor(11);
Line ( Round(plx), Round(ply*k), Round(xp), Round(yp*k) );
Circle( Round(xp), Round(yp*k), Round(Bedro/20) );
MoveTo( Round( El[0].x), Round(El[0].y*k) );
For i := 1 To cb-1 Do With El[i] Do LineTo( Round(x), Round(y*k) );
If Rot = 1
Then Begin LineTo ( Round( El[cb-1].x - Round(Bedro/4) ),
Round( El[cb-1].y*k) );
LineTo ( Round( El[0].x ), Round(El[0].y*k) );
End
Else Begin LineTo ( Round( El[0].x ), Round(El[0].y*k) );
LineTo ( Round( El[0].x - Round(Bedro/4) ),
End;
If col = 0 Then SetFillStyle(1,0)
Else Begin SetColor(12); SetFillStyle(1,12); End;
If Rot = 0 Then FillEllipse( Round( xp + Bedro/3),
Else Line( Round( xp + Bedro/3-Bedro/15 ),
Round( xp + Bedro/3+Bedro/15 ),
Round( yp*k - Bedro/6 ));
End;
{******************** методы Telo ****************************}
Constructor TTelo. Init ( xtaz, ytaz :Integer; Bdr :Real );
Begin Bedro := Bdr; faza := 0; Bashka.Rot := faza;
xt := xtaz; yt := ytaz;
Noga1.Init ( 1, xt, yt, Bdr );
Noga2.Init ( 2, xt, yt, Bdr );
xpl := xtaz + 0.2*1.4*Bedro;
ypl := ytaz - 1.4*Bedro;
Ruka1. Init ( 1, xpl, ypl, Noga1.Fip, Noga1.Fiz, Bdr );
Ruka2. Init ( 2, xpl, ypl, Noga1.Fip, Noga1.Fiz, Bdr );
Bashka.Init (xpl, ypl, Bdr );
ExitCode:=Start;
End;
Procedure TTelo. Show( col :Byte; x, y :Real );
Begin Noga1. Show ( col, xt, yt );
Noga2. Show ( col, xt, yt );
Ruka1. Show ( col, xpl, ypl );
Ruka2. Show ( col, xpl, ypl );
If col <> 0 Then SetColor(13);
Line ( Round( xt ), Round( yt*k ), Round( xpl ), Round( ypl*k ) );
Bashka. Show( col, xpl, ypl );
End;
Procedure TTelo. Dvig_Akt ( i :Integer );
{реализует единичный акт движения всего тела на i-м такте}
Begin If faza = 0
Then Begin Noga1. Povorot( i*pi/180, xt, yt, dx, dy );
Noga2. Povorot( (i+ik+1)*pi/180, xt, yt, dx, dy );
xpl := xpl + dx; ypl := ypl + dy;
With Ruka1 Do Begin xp:=xp + dx; yp:=yp + dy;
With Ruka2 Do Begin xp:=xp + dx; yp:=yp + dy;
xk:=xk + dx; yk:=yk + dy; End;
Ruka1. Povorot( i*pi/180, xpl, ypl, dx, dy );
Ruka2. Povorot( (i+ik+1)*pi/180, xpl, ypl, dx, dy );
End
Else Begin Noga2. Povorot( i*pi/180, xt, yt, dx, dy );
Noga1. Povorot( (i+ik+1)*pi/180, xt, yt, dx, dy );
xpl := xpl + dx; ypl := ypl + dy;
With Ruka1 Do Begin xp:=xp + dx; yp:=yp + dy;
With Ruka2 Do Begin xp:=xp + dx; yp:=yp + dy;
xk:=xk + dx; yk:=yk + dy; End;
Ruka2. Povorot ( i*pi/180, xpl, ypl, dx, dy );
Ruka1. Povorot ( (i+ik+1)*pi/180, xpl, ypl, dx, dy );
End;
With Bashka Do Begin xp:=xp + dx; yp:=yp + dy; End;
For i := 0 To cb-1 Do With Bashka.El[i] Do
Begin x := x + dx; y := y + dy; End;
End;
Procedure TTelo. TipTop ( Var Tek, Old :TTelo);
{ реализует перемещение тела шагающего человека }
{ анимация с использованием 2-х видеостраниц }
Var i, Page :Integer;
Begin
With Noga1 Do ik:=Round( ( Fiz - Fip ) *180/pi );
Repeat { цикл всех шагов }
Page := 1; { уст-ка номера в/страницы }
For i:=0 To (ik Div kc)-1 Do Begin { цикл одного шага }
Dvig_Akt(kc*i); { акт движения }
If (i Mod 30)=0 Then Bashka.Rot := Bashka.Rot Xor 1;
Show(12, 1.72*Bedro, 1.72*Bedro); { нарисовать }
SetVisualPage(Page);
Repeat Until Port[$03DA] And $08 = $08; {синхр-ция кадров}
Repeat Until Not(Port[$03DA] And $08 = $08);
Page := Page Xor 1; { смена в/страницы }
SetActivePage(Page);
Old.Show(0, 1.72*Bedro, 1.72*Bedro); { стереть предыдущий кадр }
Old := Tek; { заменить старый объект на текущий }
If KeyPressed Then Begin ExitCode:=Stop; Exit; End;
End;{For}
faza := faza Xor 1; { следующий шаг - новая фаза }
Noga1.m:=0; Noga2.m:=0; { восстановить коэффициенты сгибания }
Ruka1.m:=0; Ruka2.m:=0;
Until xt > 640 ;
End;
{********************* методы TScreen *************************}
Constructor TScreen. Init;
Begin GrInit; SetGraphMode(VGAMed); Randomize;
Telo. Init ( 320, yst, 50);
TeloOld. Init ( 320, yst, 50);
Telo. Show ( 12, 100, 100);
End;
Procedure TScreen. SetRatio ( Var Ratio :Real );
{ устaнав-ет текущий масштаб изображения в виде размера бедра (Ratio) }
Const Rost = 100; Var i :Word;
Begin
Repeat
If KeyPressed Then
Case ReadKey Of
#0: Case ReadKey Of
{up} #72: Begin Ratio := Ratio + 1;
If Not( Round(Ratio) In [ 30..Rost ] ) Then Ratio := Ratio - 1;
Telo. Init (320, yst, Ratio );
ClearDevice;
Telo. Show(12,100,100); End;
{down} #80: Begin Ratio := Ratio - 1;
If Not( Round(Ratio) In [ 30..Rost ] ) Then Ratio := Ratio + 1;
Telo. Init(320,yst,Ratio);
ClearDevice;
Telo. Show(12,100,100); End;
End;
#13: Begin SetFillStyle(8,6);
For i := 0 To 1 Do With Telo Do Begin
SetActivePage(i); ClearDevice;
Bar( 0, Round(yt*k + 1.78*Bedro*k), 639, 340 );
SetColor(10); OutTextXY(550,335,'GRM-2001');
Line( 0, Round(yt*k + 1.79*Bedro*k + 1 ),
639, Round(yt*k + 1.79*Bedro*k + 1) );
End;
Exit;
End;
End;
Until False;
End;
Procedure TScreen. Activation;
{ активирует движение шагающего человека }
Begin With Telo Do Begin
SetRatio(Bedro);
Repeat
Init ( 0, yst, Bedro );
TipTop ( Telo, TeloOld );
Until ExitCode = Stop;
End;
End;
Procedure TScreen. Done;
Begin CloseGraph; End;
End.
{*********** Головная программа ************************}
Program Homo;
Uses Men;
Var Screen :TScreen;
Begin
Screen.Init;
Screen.Activation;
Screen.Done;
End.
{*****************************
4.2. В заключение отметим, что на базе этой программы возможно создать множество вариантов движущихся фигур (людей, животных и др.), например, бегущего человека, человека бегущего на лыжах или выполняющего определенные гимнастические упражнения, бегущую лошадь и кошку (рис.12) и т.п.
5. Варианты курсовой работы «Объектно-ориентированное программирование»
В вариантах, имеющих демонстрационные версии, указаны имена exe-файлов, которые можно запустить для выполнения из каталога c:\_PREPOD_\FOR134\
Вариант 1.
Написать программу, реализующую волновой алгоритм Ли трассировки печатных плат.
Литература: 1. Норенков И.П., Маничев В.Б. Системы автоматизированного проектирования электронной и вычислительной аппаратуры. Высшая школа, 1983.
2. Селютин В.А. Машинное конструирование электронных устройств. Советское радио, 1977.
Demo: platadem.exe; plata2.exe; pltext.exe.
Разработать программную модель перцептрона.
Литература: Бонгард М. Проблемы узнавания. Наука, М., 1967. стр. 34.
Вариант 3.
На горизонтальной поверхности находится куб, который вращается вокруг некоторой оси и может перемещаться в плоскости (x,y). Куб освещается точечным источником света S(x,y,z). Написать программу, позволяющую получить изображение сцены с учетом освещенных и неосвещенных граней и возникающей тени.
Demo: tencub0.exe.
Вариант 4.
На горизонтальной поверхности находится шар, который может перемещаться в плоскости (x,y). Шар освещается точечным источником света S(x,y,z). Написать программу, позволяющую получить изображение сцены с учетом освещенных и неосвещенных частей шара и возникающей тени.
Demo: tensha3.exe.
Вариант 5.
На горизонтальной поверхности находится додекаэдр, который вращается вокруг некоторой оси и может перемещаться в плоскости (x,y). Додекаэдр освещается точечным источником света S(x,y,z). Написать программу, позволяющую получить изображение сцены с учетом освещенных и неосвещенных граней и возникающей тени.
Demo: ddcdr7.exe.
Вариант 6.
На горизонтальной поверхности находится икосаэдр, который вращается вокруг некоторой оси и может перемещаться в плоскости (x,y). Икосаэдр освещается точечным источником света S(x,y,z). Написать программу, позволяющую получить изображение сцены с учетом освещенных и неосвещенных граней и возникающей тени.
Demo: icsdr6.exe.
Вариант 7.
Разработать программу скелетного преобразования изображений.
Литература: Бутаков Е.А., Островский В.И., Фадеев И.Л. Обработка изображений на ЭВМ. Радио и связь, М., 1987. стр. 64-69.
Demo: scel2.exe.
Вариант 8.
Разработать программу скелетного преобразования изображений.
Литература: Павлидис Т. Алгоритмы машинной графики и обработки изображений. Радио и связь, М., 1986. стр. 200-217.
Demo: scel2.exe.
Вариант 9.
Разработать программу скелетного преобразования изображений методом “степного пожара”.
Литература: . Прэтт У. Цифровая обработка изображений. Т.2. Мир, М., 1982. стр. 541-542(рис. 18.2.3).
Demo: scel2.exe.
Вариант 10.
Разработать программу скелетного преобразования изображений.
Литература: Кучуганов В.Н. Классификация объектов сложной конфигурации. Сб. «Автоматические устройства учета и контроля», вып. 4. Ижевск, 1969. стр. 162-164.
Demo: scel2.exe.
Вариант 11.
Разработать программу контурного преобразования изображений.
Литература: Павлидис Т. Алгоритмы машинной графики и обработки изображений. Радио и связь, М., 1986. стр. 153.
Вариант 12.
Реализовать алгоритм распознавания изображений по коэффициенту формы для системы технического зрения.
Литература: Полоник В.С. Телевизионная автоматика. М., Энергия. Стр. 65.
Demo: letter.exe.
Вариант 13.
Разработать программу кодирования (декодирования) бинарных изображений тетрарными деревьями. Исследовать операции на объектах, представленных линейными тетрарными деревьями.
Литература: Чэн Ш.-К. Принципы проектирования систем визуальной информации. М., Мир, 1994, стр. 151.
Demo: kurs2.exe.
Вариант 14.
Разработать программу кодирования (декодирования) бинарных изображений по одномерной схеме. Провести исследования коэффициента сжатия информации для различных изображений.
Литература: Чэн Ш.-К. Принципы проектирования систем визуальной информации. М., Мир, 1994, стр. 151.
Вариант 15.
Разработать программное обеспечение системы технического зрения робота, обеспечивающего выборку деталей из конвейера. Номенклатура деталей - (6-10) шт. Деталь на конвейере может иметь произвольную ориентацию.
Литература: Хорн Б.К. Зрение роботов. М. , Мир, 1989.
Вариант 16.
Реализовать алгоритм медианной фильтрации для полутоновых и бинарных изображений.
Литература: Путятин Е.П., Аверин С.И. Обработка изображений в робототехнике. М., Машиностроение, 1990, стр. 44-51.
Demo: filtrmed.exe.
Вариант 17.
Разработать и исследовать алгоритм кодирования (декодирования) изображений бинарными деревьями.
Литература: Павлидис Т. Алгоритмы машинной графики и обработки изображений. Радио и связь, М., 1986. стр. 123.
Demo: kurs2.exe.
Вариант 18.
Разработать и исследовать алгоритм кодирования (декодирования) бинарных изображений по двумерной схеме. Исследовать коэффициент сжатия информации для различных изображений.
Литература: Чэн Ш.-К. Принципы проектирования систем визуальной информации. М., Мир, 1994, стр. 125.
Вариант 19.
Задана функциональная схема логического устройства, имеющего n входов (x1,x2,...,xn) и m выходов (y1,y2,...ym). Схема построена на элементах “И”, “ИЛИ”, “НЕ”. Связи между элементами отражены в таблице связей. Составить программу, печатающую логическую функцию yk(x1,x2,..,xn) для каждого из m выходов. Число элементов схемы L>=10.
Вариант 20.
Реализовать алгоритм полигональной аппроксимации контуров фигур.
Литература: Бутаков Е.А., Островский В.И., Фадеев И.Л. Обработка изображений на ЭВМ. Радио и связь, М., 1987. стр. 27.
Demo: approx.exe.
Вариант 21.
Разработать программную модель зрительного анализатора робота для подсчета числа связных объектов в поле зрения робота.
Demo: svjz_vol.exe.
Вариант 22.
Разработать программную систему восстановления одномерного зашумленного сигнала с использованием усредняющих фильтров. На экране отобразить:
меню;
сигнал источника;
зашумленный сигнал;
отфильтрованный сигнал;
сигнал рассогласования.
Demo: filtr.exe.
Вариант 23.
Разработать программную модель 4-тактного двигателя внутреннего сгорания.
Demo: dvc4.exe.
Вариант 24.
Написать программу «Читающий автомат».
Исходные данные:
изображение двузначного числа:
набор эталонных изображений цифр:
Распознавание исходного двузначного числа осуществляется путем сопоставления с эталонами цифр.
Литература: Ковалевский В.А. Методы оптимальных решений в распознавании изображений. Наука, М., 1976, стр. 144-153
Вариант 25.
Получить на экране фигуры Лиссажу.
Demo: lissagy1.exe, lissaju.exe.
Вариант 26.
Реализовать алгоритм определения проекций при анализе формы объектов.
Реализовать алгоритм восстановления изображений с помощью обратных проекций.
Литература: Павлидис Т. Алгоритмы машинной графики и обработки изображений. Радио и связь, М., 1986. стр. 101-102.
Вариант 27.
Реализовать алгоритмы Брозенхэма для отрезков прямых и окружности.
Литература: Дж. Фолли и др. Основы интерактивной машинной графики. Т.2. М., Мир, 1985, стр.140-148.
Demo: bl2.exe, brcircle.exe.
Вариант 28.
Написать программу, осуществляющую следующие преобразования изображения:
сдвиг изображения на заданную величину по осям Х и У;
поворот изображения на заданный угол fi;
изменение масштаба mx, my по осям Х и У(mx<>my);
Преобразования для исходного изображения осуществить попиксельно.
Литература: 1. Выгодский М.Я. Справочник по высшей математике. Наука, 1966, стр. 48-50.
2. Моденов П.С. Аналитическая геометрия.
Вариант 29.
Разработать программную модель движения тележки по пересеченной местности с учетом различного диаметра передних и задних колес.
Demo: velo0.exe, velo00.exe (совместно с файлом velo2.dat).
Вариант 30.
Разработать программную модель движения танка на гусеничном ходу по пересеченной местности .
Demo: tank9.exe; tankdemo.exe(совместно с файлом tank.dat).
Вариант 31.
Реализовать программно функции калькулятора, выполняющего арифметические операции над вещественными числами. Арифметические операции реализовать на Ассемблере.
Литература: Савельев А.Я. Прикладная теория цифровых автоматов. Высшая школа, 1987.
Вариант 32.
Разработать программную модель работы зрительного анализатора робота, собирающего с пола разбросанные спички и складывающего их в коробок.
Demo: rrobot.exe
Вариант 33.
Разработать и исследовать алгоритмы выделения характерных фрагментов изображений.
Литература: Завалишин Н.В., Мучник И.Б. Модели зрительного восприятия и алгоритмы анализа изображений. М., Наука, 1974, стр. 150.
Вариант 34.
Разработать программную модель велосипедиста, движущегося по пересеченной местности.
Demo: velo0.exe, velo00.exe (совместно с файлом velo2.dat).
Вариант 35.
Разработать и исследовать алгоритм кодирования (декодирования) изображений бинарными деревьями.
Литература: Павлидис Т. Алгоритмы машинной графики и обработки изображений. Радио и связь, М., 1986. стр. 123.
Demo: kurs2.exe.
Вариант 36.
Разработать программную модель движения n шариков (n=10..15) в замкнутом пространстве.
Demo: shariki.exe, shariki0.exe, shariki3.exe, shariki4.exe, sharsor2.exe(совместно с файлом sferebst).
Вариант 37.
Разработать программную модель движения квадратного колеса по пересеченной местности. Отображение с помощью 2-х видеостраниц с синхронизацией.
Demo: kwadrat1.exe, squ_roll.exe.
Вариант 38.
Разработать программную модель движения колеса овальной формы по пересеченной местности.
Demo: oval2.exe, oval3.exe.
Вариант 39.
Разработать программную модель игры в бильярд.
Вариант 40.
“Периметр”. Нарисовать на экране круг и эллипс. Осуществить обход этих фигур по контуру и вычислить их периметры. Построить зависимость относительной ошибки вычисления периметров этих фигур от их размеров.
Вариант 41.
Разработать программную модель мяча, подпрыгивающего на рельефной поверхности под действием сил гравитации.
Demo: ball1.exe, ball2.exe.
Вариант 42.
Разработать программную модель взаимного качения квадрата и эллипса.
Demo: kwaoval.exe, kwaoval2.exe.
Вариант 43.
Разработать программную модель взаимного качения квадрата и эллипса.
Вариант 44.
Разработать программную модель взаимного качения двух квадратов.
Вариант 45.
Разработать программную модель взаимного качения квадрата и эллипса.
Вариант 46.
Разработать программную модель взаимного качения квадрата и эллипса.
Demo: ovalkwa.exe.
Вариант 47.
Разработать программную модель взаимного качения двух эллипсов.
Demo: ovaloval.exe.
Вариант 48.
Разработать программную модель взаимного качения двух эллипсов.
Вариант 49.
Разработать программную модель взаимного качения двух квадратов.
Разработать графическую программу игры в «Tetris»
Разработать программную модель движения тележки с рулевым управлением передних колес. Отображать на экране в плане (вид сверху).
Demo: avto3.exe, avto4exe.
6. Литература
1. Марченко А.И., Марченко Л.А. Программирование в среде Turbo Pascal 7.0. – К: Век+, 1999. – 464с.
2. Турбо Паскаль 7.0. – К.: Торгово-издательское бюро BHV, 1996-448с.
7. Приложение
Структура отчета по курсовой работе
1. Введение.
Дается характеристика предметной области, к которой относится решаемая задача и обосновывается ее актуальность.
2. Задание.
В соответствии с вариантом формулируется задание по курсовой работе.
3. Цель работы.
Формулируется цель выполнения курсовой работы.
4. Постановка задачи.
В этом разделе требуется формализовать задачу, указать возможные
ограничения на ее решение и т.п.
5. Анализ и разработка алгоритма.
Основной раздел отчета. Должен отражать результаты анализа возможных вариантов решения задачи и выбора среди них наиболее рационального. Приводятся математические выкладки и рисунки, поясняющие зависимости параметров решения задачи от данных. Обосновывается выбор структур данных и основных операций над ними. Приводятся результаты моделирования программ-прототипов (если они использовались для разработки алгоритма). Определяются источники и форматы исходных данных и содержание вывода программы. Объем ~ 8-10 страниц.
6. Алгоритм программы.
В этом разделе приводится блок-схема алгоритма программы.
7. Текст программы.
Приводится текст программы с комментариями.
8. Результаты моделирования программы на ЭВМ.
Должны быть приведены результаты тестирования программы с различными наборами данных, в том числе и с некорректными данными.
9.Выводы.
Приводятся комментарии к результатам и рекомендации к дальнейшему совершенствованию программы.
10. Литература.
Примечание. Пример оформления титульного листа отчета приведен ниже:
Министерство народного образования Российской Федерации
ИжГТУ
Кафедра вычислительной техники
Курсовая работа
по курсу «Технология программирования»
на тему: «Объектно-ориентированное программирование»
Вариант 10
Разработал студент группы 462
Принял к.т.н., доцент кафедры ВТ Гафаров Р.М.
Ижевск 2002
Информация о работе Объектно-ориентированное программирование