Авторизация



Обмен ссылками

Блог программиста
Главная Borland C++ Builder Построение графиков
Построение графиков PDF Печать E-mail
Автор: Андрей   
02.04.2009 22:50

Описание программы
Расположение компонентов на форме
Свойства компонент
Код программы
Код с подробными комментариями
Советы по улучшению и расширению программы

Описание программы
Данная программа позволяет строить графики различных функций на плоскости, задаваемых параметрически (т.е. в виде X = X(t), Y = Y(t)). Вид функций для X и Y можно выбирать из списков, при этом в соответствующих полях можно задавать числовые параметры этих функций. Также можно менять масштаб графика как по X, так и по Y, с помощью ползунков.
При построении графика значение параметра пробегает целые значения из некоторого фиксированного интервала (например, от -1000 до +1000), затем высчитываются значения X и Y для данного значения параметра - по различным формулам, в зависимости от выбора пользователя, после чего с учетом заданных масштабов рисуется точка, отвечающая вычисленным значениям X и Y.
Такой вариант программы, конечно, сильно отличается от простейшей программки построения единственного графика вида Y = Y(X), без построения осей и возможности редактировать численные параметры. Хотя в таком случае объем исходного кода и был бы на порядок меньше, но программа получилась бы вялой, скучной и неудобной.
Мне же хотелось представить вниманию почтеннейшей публики действительно красивую и занимательную вещь. Комбинируя различные виды функций, с различными параметрами, можно получить действительно большое количество самых причудливых линий (в отдельных случаях - прямо-таки узоров)! Масштабирование с мгновенной перерисовкой графика (и столь же мгновенная перерисовка при изменении вида функций и изменении числовых параметров) делают процесс работы с программой очень удобным. Если вам нравятся красивые узоры или же графики математических функций, то вы можете просидеть за этой вещицей немало времени, меняя параметры и комбинации функций! (проверено на личном опыте!;)) Честное слово, "геймплей" у этой (по идее) математической программы весьма увлекательный!;) Впрочем, что-то я увлекся.) Займемся же наконец программированием!

Используются следующие компоненты: Button, ComboBox, Edit, PaintBox, StaticText, TrackBar.

Расположение компонентов на форме

Расположение компонентов на форме программы "Построение графиков"

Свойства компонент, измененные по сравнению со стандартными
Button1:
Caption: "Рисовать оси"
ComboBox1:
Items:
const
a * t + b
a * (t ^ b + c)
a * sin (b * t + c)
a * cos (b * t + c)
a * tg (b * t + c)
a * ctg (b * t + c)
a * e ^ (b * t + c)
a * sin (b * t) * sin (c * t)
a * sin (b * t) * cos (c * t)
a * cos (b * t) * cos (c * t)
a * e ^ (b * t) * sin (c * t)
Text: "Формула для первой координаты"
ComboBox2:
Items: (в точности то же, что и для ComboBox1)
Text: "Формула для второй координаты"
Edit1, Edit2, Edit4, Edit5:
Text: "1"
Edit3, Edit6:
Text: "0"
Edit7, Edit8:
Text: "10"
PaintBox1:
Height: 465
Width: 681
StaticText1:
Caption: "X"
StaticText2:
Caption: "Y"
StaticText3, StaticText6:
Caption: "a"
StaticText4, StaticText7:
Caption: "b"
StaticText5, StaticText8:
Caption: "c"
StaticText9:
Caption: "deltaX"
StaticText10:
Caption: "deltaY"
TrackBar1, TrackBar2:
Frequency: 9
Max: 100
Min: 1
Position: 10

Код программы

#include "math.h"

class GraphicClass
 {
 private:
 int xmax, ymax;
 int xmid, ymid;
 int XKind, YKind;
 double X, Y;
 double deltaX, deltaY;
 double a_x, b_x, c_x, a_y, b_y, c_y;
 public:
 GraphicClass();
 void DrawAxis();
 void DrawGraphic();
 void SetXKind(int xkind);
 void SetYKind(int ykind);
 void SetAX(double ax);
 void SetBX(double bx);
 void SetCX(double cx);
 void SetAY(double ay);
 void SetBY(double by);
 void SetCY(double cy);
 void SetDeltaX(double dX);
 void SetDeltaY(double dY);
 };
GraphicClass::GraphicClass()
 {
 xmax = 681;
 ymax = 465;
 xmid = xmax / 2;
 ymid = ymax / 2;
 deltaX = 10;
 deltaY = 10;
 a_x = 1;
 b_x = 1;
 c_x = 0;
 a_y = 1;
 b_y = 1;
 c_y = 0;
 XKind = 0;
 YKind = 0;
 }
void GraphicClass::DrawAxis()
 {
 Form1 -> PaintBox1 -> Repaint();
 Form1 -> PaintBox1 -> Canvas -> MoveTo(1, ymid);
 Form1 -> PaintBox1 -> Canvas -> LineTo(xmax, ymid);
 Form1 -> PaintBox1 -> Canvas -> MoveTo(xmid, 1);
 Form1 -> PaintBox1 -> Canvas -> LineTo(xmid, ymax);

 for (int i = 0; i <= xmax / deltaX; i++)
  {
  Form1 -> PaintBox1 -> Canvas -> MoveTo(xmid + i * deltaX, ymid - 1);
  Form1 -> PaintBox1 -> Canvas -> LineTo(xmid + i * deltaX, ymid + 2);
  Form1 -> PaintBox1 -> Canvas -> MoveTo(xmid - i * deltaX, ymid - 1);
  Form1 -> PaintBox1 -> Canvas -> LineTo(xmid - i * deltaX, ymid + 2);
  }
 for (int i = 0; i <= ymax / deltaY; i++)
  {
  Form1 -> PaintBox1 -> Canvas -> MoveTo(xmid - 1, ymid + i * deltaY);
  Form1 -> PaintBox1 -> Canvas -> LineTo(xmid + 2, ymid + i * deltaY);
  Form1 -> PaintBox1 -> Canvas -> MoveTo(xmid - 1, ymid - i * deltaY);
  Form1 -> PaintBox1 -> Canvas -> LineTo(xmid + 2, ymid - i * deltaY);
  }
 }
void GraphicClass::DrawGraphic()
 {
 for (int t = -1000; t <= 1000; t++)
  {
  switch(XKind)
   {
   case 0: X = a_x; break;
   case 1: X = a_x * 0.01 * t + b_x; break;
   case 2: if (!(b_x < 0 && t == 0))
    X = a_x * (pow(0.01 * t, b_x) + c_x); break;
   case 3: X = a_x * sin(b_x * 0.01 * t + c_x); break;
   case 4: X = a_x * cos(b_x * 0.01 * t + c_x); break;
   case 5: X = a_x * tan(b_x * 0.01 * t + c_x); break;
   case 6: if (b_x * 0.01 * t + c_x != 0)
    X = a_x / tan(b_x * 0.01 * t + c_x); break;
   case 7: X = a_x * exp(b_x * 0.01 * t + c_x); break;
   case 8: X = a_x * sin(b_x * 0.01 * t) * sin(c_x * 0.01 * t); break;
   case 9: X = a_x * sin(b_x * 0.01 * t) * cos(c_x * 0.01 * t); break;
   case 10: X = a_x * cos(b_x * 0.01 * t) * cos(c_x * 0.01 * t); break;
   case 11: X = a_x * exp(b_x * 0.01 * t) * sin (c_x * 0.01 * t); break;
   };
  switch(YKind)
   {
   case 0: Y = a_y; break;
   case 1: Y = a_y * 0.01 * t + b_y; break;
   case 2: if (!(b_y < 0 && t == 0))
    Y = a_y * (pow(0.01 * t, b_y) + c_y); break;
   case 3: Y = a_y * sin(b_y * 0.01 * t + c_y); break;
   case 4: Y = a_y * cos(b_y * 0.01 * t + c_y); break;
   case 5: Y = a_y * tan(b_y * 0.01 * t + c_y); break;
   case 6: if (b_y * 0.01 * t + c_y != 0)
    Y = a_y / tan(b_y * 0.01 * t + c_y); break;
   case 7: Y = a_y * exp(b_y * 0.01 * t + c_y); break;
   case 8: Y = a_y * sin(b_y * 0.01 * t) * sin(c_y * 0.01 * t); break;
   case 9: Y = a_y * sin(b_y * 0.01 * t) * cos(c_y * 0.01 * t); break;
   case 10: Y = a_y * cos(b_y * 0.01 * t) * cos(c_y * 0.01 * t); break;
   case 11: Y = a_y * exp(b_y * 0.01 * t) * sin (c_y * 0.01 * t); break;
   };
  Form1 -> PaintBox1 -> Canvas -> Pixels[xmid + X * deltaX][ymid - Y * deltaY] = clGreen;
  }
 }
void GraphicClass::SetXKind(int xkind)
 {
 XKind = xkind;
 }
void GraphicClass::SetYKind(int ykind)
 {
 YKind = ykind;
 }
void GraphicClass::SetAX(double ax)
 {
 a_x = ax;
 }
void GraphicClass::SetBX(double bx)
 {
 b_x = bx;
 }
void GraphicClass::SetCX(double cx)
 {
 c_x = cx;
 }
void GraphicClass::SetAY(double ay)
 {
 a_y = ay;
 }
void GraphicClass::SetBY(double by)
 {
 b_y = by;
 }
void GraphicClass::SetCY(double cy)
 {
 c_y = cy;
 }
void GraphicClass::SetDeltaX(double dX)
 {
 deltaX = dX;
 }
void GraphicClass::SetDeltaY(double dY)
 {
 deltaY = dY;
 }

GraphicClass Graphic;
//--------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Graphic.DrawAxis();
}
//--------------------------------------------------------------------
void __fastcall TForm1::TrackBar1Change(TObject *Sender)
{
Edit7 -> Text = TrackBar1 -> Position;
Graphic.SetDeltaX(TrackBar1 -> Position);
Graphic.DrawAxis();
Graphic.DrawGraphic();
}
//--------------------------------------------------------------------
void __fastcall TForm1::TrackBar2Change(TObject *Sender)
{
Edit8 -> Text = TrackBar2 -> Position;
Graphic.SetDeltaY(TrackBar2 -> Position);
Graphic.DrawAxis();
Graphic.DrawGraphic();
}
//--------------------------------------------------------------------
void __fastcall TForm1::ComboBox1Change(TObject *Sender)
{
Graphic.SetXKind(ComboBox1 -> ItemIndex);
Graphic.DrawAxis();
Graphic.DrawGraphic();
}
//--------------------------------------------------------------------
void __fastcall TForm1::ComboBox2Change(TObject *Sender)
{
Graphic.SetYKind(ComboBox2 -> ItemIndex);
Graphic.DrawAxis();
Graphic.DrawGraphic();
}
//--------------------------------------------------------------------
void __fastcall TForm1::Edit1Change(TObject *Sender)
{
if (Edit1 -> Text == "") Edit1 -> Text = "0";
Graphic.SetAX(Edit1 -> Text.ToDouble());
Graphic.DrawAxis();
Graphic.DrawGraphic();
}
//--------------------------------------------------------------------
void __fastcall TForm1::Edit2Change(TObject *Sender)
{
if (Edit2 -> Text == "") Edit2 -> Text = "0";
Graphic.SetBX(Edit2 -> Text.ToDouble());
Graphic.DrawAxis();
Graphic.DrawGraphic();
}
//--------------------------------------------------------------------
void __fastcall TForm1::Edit3Change(TObject *Sender)
{
if (Edit3 -> Text == "") Edit3 -> Text = "0";
Graphic.SetCX(Edit3 -> Text.ToDouble());
Graphic.DrawAxis();
Graphic.DrawGraphic();
}
//--------------------------------------------------------------------
void __fastcall TForm1::Edit4Change(TObject *Sender)
{
if (Edit4 -> Text == "") Edit4 -> Text = "0";
Graphic.SetAY(Edit4 -> Text.ToDouble());
Graphic.DrawAxis();
Graphic.DrawGraphic();
}
//--------------------------------------------------------------------
void __fastcall TForm1::Edit5Change(TObject *Sender)
{
if (Edit5 -> Text == "") Edit5 -> Text = "0";
Graphic.SetBY(Edit5 -> Text.ToDouble());
Graphic.DrawAxis();
Graphic.DrawGraphic();
}
//--------------------------------------------------------------------
void __fastcall TForm1::Edit6Change(TObject *Sender)
{
if (Edit6 -> Text == "") Edit6 -> Text = "0";
Graphic.SetCY(Edit6 -> Text.ToDouble());
Graphic.DrawAxis();
Graphic.DrawGraphic();
}

Код программы с комментариями

#include "math.h" //Подключаем математическую библиотеку

class GraphicClass //Класс, реализующий наш график
 {
 private:
 int xmax, ymax; //Размеры экрана, на котором рисуется график
 int xmid, ymid; //Середина экрана
 int XKind, YKind; //Индекс функций для X и Y из списка
 double X, Y; //Вычисляемые координаты выводимой на экран точки
 double deltaX, deltaY; //Масштаб - длины (в пикселах)
  единичных отрезков в смысле вычисляемых значений
 double a_x, b_x, c_x, a_y, b_y, c_y; //Числовые параметры функций
 public:
 GraphicClass(); //Конструктор класса
 void DrawAxis(); //Функция, рисующая оси
 void DrawGraphic(); //Функция, рисующая сам график
 void SetXKind(int xkind); //Интерфейсные функции, устанавливающие
 void SetYKind(int ykind); //значения соответствующих величин
 void SetAX(double ax);
 void SetBX(double bx);
 void SetCX(double cx);
 void SetAY(double ay);
 void SetBY(double by);
 void SetCY(double cy);
 void SetDeltaX(double dX);
 void SetDeltaY(double dY);
 };
GraphicClass::GraphicClass()
 { //Устанавливаем значения по умолчанию
 xmax = 681;
 ymax = 465;
 xmid = xmax / 2;
 ymid = ymax / 2;
 deltaX = 10;
 deltaY = 10;
 a_x = 1;
 b_x = 1;
 c_x = 0;
 a_y = 1;
 b_y = 1;
 c_y = 0;
 XKind = 0;
 YKind = 0;
 }
void GraphicClass::DrawAxis()
 {
 Form1 -> PaintBox1 -> Repaint(); //Очищаем экран
 Form1 -> PaintBox1 -> Canvas -> MoveTo(1, ymid); //Рисуем линии осей
 Form1 -> PaintBox1 -> Canvas -> LineTo(xmax, ymid);
 Form1 -> PaintBox1 -> Canvas -> MoveTo(xmid, 1);
 Form1 -> PaintBox1 -> Canvas -> LineTo(xmid, ymax);

 for (int i = 0; i <= xmax / deltaX; i++)
  { //Для явного отображения масштаба чертим деления на осях-на X...
  Form1 -> PaintBox1 -> Canvas -> MoveTo(xmid + i * deltaX, ymid - 1);
  Form1 -> PaintBox1 -> Canvas -> LineTo(xmid + i * deltaX, ymid + 2);
  Form1 -> PaintBox1 -> Canvas -> MoveTo(xmid - i * deltaX, ymid - 1);
  Form1 -> PaintBox1 -> Canvas -> LineTo(xmid - i * deltaX, ymid + 2);
  }
 for (int i = 0; i <= ymax / deltaY; i++)
  { //...и на Y
  Form1 -> PaintBox1 -> Canvas -> MoveTo(xmid - 1, ymid + i * deltaY);
  Form1 -> PaintBox1 -> Canvas -> LineTo(xmid + 2, ymid + i * deltaY);
  Form1 -> PaintBox1 -> Canvas -> MoveTo(xmid - 1, ymid - i * deltaY);
  Form1 -> PaintBox1 -> Canvas -> LineTo(xmid + 2, ymid - i * deltaY);
  }
 }
void GraphicClass::DrawGraphic()
 {
 for (int t = -1000; t <= 1000; t++)
  { //2001 точка, соответствующая различным значениям параметра
  switch(XKind) //Считаем X для точки в зависимости от выбранной
   { //пользователем формулы
   case 0: X = a_x; break;
   case 1: X = a_x * 0.01 * t + b_x; break;
   case 2: if (!(b_x < 0 && t == 0)) //Ноль в отрицательной степени
    X = a_x * (pow(0.01 * t, b_x) + c_x); break;
   case 3: X = a_x * sin(b_x * 0.01 * t + c_x); break;
   case 4: X = a_x * cos(b_x * 0.01 * t + c_x); break;
   case 5: X = a_x * tan(b_x * 0.01 * t + c_x); break;
   case 6: if (b_x * 0.01 * t + c_x != 0)
    X = a_x / tan(b_x * 0.01 * t + c_x); break;
   case 7: X = a_x * exp(b_x * 0.01 * t + c_x); break;
   case 8: X = a_x * sin(b_x * 0.01 * t) * sin(c_x * 0.01 * t); break;
   case 9: X = a_x * sin(b_x * 0.01 * t) * cos(c_x * 0.01 * t); break;
   case 10: X = a_x * cos(b_x * 0.01 * t) * cos(c_x * 0.01 * t); break;
   case 11: X = a_x * exp(b_x * 0.01 * t) * sin (c_x * 0.01 * t); break;
   };
  switch(YKind) //Аналогично считаем для той же точки Y
   {
   case 0: Y = a_y; break;
   case 1: Y = a_y * 0.01 * t + b_y; break;
   case 2: if (!(b_y < 0 && t == 0))
    Y = a_y * (pow(0.01 * t, b_y) + c_y); break;
   case 3: Y = a_y * sin(b_y * 0.01 * t + c_y); break;
   case 4: Y = a_y * cos(b_y * 0.01 * t + c_y); break;
   case 5: Y = a_y * tan(b_y * 0.01 * t + c_y); break;
   case 6: if (b_y * 0.01 * t + c_y != 0)
    Y = a_y / tan(b_y * 0.01 * t + c_y); break;
   case 7: Y = a_y * exp(b_y * 0.01 * t + c_y); break;
   case 8: Y = a_y * sin(b_y * 0.01 * t) * sin(c_y * 0.01 * t); break;
   case 9: Y = a_y * sin(b_y * 0.01 * t) * cos(c_y * 0.01 * t); break;
   case 10: Y = a_y * cos(b_y * 0.01 * t) * cos(c_y * 0.01 * t); break;
   case 11: Y = a_y * exp(b_y * 0.01 * t) * sin (c_y * 0.01 * t); break;
   };
  Form1 -> PaintBox1 -> Canvas -> Pixels[xmid +
   X * deltaX][ymid - Y * deltaY] = clGreen; //Рисуем эту точку
  }
 }
void GraphicClass::SetXKind(int xkind) //Простые интерфейсные функции
 {
 XKind = xkind;
 }
void GraphicClass::SetYKind(int ykind)
 {
 YKind = ykind;
 }
void GraphicClass::SetAX(double ax)
 {
 a_x = ax;
 }
void GraphicClass::SetBX(double bx)
 {
 b_x = bx;
 }
void GraphicClass::SetCX(double cx)
 {
 c_x = cx;
 }
void GraphicClass::SetAY(double ay)
 {
 a_y = ay;
 }
void GraphicClass::SetBY(double by)
 {
 b_y = by;
 }
void GraphicClass::SetCY(double cy)
 {
 c_y = cy;
 }
void GraphicClass::SetDeltaX(double dX)
 {
 deltaX = dX;
 }
void GraphicClass::SetDeltaY(double dY)
 {
 deltaY = dY;
 }

GraphicClass Graphic; //Объект класса графика
//--------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Graphic.DrawAxis(); //По нажатию кнопки просто рисуются оси
}
//--------------------------------------------------------------------
void __fastcall TForm1::TrackBar1Change(TObject *Sender)
{
Edit7 -> Text = TrackBar1 -> Position; //Выводим значение
Graphic.SetDeltaX(TrackBar1 -> Position); //Устанавливаем масштаб по X
Graphic.DrawAxis(); //Рисуем оси (с перерисовкой экрана)
Graphic.DrawGraphic(); //Рисуем график с учетом изменения масштаба
}
//--------------------------------------------------------------------
void __fastcall TForm1::TrackBar2Change(TObject *Sender)
{ //Аналогично для масштаба по оси Y
Edit8 -> Text = TrackBar2 -> Position;
Graphic.SetDeltaY(TrackBar2 -> Position);
Graphic.DrawAxis();
Graphic.DrawGraphic();
}
//--------------------------------------------------------------------
void __fastcall TForm1::ComboBox1Change(TObject *Sender)
{ //Устанавливаем новую формулу для X и рисуем новый график
Graphic.SetXKind(ComboBox1 -> ItemIndex);
Graphic.DrawAxis();
Graphic.DrawGraphic();
}
//--------------------------------------------------------------------
void __fastcall TForm1::ComboBox2Change(TObject *Sender)
{ //Новая формула для Y, рисуем график с учетом изменения
Graphic.SetYKind(ComboBox2 -> ItemIndex);
Graphic.DrawAxis();
Graphic.DrawGraphic();
}
//--------------------------------------------------------------------
void __fastcall TForm1::Edit1Change(TObject *Sender)
{
if (Edit1 -> Text == "") Edit1 -> Text = "0";
 //Если поле станет пустым, интерпретируем это как "ноль"
Graphic.SetAX(Edit1 -> Text.ToDouble());
 //Преобразуем строку в число и устанавливаем численный параметр
Graphic.DrawAxis(); //Как и везде, перерисовываем оси и график
Graphic.DrawGraphic();
}
//--------------------------------------------------------------------
void __fastcall TForm1::Edit2Change(TObject *Sender)
{ //Здесь и далее - аналогично
if (Edit2 -> Text == "") Edit2 -> Text = "0";
Graphic.SetBX(Edit2 -> Text.ToDouble());
Graphic.DrawAxis();
Graphic.DrawGraphic();
}
//--------------------------------------------------------------------
void __fastcall TForm1::Edit3Change(TObject *Sender)
{
if (Edit3 -> Text == "") Edit3 -> Text = "0";
Graphic.SetCX(Edit3 -> Text.ToDouble());
Graphic.DrawAxis();
Graphic.DrawGraphic();
}
//--------------------------------------------------------------------
void __fastcall TForm1::Edit4Change(TObject *Sender)
{
if (Edit4 -> Text == "") Edit4 -> Text = "0";
Graphic.SetAY(Edit4 -> Text.ToDouble());
Graphic.DrawAxis();
Graphic.DrawGraphic();
}
//--------------------------------------------------------------------
void __fastcall TForm1::Edit5Change(TObject *Sender)
{
if (Edit5 -> Text == "") Edit5 -> Text = "0";
Graphic.SetBY(Edit5 -> Text.ToDouble());
Graphic.DrawAxis();
Graphic.DrawGraphic();
}
//--------------------------------------------------------------------
void __fastcall TForm1::Edit6Change(TObject *Sender)
{
if (Edit6 -> Text == "") Edit6 -> Text = "0";
Graphic.SetCY(Edit6 -> Text.ToDouble());
Graphic.DrawAxis();
Graphic.DrawGraphic();
}

Советы по улучшению и расширению программы
В данной программе изменение масштабов графика реализовано только с помощью ползунков, а поля Edit служат только для вывода получающихся значений. Для большей завершенности такой версии программы можно добавить изменение масштабов при изменении содержимого соответствующих полей, аналогично тому, как это делается для числовых параметров. При этом, конечно, должна меняться позиция ползунка. Это оставляется читателю в качестве несложного домашнего упражнения.
Само собой, что также можно добавить новые функции для X и Y. Только не забывайте не допускать неопределенностей - например, деления на ноль и извлечения корней из отрицательных чисел.
Для расширения функциональности можно добавить возможность изменять систему координат на полярную. Для этого просто считаем, что мы находили по формулам не X и Y, а R и phi, после чего воспользоваться формулами:
X = R * cos(phi); Y = R * sin(phi);
Чтобы получить интересные эффекты, можно также рассматривать параметр t как момент времени и делать паузу после рисования каждой точки графика (со стиранием предыдущих точек или без) - получится движение по заданной траектории, с сохранением рисунка пройденного маршрута или без.

Обновлено 18.04.2009 16:41