Глава 24. График линейной функции
Листинг 24.1. Наш код в шаблоне метода для кнопки Graph.
double a_Form3, b_Form3, x_min_Form3, x_max_Form3;
a_Form3 = Convert.ToDouble(textBox1.Text);
b_Form3 = Convert.ToDouble(textBox2.Text);
x_min_Form3 = Convert.ToDouble(textBox3.Text);
x_max_Form3 = Convert.ToDouble(textBox4.Text);
Form5 myForm5 = new Form5();
myForm5.a = a_Form3;
myForm5.b = b_Form3;
myForm5.x_min = x_min_Form3;
myForm5.x_max = x_max_Form3;
myForm5.Show();
Для программной реализации этого алгоритма дважды щелкаем значок timer1 (ниже формы в режиме проектирования). Появляется файл с шаблоном, в который записываем наш следующий код:
//Объявляем булеву переменную myColor со значением false:
bool myColor = false;
private void timer1_Tick(object sender, EventArgs e)
{
//Вводим анимацию:
if (myColor == false)
{
//Выводим красный цвет переключателя:
this.radioButton2.BackColor =
System.Drawing.Color.Red;
//Изменяем значение myColor на противоположное:
myColor = true;
}
else
{
//Выводим белый цвет переключателя:
this.radioButton2.BackColor =
System.Drawing.Color.White;
//Изменяем значение myColor на противоположное:
myColor = false;
}
}
Листинг 24.2. Первая часть кода (выше шаблона pictureBox1_Paint).
//Параметры графика функции y = a*x + b
//в виде глобальных переменных:
public float a, b, x_min, x_max;
float Function_of_graph(float x)
{
float Function;
//Метод y = f(x), график которой будем строить:
Function = a * x + b;
return Function;
}
Листинг 24.3. Вторая часть кода (выше шаблона pictureBox1_Paint).
//Число точек графика:
public int Npoints = 100;
//Величины (в пикселах) O_x_pix и O_y_pix
//для параллельного переноса
//осей "x" и "y" новой системы координат (по сравнению
//со старой системой в верхнем левом углу рамки PictureBox):
public float O_x_pix = 500;
public float O_y_pix = 350;
//Масштабы по осям "x" и "y" (M_x и M_y) для перехода
//от действительных значений к пикселам
//и построения графика в пикселах:
public float M_x = 450;
public float M_y = 300;
Теперь внутрь этого (приведенного выше) шаблона для процедуры pictureBox1_Paint записываем основную часть нашего кода для вывода графика на экран монитора. Перед каждым логическим блоком кода мы даем подробный комментарий, чтобы читатель мог изучить это важный код и мог грамотно внести изменения (в случае необходимости).
Листинг 24.4. Главный код построения графика функции на экране монитора.
//Шаг по оси абсцисс "x" между точками графика:
float step_x = (x_max-x_min)/Npoints;
//Наибольшее абсолютное значение x_max_abs
//из двух концов заданного нами числового интервала
//x_min и x_max:
float x_max_abs = Math.Abs(x_max);
if (x_max_abs < Math.Abs(x_min)) x_max_abs = Math.Abs(x_min);
//Промежуточные локальные переменные:
float x_0, y_0, x_1, y_1, x_0_pix, y_0_pix, x_1_pix, y_1_pix;
//Расчет минимального y_min и максимального y_max
//действительных значений функции:
float y_min, y_max;
//Присваиваем y_min, y_max значение y_0
//для нулевой точки (i=0):
x_0 = x_min; y_0 = Function_of_graph(x_0);
y_min = y_0; y_max = y_0; int i;
//Организовываем цикл по всем точкам, начиная с i=1:
for (i=1; i<=(Npoints-1); i++)
{
x_1 = x_min + i * step_x;
y_1 = Function_of_graph(x_1);
//Расчет минимального и максимального значений функции:
if (y_min > y_1) y_min = y_1;
if (y_max < y_1) y_max = y_1;
}
//Т.к. в последней точке i = Npoints
//значение x_1 = x_min + Npoints * step_x
//может отличаться от заданного значения x_max
//(из-за накапливания погрешности в цикле), то проверяем,
//может быть y_min или y_max находится в последней
//точке при точном задании нами значения x_max:
x_1 = x_max; y_1 = Function_of_graph(x_1);
//Проверка минимального и максимального
//значений функции в последней точке:
if (y_min > y_1) y_min = y_1;
if (y_max < y_1) y_max = y_1;
//Наибольшее абсолютное значение функции y_max_abs
//из двух значений y_min и y_max:
float y_max_abs = Math.Abs(y_max);
if (y_max_abs < Math.Abs(y_min)) y_max_abs = Math.Abs(y_min);
//Строим сетку координат:
//Сначала строим ось абсцисс "x" от x = -1 до x = 1:
//Задаем абсциссу последней точки оси абсцисс "x"
//при x = 1:
float x_point_end, x_point_end_pix; x_point_end = 1;
x_point_end_pix = x_point_end * M_x + O_x_pix;
//Выбираем зеленое перо толщиной 2:
Pen greenPen_x = new Pen(Color.Green, 2);
//Задаем координаты двух граничных точек оси:
PointF point1 = new PointF(-1 * M_x + O_x_pix, O_y_pix);
PointF point2 = new PointF(x_point_end_pix, O_y_pix);
//Строим линию через две заданные граничные точки:
e.Graphics.DrawLine(greenPen_x, point1, point2);
//Строим горизонтальные линии сетки координат
//(кроме оси "x"):
//Ширина (размах) графика по оси ординат "y":
float span_y = y_max - y_min;
//Число шагов по всей высоте сетки (по оси "y"):
int N_step_grid_y = 20;
//Шаг сетки в направлении оси "y"
//(высота всей сетки равна 2 единицам):
float step_grid_y, step_grid_y_pix;
//Преобразование типов переменных:
step_grid_y = (float) 2 / N_step_grid_y;
step_grid_y_pix = step_grid_y * M_y;
//Выбираем красное перо толщиной 1:
Pen redPen = new Pen(Color.Red, 1);
//Строим сетку от нулевой линии в одну сторону (вниз):
int j_y; float y1, y1_pix;
for (j_y = 1; j_y<=(N_step_grid_y/2); j_y++)
{
y1 = j_y * step_grid_y;
y1_pix = O_y_pix + j_y * step_grid_y_pix;
//Задаем координаты двух граничных точек линии сетки:
PointF point3 = new PointF(-1 * M_x + O_x_pix, y1_pix);
PointF point4 = new PointF(x_point_end_pix, y1_pix);
//Строим линию через две заданные граничные точки:
e.Graphics.DrawLine(redPen, point3, point4);
}
//Строим сетку от нулевой линии в другую сторону (вверх):
for (j_y = 1; j_y<=(N_step_grid_y / 2); j_y++)
{
y1_pix = O_y_pix - j_y * step_grid_y * M_y;
//Задаем координаты двух граничных точек линии сетки:
PointF point5 = new PointF(-1 * M_x + O_x_pix, y1_pix);
PointF point6 = new PointF(x_point_end_pix, y1_pix);
//Строим прямую линию через две заданные точки:
e.Graphics.DrawLine(redPen, point5, point6);
}
//Строим ось ординат "y" от y= -1 до y = 1:
//Задаем ординату последней точки оси ординат "y" при y = 1:
float y_point_end, y_point_end_pix; y_point_end = 1;
y_point_end_pix = y_point_end * M_y + O_y_pix;
//Выбираем зеленое перо толщиной 2:
Pen greenPen_y = new Pen(Color.Green, 2);
//Задаем координаты двух граничных точек оси:
PointF point7 = new PointF(O_x_pix, -1 * M_y + O_y_pix);
PointF point8 = new PointF(O_x_pix, y_point_end_pix);
//Строим линию через две заданные граничные точки:
e.Graphics.DrawLine(greenPen_y, point7, point8);
//Строим вертикальные линии сетки координат (кроме оси "y"):
//Ширина (размах) графика по оси абсцисс "x":
float span_x = x_max - x_min;
//Число шагов по всей ширине сетки по обе стороны от оси y:
int N_step_grid_x = 20;
//Шаг сетки в направлении оси "x"
//(ширина всей сетки равна 2 единицам):
float step_grid_x = 0.1F, step_grid_x_pix;
step_grid_x_pix = step_grid_x * M_x;
//Выбираем красное перо толщиной 1:
Pen redPen_y = new Pen(Color.Red, 1);
//Строим сетку от нулевой линии в одну сторону (вправо):
int j_x; float x1, x1_pix;
for (j_x = 1; j_x<=(N_step_grid_x / 2); j_x++)
{
x1 = j_x * step_grid_x;
x1_pix = O_x_pix + j_x * step_grid_x_pix;
//Задаем координаты двух граничных точек линии сетки:
PointF point9 = new PointF(x1_pix, -1 * M_y + O_y_pix);
PointF point10 = new PointF(x1_pix, y_point_end_pix);
//Строим линию через две заданные граничные точки:
e.Graphics.DrawLine(greenPen_y, point9, point10);
}
//Строим сетку от нулевой линии в другую сторону (влево):
for (j_x = 1; j_x<=(N_step_grid_x / 2); j_x++)
{
x1 = j_x * step_grid_x;
x1_pix = O_x_pix - j_x * step_grid_x_pix;
//Задаем координаты двух граничных точек линии сетки:
PointF point11 = new PointF(x1_pix, -1 * M_y +
O_y_pix);
PointF point12 = new PointF(x1_pix, y_point_end_pix);
//Строим прямую линию через две заданные точки:
e.Graphics.DrawLine(greenPen_y, point11, point12);
}
//Записываем числа по осям координат:
//Объявляем локальные переменные:
int n; float p1 = 1, p2; string msg;
//Записываем числа по оси "O","+y":
for (n = 0; n<=9; n++)
{
p2 = p1 - n * 0.1F;
msg = "" + p2.ToString() + "";
e.Graphics.DrawString(msg, this.Font, Brushes.Blue,
O_x_pix - 35, O_y_pix - 323 + n * step_grid_y_pix);
}
//Записываем числа по оси "O","-y":
p1 = 0;
for (n = 1; n<=10; n++)
{
p2 = p1 - n * 0.1F;
msg = "" + p2.ToString() + "";
e.Graphics.DrawString(msg, this.Font, Brushes.Blue,
O_x_pix - 40, O_y_pix - 23 + n * step_grid_y_pix);
}
//Записываем числа по оси "O","+x":
p1 = 0;
for (n = 0; n<=10; n++)
{
p2 = p1 + n * 0.1F;
msg = "" + p2.ToString() + "";
e.Graphics.DrawString(msg, this.Font, Brushes.Blue,
O_x_pix - 0 + n * step_grid_x_pix, O_y_pix - 0);
}
//Записываем числа по оси "O","-x":
p1 = 0;
for (n = 1; n<=10; n++)
{
p2 = p1 - n * 0.1F;
msg = "" + p2.ToString() + "";
e.Graphics.DrawString(msg, this.Font, Brushes.Blue,
O_x_pix - 39 - n * step_grid_x_pix, O_y_pix - 0);
}
//Записываем обозначение оси y' = y / y_max:
msg = "y ' = y / | y_max | ;";
e.Graphics.DrawString(msg, this.Font,
Brushes.Blue, O_x_pix - 5, O_y_pix - 333);
//Записываем значение | y_max |:
msg = "| y_max | = " + y_max_abs.ToString() + ";";
e.Graphics.DrawString(msg, this.Font,
Brushes.Blue, O_x_pix + 170, O_y_pix - 333);
//Записываем значение y_max:
msg = "y_max = " + y_max.ToString() + ";";
e.Graphics.DrawString(msg, this.Font,
Brushes.Blue, O_x_pix + 380, O_y_pix - 333);
//Записываем значение y_min:
msg = "y_min = " + y_min.ToString() + "";
e.Graphics.DrawString(msg, this.Font,
Brushes.Blue, O_x_pix + 455, O_y_pix - 300);
//Записываем обозначение оси x' = x / | x_max |:
msg = "x ' = x / | x_max |";
e.Graphics.DrawString(msg, this.Font,
Brushes.Blue, O_x_pix + 455, O_y_pix - 30);
//Записываем значение | x_max |:
msg = "| x_max | = " + x_max_abs.ToString() + "";
e.Graphics.DrawString(msg, this.Font,
Brushes.Blue, O_x_pix + 455, O_y_pix + 40);
//Записываем значение x_max:
msg = "x_max = " + x_max.ToString() + "";
e.Graphics.DrawString(msg, this.Font,
Brushes.Blue, O_x_pix + 455, O_y_pix + 90);
//Записываем значение x_min:
msg = "x_min = " + x_min.ToString() + "";
e.Graphics.DrawString(msg, this.Font,
Brushes.Blue, O_x_pix + 455, O_y_pix + 140);
//Построение графика функции y = f(x):
//Координаты нулевой (i=0) точки,
//с которой строится график:
x_0 = x_min; x_0_pix = x_0 / x_max_abs * M_x + O_x_pix;
//Рассчитываем "y" и вводим знак минус,
//чтобы положительное значение "y"
//отложилось вверх по оси "y" (а не вниз):
y_0 = -(Function_of_graph(x_0));
y_0_pix = y_0 / y_max_abs * M_y + O_y_pix;
//Выбираем черное перо толщиной 4:
Pen blackPen = new Pen(Color.Black, 4);
//Организовываем цикл по всем точкам, начиная с i = 1:
for (i = 1; i<=Npoints; i++)
{
//Рассчитываем абсциссу "x" данной i-й точки:
x_1 = x_min + i * step_x;
//Рассчитываем ординату "y" этой i-й точки
//и вводим знак минус, чтобы положительные значения "y"
//откладывались вверх (а не вниз), и чтобы
//строить график традиционно снизу-вверх по оси "y":
y_1 = -(Function_of_graph(x_1));
//Переходим к относительным величинам и пикселам:
x_1_pix = x_1 / x_max_abs * M_x + O_x_pix;
y_1_pix = y_1 / y_max_abs * M_y + O_y_pix;
//Строим отрезок линии графика y = f(x)
//между двумя известными точками:
e.Graphics.DrawLine(blackPen, x_0_pix, y_0_pix,
x_1_pix, y_1_pix);
//Присваиваем предыдущей (i-1)-й точке
//координаты данной i-й точки:
x_0_pix = x_1_pix; y_0_pix = y_1_pix;
//Задаем следующую (i+1)-ю точку:
}
Отметим еще раз, что наши подробные комментарии внутри кода позволят читателю на этом примере понять и изучить основные конструкции и синтаксис языка C#. А также грамотно внести изменения в код, если читатель пожелает модернизировать внешний вид графика для учета своих конкретных требований.
Листинг 24.5. Код для выполнения анимации.
//Объявляем булеву переменную myText со значением false:
bool myText = false;
private void timer1_Tick(object sender, EventArgs e)
{
//Вводим анимацию:
if (myText == false)
{
//Выводим текст:
this.label1.Text =
"Graph 'Straight Line' of function y = a*x + b:";
//Изменяем значение myText на противоположное:
myText = true;
}
else
{
//Удаляем текст:
this.label1.Text = "";
//Изменяем значение myText на противоположное:
myText = false;
}
}