Наверное, все видели программы, которых довольно много, для построения всяческих графиков. Если Вы хотите написать что-то подобное на Delphi, то эта статья для Вас. В самых-самых программах Вам предлагается ввести формулу строкой, а не выбирать функции из списка. Самым сложным (?) элементом таких программ является текстовый анализатор. В данной статье я не собираюсь рассказывать, как пишутся текстовые анализаторы. Я собираюсь рассказать о некоторых основных принципах построения графиков, и работы с графикой в среде Delphi без использования специальных графических платформ, таких как DirectX или OpenGL.
Введение
В Delphi для прорисовки различных элементов управления используется специальный класс TCanvas. Можно выделить 4 основных направления, в которых используется этот класс:
1. Загрузка и хранение графических изображений. 2. Создание новых и изменение хранимых изображений с помощью пера, кисти, шрифта. 3. Рисование и/или закраска фигур, линий, текстов. 4. Комбинирование изображений.
Как Вы уже поняли, мы пойдем по третьему направлению. Теперь давайте рассмотрим некоторые свойства и методы класса TCanvas. Сразу скажу, что я буду рассматривать далеко не все методы и свойства класса TCanvas, а лишь те, которые мы будем использовать потом.
TCanvas Свойства:
1. property Brush: TBrush; Данное свойство позволяет определить цвет (Brush.Color) и стиль (Brush.Style) заполнения замкнутых фигур и фона.
2. property ClipRect: TRect; - read-only Данное свойство позволяет получить доступную область рисования. Вне этой области рисовать невозможно. Тип TRect, описанный в модуле Windows, имеет следующий синтаксис:
Type TRect = record Case integer of 0: (Left, Top, Right, Bottom: Integer); 1: (TopLeft, BottomRight: TPoint); end;
3. property Pen: TPen; Данное свойство позволяет задать цвет пера, рисующего фигуры или линии.
Методы:
1. procedure FillRect (const Rect: TRect); Метод позволяет заполнить цветом прямоугольную область холста Rect, используя текущее значение кисти Brush.
2. procedure MoveTo (x, y: integer); Метод позволяет переместить перо в точку (X, Y).
3. procedure LineTo (x, y: integer); Метод позволяет нарисовать прямую линию, которая начинается с текущей позиции пера и заканчивается точкой (x, y). При рисовании используются текущие установки пера Pen.
Ну вот, пожалуй, и все, что нам будет нужно, для успешного построения графика.
Построение графика. Теория.
Для начала предлагаю немного теории. Мы собираемся писать процедуру построения графика функции на определенной поверхности, заданной свойством Canvas. Я предлагаю поставить оси координат в середине этой области, а график растянуть так, чтобы он растянулся на всю область. Строить мы будем методом lineto. Поэтому нам нужно определиться с шагом изменения величины аргумента. Я предлагаю взять его обратным к масштабу по оси ординат. Так наш график будет выглядеть плавно при любой функции и любом начальном и конечном значении абсциссы. Масштаб по оси абсцисс считается, отношение ширины поверхности к разнице максимального и минимального значения абсциссы. Масштаб по оси ординат считается аналогично: отношение высоты поверхности к разнице между максимальным и минимальным значениями данной функции на данном интервале.
Процедура DrawGraph
Ну вот и все с теорией и я приведу полный код функции, а потом разберем его.
Type TFunc = function (x: real): real; procedure DrawGraph (f: TFunc; a: real; b: real; C: TCanvas); var x, y, h: real; max, min: real; sx, sy: real; xmid, ymid: integer; begin sx := (c.ClipRect.Right)/(b-a); h := 1/sx; xmid := c.ClipRect.Right div 2; ymid := c.ClipRect.Bottom div 2; x := a; max := f( x); min := max; while xmax then max := y; x := x + h; end; sy := c.ClipRect.Bottom/ (max-min); c.Brush.Color := clBlack; c.FillRect(Rect(0, 0, c.ClipRect.Right, c.ClipRect.Bottom)); c.Pen.Color := clYellow; c.MoveTo(0, ymid); c.LineTo(c.ClipRect.Right, ymid); c.MoveTo(xmid, 0); c.LineTo(xmid, c.ClipRect.Bottom); x := a; y := f(x); c.Pen.Color := clWhite; c.MoveTo(xmid+round(sx*x), ymid-round(sy*y)); while x
А теперь, если Вы чего-либо не поняли, давайте разберем этот код.
Type TFunc = function (x: real): real; Здесь я создал тип-функцию, для того, чтобы передавать в функцию построения графика можно было передавать имя функции в эту процедуру.
procedure DrawGraph (f: TFunc; a: real; b: real; C: TCanvas); Заголовок функции. Параметры: f – функция, график, которой будем троить. a – начальное значение переменной “x”. b – конечное значение переменной “x”. C – канва, на которой будем рисовать.
sx := (c.ClipRect.Right)/(b-a); h := 1/sx; xmid := c.ClipRect.Right div 2; ymid := c.ClipRect.Bottom div 2; x := a; max := f( x); min := max; while xmax then max := y; x := x + h; end; sy := c.ClipRect.Bottom/ (max-min);
В этом куске кода мы считаем масштабы по осям координат, и среднее значения высоты и ширины канвы, чтобы отобразить оси координат.
Здесь мы заливаем всю канву черным цветом и рисуем желтым цветом оси координат.
x := a; y := f(x); c.Pen.Color := clWhite; c.MoveTo(xmid+round(sx*x), ymid-round(sy*y)); while x
Ну и, наконец, нарисовали график нужной нам функции.
Небольшой пример: Положим на форму одну кнопку и один компонент TImage. Создадим обработчик функции OnClick для кнопки примерно следующего характера, и следующую функцию для расчета функции
Function f(x: real): real; Begin Result := sin(x)*cos(x); End;
procedure TForm1.Button1Click(Sender: TObject); begin DrawGraph (f, -10, 10, Image1.Canvas); end;