Главная страница

учебник по паскалю. Программа 5 Алгоритм 5 Свойства алгоритма 6 Формы записи алгоритма 6


Скачать 2.21 Mb.
НазваниеПрограмма 5 Алгоритм 5 Свойства алгоритма 6 Формы записи алгоритма 6
Анкоручебник по паскалю.doc
Дата03.02.2017
Размер2.21 Mb.
Формат файлаdoc
Имя файлаучебник по паскалю.doc
ТипПрограмма
#1964
страница29 из 35
1   ...   25   26   27   28   29   30   31   32   ...   35

25. Модуль graph и создание графики на Паскале



Для работы с графикой из программы на Паскале в папке, откуда она запускается, должен присутствовать файл egavga.bgi. Он представляет собой графический драйвер, предназначенный для управления видеопамятью в режимах поддержки мониторов типов EGA и VGA. Разумеется, современные мониторы давно "переросли" эти два исторически распространенных класса дисплеев. Однако, на любом современном компьютере поддержка видеорежимов EGA и VGA по-прежнему возможна, если не напрямую, то через специальную программу-эмулятор (см. конец главы).

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

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

Библиотека graph.tpu подключается стандартным способом с помощью директивы uses в разделе описаний программ:

uses graph;

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

  • координата x -- номер пиксела в строке. Нумерация выполняется слева направо, начиная с 0;

  • координата y -- номер строки пикселов. Нумерация строк производится сверху вниз, начиная с 0.

Таким образом, координаты левого верхнего угла экрана равны (0, 0).

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

Классический Паскаль поддерживает монитор CGA, имеющий разрешение до 320200 пикселов, монитор EGA с разрешением 640350, монитор VGA с разрешением до 640480. Работу с более современными и мощными графическими устройствами, относящимися к классу superVGA, Паскаль непосредственно не поддерживает, хотя существуют созданные независимыми разработчиками графические драйверы этих режимов.

Графический режим работы экрана кроме количества пикселов характеризуется определенной палитрой -- набором видимых цветов. Каждая палитра состоит из 4 цветов для монитора CGA или 16 цветов для EGA и VGA.

Установка графического режима осуществляется путем обращения к процедуре initgraph:

initgraph(var gd:integer, var gm:integer,

pt:string);

Целочисленные переменные gd и gm задают тип графического драйвера и режим его работы, строковая переменная pt -- путь к файлу *.bgi. Например, при выборе основного для Паскаля видеорежима VGA с разрешением 640480 пикселов и поддержкой 16 цветов подойдет следующий код:

uses graph;

var gd,gm,error: integer;

begin

gd:=VGA; {адаптер VGA}

gm:=VGAHi; {режим 640*480пикс.*16 цветов}

initgraph(gd,gm,'');

error:=graphresult;

if error <> grOk then begin

write ('Ошибка графики: ',

grapherrormsg(error));

readln; halt;

end;

line (0,0,getmaxx,getmaxy);

readln; closegraph;

end.

Так как путь к файлу egavga.bgi указан пустым, предполагается, что он находится в текущей папке. После перехода в графический режим процедурой line рисуется линия из левого верхнего в правый нижний угол экрана, затем, после нажатия Enter, графический режим закрывается и происходит выход из программы.

Для автоматического выбора максимально возможного режима переменной gd необходимо присвоить значение detect, при этом переменные gm и pt не определяются, если в текущем каталоге, в котором находится система Турбо Паскаль, имеются файлы *.bgi. Пример:

uses graph; var gd,gm: integer;

begin

gd:=detect; initgraph(gd,gm,''); ...

Рассмотрим основные стандартные процедуры и функции модуля graph.

closegraph;

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

cleardevice;

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

function getmaxx:integer;

— функция возвращает максимальную координату пиксела по оси x.

function getmaxy:integer;

— функция возвращает максимальную координату пиксела по оси y.

setcolor(color:word);

— процедура устанавливает цвет рисования линий, точек и текста (аналог "пера" в программах для рисования). Цвета кодируются так же, как в текстовом режиме (см. табл. 24.1).

setfillstyle (style:word, color:word);

— процедура устанавливает цвет заполнения областей экрана (параметр color) и способ наложения цвета (параметр style). Является аналогом "кисти" в программах для рисования. Параметр color принимает значения, указанные в табл. 24.1, параметр style -- значения от 1 до 11. При style=1 происходит сплошное заполнение цветом, другие стили позволяют создать различные штриховки. Здесь и далее вместо цифр можно использовать символические имена стилей, узнать о них можно в справочной системе.

Приведем примеры.

setfillstyle (linefill,GREEN);

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

setfillstyle (solidfill,RED);

{ установили сплошную заливку красным}

Следующая процедура определяет стиль рисования линий:

setlinestyle (linestyle:word, pattern:word,

thickness:word);

Параметр linestyle (стиль линии) принимает значения от 0 до 4, значение 0 соответствует сплошной линии, параметр pattern при использовании готовых стилей со значением linestyle от 0 до 3 игнорируется, толщина линии thickness указывается значением 1 или 3 (в пикселах). Например, оператор setlinestyle (0,0,1); устанавливает стиль сплошной тонкой линии, а setlinestyle (1,0,3); -- толстую пунктирную линию. Для цифровых значений linestyle и thickness в библиотеке также определены символические имена, при значении linestyle=4 можно определить собственный стиль, задав его параметром pattern с помощью битовой маски.

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

putpixel(x,y:integer,color:word);

— процедура высвечивает на экране пиксел с координатами (x, y) цветом color;

function getpixel (x,y:integer):word;

— функция вернет код цвета пиксела с координатами (x, y).

line(x1,y1,x2,y2:integer);

— процедура рисует текущим цветом прямую линию с экранными координатами начала (x1, y1), и конца (x2, y2).

moveto(x,y:integer);

— процедура устанавливает текущую позицию рисования (пера, графического курсора) в точку с экранными координатами (x, y).

lineto(x,y:integer);

— процедура проводит прямую линию из текущей позиции пера в точку с экранными координатами (x, y). Линия проводится текущим цветом пера.

linerel(dx,dy:integer);

— процедура проводит прямую линию из текущей позиции в точку с приращением координат от текущих на dx и dy, приращения могут быть как положительными так и отрицательными. Таким образом, процедура linerel позволяет указывать, в отличие от line и lineto, не абсолютные, а относительные координаты точки, куда нужно провести линию.

rectangle(x1,y1,x2,y2:integer);

— процедура рисует прямоугольник с координатами левого верхнего угла (x1, y1) и правого нижнего угла (x2, y2). Цвет прямоугольника, как и других незакрашенных фигур, определяется установкой, сделанной процедурой setcolor.

bar(x1,y1,x2,y2);

— процедура рисует закрашенный прямоугольник с координатами углов (x1, y1) и (x2, y2). Цвет и стиль заливки определяются процедурой setfillstyle.

bar3d (x1, y1, x2, y2, depth :integer;

top:boolean);

— процедура рисует трехмерный параллелепипед. Параметр depth определяет глубину фигуры по оси x, top указывает, рисовать ли верхнюю грань:

bar3d (50,50,100,100,20,true);

Следующая процедура рисует многоугольник или ломаную линию:

drawpoly (numpoint:integer;

var polypoints);

Аналогичная процедура fillpoly создает закрашенный цветом заливки многоугольник. Покажем работу процедуры на примере:

var poly: array [1..10] of integer;

poly[1]:=20; poly[2]:=20;

poly[3]:=60; poly[4]:=30;

poly[5]:=60; poly[6]:=60;

poly[7]:=40; poly[8]:=80;

poly[9]:=20; poly[10]:=20;

drawpoly (5,poly);

Элементы с нечетными номерами массива poly задают x-координаты точек, а с четными -- y-координаты. Таким образом, в данном случае нарисован пятиугольник.

floodfill (x,y,bordercolor:integer);

мощная процедура, позволяющая закрасить любую замкнутую область, которой принадлежит точка (x, y) и которая ограничена по краям цветом bordercolor.

circle(x,y:integer,r:word);

— несложная процедура рисует окружность с центром в точке с координатами (x, y) и радиусом r.

arc(x,y:integer,sa,ea,r:word);

— процедура рисует дугу окружности с центром в точке с координатами (x, y), радиусом r, начальным углом sa и конечным углом ea. Углы sa и ea измеряются в градусах и отсчитываются против часовой стрелки от оси абсцисс.

Существуют также процедуры для рисования эллипсов и секторов.

Для вывода текста на графический экран имеются 2 основные функции.

outtextxy(x,y:integer,text:string);

— процедура выводит текст на экран, начиная с точки с координатами (x, y). Здесь text -- константа или переменная строкового типа, содержащая нужное сообщение. Текст выводится установленным цветом рисования линий. Заметим, что применение стандартных процедур write и writeln для вывода текста в графическом режиме нежелательно, так как они не позиционируют текст по пикселам и не учитывают установок цвета и фона графического экрана.

outtext(text:string);

— процедура выводит текст, заданный параметром, на экран, начиная с текущей позиции графического курсора.

Для краткости мы не рассматриваем методы привязки текста к позициям на экране.

В библиотеке graph нет процедур для вывода численных данных. Для этого необходимо сначала преобразовать число в строку с помощью процедуры str, а затем посредством операции '+' подключить строку к сообщению, выводимому процедурой outtextxy. Например:

max:=34.56; {Число}

str(max:6:2,smax);

{Преобразование числа max в строку smax}

outtextxy(400,40,'Максимум=' + smax);

{Вывод строки smax с комментарием}

Узнать ширину и высоту строки в пикселах можно с помощью стандартных функций function textwidth (s: string):word; и function textheight (s: string):word; соответственно.

Существуют также процедуры для управления внешними графическими шрифтами, хранящимися в файлах *.chr.

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

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

procedure centerstring

(x1,y1,x2,y2,color :integer; str: string);

var cx,cy:integer;

begin

setcolor (color); {Устанавливаем цвет}

rectangle (x1,y1,x2,y2); {Рамка}

rectangle (x1+2,y1+2,x2-2,y2-2);

cx:=(x1+ x2) div 2; {Координаты}

cy:=(y1+ y2) div 2; {центра}

settextJustify (centertext,centertext);

{Выравнивание текста по центру}

outtextxy (cx,cy,str); {Вывод строки}

end;

...

{ Обращение к данной процедуре: }

centerstring (100, 100, 200, 200, yELLOW,

'Hello!');

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


Рис. 25.1. Пересчет из декартовых координат в полярные
program SunHouse;

uses graph,crt;

var Driver, Mode: integer;

i,j,u,N,K,x2,y2:integer;

rad:real; sunx,suny:integer;
begin

{Не проверяем правильность ввода}

writeln ('Сколько этажей?'); read (N);

writeln ('Сколько окон на этаж?');

read (K);

Driver := VGA; Mode:=VGAHi;

initgraph(Driver, Mode,'');

{Домик}

setcolor (15);

rectangle (20, getmaxy-20-70*n,

20+k*50+(k+1)*20, getmaxy-20);

{Крыша}

moveto (20,getmaxy-20-70*n);

lineto(10,getmaxy-20-70*n);

lineto (20,getmaxy-40-70*n);

lineto (20+k*50+(k+1)*20,getmaxy-40-70*n);

lineto (30+k*50+(k+1)*20,getmaxy-20-70*n);

lineto (20+k*50+(k+1)*20,getmaxy-20-70*n);

{Линии между этажами}

for i:=1 To N Do

line (20, getmaxy-20-70*i,

20+k*50+(k+1)*20, getmaxy-20-70*i);

setfillstyle (solidfill, YELLOW);

{Окна на каждом этаже}

for i:=1 To N Do {Цикл по этажам}

for j:=1 To K Do begin {Цикл по окнам}

bar(20+(j-1)*70+20,getmaxy-20-(i-1)*70-

60,20+(j-1)*70+70, getmaxy-20-(i-1)*70-10);

end;

sunx:=getmaxx-50; suny:=50;

{Центр солнца – координаты на экране}

FillEllipse (sunx, suny, 30, 30);

{Рисуем контур солнца}

setcolor (YELLOW);

{Рисуем лучи}

u:=0;

while u<=360 Do begin

{угол u меняем от 0 до 360 градусов}

rad:=u*pi/180;

{перевод в радианы для функций sin,cos }

x2:=round(sunx+50*cos(rad));

y2:=round(suny+50*sin(rad));

{перевод из полярных координат в декартовы}

line (sunx,suny,x2,y2);

u:=u+12; {шаг по углу=12 градусов}

end;

repeat until keypressed;

closegraph;

end.
3. Этот пример реализует программу построения графика функции f(x), заданной отдельной подпрограммой, в границах [a, b] изменения аргумента x.

Схема пересчета значений (x, f(x)) при в экранные координаты приведена на рис. 25.2. Пересчет выполняется в 2 этапа.

Узнав с помощью процедур getmaxx, getmaxy размеры графического экрана и определив значение xstep -- шаг по x, соответствующий одному пикселу на экране, мы сможем обеспечить масштабирование графика по оси X. Для масштабирования по оси Y на первом этапе пересчета требуется также определить максимальное и минимальное значения f(x) на интервале [a, b] при изменении x с шагом xstep.

Второй этап связан с непосредственным пересчетом значений (x, f(x)) в экранные координаты (cx, cy). Для решения этой задачи воспользуемся формулой, согласно которой значение x, принадлежащее интервалу [a, b], можно линейно преобразовать в значение y, принадлежащее интервалу [c, d]: . Эта формула позволит получить коэффициенты преобразования величин (x, f(x)) к экранным координатам. Дополнительно придется учесть то, что экранная ось Y проведена сверху вниз.


Рис. 25.2. Пересчет из декартовых координат в экранные
program graphOfFun;

uses graph,crt;
function f(x:real):real;

{ Функция, график которой строим }

begin

f:=sin(x)+cos(x);

end;
function getreal(s:string):real;

var f:real; {Ввод числа с контролем ошибок}

begin

repeat

write (s);

{$I-}readln(f);{$I+}

if IoResult=0 then break

else writeln

('Ошибка! Введено не число');

until false;

getreal:=f;

end;
procedure Init;

{Инициализация графического режима }

{VGA 640*480 пикселов, 16 цветов}

var driver,mode,error:integer;

begin

driver:=VGA; mode:=VGAHi;

initgraph(driver,mode,'');

error:=graphresult;

if error<>0 then begin

{Не ноль означает ошибку!}

writeln;

write ('Не могу инициализировать ',

'графику! Ошибка ',grapherrormsg(error));

halt(1)

end;

end;
var a,b: real; { Границы изменения x }

xmax,ymax: integer; { Размеры графического

экрана по длине и высоте }

xstep:real; { Шаг по x }

x,fx:real;

fmax,fmin:real;

cx,cy:integer; { Экранные координаты }

oldx,oldy:integer;{В этих переменных будем

запоминать координаты последней точки,

чтобы соединить ее с текущей }

xcoef,ycoef:real; {Коэффициенты пересчета к

экранным координатам }

ss:string;

begin

clrscr;

repeat

a:=getreal ('Левая граница по x=');

b:=getreal ('Правая граница по x=');

if a>b then write('Ошибка!Левая граница',

' должна быть меньше правой');

until a
Init; { Инициализировать графику }

xmax:=getmaxx; ymax:=getmaxy;

{ размеры графического экрана }

xstep:=(b-a)/(xmax-19);

{ шаг по x, соответствующий 1 пикселу.}

x:=a; fmax:=f(a); fmin:=fmax;

while x<=b do begin

fx:=f(x);

if fx>fmax then fmax:=fx

else if fx
x:=x+xstep;

end;

xcoef:=(xmax-19)/(b-a);

ycoef:=(ymax-19)/(fmax-fmin);

{ обрамление графика: }

setfillstyle (solidfill,CYAN);

bar (10,10,xmax-10,ymax-10);

setcolor (YELLOW);

rectangle (9,9,xmax-9,ymax-9);

str (a:8:3,ss); outtextxy (2,ymax-8, ss);

str (b:8:3,ss); outtextxy

(xmax-66,ymax-8, ss); {Границы по x }

settextstyle (DefaultFont,VertDir,1);

{ Границы по y выводим вертикально }

str(fmax:8:3,ss); outtextxy(9,2, ss);

str(fmin:8:3,ss);outtextxy(9,ymax-66, ss);

setcolor (White);{цвет рисования графика}

x:=a;

while x<=b do begin

fx:=f(x);

cx:=10+round((x-a)*xcoef);

cy:=ymax-10-round((fx-fmin)*ycoef);

putpixel (cx,cy,LightRED);

if x>a then line (oldx,oldy,cx,cy);

{ Соединяем две последние точки }

oldx:=cx; oldy:=cy;

{ Запоминаем текущую точку }

x:=x+xstep;

end;

repeat until keyPressed;

closegraph;

end.

Недостаток этой программы -- отсутствие пропорционального масштабирования по осям x и y. Подумайте, как ее можно улучшить. Листинг 12 из Приложения 4 представляет более объемную графическую программу, реализующую несложную компьютерную игру. Функция Draw этого листинга может также служить примером обработки 16-цветного изображения в формате BMP из программы на Паскале.

Решение задач, связанных с выводом графики на экран, часто требует сохранения участка экранного изображения в оперативной памяти с последующим восстановлением прежней картинки. Для решения этой проблемы в библиотеке graph предусмотрен набор соответствующих функций.

В первую очередь требуется оценить объем памяти, требуемый для сохранения участка экрана. Стандартная функция библиотеки graph, имеющая вид imagesize (x1,y1,x2,y2:integer):word, где x1, y1 -- экранные координаты верхнего левого, а x2, y2 -- правого нижнего угла, возвращает число байт памяти, необходимых для сохранения заданной прямоугольной области. Эта функция может определить объем памяти до 64 Кб включительно, так как тип возвращаемого значения -- беззнаковое целое типа word. Если количество требуемой памяти больше либо равно 64 Кб, возвращается значение ошибки -11 (grError). Разумеется, вызов функции предполагает, что монитор находится в графическом режиме.

После того, как требуемое количество байт определено, программа должна позаботиться о выделении участка оперативной памяти, предназначенного для сохранения изображения. Это легко сделать, используя системную процедуру getmem (var p:pointer; size:word), где объем памяти size ранее определен функцией imagesize, а переменная p представляет собой указатель. Ранее незнакомый нам тип данных "указатель" служит для косвенного вызова одних переменных через другие. Фактически, переменная-указатель хранит адрес другой типизированной переменной и может обратиться к ней, используя синтаксис p^, где p -- имя указателя. Применение указателей позволяет создавать динамические переменные, способные в разные моменты времени адресовать различные участки оперативной памяти, в которых хранятся данные. Самый большой блок памяти, который может выделить getmem, также равен 64 Кб. Освободить ранее занятую память можно процедурой Freemem (var p:pointer; size:word).

Наконец, третий шаг -- сохранить участок экрана, используя только что сформированный в оперативной памяти буфер. Для этой цели достаточно использовать процедуру Getimage (x1,y1,x2,y2:integer; var p). Здесь параметры x1,y1,x2,y2 имеют тот же смысл, что для функции imagesize, а нетипизированный параметр-указатель p получен процедурой getmem.

Теперь требуемый участок экрана сохранен в памяти и может быть занят новым изображением. После того, как изображение выполнило свои функции и нужно восстановить прежний фрагмент экрана, достаточно вызвать процедуру putimage (x,y:integer; var p; mode:word), где x,y -- экранные координаты левого верхнего угла восстанавливаемой области, p -- указатель на сохраненную память, а переменная mode определяет, какая двоичная операция будет использована при выводе изображения на экран. Для неизмененного восстановления изображения следует передавать в качестве mode значение NormalPut, другие возможные значения параметра -- copyPut, XORPut, ORPut, ANDput и NOTPut. Все описанные функции использованы в листинге, приведенном ниже. Его изучение поможет вам в написании аналогичных программ, поддерживающих движение по экрану графических объектов.

uses graph,crt;

var Gd, Gm : integer;

P : pointer;

size : word;

x,y,width,height: integer;

ch:char;

changed:boolean;

begin

{Инициализация графики}

Gd:=VGA; Gm:=VGAHi;

initgraph(Gd, Gm, '');

if graphresult <> grOk then halt(1);

{Отрисовка фона}

setfillstyle(xHatchFill, CYAN);

bar(0, 0, getmaxx, getmaxy);

{Параметры активного окна}

x:=getmaxx div 2;

y:=getmaxy div 2;

width:=40;

height:=30;

{Выделение памяти для сохранения

фона под окном}

size:=imagesize(x,y,x+width-1,y+height-1);

getmem(P, size);

getimage(x, y, x+width-1, y+height-1, P^);

{Первая отрисовка активного окна}

setfillstyle(solidfill, RED);

bar (x,y,x+width-1,y+height-1);

{Признак изменения положения окна}

changed:=false;

repeat {Цикл движения объекта}

ch:= readkey; {Читаем код клавиши}

if ch=#0 then begin

{Если это расширенный код...}

ch:= readkey; {то читаем второй байт}

case ch of

{Реагируем только на 4 клавиши:}

#72: if y>0 then changed:=true;

{стрелка вверх}

#80: if y+height
changed:=true; {стрелка вниз}

#75: if x>0 then changed:=true;

{стрелка влево}

#77: if y+width
changed:=true; {стрелка вправо}

end;

if changed=true then begin

{если флаг реакции выставлен}

PutImage(x, y, P^, NormalPut);

{восстанавливаем экран под окном}

case ch of

{и меняем нужную координату окна}

#72: dec(y);

#80: inc(y);

#75: dec(x);

#77: inc(x);

end;

getimage(x,y,x+width-1,y+height-1,P^);

{сохраняем экран под новым положением окна}

bar (x,y,x+width-1,y+height-1);

{и перерисовываем окно}

changed:=false;

{сбросить флаг изменения}

end;

end;

until ch=#27; {...пока не нажата Esc}

Freemem (p,size); {освобождаем память}

closegraph; {и закрываем графику}

end.

Говоря о написании графических программ применительно к Паскалю, нельзя обойти стороной вопрос о поддержке DOS-графики современными компьютерами. К сожалению, многие современные платформы не поддерживают графические видеорежимы DOS. Помочь может эмулятор DOS-машины, такой как свободно распространяемая программа DOSBox. Скачав DOSBox по адресу http://dosbox.sourceforge.net и установив ее, мы можем запускать приложения DOS в любом видеорежиме с поддержкой (эмуляцией) многочисленных устаревших программных и аппаратных решений.

Желательно также установить оболочку эмулятора, позволяющую создавать для DOS-приложений настраиваемые ярлыки. Оболочка DOSShell доступна по адресу http://www.loonies.narod.ru/dosshell.htm, а узнать об эмуляторах DOS больше Вы можете по адресам http://ru.wikipedia.org/wiki/DOSBox и http://gh.gameslife.ru/text/dosbox.htm.

1   ...   25   26   27   28   29   30   31   32   ...   35


написать администратору сайта