Программирование на Free Pascal и Lazarus

Графика в Lazarus

Показывать лекцию целиком

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

10.1 Средства рисования в Lazarus

При разработке проекта, в котором можно рисовать, в распоряжении программиста находится полотно (холст) — свойство Canvas, карандаш — свойство Pen, и кисть — свойство Brush.

Свойством Canvas обладают следующие компоненты:

  • форма (класс Tform);
  • таблица (класс TSringGrid);
  • растровая картинка (класс Timage);
  • принтер (класс TPrinter).
  • При рисовании компонента, обладающего свойством Canvas, сам компонент рассматривается как прямоугольная сетка, состоящая из отдельных точек, называемых пикселями. Положение пикселя характеризуется его вертикальной (X) и горизонтальной (Y) координатами. Левый верхний пиксель имеет координаты (0,0). Вертикальная координата возрастает сверху вниз, горизонтальная — слева направо. Общее количество пикселей по вертикали определяется свойством Height, а по горизонтали — свойством Weight. Каждый пиксель может иметь свой цвет. Для доступа к любой точке полотна используется свойство Pixels[X,Y]:TColor. Это свойство определяет цвет пикселя с координатами X(integer), Y(integer).

    Изменить цвета любого пикселя полотна можно с помощью следующего оператора присваивания:

    Компонент. Canvas. Pixels [X,Y] : = Color;

    где Color — переменная или константа типа Tcolor.

    Значение свойств Color
    Константа Цвет Константа Цвет
    clBlack Чёрный clSilve Серебристый
    clMaroon Каштановый clRed Красный
    clGreen Зелёный clLime Салатовый
    clOlive Оливковый clBlue Синий
    clNavy Тёмно-синий clFuchsia Ярко-розовый
    clPurple Розовый clAqua Бирюзовый
    clTeal Лазурный clWhite Белый
    clGray Серый

    Определены следующие константы цветов (табл. 10.1).

    Цвет любого пикселя можно получить с помощью следующего оператора присваивания:

    Color :=Компонент. Canvas. Pixels [X,Y ];

    где Color — переменная типа Tcolor.

    Класс цвета точки Tcolor определяется как длинное целое longint. Переменные этого типа занимают в памяти четыре байта. Четыре байта переменных этого типа содержат информацию о долях синего (B), зелёного (G) и красного (R) цветов и устроены следующим образом: $00BBGGRR.

    Для рисования используются методы класса TСanvas, позволяющие изобразить фигуру (линию, прямоугольник и т. д.) или вывести текст в графическом режиме, и три класса, определяющие инструменты вывода фигур и текстов:

  • TFont (шрифты);
  • TPen (карандаш, перо);
  • TBrush (кисть).
  • Класс TFONT. Можно выделить следующие свойства соответствующего объекта Canvas.TFont:

  • Name (тип string) — имя используемого шрифта.
  • Size (тип integer) — размер шрифта в пунктах (points). Пункт — это единица измерения шрифта, равная 0,353 мм или 1/72 дюйма.
  • Style — стиль начертания символов, который может быть обычным, полужирным (fsBold), курсивным (fsItalic), подчёркнутым (fsUnderline) и перечёркнутым (fsStrikeOut). В программе можно комбинировать необходимые стили, например, чтобы установить стиль "полужирный курсив" необходимо написать следующий оператор: Объект. Canvas. Font. Style : = [ fsItalic, fsBold ]
  • Color (тип Tcolor) — цвет символов.
  • Charset (тип 0..255) — набор символов шрифта. Каждый вид шрифта, определяемый его именем, поддерживает один или более наборов символов. В табл. 10.2 приведены некоторые значения Charset.
  • Значения свойства Charset
    Константа Значение Описание
    ANSI_CHARSET 0 Символы ANSI
    DEFAULT_CHARSET 1 Задаётся по умолчанию. Шрифт выбирается только по его имени Name и размеру Size. Если описанный шрифт недоступен в системе, он будет заменён другим
    SYMBOL_CHARSET 2 Стандартный набор символов
    MAC_CHARSET 77 Символы Macintosh
    GREEK_CHARSET 161 Греческие символы
    RUSSIAN_CHARSET 204 Символы кириллицы
    EASTEUROPE_CHARSET 238 Включает диалектические знаки (знаки, добавляемые к буквам и характеризующие их произношение) для восточно-европейских языков

    Класс TPEN. Карандаш (перо) используется как инструмент для рисования точек, линий, контуров геометрических фигур. Основные свойства объекта Canvas.TPen:

  • Color (тип Tcolor) — определяет тип линии;
  • Width (тип Integer) — задаёт толщину линии в пикселях;
  • Style — даёт возможность выбрать вид линии. Это свойство может принимать значение, указанное в таблице 10.3.
    Виды линий
    Значение Описание
    psSolid Сплошная линия
    psDash Штриховая линия
    psDot Пунктирная линия
    psDashDot Штрихпунктирная линия
    psDashDodDot Линия, чередующая штрих и два пунктира
    psClear Нет линии
  • Mode — определяет, каким образом взаимодействуют цвета пера и полотна. Выбор значения этого свойства позволяет получать различные эффекты. Возможные значения Mode приведены в табл. 10.4. По умолчанию вся линия вычерчивается цветом, определяемым значением Pen.Color, но можно определять инверсный цвет линии по отношению к цвету фона. В этом случае независимо от цвета фона, даже если цвет линии и фона одинаков, линия будет видна.
  • Возможные значения свойства Mode
    Режим Операция Цвет пикселя
    pmBlack Black Всегда чёрный
    pmWhite White Всегда белый
    pmNop Неизменный
    pmNot Not Screen Инверсный цвет по отношению к цвету фона
    pmCopy Pen Цвет, указанный в свойствах Color пера Pen (это значение принято по умолчанию)
    pmNotCopy Not Pen Инверсия цвета пера
    pmMergePenNot Pen or Not Pen Дизъюнкция цвета пера и инверсного цвета фона
    pmMaskPenNot Pen and Not Screen Конъюнкция цвета пера и инверсного цвета фона
    pmMergeNotPen Not Pen or Screen Дизъюнкция цвета фона и инверсного цвета пера
    PmMaskNotPen Not Pen and Screen Конъюнкция цвета фона и инверсного цвета пера
    pmMerge Pen or Screen Дизъюнкция цвета пера и цвета фона
    pmNotMerge Not (Pen or Screen) Инверсия режима pmMerge
    pmMask Pen and Screen Конъюнкция цвета пера и цвета фона
    pmNotMask Not (Pen and Screen) Инверсия режима pmMask
    pmXor Pen xor Screen Операция xor над цветом пера и цветом фона
    pmNotXor Not (Pen xor Screen) Инверсия режима pmXor

    Класс TBRUSH. Кисть (Canvas.Brush) используется методами, обеспечивающими вычерчивание замкнутых фигур для заливки. Кисть обладает двумя основными свойствами:

  • Color (тип Tcolor) — цвет закрашивания замкнутой области;
  • Style (тип TBrushStyle) — определяет стиль заполнения области (bsSolid — сплошное заполнение, bsClear — прозрачное, bsHorizontal — горизонтальные линии, bsVertical — вертикальные линии, bsFDiagonal, bsBDiagonal — диагональные линии, bsCross — решётка, bsDiagCross — диагональная решетка).
  • Класс TCANVAS. Этот класс является основным инструментом для рисования графики. Рассмотрим его наиболее часто используемые методы.

    procedure MoveTo(X, Y : integer );

    Метод MoveTo изменяет текущую позицию пера на позицию, заданную точкой (X,Y). Текущая позиция хранится в переменной PenPos типа Tpoint. Определение типа TPoint следующее:

    type TPoint =record
    	X : longint;
    	Y: longint;
    end;
    

    Текущую позицию пера можно считывать с помощью свойства PenPos следующим образом:

    X:=PenPos.X;
    Y:=PenPos.Y;
    

    procedure LineTo (X, Y : integer);

    Метод LineTo соединяет прямой линией текущую позицию пера и точку с координатами (X,Y). При этом текущая позиция пера перемещается в точку с координатами (X,Y).

    Рассмотрим работу процедуры на примере. Расположим на форме кнопку и рассмотрим процедуру обработки события TForm1.Button1Click, которая рисует прямые линии:

    procedure TfForm1. Button1Click ( Sender : TObject )
    begin
    	Form1. Canvas. LineTo ( 30, 50 );
    end;
    

    В результате щелчка по кнопке на форме будет нарисована прямая линия, соединяющая точку с координатами (0,0) и точку с координатами (30,50). При повторном щелчке по кнопке процедура продолжит рисовать эту же линию.

    Теперь перепишем процедуру обработки события следующим образом:

    procedure TForm1. Button1Click ( Sender : TObject )
    begin
    	Form1. Canvas. LineTo ( Canvas. PenPos. x+30, Canvas. PenPos. y +50);
    end;
    

    При первом щелчке по кнопке на экране прорисуется аналогичная линия. Но при повторном щелчке процедура рисует линию, которая соединяет текущую точку с точкой, получившейся из текущей добавлением к координате X числа 30, а к координате Y — числа 50. Т. е. при повторном щелчке по кнопке процедура соединяет прямой линией точки (30,50) и (60,100). При третьем щелчке по кнопке будут соединяться прямой линией точки (60,100) и (90,150) и т. д.

    procedure PolyLine ( const Points array of TPoint );

    Метод PolyLine рисует ломаную линию, координаты вершин которой определяются массивом Points.

    Рассмотрим работу процедуры на примере. Расположим на форме кнопки Рисовать и Выход и запишем следующие операторы процедур обработки события:

    procedure TForm1. Button1Click ( Sender : TObject );
    var temp : array [ 1.. 2 5 ] of TPoint;
    	i : byte;
    	j : integer;
    begin
    	j : = 1;
    	for i :=1 to 25 do
    	begin
    //вычисление координат вершин ломаной линии
    		temp [ i ]. x:=25+( i _ 1) _ 10;
    		temp [ i ]. y:=150 _ j _ ( i _ 1) _ 5;
    		j :=- j;
    	end;
    	Form1. Canvas. Polyline ( temp );
    end;
    procedure TForm1. Button2Click ( Sender : TObject );
    begin
    	Form1. Close;
    end;
    

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

    procedure Ellipse (X1, Y1, X2, Y2 : integer );

    Метод Ellipse вычерчивает на холсте эллипс или окружность. X1, Y1, X2, Y2 — это координаты прямоугольника, внутри которого вычерчивается эллипс. Если прямоугольник является квадратом, то вычерчивается окружность.

    procedure Arc (X1, Y1, X2, Y2, X3, Y3, X4, Y4 : integer );

    Метод Arc вычерчивает дугу эллипса. X1, Y1, X2, Y2 — это координаты, определяющие эллипс, частью которого является дуга; X3, Y3 — координаты, определяющие начальную точку дуги; X4, Y4 — координаты, определяющие конечную точку дуги. Дуга рисуется против часовой стрелки.

    procedure Rectangle (X1, Y1, X2, Y2 : integer );

    Метод Rectangle рисует прямоугольник. X1, Y1, X2, Y2 — координаты верхнего левого и нижнего правого углов прямоугольника.

    (рис 10.1) Пример использования процедуры PolyLine

    procedure RoundRect (X1, Y1, X2, Y2, X3, Y3 : integer );

    Это метод вычерчивания прямоугольника со скруглёнными углами. X1, Y1, X2, Y2 — координаты верхнего левого и нижнего правого углов прямоугольника, а X3, Y3 — размер эллипса, одна четверть которого используется для вычерчивания скругленного угла.

    procedure PolyGon ( const Points array of TPoint );

    Метод PolyGon рисует замкнутую фигуру (многоугольник) по множеству угловых точек, заданному массивом Points. При этом первая точка соединяется прямой линией с последней. Этим метод PolyGon отличается от метода Poliline, который не замыкает конечные точки. Рисование осуществляется текущим пером Pen, а внутренняя область фигуры закрашивается текущей кистью Brush.

    procedure Pie (X1, Y1, X2, Y2, X3, Y3, X4, Y4 : integer );

    Метод Pie рисует замкнутую фигуру — сектор окружности или эллипса — с помощью текущих параметров пера Pen, внутренняя область закрашивается текущей кистью Brush. Точки (X1,Y1) и (X2,Y2) задают прямоугольник, описывающий эллипс. Начальная точка дуги определяется пересечением эллипса с прямой, проходящей через его его центр и точку (X3,Y3). Конечная точка дуги определяется пересечением эллипса с прямой, проходящей через его центр и точку (X4,Y4). Дуга рисуется против часовой стрелки от начальной до конечной точки. Рисуются прямые, ограничивающие сегмент и проходящие через центр эллипса и точки (X3,Y3) и (X4,Y4).

    (рис 10.2) Пример использования методов рисования фигур

    Создадим форму, установим ей размеры Heigth — 500, Weight — 500. Внизу разместим кнопку, зададим ей свойство Caption — "Рисовать". При запуске программы и щелчке по этой кнопке на форме прорисуются различные фигуры (см. рис. 10.2). Ниже приведён листинг программы, демонстрирующий работу перечисленных методов. Результат работы программы приведен на рис. 10.2.

    unit Unit1;
    {$mode objfpc}{$H+}
    interface
    uses
    	Classes, SysUtils, LResources, Forms, Controls, Graphics,
    	Dialogs, StdCtrls;
    type
    	{ TForm1 }
    	TForm1 = class (TForm)
    	Button1 : TButton;
    	procedure Button1Click ( Sender : TObject );
    private
    	{ private declarations }
    public
    	{ public declarations }
    end;
    var
    	Form1 : TForm1;
    implementation
    { TForm1 }
    procedure TForm1. Button1Click ( Sender : TObject );
    var t : array [ 1.. 5 ] of TPoint;
    begin
    	//рисование линии
    	Form1. Canvas. LineTo ( 500, 500 );
    //изменяем цвет и толщину линии
    	Form1. Canvas. Pen. Color := clMaroon;
    	Form1. Canvas. Pen. Width:= 5;
    //рисование прямоугольника
    	Form1. Canvas. R e c tan gle ( 50, 50, 200, 200 );
    	Form1. Canvas. Pen. Color := clolive;
    //рисование эллипса
    	Form1. Canvas. Ellipse ( 50, 50, 100, 200 );
    //рисование прямоугольника со скруглёнными углами
    	Form1. Canvas. RoundRect ( 250, 250, 400, 400, 30, 30 );
    //рисование сектора окружности
    	Form1. Canvas. Pie ( 300, 300, 400, 400, 350, 350, 500, 500 );
    //формирование массива координат вершин пятиугольника
    	t [ 1 ]. x :=250; t [ 1 ]. y := 10;
    	t [ 2 ]. x :=350; t [ 2 ]. y := 15;
    	t [ 3 ]. x :=400; t [ 3 ]. y := 50;
    	t [ 4 ]. x :=300; t [ 4 ]. y :=150;
    	t [ 5 ]. x :=250; t [ 5 ]. y :=100;
    	Form1. Canvas. Polygon ( t );
    	Form1. Canvas. TextOut ( 10, 10, ’это вывели текст ’ );
    end;
    initialization
    	{$I unit1.lrs}
    end.
    

    procedure TextOut (X, Y : integer; const Text : String );

    Эта функция пишет строку текста Text, начиная с позиции с координатами (X,Y). Текущая позиция PenPos пера Pen перемещается в конец выведенного текста. Надпись выводится в соответствии с текущими установками шрифта Font, фон надписи определяется установками текущей кисти. Для выравнивания позиции текста на канве можно использовать методы, позволяющие определить высоту и длину текста в пикселях — TextExtent, TextHeight и TextWidth. Рассмотрим эти функции.

    function TextExtent ( const Text : String ) : Tsize;

    Эта функция возвращает структуру типа Tsize, содержащую длину и высоту в пикселях текста Text, который предполагается написать на канве текущим шрифтом.

    type
    Tsize = record
    	cx : longint;
    	cx : longint;
    end;
    

    function TextHeight ( const Text : String ) : integer;

    Функция возвращает высоту в пикселях текста Text, который предполагается написать на канве текущим шрифтом.

    function TextWidth ( const Text : String ) : integer;

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

    Если цвет кисти в момент вывода текста отличается от того, которым закрашена канва, то текст будет выведен в цветной прямоугольной рамке, но её размеры будут точно равны размерам надписи.

    Мы рассмотрели основные функции рисования. Теперь можно перейти непосредственно к рисованию. Но перед этим следует заметить, что если вы свернёте окно с графикой, а затем его восстановите, то картинка на форме исчезнет. Изменение размеров окна также может испортить графическое изображение в нём. Для решения этой проблемы существуют процедуры обработки событий Объект.FormPaint и Объект.FormResize. Процедура Объект.FormPaint выполняется после появления формы на экране, а процедура Объект.FormResize — после изменения размера формы. Следовательно, все операторы рисования нужно помещать внутрь Объект.FormPaint и дублировать в процедуре Объект.FormResize.

    10.2 Построение графиков

    Алгоритм построения графика непрерывной функции $$y = f (x)$$ на отрезке $$[a; b]$$ состоит в следующем: необходимо построить точки $$(x_i, f (x_i ))$$ в декартовой системе координат и соединить их прямыми линиями. Координаты точек определяются по следующим формулам:

    $$hx = (b - a)/N,$$

    где $$N$$ — количество отрезков на отрезке $$[a; b]$$.

    $$x_i = a + (i - 1)hx; y_i = f (x_i ),$$

    где $$i = 0,..., N$$.

    Чем больше точек будет изображено, тем более плавным будет построенный график.

    При переносе этого алгоритма на форму или другой компонент Lazarus учитывает размеры и особенности компонента (ось ОX направлена слева направо, её координаты лежат в пределах от 0 до Width; ось OY направлена вниз, её координаты находятся в пределах от 0 до Height). Значения координат X и Y должны быть целыми.

    Необходимо пересчитать все точки из "бумажной" системы координат ($$X$$ изменяется в пределах от $$a$$ до $$b$$, $$Y$$ изменяется от минимального до максимального значения функции) в " компонентную Система координат, связанная с конкретным компонентом класса Tform, Timage, Tprinter и т. д. " (в этой системе координат ось абсцисс обозначим буквой $$U, 0 \le U \le Width$$, а ось ординат — буквой $$V, 0 \le V \le Height$$).

    Для преобразования координаты $$X$$ в координату $$U$$ построим линейную функцию $$cX + d$$, которая переведёт точки из интервала $$(a; b)$$ в точки интервала $$(X_0, Width - X_k)$$ $$X_0, X_K, Y_0, Y_K$$ — отступы от левой, правой, нижней и верхней границы компонента Её решение позволит найти нам коэффициенты g и h. . Поскольку точка $$a$$ "бумажной" системы координат перейдет в точку $$X_0$$ "экранной", а точка $$b$$ — в точку $$Width - X_k$$, то система линейных уравнений для нахождения коэффициентов $$c$$ и $$d$$ имеет вид:

    $$\left\{\begin{aligned} c \times a + d = X_0 \\ c \times b + d = Width - X_k \end{aligned}\right.$$

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

    $$\left\{\begin{aligned} d = X_0 - c \times a \\ c = (Width - X_k - X_0)/(b - a) \end{aligned}\right.$$

    Для преобразования координаты $$Y$$ в координату $$V$$ построим линейную функцию $$V = gY + h$$. Точка $$min$$ "бумажной" системы координат перейдет в точку $$Height - Y_k$$ "компонентной", а точка $$max$$ — в точку $$Y_0$$. Для нахождения коэффициентов $$g$$ и $$h$$ решим систему линейных алгебраических уравнений:

    $$\left\{\begin{aligned} g \times min + h = Heignt - Y_k \\ g \times max + h = Y_0 \end{aligned}\right.$$

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

    $$\left\{\begin{aligned} h = Y_0 - g \times max \\ g = (Height - Y_k - Y_0)/(min - max) \end{aligned}\right.$$

    Перед описанием алгоритма построения графика давайте уточним формулы для расчёта коэффициентов $$c, d, g$$ и $$h$$. Дело в том, что Width — это ширина компонента с учётом рамок слева и справа, а Height — полная высота компонента с учётом рамки, а если в качестве компонента будет использоваться форма, то необходимо учесть заголовок окна. Однако для изображения графика нам нужны вертикальные и горизонтальные размеры компонент без учёта рамок и заголовка. Эти размеры хранятся в свойствах ClientWidth (ширина клиентской области компонента без учёта ширины рамки) и ClientHeight (высота клиентской области компонента, без учёта ширины рамки и ширины заголовка) компонента. Поэтому для расчёта коэффициентов $$c, d, g$$ и $$h$$ логичнее использовать следующие формулы:

    $$\left\{\begin{aligned} d = X_0 - c \times a \\ c = (ClientWidth - X_k - X_0)/(b - a) \end{aligned}\right.$$ $$\left\{\begin{aligned} h = Y_0 - g \times max \\ c = (ClientHeight - Y_k - Y_0)/(min - max) \end{aligned}\right.$$

    Алгоритм построения графика на экране монитора можно разделить на следующие этапы:

  • Определить число отрезков $$N$$, шаг изменения переменной $$X$$.
  • Сформировать массивы $$X, Y$$, вычислить максимальное (max) и минимальное (min) значения $$Y$$.
  • Найти коэффициенты $$c, d, g$$ и $$h$$ по формулам (10.1), (10.2).
  • Создать массивы $$U_i = cX_i + d, V_i = gY_i + h$$.
  • Последовательно соединить соседние точки прямыми линиями с помощью функции LineTo.
  • Изобразить систему координат, линий сетки и подписи.
  • При построении графиков нескольких непрерывных функций вместо массивов $$Y$$ и $$V$$ рационально использовать матрицы размером $$k \times n$$, где $$k$$ — количество функций. Элементы каждой строки матриц являются координатами соответствующего графика в "бумажной" ($$Y$$) и "компонентной" ($$V$$) системах координат.

    Блок-схема алгоритма изображения графика представлена на рис. 10.3, блок-схема построения графиков k непрерывных функций на рис. 10.4.

    Рассмотрим поэтапно каждую схему.

    Для рис. 10.3 первый этап алгоритма представлен в блоках 2 и 3. Второй этап реализуется в блоках 4—13. Коэффициенты третьего этапа рассчитываются в блоке 14. В блоках 15—16 формируются массивы значений $$U$$ и $$V$$ "компонентной системы координат (этап 4). Блоки 17—19 — это вывод графиков на экран, и блок 20 предназначен для шестого этапа.

    (рис 10.3) Блок-схема алгоритма построения графика функции

    Для второй схемы (рис. 10.4) реализация второго этапа представлена в блоках 4—15. В блоке 16 рассчитываются коэффициенты $$c, d, g$$ и $$h$$. В блоках 17—20 формируются массивы четвёртого этапа. Блоки 21—24 предназначены для вывода графиков, а блок 25 — для построения осей.

    ЗАДАЧА 10.1. Построить график функции f (x) на интервале [a, b].Функция задана следующим образом:

    $$f(x) = \left\{\begin{array}{rl} \sin(x/2), \text{если } x \le 0 \\ \sqrt{(1+x)/3}, \text{если } x > 0 \end{array}\right.$$

    Создадим новый проект, изменим высоту и ширину формы до размеров, достаточных для отображения на ней графика. Например, можно установить следующие свойства: Width — 800, Height — 700. Разместим на форме кнопку и компонент класса TImage. Объект TImage1 — это растровая картинка, которая будет использоваться для отображения графика после щелчка по кнопке Button1. Размеры растровой картинки сделаем чуть меньше размеров формы.

    (рис 10.4) Блок-схема алгоритма построения графика нескольких функции

    Установим в качестве свойства формы Caption сроку "График функции".

    Чтобы избавиться от проблемы перерисовки будущего графика при изменении размера формы, запретим изменение формы и уберём кнопки минимизации и максимизации окна. Свойство формы BorderStyle определяет внешний вид и поведение рамки вокруг окна формы. Для запрета изменения формы установим значение свойства BorderStyle в bsSingle — это значение определяет стандартную рамку вокруг формы и запрещает изменение размера формы. Чтобы убрать кнопки минимизации и максимизации формы, установим её свойства BolderIcons.BiMaximize и BolderIcons.BiMinimize в False.

    Для кнопки установим свойство Caption — фразу "Построить график".

    (рис 10.5) Окно формы после установки свойств

    После установки всех описанных свойств окно формы должно стать подобным представленному на рис. 10.5.

    Ввод интервала $$a$$ и $$b$$ выполним с помощью запроса в момент создания формы (в методе инициализации формы $$FormCreate$$). Ниже приведён листинг модуля проекта рисования графика с комментариями:

    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.6) Окно ввода левой границы интервала (рис 10.7) Окно ввода правой границы интервала

    После этого появится окно формы с кнопкой График. После щелчка по кнопке на форме прорисуется график функции (рис. 10.8).

    (рис 10.8) Окно с прорисованным графиком

    Вместо заключения

    Перевёрнута последняя страница книги. Что теперь? Авторы надеются, что знакомство с языком Free Pascal будет только первым этапом в изучении программирования. Желание читателя что-то исправить в книге: переписать приведённые в ней программы, предложить более простые и быстро работающие алгоритмы, написать свои программы и модули, будет лучшей благодарностью авторам. Если у Вас, читатель, появилось подобное желание, то мы выполнили свою задачу — научили Вас основам программирования.

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

    Мы надеемся, что это — только первая книга, посвящённая программированию. Думаем, что наша книга положит начало новой серии книг, посвящённой программированию в Linux. Следующей может стать книга, посвящённая решению более сложных задач на Free Pascal.

    Вернуться к учебному плану