Этапы создания программного обеспечения

ЭТАПЫ СОЗДАНИЯ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ

В процессе разработки программ с использованием процедурного подхода можно выделить следующие этапы:

• постановка задачи - определение требований к программному продукту;

• анализ - осуществление формальной постановки задачи и определение методов ее решения;

• проектирование - разработка структуры программного продукта, выбор структур для хранения данных, построение и оценка алгоритмов подпрограмм и определение особенностей взаимодействия программы с вычислительной средой (другими программами, операционной системой и техническими средствами);

• реализация - составлениепрограммы на выбранном языке программирования, ее тестирование и отладка.

• модификация - выпуск новых версий программного продукта.

Постановка задачи

Процесс создания нового программного обеспечения начинают с постановки задачи, в процессе которой определяют требования к программному продукту.

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

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

этапы создания программного обеспечения - student2.ru

Рис. 1.1. Факторы, определяющие параметры разрабатываемого программного обеспечения

В результате согласования между заказчиком и исполнителем всех перечисленных вопросов составляют техническое задание в соответствии с ГОСТ 19.201-78, которое служит основанием для дальнейшей работы.

Проектирование

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

При выполнении физического проектирования все эти параметры должны быть учтены.

Логическое проектирование.Логическое проектирование при процедурном подходе предполагает детальную проработку последовательности действий будущей программы. Его начинают с определения структуры будущего программного продукта: отдельная программа или программная система, состоящая из нескольких взаимосвязанных программ. Затем переходят к разработке алгоритмов программ.

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

Различают последовательности действий (вычислений) линейной, разветвленной и циклической структуры.

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

Разветвленная структура процесса вычислений предполагает, что конкретная последовательность операций зависит от значений одного или нескольких параметров. Например, если дискриминант квадратного уравнения не отрицателен, то уравнение имеет два корня, а если отрицателен, то действительных корней нет.

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

Процессы вычислений циклической структуры в свою очередь можно разделить на три группы:

• циклические процессы, для которых количество повторений известно - счетные циклы или циклы с заданным количеством повторений;

• циклические процессы, завершающиеся по достижении или нарушении некоторых условий - итерационные циклы;

• циклические процессы, из которых возможны два варианта выхода: выход по завершении процесса и досрочный выход по какому-либо дополнительному условию - поисковые циклы.

Формальное описание алгоритмов осуществляют с использованием схем алгоритмов и псевдокодов.

На изображение схем алгоритмов существует ГОСТ 19.701-90, согласно которому каждой группе действий ставится в соответствие блок особой формы.

Некоторые часто используемые обозначения приведены в табл. 1.1.

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

В случае, когда схема алгоритма не умещается на листе, используют соединители.

При переходе на другой лист или получении управления с другого листа в комментарии указывается номер листа, например «с листа 3» «на лист 1».

В теории программирования доказано, что для записи любого сколь угодно сложного алгоритма достаточно трех базовых структур:

• следование - обозначает последовательное выполнение действий (рис. 1.2, а);

• ветвление - соответствует выбору одного из двух вариантов действий (рис. 1.2, б);

• цикл-пока - определяет повторение действий, пока не будет нарушено условие, выполнение которого проверяется в начале цикла (рис. 1.2, в).

этапы создания программного обеспечения - student2.ru

Рис. 1.2. Базовые алгоритмические структуры: следование (а), ветвление (б) и цикл-пока (в)

Таблица 1.1

этапы создания программного обеспечения - student2.ru

Помимо базовых структур используют три дополнительные структуры, производные от базовых:

• выбор - выбор одного варианта из нескольких в зависимости от значения некоторой величины (рис. 1.3, а);

• цикл-до - повторение некоторых действий до выполнения заданного условия, проверка которого осуществляется после выполнения действий в цикле (рис. 1.3, в);

• цикл с заданным числом повторений {счетный цикл) – повторение некоторых действий указанное число раз (рис. 1.3, д).

На рис. 1.3, б, г и е показано, как каждая из дополнительных структур может быть реализована через базовые структуры.

этапы создания программного обеспечения - student2.ru

Рис.1.3. Дополнительные структуры и их реализация через базовые структуры: выбор (а-б), цикл-до (в-г) и цикл с заданным числом повторений (д-е)

Перечисленные структуры были положены в основу структурного программирования - технологии, которая представляет собой набор рекомендаций по уменьшению количества ошибок в программах [4, 8]. В том случае, если в схеме алгоритма отсутствуют другие варианты передачи управления, алгоритм называют структурным, подчеркивая, что он построен с учетом рекомендаций структурного программирования.

Схема алгоритма детально отображает особенности разработанного алгоритма.

Иногда такой высокий уровень детализации не позволяет выделить суть алгоритма. В этих случаях для описания алгоритма используют псевдокод.

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

Для каждой структуры используют свою форму описания. В литературе были предложены несколько вариантов форм псевдокодов. Один из вариантов приведен в табл. 1.2.

Таблица 1.2

этапы создания программного обеспечения - student2.ru

Пример 1.2.Разработать алгоритм определения наибольшего общего делителя двух натуральных чисел.

Существует несколько способов определения наибольшего общего делителя двух натуральных чисел. Самым простым из них является так называемый алгоритм Евклида. Суть этого метода заключается в последовательной замене большего из чисел на разность большего и меньшего. Вычисления заканчиваются, когда числа становятся равны.

Например:

а) А

225-125 = 100

100-25 = 75

75-25 = 50

50-25 = 25

В

25-100 = 25

= 25

б) А

13-4=9

9-4=5

5-4=1

В

4-1=3

3-1=2

=2-1=1

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

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

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

Управление клавиатурой

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

Клавиши клавиатуры делят на буквенно-цифровые, специальные и клавиши смещения.

К буквенно-цифровым относят клавиши букв, цифр, специальных знаков и пробела. Их используют для ввода информации.

Специальные клавиши - это клавиши управления курсором (←, ↑, →, ↓, Home, End, Tab, Page up. Page down), удаления (Del, Backspace), клавиши переключения режимов (Ins, Caps lock, Num lock, Scrool lock), функциональные клавиши (Esc, Break, Fl, F2, F3, ..., FI2) и т.д. Эти клавиши используют для выполнения вспомогательных операций во время работы с компьютером.

Клавиши смещения - это клавиши Shift, Ctrl и Alt. Их используют совместно с другими клавишами для изменения вводимых кодов. Так, если при нажатии клавиши «а» формируется код строчной буквы а латинского алфавита, то нажатие Shift-a приведет к вводу кода заглавной буквы А латинского алфавита.

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

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

В настоящее время для представления комбинаций, не вошедших в таблицу ASCII, используют расширенные коды, состоящие из двух байт: первый байт равен 0, а второй - содержит расширенный scan-код (см. приложение 3).

Ввод буквенно-цифровых данных с клавиатуры осуществляется процедурами Read и ReadLn,при этом реально происходит чтение кодов ASCII из буфера BIOS клавиатуры. Считанные символьные коды преобразуются во внутренний формат в соответствии с типом переменной.

Процедуры Read и ReadLn обрабатывают только комбинации, соответствующие буквам и цифрам, а также некоторые специальные комбинации, например, маркер конца строки (комбинация символов #13, #10).

Модуль crt содержит специальные функции управления клавиатурой, которые позволяют работать с расширенными кодами.

1. Функция KeyPressed: boolean - возвращает true, если нажата любая клавиша, false - если буфер BIOS клавиатуры пуст; функция не извлекает символы из буфера, не ожидает ввода;

2. Функция ReadKey:char - извлекает очередной код из буфера BIOS клавиатуры и возвращает его как результат операции, ожидает ввода, но не высвечивает вводимого символа.

Для чтения расширенного кода функцию ReadKey необходимо вызывать дважды: первый раз она вернет 0, а второй - расширенный scan-код:

chl:=ReadKey; {читаем код}

if chl=#0 then ch2:=ReadKey; {если код=0, то читаем второй байт}

Пример 8.2.Разработать программу определения кодов клавиш и их комбинаций. Выход из цикла осуществлять по нажатию клавиши Esc.

Program ex;

Uses crt;

Var c1,c2:char;

Begin

repeat c1:=ReadKey; {вводим код}

if c1=#0 then {если расширенный код}

begin

c2:=ReadKey; {то читаем расширенный scan-код}

WriteLn(ord(c1):5, ord(c2):5) {выводим расширенный код}

end

else WriteLn(ord(c1):5) {выводим код ASCII}

until c1 =#27;{до нажатия Esc}

End.

Таблица 8.2

этапы создания программного обеспечения - student2.ru

Примечание. Функция ReadKey обрабатывает коды из буфера BIOS клавиатуры, поэтому с ее помощью нельзя получить коды нажатия/отпускания отдельных клавиш, не преобразуемых в расширенные scan-коды, например, клавиш смещения, клавиш переключения режимов.

Состояния клавиш смещения и клавиш переключения режимов BIOS фиксирует в байте состояния клавиатуры (табл. 8.2), который расположен в

оперативной памяти по адресу $0:$417.

Для прямого обращения к этому байту можно использовать стандартно объявленный массив Mem:array of byte, например: Mem[$0:$417], или наложить некоторую переменную на интересующий нас байт оперативной памяти:

Var KeyState:byte absolute $0:$417; … .

Управление динамиком

Модуль crt также содержит процедуры, обеспечивающие управление динамиком.

1. Процедура Sound (f:word) - генерирует звук указанной частоты в Гц.

Для справки, основной гамме соответствуют следующие частоты: нота «до» основной октавы - 330 Гц, далее - 349 Гц, 370 Гц, 392 Гц, 415 Гц, 440 Гц, 466 Гц, 494 Гц, 523 Гц, 554 Гц, 588 Гц, 622 Гц и, наконец, нота «до» следующей октавы - 660 Гц. Частоты нот других октав кратны частотам основной.

2. Процедура NoSound - выключает динамик.

3. Процедура Delaу (t:word) - обеспечивает задержку на заданный интервал времени, мс.

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

4. Процедура GetTime (VarHour, Minute, Second, Sec100:word) – возвращает текущее время суток. Определена в модуле Dos.

Пример 8.3.Разработать программу проигрывания основной октавы.

Проигрывание гаммы осуществляется включением и выключением динамика с разными частотами.

Program ex;

Uses Crt;

Const f: array[1..13] of word = (330, 349, 370, 392, 415, 440,

466, 494, 523, 554, 588, 622, 660);

Var i:byte;

Begin for i:=1 to 13 do

begin

Sound(f[i]);

for j:=1 to 5000 do Delay(1000);{задержка ?!}

NoSound;

end;

End.

Чтобы не подбирать время задержки для конкретного компьютера, построим свою процедуру обеспечения требуемой задержки, использующую процедуру GetTime:

Program ex;

Uses Crt, Dos;

Procedure NewDelay(dTime:word);

Var key:boolean;

Hour, Min, Sec, Sec100, MyHour, MyMin, MySec, MySec100: Word;

Begin

GetTime(Hour, Min, Sec, Sec100); {узнаем текущее время}

{определяем время завершения задержки}

MySec100:=Sec100+dTime; MySec:=Sec+MySecl00 div 100;

MySec100:=MySec100 mod 100;

MyMin:=Min+MySec div 60; MySec:=MySec mod 60;

MyHour:=Hour+MyMin div 60; MyMin:=MyMin mod 60;

key:=false;

while not key do {цикл задержки}

Begin

GetTime(Hour, Min, Sec, Sec100); {узнаем текущее время}

{проверяем, наступил ли заданный момент}

if (Ноиr>МуНоиr) or ((Ноиr=МуНоиr) and ((Min>MyMin) or

((Min=MyMin) and ((Sec>MySec) or

((Sec=MySec) and ((Sec100>=MySec100)))))))

then key:=true;

End

End;

Const farray[1..13] of word = (330, 349, 370, 392, 415, 440,

466, 494, 523, 554, 588, 622, 660);

Var i:byte; j:integer;

{описываем массив окон пунктов меню}

Const menu:array[1..4] of win=

((x1:5;y1:4;x2:15;y2:4;text: 'new '),

(x1:5;y1:5;x2:15;y2:5;text: 'open'),

(x1:5;y1:6;x2:15;y2:6;text: save'),

(x1:5;y1: 7;x2:15;y2:7;text: 'exit'));

{процедура рисования пункта меню}

Procedure DrawWin(w:win;attr:byte);

Begin

with w do

begin

TextAttr:=attr; {устанавливаем атрибут окна пункта}

Window(xl,у1,х2,у2); {устанавливаем окно пункта}

Clrscr; {высвечиваем окно пункта}

GotoXY(2,1); {устанавливаем курсор}

Write(text); {выводим название пункта}

end;

End;

{процедура рисования меню с выделенным пунктом npos}

Procedure DrawMenu(npos: integer);

Begin

Clrscr;

for i:=1 to 4 do

if i=npos then DrawWin(menu[i],94) {выводим выделенный пункт}

else DrawWin (menu[i], 30); {выводим невыделенный пункт}

End;

{основная программа}

Begin

npos:=1; {выделенный пункт меню}

DrawMenu(npos); {выводим меню}

repeat

ch1:=ReadKey; if ch1=#0 then ch2:=ReadKey;

case ch1 of

#0: case ch2 of

#72: begin {стрелка вверх}

if npos>1 then {если не верхний пункт}

begin

DrawWin(menu[npos],30); {убираем выделение текущего пункта меню}

npos:=npos-1; {переходим к предыдущему пункту}

DrawWin(menu[npos],94); {выделяем новый пункт}

end;

end;

Begin for i:=l to 13 do

begin

Sound(f[i]):

NewDelay(50);

NoSound;

end;

End.

Все-цикл

Конец.

В а р и а н т 1. Координаты вершин квадрата будем хранить в специальных массивах х, у, а квадрат рисовать линиями, проводя их из одной вершины в другую. Изображение квадрата будет осуществлять специальная процедура Square.

Пересчет координат вершин реализуем через разложение «движения» прямоугольника на элементарные составляющие (рис. 8.10): эффект удаления от зрителя получим, используя масштабирование относительно точки горизонта, эффект вращения - за счет поворота вокруг геометрического центра.

этапы создания программного обеспечения - student2.ru

Рис. 8.10.Разложение движения

Вначале определим координаты вершин и центра квадрата после масштабирования, а затем координаты вершин после поворота вокруг центра

квадрата. Ниже приведена соответствующая программа:

Program ex;

Uses Crt,Graph;

Const

r=100;

Type

mas=array[1.. 4] of integer;

Var

x, y, x1, y1:mas;

gd,gm,xn,yn,xc,yc,i,j,k1: integer;

t,k:real;

{изображение квадрата по координатам его вершин}

Procedure Square(х,у:mas);

Begin

Line(x[1],y[1],x[2],y[2]);

Line(x[2],y[2],x[3],y[3]);

Line(x[3],y[3],x[4],y[4]);

Line(x[4],y[4],x[1],y[1]);

End;

{определение координат повернутой точки}

Procedure Pow(xc,yc,x,y:integer;t:real;var xl,yl:integer);

Begin

x1:=xc+round((x-xc)*cos(t))+round(y-yc)*sin(t));

y1:=yc+round((y-yc)*cos(t))-round((x-xc)*sin(t));

End;

{определение координат точки после масштабирования}

Procedure Massch(xc,yc,x,y:integer; k:real; var x1,y1:integer);

Begin

x1:=round(x*k+(1-k)*xc);

y1:=round(y*k+(1-k)*yc);

End;

{основная программа}

Begin

gd:=detect;

InitGraph(gd,gm, 'd:\bp\bgi');

SetColor(2);

xn:=GetMaxX div 4;

yn:=GetMaxY div 3*2;

xc:=GetMaxX-xn;

yc:=GetMaxY-yn;

{расчет начальных координат вершин квадрата}

x[1]:=xn-r; y[1]:=уn-r;

x[2]:=xn+r; у[2]:=yn-r;

x[3]:=xn+r; y[3]:=yn+r;

x[4]:=xn-r; у[4]:=yn+r;

k:=0.99;

t:=0;

{покадровый вывод на экран}

while (t<1) and not KeyPressed do

begin

SetColor(2); {установим цвет рисования}

Square(x,y); {нарисуем квадрат}

t:=t+0.001; {увеличим угол поворота}

{масштабирование}

for j:=1 to 4 do {определим координаты вершин}

Massch(xc,yc,x[j],y[j],k,x1[j],y1[j]);

Massch(xc,yc,xn,yn,k,xn,yn); {определим координаты центра}

{поворот}

for j:=1 to 4 do {определим координаты вершин}

Pow(xn,yn,x1[j],y1[j],-t,x1[j],y1[j]);

for j:=1 to 1500 do Delay(1000); {или NewDelay см. параграф 8.3}

SetColor(0); {установим цвет рисования - цвет фона}

Square(x,y); {стираем квадрат}

x:=x1; {заменим координаты вершин на новые}

y:=y1;

end;

CloseGraph;

End.

Недостатком данного способа является то, что квадрат на экране через несколько кадров уже выглядит не квадратным. Это происходит вследствие накопления ошибки округления при пересчете координат вершин. Избежать этого можно двумя способами:

1) все вычисления выполнять в вещественной арифметике и координаты также хранить как вещественные числа, преобразуя их в целые непосредственно перед использованием в процедуре рисования;

2) пересчитывать координаты не всех вершин, а какой-нибудь одной и центра квадрата, восстанавливая квадрат по одной вершине и положению центра квадрата.

Способы являются взаимодополняющими, поэтому используем оба.

В а р и а н т 2. Для упрощения вычислений вместо массивов, хранящих координаты вершин квадрата, будем использовать смещения этих вершин относительно центра (рис. 8.11). Соответственно процедура рисования квадрата Square 1 должна использовать именно эти параметры. Также учтем, что при масштабировании изменяются размер диагонали и положение центра, а при повороте - смещения вершин относительно центра. Ниже представлен текст программы.

этапы создания программного обеспечения - student2.ru

Рис. 8.11. Два соседних кадра при повороте

Program ex;

Uses Crt,Graph;

Const r:real=100; {размер половины стороны квадрата}

Var

x, у, dx, dy, dx1, dy1, xn, yn, xc, yc, xn1, yn1:real;

gd,gm,i,j:integer;

t,k:real; {угол поворота и масштаб}

{изображение квадрата}

Procedure Square1(x,y,dx,dy:integer);

Begin

Line(x+dx,y+dy,x-dy,y+dx);

Line(x-dy,y+dx,x-dx,y-dy);

Line(x-dx,y-dy,x+dy,y-dx);

Line(x+dy,y-dx,x+dx,y+dy);

End;

{основная программа}

Begin

gd:=detect;

InitGraph(gd,gm, 'd:\bp\bgi');

{устанавливаем начальную и конечную точки}

хп:=GetMaxX div 4;

yn:=GetMaxY div 3*2;

xc:=GetMaxX-xn;

yc:=GetMaxY-yn;

{определяем начальные значения}

dx:=r;

dy:=0;

k:=0.95;

t:=0;

{покадровый вывод на экран}

while (t<100) and not Key Pressed do

begin

SetColor(2); {выводимкадр}

Square1(round(xn), round(yn), round(dx), round(dy));

{масштабирование}

xn1:=xn*k+(1-k)*xc;

yn1:=yn*k+(1-k)*yc;

r:=k*r;

{поворот}

t:=t+1; {увеличиваем угол поворота}

dx1:=r*cos(t);

dy1:=r*sin(t);

for j:=1 to 5000 do Delay(1000); {приостановка}

SetColor(0); {стираемкадр}

Square1(round(xn), round(yn), round(dx), round(dy));

dx:=dx1; {заменяем параметры квадрата}

dy: =dy1

xn:=xn1

yn:=yn1

end;

CloseGraph;

end.

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

Для уменьшения этого времени при программировании в MS DOS часто используют прямую запись информации в видеобуфер.

Как указывалось в параграфе 8.4, формат информации в видеобуфере зависит от используемого графического режима. При использовании младших режимов VGA, на которые рассчитан Borland Pascal, видеобуфер содержит 4 бита на каждую точку, причем биты расположены в параллельных битовых плоскостях и доступ к ним напрямую существенно затруднен (программирование таких операций обычно выполняется на ассемблере). Однако существует режим VGA (режим 19: 200*320 точек 256 цветов из палитры 262144 цвета), при котором каждой точке соответствует байт (8 бит) в видеобуфере.

Именно этот режим и используется, если возникает необходимость программировать сложное движение с использованием прямой записи в видеобуфер.

Пример 8.8.Разработать программу, обеспечивающую вывод «бегущей» строки. Направление движения строки по экрану - вверх-вниз.

Для создания изображения используем возможности модуля Graph, затем перепишем изображение из видеопамяти в буфер, расположенный в динамической памяти, и перейдя в режим 200*320, организуем циклический вывод изображения напрямую в видеобуфер. Стирание старого изображения будем выполнять чистой кромкой образа (образ «не прозрачный»).

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

Program ex;

Uses Graph, Crt, Dos;

Type

ScreenType=array[0..199,0..319] of byte; {массив для хранения образа экрана - формат видеобуфера}

ImageType=array[0..999] of byte; {развертка изображения}

ScrTypePtr=^ScreenType; {указатель на массив образа экрана}

ImageTypePtr=^ImageType; {указатель на развертку изображения}

{процедура установки режима с номером mode}

Procedure SetBIOSMode(mode:byte);

Var r:registers;

Begin

r.AL:=mode; {запись номера режима в регистр AL}

r.АН:=0; {запись номера функции в регистр АН}

intr($10,r); {вызов 0-й функции 10-го прерывания}

End;

{основная программа}

Var

Driver, Mode:integer;

s:string;

i, j, n, m, l, y, dy:integer;

Active_Ptr:ScrTypePtr; {указатель на тип "образ экрана"}

Image:ImageTypePtr; {указатель на развертку изображения}

Begin

{формирование изображения и сохранение его в памяти}

Driver: =Detect; InitGraph(Driver,Mode, ");

s:='ABCDEF';

SetColor(4); SetTextStyle(GothicFont, HorizDir, 3);

OutTextXY(2,2,s);

n:=TextHeight(s)+3;

m:=TextWidth(s)+3;

GetMem(Image,(n+1)*(m+1)); {получение памяти для записи изображения}

l:=0;

for i:=0 to n do

for j:=0 to m do

begin

image^[l]:=Lo(GetPixel(j,i)); {запись изображения в буфер}

l:=l+1;

end;

CloseGraph;

{запись изображения «напрямую» в видеобуфер}

SetBIOSMode($13); {установка 19-го графического режима}

Active_Ptr:=Ptr($A000,0); {стандартный адрес видеобуфера}

у:=0;

dy:=1;

{покадровый вывод изображения}

repeat

{побайтная запись изображения в видеобуфер}

l:=0;

for i:=0 to п do

for j:=0 to т do

begin

Active_Ptr^[y+i+10,j+20]:=image^[l];

l:=l+1;

end;

for i:=1 to 1000 do Delay(3000); {задержка}

Inc(y,dy); {смещение изображения}

if (y>120) or (y<0) then dy:=-dy; {организация колебательного движения}

until KeyPressed;

SetBIOSMode(3); {возврат к стандартному текстовому режиму}

End.

ПРИМЕРЫ

Пример 8.7. Программа рисует человечка, делающего утреннюю зарядку.

этапы создания программного обеспечения - student2.ru

Program Animation; Uses Crt, Graph; {подключение к программе библиотек Crt и Graph} Const {вертикальные и горизонтальные координаты положения рук} Vert : Array[1..3] of Integer = (190, 157, 120); Horizont : Array[1..3] of Integer = (200, 190, 200); Var GrDriver, GrMode, GrError, i, j : Integer; BEGIN GrDriver := Detect; InitGraph(GrDriver, GrMode, 'C:\TP\BGI'); GrError := GraphResult; If GrError <> GrOk then Halt; SetColor(LightGray); { установка светлосерого цвета для рамки} Rectangle(20, 20, 480, 400); {рисование рамки} SetColor(LightCyan); {установка яркоголубого цвета для текста} OutTextXY(200, 40, 'П Р И В Е Т !'); SetColor(LightGray); Circle (250, 130, 20); {голова} SetColor(Yellow); Arc(250, 130, 0, 180, 26); {волосы} Arc(250, 130, 0, 180, 24); Arc(250, 130, 0, 180, 22); Line(250, 105, 244, 115); Line(250, 105, 250, 116); {чубчик} Line(250, 105, 256, 115); SetColor(LightCyan); Circle(241, 125, 4); {левый глаз } Circle(259, 125, 4); {правый глаз} SetColor(LightRed); SetFillStyle(SolidFill, LightRed); FillEllipse(250, 140, 6, 3); {рот } Setcolor(Green); Line(250, 152, 250, 220); {туловище } Line(250, 220, 210, 290); {левая нога } Line(250, 220, 290, 290); {правая нога} Repeat {цикл прерывается нажатием любой клавиши} For i := 1 to 3 do {Последовательный вывод трех положений рук:} begin { вниз, на уровне плеч, вверх } SetColor(LightCyan); Sound(200*i); Line(250, 157, Horizont[i], Vert[i]); {левая рука} Line(250, 157, 500-Horizont[i], Vert[i]); {правая рука} Delay(300); {задержка} SetColor(Black); {смена цвета на черный для повторного pисования рук в том же положении ("стирания" их с экрана) } Line(250, 157, Horizont[i], Vert[i]); {левая рука } Line(250, 157, 500-Horizont[i], Vert[i]); {правая рука} end until Keypressed; SetColor(LightCyan); Line(250, 157, Horizont[3], Vert[3]); {левая рука поднята } Line(250, 157, 500-Horizont[3], Vert[3]); {правая рука поднята} For i := 1 to 10 do { звуковая трель } begin Sound(1000); Delay(50); Sound(1500); Delay(50) end; NoSound; { выключение звука } CloseGraph;END.

Пример 8.10. Программа изображает планету, вращающуюся вокруг Солнца на фоне мерцающих звезд и расходящейся галактики.

этапы создания программного обеспечения - student2.ru Перемещение и изменение размеров изображений на экране можно организовать по разному. Так, в примере 8.6 эффект движения изображения достигается следующим образом: выводится изображение, затем оно стирается с экрана с помощью процедуры ClearViewPort, повторно выводится с некоторым перемещением и т.д.

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

Оба способа имеют одинаковый недостаток —
движение изображения является прерывистым и вызывает мелькание экрана.

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

Изображение сначала создается на странице с нулевым номером, видимой по умолчанию, а на невидимой странице с номером 1 формируется изображение с небольшим перемещением. Затем страница с номером 1 делается видимой, а новое изображение формируется на ставшей невидимой странице с нулевым номером и т.д.

Демонстрация

Внимание: будет работать только если Turbo Pascal установлен в каталог C:\TP и каталог C:\TP\BGI содержит необходимый файл egavga.bgi.

Program Space; {составил студент Тетуев Р., мат.фак. КБГУ} Uses Graph, Crt; Const RadOrb = 250 {радиус орбиты Земли}; RadSun = 70 {радиус Солнца}; RadGal = 100 {радиус галактики }; RadZem = 18 {радиус Земли }; Naklon = 0.2 {коэффициент наклона плоскости орбиты Земли}; PressZem = 0.65 {коэффициент сплющенности полюсов Земли}; Compress = 0.8 {коэффициент сжатия при переходе из }; {расширения режима VGA в режим CGA } Var ZemX, ZemY, UgMer, PixelY, DUgZem , UpDown, XRad, Grad, UgZem, PixelX, StAngle, Ua, Ub, ParallelY , Color, ZemPix, EndAngle, VisualPage, GrMode, GrError, GrDriver, i : Integer; Ugol, CompressZem, Expansion, DUgol, Projection, PolUgol : Real;BEGIN {установка графического режима и проверка возможных ошибок} GrDriver := EGA; GrMode := EGAHi; InitGraph(GrDriver, GrMode, 'C:\TP\BGI'); GrError := GraphResult; If GrError<>GrOk then Halt; SetBkColor(Black); SetFillStyle(1, Yellow); {установка стиля заполнения и цвета Cолнцa} Ugol := 0; DUgol := 2*Pi/180; {орбитальное угловое смещение Земли} UgZem := 0; DUgZem := 14; {осевое угловое смещение Земли} {------------------------------------------------------------------} VisualPage := 1; Repeat {цикл прерывается нажатием любой клавиши} SetVisualPage(1- (VisualPage mod 2)); {установка номера видимой видеостраницы} VisualPage := VisualPage+1; {листание видеостраниц} SetActivePage(1 - (VisualPage mod 2)); {установка номера невидимой (активной) видеостраницы,} {используемой для построения смещенного изображения } ClearDevice; {очистка графического экрана} {--------------------------------------------------------------} {Рисование "расходящейся" галактики} RandSeed:=1; {исходное значение датчика случайных чисел} Expansion:=VisualPage/100; {cкорость расширения галактики} For i:= 1 to VisualPage do begin XRad := Trunc(Expansion*RadGal*Random); {текущее расстояние от звезды до центра галактики} PolUgol:= 2*Pi*Random-VisualPage/30; {текущий центральный угол положения звезды галактики} PixelX := 370+Trunc(XRad*cos(PolUgol+1.8)); {координаты} PixelY := 250+Trunc(XRad*0.5*sin(PolUgol)); { звезды } PutPixel(PixelX, PixelY, White) {рисование звезды} end; {--------------------------------------------------------------} {Рисование мерцающих звезд} Randomize; {инициализация датчика случайных чисел} For i:=1 to 70 do PutPixel(Random(640),Random (350),White); {вспыхивающие звезды} {--------------------------------------------------------------} For i := 1 to 100 do {Рисование орбиты} PutPixel(320+Round(RadOrb * cos((i+VisualPage/5)*Pi/50+0.3)), 160+Round(RadOrb*Naklon*sin((i+VisualPage/5)*Pi/50-Pi/2)),15); {--------------------------------------------------------------} PieSlice(310, 160, 0, 360, RadSun); {Рисование Солнца} {--------------------------------------------------------------} {Рисование Земли (ее параллелей и меридианов)} Ugol := Ugol+DUgol ; {угол поворота Земли относительно Солнца} Grad := Round(180*Ugol/Pi) mod 360; {в рад.(Ugol) и в град.(Grad)} ZemX := 320+Round(RadOrb*cos((Ugol+Pi/2+0.3))); { координаты } ZemY:=160+Round(RadOrb*Naklon*sin(Ugol)); {центра Земли} CompressZem := 2.5-cos(Ugol+0.3); {коэффициент учета удаленности Земли от наблюдателя} ZemPix := Round(RadZem*CompressZem); {текущий радиус Земли} UgZem := UgZem+DUgZem; {угол поворота Земли относительно своей оси} For i := 0 to 11 do { рисование меридианов } begin UgMer := (UgZem+i*30) mod 360; If (90<UgMer) and (UgMer<270) {установка начального и конечного} then begin StAngle := 90; EndAngle := 270 end { углов дуги } else begin StAngle := 270; EndAngle := 90 end; {эллипса меридиана} Ua := (Grad+220) mod 360; Ub := (Grad+400) mod 360; {установка цветов рисования затененной и освещенной частей меридиана} Color := LightBlue; If Ua<=Ub then if (Ua<UgMer) and (UgMer<Ub) then Color := White; If Ua >Ub then if (Ua<UgMer) or (UgMer<Ub) then Color := White; SetColor(Color); XRad := round((ZemPix*cos(UgMer*Pi/180))); Ellipse(ZemX,ZemY,StAngle,EndAngle,abs(XRad),round(PressZem*ZemPix)); end; For i := 2 to 7 do {рисование параллелей} begin XRad := abs(Round(ZemPix*sin(i*Pi/9))); {большая полуось эллипса параллели} UpDown := Round(ZemPix*PressZem*cos(i*Pi/9)); {высота параллели над плоскостью экватора} ParallelY := ZemY+UpDown; {координата Y центра эллипса параллели} SetColor(LightBlue); Ellipse(ZemX, ParallelY, 0, 360, XRad, Round(Naklon*XRad)); {затененная часть параллели} SetColor(White); Ellipse(ZemX,ParallelY,Grad+220,Grad+400,XRad,Round(Naklon*XRad)); {освещенная часть параллели} end; {------------------------------------------------------------------} {Повторное рисование Cолнца, если оно ближе к наблюдателю, чем Земля} If CompressZem<2 then PieSlice(310, 160, 0, 360, RadSun); {------------------------------------------------------------------} RandSeed := VisualPage mod 12; For i := 1 to 250 do {Рисование протуберанцев} begin Projection := (1-sqr(Random))*Pi/2; XRad := RadSun+Round((20)*sin(Projection))-15; PolUgol := 2 * Pi * Random+VisualPage/20; {PolUgol, XRad - полярные координаты протуберанца} PixelX := 310 + Round( XRad * cos(PolUgol)); PixelY := 160 + Round( Compress * XRad * sin(PolUgol)); PutPixel(PixelX, PixelY, LightRed) end; until KeyPressed END.

Создание иллюзии движения

Создать видимость движения изображения на экране можно несколькими способами. Рассмотрим два из них.

I способ. Имитация движения объекта на экране за счет многократного выполнения программой набора действий: нарисовать – пауза – стереть (нарисовать в том же месте цветом фона) – изменить координаты положения рисунка.
Перед началом составления программы надо продумать описание «двигающегося» объекта, характер изменения координат, определяющих текущее положение объекта, диапазон изменения и шаг.
Упражнение 1. Изучить текст программы, которая рисует модель атома.

program Model_At;
uses Crt, Graph;
const Ra=100; {радиус атома}
Rc=10; {радиус ядра}
Re=4; {радиус электрона}
k=0.5; {коэффициент сжатия орбит электронов}
Dr=30; {параметр изменения координат электрона}
Step1=0.2; {шаг изменения положения электрона}
Step=100; {время задержки – скорость движения электронов}
var cx, cy, y, y1, y2, x, x1, x2, x3,y3:integer;
I, I1, I2, I3: real; gd, gm: integer;
begin
clrscr;
gd:=detect;
initgraph(gd, gm, ‘путь к драйверу’);
SetTextStyle(0, 0, 2);
OutTextXY(200, 30, ‘Модель атома’);
cx:=GetMaxX div 2; {определить центр экрана- положение ядра}
cy:=GetMaxY div 2;
PieSlice(cx, cy, 0, 360, Rc);{нарисовать ядро атома}
SetColor(Red);
SetLineStyle(0, 0, 3);
Line(cx-7, cy, cx+7, cy);
Line(cx, cy-5, cx, cy+5);
SetLineStyle(0,0,1);
SetFillStyle(1,1);
I:=Pi/4; {задать начальное положение 4 электронов}
I1:= - Pi/4;
I2:= - Pi/2;
I3:= Pi/2;
SetTextStyle(0, 0, 1);
SetColor(jellow);
OutTextXY (180, 420,’Для отмены нажмите любую клавишу’);
While not KeyPressed do {повторять, пока не нажата любая клавиша }
begin {определить координаты электронов}
x:=Round (Ra*cos(I)) +cx; Y:= Round(k+Ra*sin(I)) +cy;
x1:= Round((Ra+Dr)*cos(I1)) +cx; y1:= Round (k*(Ra+Dr)*sin(I1)) +cy;
x2:= Round((Ra-Dr)*cos(I2)) +cx; y2:= Round (k*(Ra-Dr)*sin(I2)) +cy;
x3:= Round((Ra-Dr)*cos(I3)*2.3) +cx; y3:= Round (k*(Ra-Dr)*sin(I3)*2.3) +cy;{установить синий цвет и нарисовать электроны}
SetColor(1);
Circle(x, y, Re);
PutPixel(x, y, 2);
Circle(x1, y1, Re);
PutPixel(x1, y1, 2);
Circle(x2, y2, Re);
PutPixel(x2, y2, 2);
Circle(x3, y3, Re);
PutPixel(x3, y3, 2);
Delay(Step); {нар

Наши рекомендации