013 – Розробка програми рисування графіку функції двох змінних 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).

Для використання повороту системи у 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

Після перемноження матриць отримуємо формулу для обчислення:

Формула перетворення системи координат 3D

За цією формулою буде відбуватись перетворення системи координат і масштабування (рисунок 1).

Рисунок Зсув і поворот системи координат

Рис. 1. Зсув і поворот системи координат

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

Координати розраховуваної точки (x, y) притискаються до точки (0, 0) за формулами:

Формули перспективи у 3D графіці

де A, a – коефіцієнти перспективи, які підбираються експериментально в залежності від функції.

 


Виконання

1. Створити проект як Windows Forms Application

Створити проект як Windows Forms Application. Детальний приклад створення проекту за шаблоном Windows Forms Application описується тут. Автоматично створюється головна форма програми з іменем Form1. Ім’я вихідного модуля основної форми “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 Форма Form2

Рис. 3. Форма Form2 додатку

 


4. Ввід внутрішніх змінних у форму Form2.

Усі внутрішні змінні, що використовуються для організації виведення графіку, розміщуються в формі Form2. Тому, спочатку треба активізувати модуль (файл) «Form2.cs».

У модуль форми 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];
  private int left;
  private int top;
  private int width;
  private 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

Рис. 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 Forms Результат виконання програмиРис. 5. Результат виконання програми