Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лабор_4.doc
Скачиваний:
1
Добавлен:
29.04.2019
Размер:
178.69 Кб
Скачать

3. Основные сведения.

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

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

Рассмотрим все три варианта программной реализации.

Вариант 1.

Реализация прямолинейного движения трех шаров с разными скоростями на темном экране.

Программный код написан в части, ограниченной инструкциями BeginPaint(HWindow,PS) ... EndPaint(HWindow,PS). Сначала зальем экран фоновым цветом:

r.top:=0;r.left:=0;r.bottom:=768;r.right:=1024;

create_brush(bs_solid,rgb(0,0,0),hs_cross,ps.hdc);

fillrect(ps.hdc, r, brush);

delete_brush(ps.hdc);

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

repeat

x0:=v1+3*i;

create_brush(bs_solid,rgb(200,0,0),hs_bdiagonal,ps.hdc);

ellipse(ps.hdc,x0+20*a,y0+20*a,x0+50*a,y0+50*a);

delete_brush(ps.hdc);

x0:=v3+2*i;

create_brush(bs_solid,rgb(0,0,200),hs_cross,ps.hdc);

ellipse(ps.hdc,x0,y0-80*a,x0+30*a,y0-80*a+30*a);

delete_brush(ps.hdc);

x0:=v2+i;

create_brush(bs_solid,rgb(0,200,0),hs_cross,ps.hdc);

ellipse(ps.hdc,x0,y0-50*a,x0+30*a,y0-50*a+30*a);

delete_brush(ps.hdc);

{создали фоновую кисть}

create_pen(ps_solid,rgb(0,0,0),ps.hdc,3);

{пустой цикл}

for j:=1 to 50000 do begin end;

x0:=v1+3*i;

arc(ps.hdc,x0+20*a,y0+20*a,x0+50*a,y0+50*a,x0+25*a,y0,x0+50*a, y0+50*a);

x0:=v2+i;

arc(ps.hdc,x0-2,y0-2-50*a,x0+2+30*a,y0+2-50*a+30*a,x0+15*a, y0-50*a,x0+30*a,y0-50*a+30*a);

x0:=v3+2*i;

arc(ps.hdc,x0,y0-80*a,x0+30*a,y0-80*a+30*a,x0+15*a,y0-80*a, x0+30*a,y0-80*a+30*a);

delete_pen(ps.hdc);

i:=i+1;

until (i>=1024);

Для аккуратной работы иногда приходится устанавливать инструмент пожирнее, чтобы избежать незакрашенных участков шлейфа. Предпочтительно использовать инструкции циклов без параметров, так как при сворачивании окна заново отрабатывается процедура wmPaint, устанавливающая начальное значение параметра i=0. Параметр масштабирования a обусловливает изменение размеров шаров, а координаты x0, y0 изменяют положение шаров на экране. Параметры v1,v2,v3 определяют стартовую позицию каждого из шаров. Скорость перемещения шаров различна и определяется коэффициентом при i. Максимальную среди шаров скорость перемещения имеет красный шар. Общий темп изменения параметра i задает параметр j цикла задержки. Чем меньше этот параметр, тем быстрее движутся шары.

На рис. 1 показаны некоторые параметры движения шаров.

Рис. 1. Движение трех шаров.

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

1. Минимизируют площадь элементов, изменяющих свое положение в пространстве и затираемых фоновым цветом.

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

Например изобразим 3 кадра вращения треугольника внутри круга

Равномерность вращения будет зависеть от количества кадров за один оборот.

Вариант 2.

Создадим движущийся по кругу и мерцающий полигон используя перерисовку отдельной зоны экрана на которой расположен сам объект.

Для данного варианта необходимо воспользоваться сообщением wm_Timer, при отработке которого будет происходит перерисовка не всего экрана, а только той части, которая изменяется. Естественно включается программный таймер, интервал дискрет которого определяется в интервале 1..100 миллисекунд. Параллельно будет написана процедура реакции на нажатие каких-либо клавиш wmKeyDown. Для корректного выхода из программы при нажатии на Esc или применении команды Close (кнопка в правом верхнем углу Windows-окна или нажатие комбинации клавиш Alt+F4) будет использована процедура wmDestroy.

Декларации включают все требуемые процедуры:

type

PointerPaintInWindow=^TPaintInWindow;

TPaintInWindow=object(TWindow)

constructor Init;

destructor Done;

virtual;

procedure wMPaint(var Msg:TMessage);

virtual wm_First + wm_Paint;

procedure WMTimer(var Msg:TMessage);

virtual wm_First + wm_Timer;

procedure wmKeyDown(var Msg:TMessage);

virtual wm_First + wm_KeyDown;

procedure WMDestroy(var Msg:TMessage);

virtual wm_First + wm_Destroy;

procedure SetupWindow; virtual;

end;

TPaintApp=Object(TApplication)

procedure InitMainWindow;

virtual; end;

Раскроем содержание каждой из дополнительных процедур.

procedure TPaintInWindow.SetupWindow;

begin

inherited SetupWindow;

x0 := 200;

y0 := 300;

Rad := 200;

Angle := 0;

a:=3;

SetTimer(PaintApp.MainWindow^.HWindow, 1, 75, nil);

end;

Данная процедура выполняется во время запуска программы (ее первичное появление обозначено словом inherited), В ней определяются переменные изображения (Rad – радиус круга вращения, Angle– начальный угол, x0,y0 – координаты базовой точки, a – параметр масштабирования). Также включается программный таймер с интервалом 1 мс и идентификатором 1. Таким программных таймеров можно включить несколько (с разными интервалами и для разных зон, клипов), например:

SetTimer(HWindow, 2, 100, nil);

SetTimer(HWindow, 3, 200, nil);

SetTimer(HWindow, 4, 100, nil);

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

procedure TPaintInWindow.WMDestroy(var Msg: TMessage);

begin

KillTimer(HWindow, 1);

KillTimer(HWindow, 2);

KillTimer(HWindow, 3);

KillTimer(HWindow, 4);

PostQuitMessage(0);

end;

Функция PostQuitMessage(0) помещает в очередь сообщение wm_Quit. Когда функция PeekMessage функции Keypr получает это сообщение из очереди, цикл обработки сообщений завершает свою работу и программа завершается с кодом TMsg.wParam (в нашем случае 0 – это успешное завершение).

Реализация процедуры для выхода из программы при нажатии на клавишу Esc будет выглядеть так:

procedure TPaintInWindow.WMKeyDown(var Msg:TMessage);

begin

if Getkeystate(vk_Escape)<0 then begin CloseWindow;

PostQuitMessage(0); end; end;

Инструкция CloseWindow позволяет отобразить курсор после закрытия окна программы. Функция KeyPr немного видоизменилась.

function KeyPr: Boolean;

var

M: TMsg;

begin

while PeekMessage(M, 0, 0, 0, pm_Remove) do

begin

TranslateMessage(M);

DispatchMessage(M);

end;

KeyPr := KeyCount > 0;

end;

Оформим процедуру прорисовки звезды отдельно, используя параметры: контекст устройства, координаты базовой точки и цвет.

procedure DrawStar(cont:hdc; x0,y0,a:integer; color:longint);

var

matrix:array[1..30]of tpoint;

begin

matrix[1].x:=x0; matrix[1].y:=y0;

matrix[2].x:=x0+30*a; matrix[2].y:=y0-5*a;

matrix[3].x:=x0+35*a; matrix[3].y:=y0-35*a;

matrix[4].x:=x0+40*a; matrix[4].y:=y0-5*a;

matrix[5].x:=x0+70*a; matrix[5].y:=y0;

matrix[6].x:=x0+40*a; matrix[6].y:=y0+5*a;

matrix[7].x:=x0+35*a; matrix[7].y:=y0+35*a;

matrix[8].x:=x0+30*a; matrix[8].y:=y0+5*a;

create_brush(bs_solid,color,hs_cross,cont);

polygon(cont,matrix,8);

delete_brush(cont);

end;

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

procedure TPaintInWindow.WMTimer(var Msg: TMessage);

var

rect:TRect;

begin

if (Msg.WParam = 1) then

begin

rect.left := trunc(x0+Rad*cos(Angle*pi/180.0)) ;

rect.top := trunc(y0+Rad*sin(Angle*pi/180.0))-35*a;

rect.right := rect.left + 70*a+1;

rect.bottom := rect.top + 70*a+1;

InvalidateRect(HWindow, @rect, true);

Angle := (Angle+1) mod 360;

end;

if (Msg.WParam = 3) then

begin

Randomize;

color := rgb(random(255),random(255),random(255));

end;

if (Msg.WParam = 4) then

begin

Randomize;

color2 := rgb(random(255),random(0),random(0));

end;

end;

Сама процедура отработки сообщения wm_Paint выглядит довольно компактно, по сравнению с первым вариантом.

procedure TPaintInWindow.WMPaint(var Msg:TMessage);

begin

beginPaint(HWindow,PS);

DrawStar(ps.hdc,trunc(x0+Rad*cos(Angle*pi/180.0)), trunc(y0+Rad*sin(Angle*pi/180.0)), a, color);

endPaint(HWindow,PS);

Вариант 3.

Создадим перемещение по экрану закрашенного круга под действием активации клавиш со стрелками используя функцию скроллинга экрана.

В процедуре wmKeyDown для захвата контекста устройства с дальнейшей дорисовкой с помощью графических функций может использоваться getdc. Далее контекст устройства может быть освобожден от использования. Скроллинг выполняется функцией ScrollWindow, которая выполняется в процедуре отработки нажатия клавиш управления wmKeyDown. Аргументами функции является величина прокрутки (в данном случае 5 пикселей)

procedure TPaintinwindow.wmKeyDown;

var dc:hdc;

begin

if GetKeyState(vk_right)<0 then

begin

scrollwindow(Hwindow,5,0,nil,nil);

dc:=getdc(Hwindow);

x0:=x0+5;

y0:=y0;

releaseDC(Hwindow,dc);

end;

if GetKeyState(vk_left)<0 then

begin

scrollwindow(Hwindow,-5,0,nil,nil);

x0:=x0-5;

y0:=y0;

end;

if GetKeyState(vk_down)<0 then

begin

scrollwindow(Hwindow,0,5,nil,nil);

x0:=x0;

y0:=y0+5;

end;

if GetKeyState(vk_up)<0 then

begin

scrollwindow(Hwindow,0,-5,nil,nil);

x0:=x0;

y0:=y0-5;

end;

end;

Процедура отработки сообщения wm_Paint выглядит также компактно, по сравнению с первым вариантом. Параметры b и c определяют коэффициенты сжатия по горизонтали и вертикали (если необходимо отобразить эллипс).

procedure TPaintInWindow.WMPaint(var Msg:TMessage);

begin

beginPaint(HWindow,PS);

x0:=170;y0:=50;a:=10;b:=1;c:=1;

create_brush(bs_solid, rgb(200,0,0),hs_cross,ps.hdc);

create_pen(ps_solid, rgb(200,0,0),ps.hdc,2);

ellipse (ps.hdc,x0+10*a,y0+10*a,x0+20*a*b,y0+20*a*c);

delete_brush(ps.hdc);

delete_pen(ps.hdc);

endPaint(HWindow,PS);

end;

Приведем виртуальные коды некоторых клавиш:

vk_left – клавиша Влево 

vk_rightклавиша Вправо 

vk_upклавиша Вверх 

vk_downклавиша Вниз 

vk_addклавиша Серый плюс

vk_subtractклавиша Серый минус

vk_nextклавиша PageDown

vk_priorклавиша PageUp

vk_escapeклавиша Esc

vk_lbutton левая кнопка мыши

vk_rbutton правая кнопка мыши

vk_returnклавиша Enter

vk_shiftклавиша Shift

vk_controlклавиша Ctrl

vk_spaceклавиша Пробел

vk_f1,…,vk_f10клавиши F1…F10

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]