10.2 Побудова графіків
Алгоритм побудови графіка неперервної функції на відрізку полягає в наступному: необхідно побудувати точки в декартовій системі координат і з'єднати їх прямими лініями. Координати точок визначаються за такими формулами:
де - кількість відрізків на відрізку .
де .
Чим більше точок буде зображено, тим більше плавним буде побудований графік.
При перенесенні цього алгоритму на форму або інший компонент Lazarus враховує розміри і особливості компонента (вісь ОX спрямована зліва направо, її координати лежать в межах від 0 до Width; вісь OY спрямована вниз, її координати знаходяться в межах від 0 до Height). Значення координат X і Y повинні бути цілими.
Необхідно перерахувати всі крапки з "паперової" системи координат ( змінюється в межах від до , змінюється від мінімального до максимального значення функції) в "компонентную1" (в цій системі координат вісь абсцис позначимо літерою , А вісь ординат - буквою ).
Для перетворення координати в координату побудуємо лінійну функцію , Яка переведе точки з інтервалу в точки інтервалу 2. Оскільки точка "Паперової" системи координат перейде в точку "Екранної", а точка - в ціль , То система лінійних рівнянь для знаходження коефіцієнтів і має вигляд:
Вирішивши її, знайдемо коефіцієнти :
Для перетворення координати в координату побудуємо лінійну функцію . Крапка "Паперової" системи координат перейде в точку "Компонентної", а точка - в ціль . Для знаходження коефіцієнтів і вирішимо систему лінійних алгебраїчних рівнянь:
Її рішення дозволить знайти нам коефіцієнти і .
Перед описом алгоритму побудови графіка давайте уточнимо формули для розрахунку коефіцієнтів і . Справа в тому, що Width - це ширина компонента з урахуванням рамок зліва і справа, а Height - повна висота компонента з урахуванням рамки, а якщо в якості компонента буде використовуватися форма, то необхідно врахувати заголовок вікна. Однак для зображення графіка нам потрібні вертикальні і горизонтальні розміри компонент без урахування рамок і заголовка. Ці розміри зберігаються у властивостях ClientWidth (ширина клієнтської області компонента без урахування ширини рамки) і ClientHeight (висота клієнтської області компонента, без урахування ширини рамки і ширини заголовка) компонента. Тому для розрахунку коефіцієнтів і логічніше використовувати такі формули:
Алгоритм побудови графіка на екрані монітора можна розділити на наступні етапи:
- Визначити число відрізків , Крок зміни змінної .
- сформувати масиви , Обчислити максимальне (max) і мінімальне (min) значення .
- знайти коефіцієнти і за формулами (10.1), (10.2).
- створити масиви .
- Послідовно з'єднати сусідні точки прямими лініями за допомогою функції LineTo.
- Зобразити систему координат, ліній сітки і підписи.
При побудові графіків декількох безперервних функцій замість масивів і раціонально використовувати матриці розміром , де - кількість функцій. Елементи кожного рядка матриць є координатами відповідного графіка в "паперової" ( ) І "компонентної" ( ) Системах координат.
Блок-схема алгоритму зображення графіка представлена на Мал. 10.3 , Блок-схема побудови графіків k безперервних функцій на Мал. 10.4 .
Розглянемо поетапно кожну схему.
для Мал. 10.3 перший етап алгоритму представлений в блоках 2 і 3. Другий етап реалізується в блоках 4-13. Коефіцієнти третього етапу розраховуються в блоці 14. У блоках 15-16 формуються масиви значень і "Компонентної системи координат (етап 4). Блоки 17-19 - це виведення на екран, і блок 20 призначений для шостого етапу.
Для другої схеми ( Мал. 10.4 ) Реалізація другого етапу представлена в блоках 4-15. У блоці 16 розраховуються коефіцієнти і . У блоках 17-20 формуються масиви четвертого етапу. Блоки 21-24 призначені для виведення графіків, а блок 25 - для побудови осей.
ЗАВДАННЯ 10.1. Побудувати графік функції f (x) на інтервалі [a, b] .Функція задана наступним чином:
Створимо новий проект, змінимо висоту і ширину форми до розмірів, достатніх для відображення на ній графіка. Наприклад, можна встановити такі властивості: Width - 800, Height - 700. Розмістимо на формі кнопку і компонент класу TImage. Об'єкт TImage1 - це растрова картинка, яка буде використовуватися для відображення графіка після клацання по кнопці Button1. Розміри растрової картинки зробимо трохи менше розмірів форми.
Встановимо в якості властивості форми Caption терміну "Графік функції".
Щоб позбутися від проблеми перемальовування майбутнього графіка при зміні розміру форми, заборонимо зміна форми і приберемо кнопки мінімізації і максимізації вікна. Властивість форми BorderStyle визначає зовнішній вигляд і поведінку рамки навколо вікна форми. Для заборони зміни форми встановимо значення властивості BorderStyle в bsSingle - це значення визначає стандартну рамку навколо форми і забороняє зміну розміру форми. Щоб прибрати кнопки мінімізації і максимізації форми, встановимо її властивості BolderIcons.BiMaximize і BolderIcons.BiMinimize в False.
Для кнопки встановимо властивість Caption - фразу "Побудувати графік".
Мал.10.5.
Вікно форми після установки властивостей
Після установки всіх описаних властивостей вікно форми повинно стати подібним до представленого на Мал. 10.5 .
введення інтервалу і виконаємо за допомогою запиту в момент створення форми (в методі ініціалізації форми ). Нижче наведено лістинг модуля проекту малювання графіка з коментарями:
unit Unit1; {$ Mode objfpc} {$ H +} interface uses Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls; // оголошення функції, яка задається // математично для побудови function f (x: real): real; // оголошення функції, яка зображує // графік функції на компоненті procedure Graphika (a, b: real); type {TForm1} TForm1 = class (TForm) Button1: TButton; Image1: TImage; procedure Button1Click (Sender: TObject); procedure FormCreate (Sender: TObject); private {private declarations} public {public declarations} end; var Form1: TForm1; // оголошення змінних // x0, xk, y0, yk - відступи від меж компонента зліва // праворуч, зверху і знизу // x, y - масиви, що визначають координати точок графіка // в "паперової" системі координат // u, v масиви, що визначають координати точок графіка // в "компонентної" системі координат // N - кількість точок x0, y0, xk, yk, a, b: real; x, y: array [0 .. 1000] of real; u, v: array [0 .. 1000] of integer; N: integer; implementation // функція, яка буде зображена на компоненті Image1 function f (x: real): real; begin if x <= 0 then Result: = sin (x / 2) else Result: = sqrt ((1 + x) / 3); end; // функція, яка малює графік // заданої функції на компоненті Image1 procedure Graphika (a, b: real); // Kx + 1 - кількість ліній сітки, перпендикулярних осі ОХ // Ky + 1 - кількість ліній сітки, перпендикулярних осі ОY const Kx = 5; Ky = 5; var dx, dy, c, d, g, h, max, min: real; i, tempx, tempy: integer; s: string; begin // обчислення кроку зміни по осі Х h: = (ba) / (N-1); // формування масивів x і y x [0]: = a; y [0]: = f (x [0]); for i: = 1 to N do begin x [i]: = x [i - 1] + h; y [i]: = f (x [i]); end; // знаходження максимального і мінімального значень масиву Y max: = y [0]; min: = y [0]; for i: = 1 to N do begin if y [i]> max then max: = y [i]; if y [i] <min then min: = y [i]; end; // формування коефіцієнтів перерахунку з "паперової" в // "компонентну" систему координат c: = (Form1. Image1. ClientWidth - x0-xk) / (ba); d: = x0-c * x [0]; g: = (Form1. Image1. ClientHeight -y0-yk) / (min- max); h: = yk-g * max; // формування масивів точок в екранній системі координат for i: = 0 to N do begin u [i]: = trunc (c * x [i] + d); v [i]: = trunc (g * y [i] + h); end; Form1. Image1. Canvas. Color: = clGray; Form1. Image1. Canvas. Pen. Mode: = pmNot; // малювання графіка функції на компоненті Image1 Form1. Image1. Canvas. MoveTo (u [0], v [0]); Form1. Image1. Canvas. Pen. Width: = 2; Form1. Image1. Canvas. Pen. Color: = clGreen; for i: = 1 to N do Form1. Image1. Canvas. LineTo (u [i], v [i]); Form1. Image1. Canvas. Pen. Width: = 1; Form1. Image1. Canvas. Pen. Color: = clBlack; // малювання осей координат, якщо вони потрапляють // в область графіка Form1. Image1. Canvas. MoveTo (trunc (x0), trunc (h)); if (trunc (h)> yk) and (trunc (h) <trunc (Form1. Image1. ClientHeight - y0)) then Form1. Image1. Canvas. LineTo (trunc (Form1. Image1. ClientWidth -xk), trunc (h)); Form1. Image1. Canvas. MoveTo (trunc (d), trunc (yk)); if (trunc (d)> x0) and (trunc (d) <trunc (Form1. Image1. ClientWidth -xk)) then Form1. Image1. Canvas. LineTo (trunc (d), trunc (Form1. Image1. ClientHeight - y0)); // малювання ліній сітки // обчислення відстані між лініями сітки в "компонентної" системі // координат, перпендикулярними осі ОХ dx: = (Form1. Image1. ClientWidth - x0-xk) / KX; // вибираємо тип лінії для ліній сітки for i: = 0 to KX do begin // першу і останню лінії сітки малюємо звичайної // суцільною лінією if (i = 0) or (i = KX) then Form1. Image1. Canvas. Pen. Style: = psSolid // інші малюємо пунктирними лініями else Form1. Image1. Canvas. Pen. Style: = psDash; // малювання лінії сітки, перпендикулярній осі ОХ Form1. Image1. Canvas. MoveTo (trunc (x0 + i * dx), trunc (yk)); Form1. Image1. Canvas. LineTo (trunc (x0 + i * dx), trunc (Form1. Image1. ClientHeight - y0)); end; // обчислення відстані між лініями сітки в "компонентної" системі // координат, перпендикулярними осі ОY dy: = (Form1. Image1. ClientHeight -y0-yk) / KY; for i: = 0 to KY do begin // першу і останню лінії сітки малюємо звичайної суцільною лінією if (i = 0) or (i = KY) then Form1. Image1. Canvas. Pen. Style: = psSolid // інші малюємо пунктирними лініями else Form1. Image1. Canvas. Pen. Style: = psDash; // малювання лінії сітки, перпендикулярній осі ОY Form1. Image1. Canvas. MoveTo (trunc (x0), trunc (yk + i * dy)); Form1. Image1. Canvas. LineTo (trunc (Form1. Image1. ClientWidth -xk), trunc (yk + i * dy)); end; Form1. Image1. Canvas. Pen. Style: = psSolid; // висновок підписів під осями // визначаємо dx - відстань між виведеними // під віссю ОХ значеннями dx: = (ba) / KX; tempy: = trunc (Form1. Image1. ClientHeight -y0 +10); for i: = 0 to KX do begin // перетворення виведеного значення в рядок Str (a + i * dx: 5: 2, s); // обчислення х-координати виведеного під віссю ОХ значення // в "компонентної" системі tempx: = trunc (x0 + i * (Form1. Image1. ClientWidth -x0-xk) / KX) -10; // вивід значення під віссю ОХ Form1. Image1. Canvas. TextOut (tempx, tempy, s); end; if (trunc (d)> x0) and (trunc (d) <Form1. Image1. ClientWidth -xk) then Form1. Image1. Canvas. TextOut (trunc (d) -5, tempy, '0'); // визначаємо dy - відстань між виведеними лівіше // осі ОY значеннями dy: = (max _min) / KY; tempx: = 5; for i: = 0 to KY do begin // перетворення виведеного значення в рядок S tr (max -i * dy: 5: 2, s); // обчислення y-координати виведеного лівіше осі ОY // значення в "компонентної" системі tempy: = trunc (yk-5 + i * (Form1. Image1. ClientHeight -y0-yk) / KY); // вивід значення лівіше осі ОY Form1. Image1. Canvas. TextOut (tempx, tempy, s); end; if (trunc (h)> yk) and (trunc (h) <Form1. Image1. ClientHeight -y0) then Form1. Image1. Canvas. TextOut (tempx + 10, trunc (h) -5, '0'); tempx: = trunc (x0 + i * (Form1. Image1. ClientWidth - x0-xk) / 2); Form1. Image1. Canvas. TextOut (tempx, 10, 'Графік функції'); end; {TForm1} procedure TForm1. FormCreate (Sender: TObject); var s: string; kod: integer; begin N: = 100; x0: = 40; xk: = 40; y0: = 40; yk: = 40; repeat s: = InputBox ( 'Графік неперервної функції', 'Введіть ліву', 'кордон', '-10'); Val (s, a, kod); until kod = 0; repeat s: = InputBox ( 'Графік неперервної функції', 'Введіть праву', 'кордон', '10'); Val (s, b, kod); until kod = 0; end; procedure TForm1. Button1Click (Sender: TObject); begin Graphika (a, b); end; initialization {$ I unit1.lrs} end.
При запуску проекту з'явиться запит введення лівої ( малюнок 10.6 ) І правої ( малюнок 10.7 ) Меж інтервалу.
Мал.10.6.
Вікно введення лівої межі інтервалу
Мал. 10.7. Вікно введення правої межі інтервалу
Після цього з'явиться вікно форми з кнопкою Графік. Після клацання по кнопці на формі окреслять графік функції ( Мал. 10.8 ).
замість висновку
Перевернута остання сторінка книги. Що тепер? Автори сподіваються, що знайомство з мовою Free Pascal буде тільки першим етапом у вивченні програмування. Бажання читача щось виправити в книзі: переписати наведені в ній програми, запропонувати більш прості і швидко працюють алгоритми, написати свої програми і модулі, буде найкращою подякою авторам. Якщо у Вас, читачу, з'явилося подібне бажання, то ми виконали своє завдання - навчили Вас основам програмування.
Наступним етапом в освоєнні програмування буде розробка своїх алгоритмів і написання реально працюючих програм для різних операційних систем.
Ми сподіваємося, що це - тільки перша книга, присвячена програмуванню. Думаємо, що наша книга покладе початок нової серії книг, присвяченої програмування в Linux. Наступною може стати книга, присвячена вирішенню більш складних завдань на Free Pascal.
Що тепер?