013 — Разработка программы рисования графика функции двух переменных

Разработка программы рисования графика функции двух переменных (3D)

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

z = f(x, y)

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

z = sin(x) + cos(y)

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


Содержание



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

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

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

 

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

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

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

Для использования поворота системы в 3-мерном пространстве возникает понятие точки (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 описывается известными соотношениями

Преобразования координат 3D Формулы

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

02_02_00_013_formula_02 По этой формуле будет происходить преобразование системы координат и масштабирование (рисунок 1).

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

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

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

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

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

 


Выполнение

1. Создание проекта как Windows Forms Application

Создать проект. Подробный пример создания проекта по шаблону Windows Forms Application описывается здесь. Автоматически создается главная форма приложения. Имя исходного модуля главной формы «Form1.cs«.

 


2. Создание формы Form1.

Создать форму по образцу, как показано на рисунке 2.

C# Windows Forms основная форма программыРис. 2. Вид основной формы программы

Настроить следующие свойства компонент и формы:

  • в форме Form1 свойство Text = «График функции двух переменных«;
  • в форме Form1 свойство MaximizeBox = False;
  • в форме Form1 свойство StartPosition = «CenterScreen«;
  • в компоненте button1 свойство Text = «Показать график функции …«.

 


3. Создание формы Form2.

Создать новую форму. Подробный процесс создания новой формы описывается здесь.

Имена файлов формы «Form2.cs» и «Form2.Designer.cs«.

Разместить на форме четыре компонента типа Button. Автоматически создается четыре объекта с именами button1, button2, button3, button4.

Настроить свойства компонент и формы следующим образом:

  • в форме Form2 свойство StartPosition = «CenterScreen»;
  • в форме Form2 свойство Text = «График функции z = f(x,y)«;
  • в компоненте button1 свойство Text = «^«;
  • в компоненте button2 свойство Text = «v«;
  • в компоненте button3 свойство Text = «<«;
  • в компоненте button4 свойство Text = «>«.

Приблизительный вид формы Form2 изображен на рисунке 3.

C# Windows Forms Application Форма Form2 приложенияРис. 3. Форма Form2 приложения


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

Все внутренние переменные, использующиеся для организации вывода графика, размещаются в классе формы Form2. Поэтому, сначала надо активизировать модуль «Form2.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 имеет вид:

...

public partial class Form2 : Form
{
  private int xx1, xx2, yy1, yy2;
  private int[] xx = new int[4];
  private int[] yy = new int[4];
  public int left;
  public int top;
  public int width;
  public int height;
  public double X_min, Y_min, X_max, Y_max;
  public double alfa, beta;
  public double x0, y0, z0;
  public double A;
  public bool f_show;

  public Form2()
  {
    InitializeComponent();
  }
}

...

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

 


5. Программирование внутренних методов в форме Form2.

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

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

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

private void Zoom_XY(double x, double y, double z, out int xx, out int yy)
{
  double xn, yn, zn;
  double tx, ty, tz;

  tx = (x - x0) * Math.Cos(alfa) - (y - y0) * Math.Sin(alfa);
  ty = ((x - x0) * Math.Sin(alfa) + (y - y0) * Math.Cos(alfa)) * Math.Cos(beta) -
       (z - z0) * Math.Sin(beta);
  tz = ((x - x0) * Math.Sin(alfa) + (y - y0) * Math.Cos(alfa)) * Math.Sin(beta) +
       (z - z0) * Math.Cos(beta);

  xn = tx / (tz / A + 1);
  yn = ty / (ty / A + 1);

  xx = (int)(width * (xn - X_min) / (X_max - X_min));
  yy = (int)(height * (yn - Y_max) / (Y_min - Y_max));
}

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

private double func(double x, double y)
{
  double res;
  res = Math.Sin(x) + Math.Cos(y);
  return res;
}

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

res = Math.Sin(x) + Math.Cos(y); 

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

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

private void Show_Graphic(PaintEventArgs e)
{
  const double h = 0.1;
  const double h0 = 0;
  int i, j;

  Rectangle r1 = new Rectangle(left, top, left+width, top+height);
  Pen p = new Pen(Color.Black);
  e.Graphics.DrawRectangle(p, r1);

  // Создать шрифт
  Font font = new Font("Courier New", 12, FontStyle.Bold);
  SolidBrush b = new SolidBrush(Color.Blue);

  // рисование осей
  // ось X
  Zoom_XY(0, 0, 0, out xx1, out yy1);
  Zoom_XY(1.2, 0, 0, out xx2, out yy2);
  e.Graphics.DrawLine(p, xx1, yy1, xx2, yy2);
  e.Graphics.DrawString("X", font, b, xx2 + 3, yy2);

  // ось Y
  Zoom_XY(0, 0, 0, out xx1, out yy1);
  Zoom_XY(0, 1.2, 0, out xx2, out yy2);
  e.Graphics.DrawLine(p, xx1, yy1, xx2, yy2);
  e.Graphics.DrawString("Y", font, b, xx2 + 3, yy2);

  // ось Z
  Zoom_XY(0, 0, 0, out xx1, out yy1);
  Zoom_XY(0, 0, 1.2, out xx2, out yy2);
  e.Graphics.DrawLine(p, xx1, yy1, xx2, yy2);
  e.Graphics.DrawString("Z", font, b, xx2 + 3, yy2 - 3);

  // рисование поверхности
  p.Color = Color.Red;
  p.Width = 1;

  for (j = 0; j <= 9; j++)
    for (i = 0; i <= 9; i++)
    {
      Zoom_XY(h0 + h * i, h0 + h * j, func(h0 + h * i, h0 + h * j),
              out xx[0], out yy[0]);
      Zoom_XY(h0 + h * i, h + h * j, func(h0 + h * i, h + h * j),
              out xx[1], out yy[1]);
      Zoom_XY(h + h * i, h + h * j, func(h + h * i, h + h * j),
              out xx[2], out yy[2]);

      Zoom_XY(h + h * i, h0 + h * j, func(h + h * i, h0 + h * j),
              out xx[3], out yy[3]);

      e.Graphics.DrawLine(p, xx[0], yy[0], xx[1], yy[1]);
      e.Graphics.DrawLine(p, xx[1], yy[1], xx[2], yy[2]);
      e.Graphics.DrawLine(p, xx[2], yy[2], xx[3], yy[3]);
      e.Graphics.DrawLine(p, xx[3], yy[3], xx[0], yy[0]);
    }
}

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

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

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

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

 


6. Программирование события Paint формы Form2.

Чтобы получить объект Graphics, нужно запрограммировать событие Paint формы Form2. Пример программирования события в MS Visual Studio – C# описывается здесь подробно.

Обработчик события Form2_Paint() получает два параметра. Первый параметр типа System.Object, второй параметр типа PaintEventArgs.

Параметр типа PaintEventArgs содержит объект Graphics, необходимый для рисования на поверхности формы.

Событие Paint возникает, если окно становится «грязным» (dirty) – то есть, если изменяется его размер, если оно перестает закрывать (частично или полностью) другое окно или если оно было свернуто а потом развернуто. Во всех этих случаях – то есть если форму необходимо перерисовать, платформа .NET автоматически вызовет событие Paint.

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

private void Form2_Paint(object sender, PaintEventArgs e)
{
  Show_Graphic(e);
}

 


7. Программирование обработчиков событий клика на кнопках button1, button2, button3, button4.

Поворот графика происходит в момент, когда пользователь делает клик на одной из кнопок, размещенных на форме Form2 (элементы управления button1, button2, button3, button4).

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

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

Листинг обработчиков событий приведен ниже.

private void button1_Click(object sender, EventArgs e)
{
  beta = beta + 0.1;
  Invalidate();
}

private void button2_Click(object sender, EventArgs e)
{
  beta = beta - 0.1;
  Invalidate();
}

private void button3_Click(object sender, EventArgs e)
{
  alfa = alfa + 0.1;
  Invalidate();
}

private void button4_Click(object sender, EventArgs e)
{
  alfa = alfa - 0.1;
  Invalidate();
}

В вышеприведенных обработчиках событий, событие Paint генерируется явно с помощью унаследованного метода Invalidate(). Этот метод делает перерисовывание всей клиентской области программным путем.

Метод Invalidate() имеет несколько перегруженных вариантов. Например, если нужно обновить заданный прямоугольник, то нужно создать такой код:

Rectangle r = new Rectangle(0, 0, 50, 50);
Invalidate(r);

 


8. Программирование обработчиков событий MouseDown, MouseMove и MouseUp.

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

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

  • MouseDown – генерируется, если пользователь делает клик мышкой на форме Form2;
  • MouseMove – генерируется, если пользователь перемещает мышку над формой Form2 (независимо, нажата ли одна из кнопок мышки);
  • MouseUp – генерируется, если пользователь отпускает кнопку мышки после нажатия.

C# Windows Forms События MouseDown, MouseMove, MouseUp элемент управления FormРис. 4. События MouseDown, MouseMove, MouseUp

private void Form2_MouseDown(object sender, MouseEventArgs e)
{
  f_show = true;
}

private void Form2_MouseUp(object sender, MouseEventArgs e)
{
  f_show = false;
}

private void Form2_MouseMove(object sender, MouseEventArgs e)
{
  double a, b;
  if (f_show)
  {
    a = e.X - (int)(width / 2);
    b = e.Y - (int)(height / 2);
    if (a != 0)
      alfa = Math.Atan(b / a);
    else
      alfa = Math.PI/2;
    beta = Math.Sqrt(Math.Pow(a / 10, 2) +    Math.Pow(b / 10, 2));
    Invalidate();
  }
}

 


9. Листинг модуля «Form2.cs».

Ниже приведен полный текст файла ”Form2.cs”, который соответствует форме Form2.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
  public partial class Form2 : Form
  {
    private int xx1, xx2, yy1, yy2;
    private int[] xx = new int[4];
    private int[] yy = new int[4];
    public int left;
    public int top;
    public int width;
    public int height;
    public double X_min, Y_min, X_max, Y_max;
    public double alfa, beta;
    public double x0, y0, z0;
    public double A;
    public bool f_show;

    public Form2()
    {
      InitializeComponent();
    }

    private void Zoom_XY(double x, double y, double z, out int xx, out int yy)
    {
      double xn, yn, zn;
      double tx, ty, tz;
      tx = (x - x0) * Math.Cos(alfa) - (y - y0) * Math.Sin(alfa);
      ty = ((x - x0) * Math.Sin(alfa) + (y - y0) * Math.Cos(alfa)) * Math.Cos(beta) -
           (z - z0) * Math.Sin(beta);
      tz = ((x - x0) * Math.Sin(alfa) + (y - y0) * Math.Cos(alfa)) * Math.Sin(beta) +
           (z - z0) * Math.Cos(beta);
      xn = tx / (tz / A + 1);
      yn = ty / (ty / A + 1);

      xx = (int)(width * (xn - X_min) / (X_max - X_min));
      yy = (int)(height * (yn - Y_max) / (Y_min - Y_max));
    }

    private double func(double x, double y)
    {
      double res;
      res = Math.Sin(x) + Math.Cos(y);
      return res;
    }

    private void Show_Graphic(PaintEventArgs e)
    {
      const double h = 0.1;
      const double h0 = 0;
      int i, j;

      Rectangle r1 = new Rectangle(left, top, left+width, top+height);
      Pen p = new Pen(Color.Black);
      e.Graphics.DrawRectangle(p, r1);

      // Создать шрифт
      Font font = new Font("Courier New", 12, FontStyle.Bold);
      SolidBrush b = new SolidBrush(Color.Blue);

      // рисование осей
      // ось X
      Zoom_XY(0, 0, 0, out xx1, out yy1);
      Zoom_XY(1.2, 0, 0, out xx2, out yy2);
      e.Graphics.DrawLine(p, xx1, yy1, xx2, yy2);
      e.Graphics.DrawString("X", font, b, xx2 + 3, yy2);

      // ось Y
      Zoom_XY(0, 0, 0, out xx1, out yy1);
      Zoom_XY(0, 1.2, 0, out xx2, out yy2);
      e.Graphics.DrawLine(p, xx1, yy1, xx2, yy2);
      e.Graphics.DrawString("Y", font, b, xx2 + 3, yy2);

      // ось Z
      Zoom_XY(0, 0, 0, out xx1, out yy1);
      Zoom_XY(0, 0, 1.2, out xx2, out yy2);
      e.Graphics.DrawLine(p, xx1, yy1, xx2, yy2);
      e.Graphics.DrawString("Z", font, b, xx2 + 3, yy2 - 3);

      // рисование поверхности
      p.Color = Color.Red;
      p.Width = 1;
      for (j = 0; j <= 9; j++)
        for (i = 0; i <= 9; i++)
        {
          Zoom_XY(h0 + h * i, h0 + h * j, func(h0 + h * i, h0 + h * j),
                  out xx[0], out yy[0]);
          Zoom_XY(h0 + h * i, h + h * j, func(h0 + h * i, h + h * j),
                  out xx[1], out yy[1]);
          Zoom_XY(h + h * i, h + h * j, func(h + h * i, h + h * j),
                  out xx[2], out yy[2]);
          Zoom_XY(h + h * i, h0 + h * j, func(h + h * i, h0 + h * j),
                  out xx[3], out yy[3]);
          e.Graphics.DrawLine(p, xx[0], yy[0], xx[1], yy[1]);
          e.Graphics.DrawLine(p, xx[1], yy[1], xx[2], yy[2]);
          e.Graphics.DrawLine(p, xx[2], yy[2], xx[3], yy[3]);
          e.Graphics.DrawLine(p, xx[3], yy[3], xx[0], yy[0]);
      }
    }

    private void Form2_Paint(object sender, PaintEventArgs e)
    {
      Show_Graphic(e);
    }

    private void button1_Click(object sender, EventArgs e)
    {
      beta = beta + 0.1;
      Invalidate();
    }

    private void button2_Click(object sender, EventArgs e)
    {
      beta = beta - 0.1;
      Invalidate();
    }

    private void button3_Click(object sender, EventArgs e)
    {
      alfa = alfa + 0.1;
      Invalidate();
    }

    private void button4_Click(object sender, EventArgs e)
    {
      alfa = alfa - 0.1;
      Invalidate();
    }

    private void Form2_MouseDown(object sender, MouseEventArgs e)
    {
      f_show = true;
    }

    private void Form2_MouseUp(object sender, MouseEventArgs e)
    {
      f_show = false;
    }

    private void Form2_MouseMove(object sender, MouseEventArgs e)
    {
      double a, b;
      if (f_show)
      {
        a = e.X - (int)(width / 2);
        b = e.Y - (int)(height / 2);
        if (a != 0)
          alfa = Math.Atan(b / a);
        else
          alfa = Math.PI/2;
        beta = Math.Sqrt(Math.Pow(a / 10, 2) + Math.Pow(b / 10, 2));
        Invalidate();
      }
    }
  }
}

 


10. Программирование события клика на кнопке button1 формы Form1 (вызов формы рисования графика функции).

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

Обработчик события клика на кнопке Button1 имеет вид.

private void button1_Click(object sender, EventArgs e)
{
  Form2 form2 = new Form2();
 
  // Прямоугольник, в котором будет выведен график функции
  form2.left = 20;
  form2.top = 20;
  form2.width = 300;
  form2.height = 300;

  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.ShowDialog();
}

11. Запуск программы.

После запуска программы на выполнение, форма графика функции изображена на рисунке 5.

C# Windows Form Рисование 3D графика выполнение программыРис. 5. Результат выполнения программы