Розширений додатковий матеріал для вивчення теми "Комп'ютерна графіка Pascal"
Структура:
Щоб почати роботу над створення найпростішого “мультика” (зображення, що рухається), спочатку розберемося, як воно створюється в реальних умовах. Всі знають, що художник мультиплікатор малює серію зображень, в кожному з яких показується один і той же рух з ледь помітними змінами. Наприклад так, як це показано на наступному малюнку.
Тепер, якщо зображення швидко змінювати одне за одним, ми не помічаємо зміну малюнків, а бачимо рух цього персонажу (в даному випадку Незнайко піднімає руку). Поміркуємо, як відтворити послідовність схожих об’єктів на екрані монітору. Перше, що спадає на думку, це намалювати зображення, затримати його трохи на екрані, витерти зображення (очистити екран) та вивести нове зображення з ледь помітними змінами. При достатньо великій швидкості малювання, око людини не помітить зміни малюнків і їй буде здаватися, що об’єкт рухається. Розв’яжемо таким методом наступну задачу.
Задача. Відобразити на екрані падіння жовтої кульки.
Для кращого розуміння, пригадаємо деякі визначення з курсу фізики. Падіння – це рух – переміщення тіла у просторі з часом. Переміщення – зміна положення тіла, відносно його початкової позиції (зміна координат, відносно початкових).
В природі існує 2 типи рухів – прямолінійний та криволінійний. Останній задається, або хаотичною зміною координат (броунівський рух частинок), або через певні залежності чи функції (функції sin x, cos x, x, рух по колу R2=x2+y2, тощо).
При прямолінійному русі тіла, або точки, їх траєкторія є прямою лінією. Опис здійснюється через рівняння прямої пропорційної залежності.
У фізиці існує також поняття рівномірного та прискореного руху. Рівномірний рух описується за наступним законом:
𝑥 = 𝑥0+𝑣𝑥𝑡
При рівномірному русі переміщення тіла здійснюється за одиницю часу на однакові проміжки.
Переміщення об’єктів у графічному режимі Pascal можна зобразити через зміну координат. Проте ця зміна повинна відбуватися не моментально, а через певні проміжки часу, щоб людське око встигало її помітити. Плин часу в програмуванні можна зобразити як кроки виконання циклу: I крок, II крок, III крок, і т.д.
На площині екрану прямолінійне переміщення може бути горизонтальним (зміна координати Х, координата по осі 0Y – стала), вертикальним (зміна координати Y, координата по осі 0X – стала) та діагональним (або наближеним до нього) – задається одночасна зміна координат по осях 0X та 0Y.
Вертикальний рух вниз |
Горизонтальний рух праворуч |
Діагональний рух з лівого верхнього кута в правий нижній |
|
|
|
100=50+50 y=y0+d x=const |
100=50+50 x=x0+d y=const |
100=50+50; 100=50+50; x=x0+dx; y=y0+dy;
|
Виконаємо реалізацію даного алгоритму, який здійснить падіння кульки згори вниз. Встановимо початкові умови:
1. Оскільки, згідно умови кулька має бути жовтого кольору – задамо колір ліній та стиль заповнення: SetColor (yellow);
SetFillStyle(1,yellow);
2. Задамо радіус кульки в розділі const, оскільки дана величина в ході виконання програми не змінюється. const r=50;
3. Визначимо початкову точку, звідки буде здійснюватися рух кульки. Нехай вона рухається відносно середини екрану по осі 0X, і починає рух згори. Для цього змістимо кульку 50px вниз (на величину радіуса): x:=getmaxx div 2; ..y:=50;
4. Побудову зображення слід виконувати в циклі, оскільки буде виконуватись повторення одних і тих самих дій. Використаємо цикл Repeat. Так, як даний цикл є умовним, то для нього необхідно задати умову зупинки виконання повторення. Крайньою нижньою точкою зупинки кульки буде максимальна координата по осі 0Y мінус величина радіуса:
repeat
Circle (x,y,r);
FloodFill(x,y,yellow); y:=y+1;
Реалізуємо дану програму:
Результатом виконання алгоритму вийшло накладання одного круга на попередній. При створенні мультфільмів чи анімацій кадри замінюються один на інший, а не накладаються. В нашому випадку, для того, щоб побачити рух однієї кульки, треба попереднє зображення стерти. Для цього використаємо процедуру ClearDevice – очистка екрану в графічному режимі.
Падіння кульки виконується занадто швидко. Даним процесом також можна керувати, використовуючи процедуру Delay (t) – процедура затримки часу виконання алгоритму, параметр t – час затримки, вказаний в мілісекундах (1с=1000мс). Остання процедура використовується з модулем CRT. Процедуру Delay слід включати у тіло циклу, щоб уповільнити рух:
Repeat
ClearDevice;
Circle (x,y,r);
FloodFill(x,y,yellow); y:=y+1; Delay(1500);
В результаті розбору задачі ми отримали наступний алгоритм дій:
1. Побудувати бажаний об’єкт в початковій позиції.
2. Очистити екран.
3. Побудувати попередній об’єкт в іншій позиції.
4. Повторити пункти 2-3.
Код програми: uses crt,graph; const r=50; var gd,gm:integer;
x,y:integer; begin gd:=detect;
InitGraph (gd,gm,'..\bgi');
if graphresult<>0 then writeln('ERROR!!!') else begin
SetColor (yellow); SetFillStyle(1,yellow); x:=getmaxx div 2;
..y:=50; repeat
Circle (x,y,r);
FloodFill(x,y,yellow);
Until y=getmaxy-r; readkey; end; CloseGraph; end.
Для того, щоб зациклити падіння кульки, тобто, щоб кулька після досягнення крайньої нижньої точки знову починала рух з крайньої верхньої. Для цього задамо відповідну умову: if y=getmaxy-r then y:=r;
де r – радіус кулі.
Дану команду потрібно прописати в тілі циклу, а умову зупинки циклу вказати як KeyPressed – функція, яка повертає значення true при натисненні будь-якої клавіші на клавіатурі.
Як бачимо, у нас будується рухоме зображення, але при русі фігура мерехтить і не встигає повністю заповнюватись. Спробуємо усунути ці недоліки і для цього розглянемо наступний підхід: існують деякі режими, що підтримують кілька графічних сторінок, кожна з яких може містити різні зображення. В один момент часу ми можемо бачити тільки одну сторінку, але в той самий момент можемо готувати складне зображення на інших сторінках і потім миттєво виводити їх на екран, зменшуючи таким чином миготіння (людина не бачить малювання окремих деталей пейзажу).
Один з таких режимів VGAMed дозволяє програмісту виводити 16-кольорове зображення з роздільною здатністю 640 на 350 пікселів, при цьому він підтримує дві графічних сторінки.
При побудові анімованих зображень слід задавати наступні параметри для ініціалізації графічного режиму:
DRIVER:=VGA; MODE:=VGAMED;
Для того, щоб скористатися цими сторінками, ми можемо в програмі застосувати дві процедури:
SetActivePage (Page:integer) – задається номер активної сторінки (Page), тобто сторінки, на якій в даний момент буде будуватися зображення;
SetVisualPage (Page:integer) – задається номер візуальної сторінки, тобто тієї сторінки, що являється видимою в даний момент.
В запропонованому режимі існує тільки дві сторінки, що мають номера 0 та 1, тому зміну сторінок можна виконувати змінною Page, що буде змінюватись за наступним законом: Page := 1-Page,
причому активна сторінка стає видимою тільки після того, як на ній повністю
побудовано нове зображення.
Покажемо застосування цього прийому побудови зображення, що рухається, на тій самій задачі. Код програми: uses crt,graph; const r=50; var gd,gm:integer; x,y,Page:integer;
begin gd:=vga; gm:=vgamed; InitGraph (gd,gm,'..\bgi'); if graphresult<>0 then writeln('ERROR!!!') else begin
SetColor (yellow); SetFillStyle(1,yellow); x:=getmaxx div 2;y:=r; repeat
SetActivePage(Page);
Circle (x,y,r);
FloodFill(x,y,yellow);
SetVisualPage(Page);
Delay (15); y:=y+1; Page:=1-Page;
readkey; end;
CloseGraph; end.
Як бачимо, мерехтіння значно зменшилось.
Для створення анімації кульки, яка буде моделювати рух кульки згори вниз - знизу вгору нескінченно, можна використати один з наступних алгоритмів: I. – використання трьох циклів:
1. Описати алгоритм руху кульки вниз;
2. Описати алгоритм руху кульки вгору;
3. Описати цикл, який пуде почергово повторювати попередні 2 пункти.
Умовою зупинки виконання алгоритму є натиснення довільної клавіші на клавіатурі. uses crt,graph; const r=50; var gd,gm:integer; x,y,Page:integer; begin
gd:=vga; gm:=vgamed; InitGraph (gd,gm,'..\bgi'); if graphresult<>0 then writeln('ERROR!!!') else begin
SetColor (yellow); SetFillStyle(1,yellow); x:= getmaxx div 2;y:=r; repeat repeat
SetActivePage(Page);
ClearDevice;
Circle (x,y,r);
FloodFill(x,y,yellow);
SetVisualPage(Page);
Delay (15); y:=y+1; Page:=1-Page;
Until (y>=getmaxy-r) or keypressed; repeat SetActivePage(Page);
ClearDevice;
Circle (x,y,r);
FloodFill(x,y,yellow);
SetVisualPage(Page); Delay (15); y:=y-1;
Page:=1-Page;
Until (y<=r) or keypressed; Until keypressed; readkey; end;
CloseGraph; end.
II. – використання розгалуження:
Для реалізації даного алгоритму введемо додаткову змінну a, яка буде слугувати перемикачем напрямку руху, і при виконанні однієї з умов буде змінювати свій знак (наприклад з +10 на -10 і навпаки):
if y>=getmaxy-r or y<=r then a:=-a;
Код програми: uses crt,graph; const r=50; var gd,gm:integer; x,y,a,Page:integer; begin gd:=vga; gm:=vgamed; InitGraph (gd,gm,'..\bgi'); if graphresult<>0 then writeln('ERROR!!!') else
begin SetColor (yellow); SetFillStyle(1,yellow); x:= getmaxx div 2;y:=r; a:=1; repeat
SetActivePage(Page);
Circle (x,y,r);
FloodFill(x,y,yellow);
SetVisualPage(Page); Delay (15); y:=y+a; Page:=1-Page;
Until keypressed; readkey; end;
CloseGraph; end.