014 — Разработка программы рисования графика функции двух переменных z=f(x, y)




Разработка программы рисования графика функции двух переменных z=f(x, y)

В данной работе описывается как в отдельной форме реализовать график функции двух переменных

z = f(x, y)

или другими словами график функции в трехмерном пространстве (3D).

В качестве примера выбрана функция

z = sin(x) + cos(y)

Используя данный пример можно создавать собственное программное обеспечение рисования графиков для других функций двух переменных.


Содержание


Условие задачи

Дана формула функции двух переменных z = sin(x) + cos(y). Разработать приложение, рисующее график функции в отдельной форме.

Дополнительно реализовать поворот графика влево, вправо, вверх, вниз. Также нужно выводить оси OX, OY, OZ.


Математическая постановка задачи

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

График функции двух переменных z(x,y) строится в параллелепипеде с размерами (xx1, xx2), (yy1, yy2), (zz1, zz2).

Для использования поворота системы в трехмерном пространстве возникает понятие точки (x0, y0, z0), относительно которой происходит поворот системы.

Также возникает понятие углов:

  • 02_02_00_013_alfa (альфа) – поворот системы относительно оси OZ;
  • 02_02_00_013_beta (бета) – поворот системы относительно оси OX.

Сдвиг в точку (x0, y0, z0) с учетом поворота на углы 02_02_00_013_alfa и 02_02_00_013_beta описывается известными соотношениями

02_02_00_013_formula_01

После перемножения матриц получаем формулу для вычисления:

02_02_00_013_formula_02

01_02_00_014_01_Рис. 1. Сдвиг и поворот системы координат

Необходимо определиться, в какой плоскости монитора будут лежать вехе координат OX, OY, OZ. Принимаем, что в плоскости монитора лежат оси OX и OY. А ось OZ перпендикулярна экрану.

Координаты расчетной точки (x, y) прижимаются к точке (0, 0) по формулам:

02_02_00_013_formula_03где A, a – коэффициенты перспективы, которые подбираются экспериментально в зависимости от функции.


Выполнение

1. Запустить Delphi. Создать приложение по шаблону VCL Forms Application

Подробный пример создания приложения по шаблону VCL Forms Application описывается здесь. Сохранить проект под любым именем. В результате будет создана главная форма приложения с именем Form1. Файл главной формы имеет имя «Unit1.pas».

 

2. Создание главной формы Form1

Создать главную форму, как изображено на рисунке 2.

01_02_00_014_02rРис. 2. Вид главной формы Form1

На форме размещается компонент типа TButton, предназначенный для вызова обработчика события отображения графика функции. В результате получается объект с именем Button1.

С помощью Object Inspector в компоненте Button1 нужно настроить свойство Caption в значение «Отобразить график…».

 

3. Создание второстепенной формы Form2

Создать форму Form2 как изображено на рисунке 3. Эта форма будет отображать график функции. Пример создания новой формы в Delphi описывается здесь.

Форма Form2 размещается в модуле “Unit2.pas”.

01_02_00_014_03_Рис. 3. Форма Form2 вывода графика функции

Из палитры Tool Palette на форме размещаются компоненты следующих типов:

  • из вкладки Standard четыре компонента типа TButton, которые представляют стрелки направлений поворота графика функции. В результате получается четыре объекта-переменные с именами Button1, Button2, Button3, Button4;
  • из вкладки Additional компонент типа TImage, в котором будет отображаться график функции. Создается объект с именем Image1.

С помощью Object Inspector настраиваются следующие компоненты:

  • в компоненте Button1 свойство Caption = «^»;
  • в компоненте Button2 свойство Caption = «v»;
  • в компоненте Button3 свойство Caption = «<<»;
  • в компоненте Button4 свойство Caption = «>>».

 

4. Ввод внутренних переменных в Form2

Все внутренние переменные, использующиеся для организации вывода графика, размещаются в форме Form2. Поэтому, сначала надо активировать модуль «Unit2.pas».

В модуль формы Form2 в разделе private вводятся следующие внутренние переменные:

  • xx1, xx2, yy1, yy2 – соответствуют координатам точек, отображающихся на экране монитора;
  • массивы xx и yy которые предназначены для вывода плоскости из 4-х точек. Область определения функции z = f(x, y) разбивается на прямоугольники, на любом из которых функция экстраполируется ребрами четырехугольника.

В разделе public вводятся:

  • переменные X_min, Y_min, X_max, Y_max вещественного типа, соответствующие реальным координатам параллелепипеда, в котором выводится график функции. Эти переменные заполняются из главной формы Form1 экспериментальным путем;
  • переменные alfa, beta вещественного типа, отображающие углы наблюдения за графиком функции. Заполняются из главной формы Form1;
  • переменные x0, y0, z0 вещественного типа. Отображают величины из главной формулы вычисления (см. математическую постановку задачи);
  • переменная A вещественного типа – есть коэффициентом перспективы и подбирается экспериментально;
  • переменная f_show логического типа используется для указания того, что нужно перерисовать график, в случае изменения положение углов alfa и beta.

После введения переменных в текст программы, фрагмент класса формы Form2 имеет вид:

type
  TForm2 = class(TForm)

  ...

  private
    { Private declarations }
    xx1, xx2, yy1, yy2 : integer;
    xx, yy : array[0..3] of integer;

  public
    { Public declarations }
    // Данные, которые заполняются из формы Form1
    X_min, Y_min, X_max, Y_max: real;
    alfa, beta:real;
    x0, y0, z0:real;
    A:real;
    f_show:boolean;

    ...

end;

Переменные из раздела public заполняются из формы Form1.

 

5. Ввод внутренних методов в текст модуля «Unit2.pas»

В текст класса формы Form2 нужно ввести три дополнительных метода:

  • процедуру преобразования системы координат и масштабирования Zoom_XY();
  • функцию func() для которой выводится график;
  • процедуру рисования графика функции Show_Graphic.

После ввода внутренних методов, фрагмент листинга класса Form2 следующий:

type

  TForm2 = class(TForm)

    ...

    private

    { Private declarations }
    xx1, xx2, yy1, yy2 : integer;
    xx, yy : array[0..3] of integer;

    // внутренние методы
    procedure Zoom_XY(x,y,z:real; var xx,yy:integer);
    procedure Show_Graphic;
    function func(x,y:real):real;

    public
    { Public declarations }
    // Данные, которые заполняются из формы Form1
    X_min, Y_min, X_max, Y_max: real;
    alfa, beta:real;
    x0, y0, z0:real;
    A:real;
    f_show:boolean;
  end;

...

Листинг процедуры преобразования системы координат следующий:

// процедура преобразования системы координат и масштабирования
procedure TForm2.Zoom_XY(x: Real; y: Real; z: Real; var xx: Integer; var yy: Integer);
var
  tx, ty, tz:real;
  xn, yn, zn:real;
begin
  tx := (x-x0)*cos(alfa) - (y-y0)*sin(alfa);
  ty := ((x-x0)*sin(alfa) + (y-y0)*cos(alfa)) * cos(beta) - (z-z0)*sin(beta);
  tz := ((x-x0)*sin(alfa) + (y-y0)*cos(alfa)) * sin(beta) + (z-z0)*cos(beta);
  xn := tx/(tz/A+1);
  yn := ty/(tz/A+1);
  xx := Trunc(Image1.Width * (xn-X_min)/(X_max-X_min));
  yy := Trunc(Image1.Height * (yn-Y_max)/(Y_min-Y_max));
end;

Непосредственный вывод графика функции реализован в процедуре Show_Graphic. Листинг процедуры Show_Graphic следующий.

procedure TForm2.Show_Graphic;
const
  h = 0.1;
  h0 = 0;
var
  i,j:integer;
  canv:TCanvas;
begin
  canv := Image1.Canvas; // холст для рисования

  // очищение холста (канвы)
  canv.Rectangle(-1,-1, Image1.Width, Image1.Height);

  // рисование осей
  Zoom_XY(0,0,0,xx1,yy1);
  Zoom_XY(1.2,0,0,xx2,yy2);
  canv.MoveTo(xx1,yy1);
  canv.LineTo(xx2,yy2);
  canv.TextOut(xx2+3,yy2,'X');

  Zoom_XY(0,0,0,xx1,yy1);
  Zoom_XY(0,1.2,0,xx2,yy2);
  canv.MoveTo(xx1,yy1);
  canv.LineTo(xx2,yy2);
  canv.TextOut(xx2+3,yy2,'Y');

  Zoom_XY(0,0,0,xx1,yy1);
  Zoom_XY(0,0,1.2,xx2,yy2);
  canv.MoveTo(xx1,yy1);
  canv.LineTo(xx2,yy2);
  canv.TextOut(xx2+3,yy2-3,'Z');

  // рисование поверхности
  for j:=0 to 9 do
    for i:=0 to 9 do
    begin
      Zoom_XY(h0+h*i, h0+h*j, func(h0+h*i, h0+h*j), xx[0], yy[0]);
      Zoom_XY(h0+h*i, h+h*j, func(h0+h*i, h+h*j), xx[1], yy[1]);
      Zoom_XY(h+h*i, h+h*j, func(h+h*i, h+h*j), xx[2], yy[2]);
      Zoom_XY(h+h*i, h0+h*j, func(h+h*i, h0+h*j), xx[3], yy[3]);
      canv.Polygon([Point(xx[0], yy[0]), Point(xx[1], yy[1]),
                   Point(xx[2], yy[2]), Point(xx[3], yy[3])]);
    end;
end;

Объясним некоторые фрагменты кода в процедуре Show_Graphic.

Область определения функции z = f(x, y) разбивается на прямоугольники, на любом из которых функция экстраполируется ребрами четырехугольника. Построение четырехугольников на экране реализуется с помощью метода Polygon().

После очистки канвы происходит рисование осей координат и методом Polygon() выводятся фрагменты поверхности.

При рисовании поверхности, из процедуры Show_Graphic вызовется процедура Zoom_XY(), которая осуществляет преобразование и масштабирование из реальных координат в экранные координаты.

Листинг метода func() следующий.

function TForm2.func(x: Real; y: Real):real;
begin
  func := sin(x) + cos(y);
end;

В этом методе вместо строки

func := sin(x) + cos(y);

можно сделать вставку собственной функции.

 

6. Программирование обработчиков событий клика на кнопках поворота графика в форме Form2

Поворот графика происходит в момент, когда пользователь делает клик на одной из кнопок, размещенных на форме Form2 (компоненты Button1, Button2, Button3, Button4).

Отображение графика зависит от внутренних переменных alfa и beta. Переменная alfa содержит угол поворота относительно оси OZ. Переменная beta содержит значение угла поворота вокруг оси OX.

Поэтому, в обработчиках событий происходит изменение значений alfa и beta на некоторую величину. По желанию, можно установить собственную величину изменения alfa и beta.

Листинг обработчиков событий следующий.

procedure TForm2.Button1Click(Sender: TObject);
begin
  beta := beta + 0.1;
  Show_Graphic;
end;

procedure TForm2.Button2Click(Sender: TObject);
begin
  beta := beta - 0.1;
  Show_Graphic;
end;

procedure TForm2.Button3Click(Sender: TObject);
begin
  alfa := alfa + 0.1;
  Show_Graphic;
end;

procedure TForm2.Button4Click(Sender: TObject);
begin
  alfa := alfa - 0.1;
  Show_Graphic;
end;

 

7. Программирование обработчика события активизации формы Form2

В момент вызова формы Form2 может выводиться график функции. Поэтому в обработчике события OnActivate формы Form2 вызывается метод Show_Graphic.

procedure TForm2.FormActivate(Sender: TObject);
begin
  Show_Graphic;
end;

 

8. Программирование обработчиков событий вращения графика с помощью мышки

Для осуществления вращения графика с помощью мышки нужно запрограммировать соответствующие обработчики событий компонента Image1.

Если нажать клавишу мыши и удерживать ее нажатой над компонентом Image1, а потом отпустить, то генерируются следующие события (рисунок 4):

  • OnMouseDown – генерируется, если пользователь делает клик мышкой на компоненте Image1;
  • OnMouseMove – генерируется, если пользователь перемещает мышку над компонентом Image1 (не зависимо, нажатая ли одна из кнопок мышки);
  • OnMouseUp – генерируется, если пользователь отпускает кнопку мышки после нажатия.

01_02_00_014_04_Рис. 4. События OnMouseDown, OnMouseMove, OnMouseUp компонента Image1

Перемещение мышки с нажатой клавишей по компоненту Image1 приводит к изменению углов alfa и beta. Переменная f_show используется для указания обработчику события OnMouseMove, что нужно изменить переменные alfa и beta и перерисовать график.

Листинги обработчиков событий OnMouseDown, OnMouseMove и OnMouseUp следующий.

procedure TForm2.Image1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  f_show := true;
end;

procedure TForm2.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,Y: Integer);
var
  a, b:real;
begin
  if f_show then
  begin
    a := x - Width div 2;
    b := y - Height div 2;
    if a<>0 then
      alfa := arctan(b/a)
    else
      alfa := Pi/2;
    beta := sqrt(sqr(a/10) + sqr(b/10));
    Show_Graphic;
  end;
end;

procedure TForm2.Image1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  f_show := false;
end;

 

9. Программирование события клика на кнопке «Отобразить график…» из формы Form1

При клике на кнопке Button1 из формы Form1 может выводиться график функции.

Обработчик события клика на кнопке Button1 следующий.

procedure TForm1.Button1Click(Sender: TObject);
begin
  Form2.f_show := false;
  Form2.x0 := 0;
  Form2.y0 := 0;
  Form2.z0 := 0;
  Form2.A := -8;
  Form2.alfa := 10;
  Form2.beta := 12;
  Form2.X_min := -3;
  Form2.X_max := 3;
  Form2.Y_min := -3;
  Form2.Y_max := 3;
  Form2.ShowModal;
end;

Как видно из листинга, перед вызовом формы Form2 формируются значения внутренних переменных. Эти значения подбираются экспериментально и могут отличаться для каждой функции.

 

10. Запуск приложения на выполнение

Теперь можно запустить приложение на выполнение и протестовать его работу (рисунок 5).

01_02_00_014_05_Рис. 5. Выполнение приложения