|
По первому тесту выполнил дважды задания. Результат получается правильный (проверял калькулятором). Пишет, что "Задание не проверено" и предлагает повторить. |
Графика в Lazarus
10.2 Построение графиков
Алгоритм построения графика непрерывной функции
на отрезке
состоит в следующем: необходимо построить точки
в декартовой системе координат и соединить их прямыми линиями. Координаты точек определяются по следующим формулам:

где
— количество отрезков на отрезке
.

где
.
Чем больше точек будет изображено, тем более плавным будет построенный график.
При переносе этого алгоритма на форму или другой компонент Lazarus учитывает размеры и особенности компонента (ось ОX направлена слева направо, её координаты лежат в пределах от 0 до Width; ось OY направлена вниз, её координаты находятся в пределах от 0 до Height). Значения координат X и Y должны быть целыми.
Необходимо пересчитать все точки из "бумажной" системы координат (
изменяется в пределах от
до
,
изменяется от минимального до максимального значения функции) в "
компонентную1Система координат, связанная с конкретным компонентом класса Tform, Timage, Tprinter и т. д." (в этой системе координат ось абсцисс обозначим буквой
, а ось ординат — буквой
).
Для преобразования координаты
в координату
построим линейную функцию
, которая переведёт точки из интервала
в точки интервала
2
— отступы от левой, правой, нижней и верхней границы компонента Её решение позволит найти нам коэффициенты g и h.. Поскольку точка
"бумажной" системы координат перейдет в точку
"экранной", а точка
— в точку
, то система линейных уравнений для нахождения коэффициентов
и
имеет вид:

Решив её, найдём коэффициенты
:

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

Её решение позволит найти нам коэффициенты
и
.

Перед описанием алгоритма построения графика давайте уточним формулы для расчёта коэффициентов
и
. Дело в том, что Width — это ширина компонента с учётом рамок слева и справа, а Height — полная высота компонента с учётом рамки, а если в качестве компонента будет использоваться форма, то необходимо учесть заголовок окна. Однако для изображения графика нам нужны вертикальные и горизонтальные размеры компонент без учёта рамок и заголовка. Эти размеры хранятся в свойствах ClientWidth (ширина клиентской области компонента без учёта ширины рамки) и ClientHeight (высота клиентской области компонента, без учёта ширины рамки и ширины заголовка) компонента. Поэтому для расчёта коэффициентов
и
логичнее использовать следующие формулы:
![]() |
( 10.2) |
![]() |
( 10.3) |
Алгоритм построения графика на экране монитора можно разделить на следующие этапы:
- Определить число отрезков
, шаг изменения переменной
. - Сформировать массивы
, вычислить максимальное (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.
Ввод интервала
и
выполним с помощью запроса в момент создания формы (в методе инициализации формы
). Ниже приведён листинг модуля проекта рисования графика с комментариями:
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 :=(b-a ) / (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 ) / ( b-a );
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 :=(b-a )/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 t r (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.8).
Вместо заключения
Перевёрнута последняя страница книги. Что теперь? Авторы надеются, что знакомство с языком Free Pascal будет только первым этапом в изучении программирования. Желание читателя что-то исправить в книге: переписать приведённые в ней программы, предложить более простые и быстро работающие алгоритмы, написать свои программы и модули, будет лучшей благодарностью авторам. Если у Вас, читатель, появилось подобное желание, то мы выполнили свою задачу — научили Вас основам программирования.
Следующим этапом в освоении программирования будет разработка своих алгоритмов и написание реально работающих программ для различных операционных систем.
Мы надеемся, что это — только первая книга, посвящённая программированию. Думаем, что наша книга положит начало новой серии книг, посвящённой программированию в Linux. Следующей может стать книга, посвящённая решению более сложных задач на Free Pascal.







