Среда программирования:
Microsoft Visual Studio 2013
Статья по теме:
Для определённого количества точек, заданных своими координатами, построить кривую, называемую β-сплайном, которая является аппроксимацией неизвестной функции, содержащей исходные точки.
Создаём форму с полем для рисования графика, кнопками "Рассчитать", "Добавить", "Удалить" (вершину) и панелью, состоящей из полей для ввода координат.
В поля вводим целые числа в необходимом порядке, если нужно, добавляем новые поля или удаляем неиспользуемые. Для построения графика нажимаем кнопку "Рассчитать".
Основные компоненты программы:
- Структура mypanel, состоящая из панели с полями для ввода;
- Массив исходных (опорных) точек coords, в который передаются данные из полей ввода;
- Функции
- 1)load_Form (загрузка формы),
- 2)new_Panel (создание новой панели),
- 3)Coefficient (расчёт коэффициентов),
- 4)Draw_spline (построение сплайна),
- 5)Calculate (перенос данных с панелей в массив и вызов функций рисования).
Код программы:
using System; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; namespace b_spline { public partial class Form1 : Form { public struct mypanel // создаём структуру, содержащую { //панель, на которой расположены public Panel pan; //два поля для ввода координат public TextBox tbxx; public TextBox tbxy; // и надписи X и Y. public Label lx; public Label ly; } public mypanel temp; //с помощью этой переменной будем инициализировать новые панели private List<mypanel> pnls;// список панелей, в котором будут храниться все наши панели public int pnls_num;// количество элементов в списке public PointF[] coords; // массив точек, в который будем загружать записанные в текстбоксы данные из списка панелей private float[] a = new float[4];// массив для рассчёта коэффициентов а private float[] b = new float[4];// для рассчёта коэффициентов b public Graphics graph;// поверхность для рисования public Bitmap btmp;// рисунок public Pen axis_pen = new Pen(Color.Black, 1);// для рисования осей public Pen spline_pen = new Pen(Color.Blue, 1); // для рисования сплайна public SolidBrush points_brush= new SolidBrush(Color.Red); // для рисования опорных точек public Form1() { this.InitializeComponent(); } private void load_Form(object sender, EventArgs e) { load(); }// обработка события " загрузка формы" public void load() { temp = new mypanel();// создаем новый объект типа mypanel, из которого будем передавать данные в список // инициализируем уже существующими элементами (для удобства построения temp.pan = panel12; temp.tbxx = TextBox23; temp.tbxy = TextBox24; temp.lx = label23; temp.ly = label24; pnls = new List<mypanel>(4);// инициализируем список панелей (минимум 4 точки) pnls.Add(temp);// добавляем существующую панель в список for (int i = 1; i < 4; i++) pnls.Add(new_Panel(pnls[i - 1])); //создаём еще 3 панели pnls_num = 4;// устанавливаем счётчик } public void Add_to_control(mypanel panel) { // добавляем родителей каждому элементу структуры. Это необходимо для отображения на экране flowLayoutPanel1.Controls.Add(panel.pan); panel.pan.Controls.Add(panel.tbxx); panel.pan.Controls.Add(panel.tbxy); panel.pan.Controls.Add(panel.lx); panel.pan.Controls.Add(panel.ly); } public mypanel new_Panel(mypanel sender) { //инициализируем объект mypanel и объекты, которые к нему подсоединим temp = new mypanel(); temp.pan = new Panel();// панель, содержащая боксы и надписи temp.lx = new Label(); temp.ly = new Label(); temp.tbxx = new TextBox();// бокс для ввода x temp.tbxy = new TextBox();// бокс для ввода y temp.pan.Location = new Point(3, 28*sender.pan.Location.Y + 3);// каждую новую панель сдвигаем на 28 пикселов вниз temp.pan.Name = "panel" + temp.pan.Location.Y;// имя temp.pan.Size = new Size(152, 25);// размер temp.lx.Font = temp.ly.Font = new Font("Times New Roman", 6.75F, FontStyle.Bold, GraphicsUnit.Point, 204);//параметры шрифта temp.lx.Margin = temp.ly.Margin = new Padding(0);//внешняя граница не видна temp.lx.Size = temp.ly.Size = new Size(14, 11);// размер temp.lx.Text = "X:";// текст в label'ах temp.ly.Text = "Y:"; //координаты label lx и ly temp.lx.Location = new Point(1, 7);//координаты temp.ly.Location = new Point(75, 6); temp.lx.Name = "temp.lx" + temp.pan.Location.Y;// имена temp.ly.Name = "temp.ly" + temp.pan.Location.Y; temp.tbxx.Size = temp.tbxy.Size = new Size(60, 20); temp.tbxx.KeyPress += (this.key_press);// добавляем функцию обработки ввода temp.tbxy.KeyPress += (this.key_press); temp.tbxx.Location = new Point(15, 2);//координаты temp.tbxy.Location = new Point(89, 2); temp.tbxx.Name = "temp.tbxx" + temp.pan.Location.Y;//имена temp.tbxy.Name = "temp.tbxy" + temp.pan.Location.Y; temp.tbxx.TabIndex = 1;//очерёдность переключения с помощью Tab temp.tbxy.TabIndex = 2; Add_to_control(temp);// добавляем к родителям return temp;// возвращаем готовый элемент списка } private int is_not_empty(TextBox tbx)// проверка заполненности поля ввода координат (возвращает содержимое) { int res;// создаем результат типа int (можно вводить только целые значения) if (!Int32.TryParse(tbx.Text, out res))// проверяем введено ли значение res = Int32.MaxValue;// если нет, то присваиваем результату 2^32 (не самый лучший способ, но размеры экрана всё-таки намного меньше) return res;// возвращаем полученнное значение } private void key_press(object s, KeyPressEventArgs e)// проверка ввода { TextBox sender = (TextBox) s;// знаем, что событие создает TextBox, поэтому производим приведение типов if (e.KeyChar != 22)// если не производится вставка // Handled- это свойство KeyPressEventArgs, которое показывает, обработано ли нажатие клавиши // проверяем ввод Разрешено вводить "-" первым символом, цифру и нажимать Backspace if (!Char.IsDigit(e.KeyChar) && e.KeyChar != (char) Keys.Back && (e.KeyChar != '-' || (sender).SelectionStart != 0 || ((sender).Text.Contains("-") && !(sender).SelectedText.Contains("-")))) e.Handled = true; } private void Add_field(object sender, EventArgs e)// если нажали кнопку "Добавить", появляется новая панель { pnls.Add(new_Panel(pnls[pnls.Count - 1])); pnls_num++; } private void Delete_field(object sender, EventArgs e)// по нажатию кнопки "Удалить" удаляется панель, если их количество больше 4 { if (pnls_num > 4) { pnls[pnls.Count - 1].pan.Dispose(); pnls.RemoveAt(pnls.Count - 1); pnls_num--; } } public void Calculate(object sender, EventArgs e) { //в функции производится считывание данных из TextBox'ов //проверка корректности и вызов функции рассчёта и отображения точек и сплайна bool correct_input = true; // флаг правильного ввода coords = new PointF[pnls_num + 2];// создаём массив точек, в которые помещаем данные из текстбоксов for (int i = 0; i < pnls_num; i++)// считываем данные из всех текстбоксов { if ((coords[i + 1].X = is_not_empty(pnls[i].tbxx)) == Int32.MaxValue ||// проверяем правильный ввод (coords[i + 1].Y = is_not_empty(pnls[i].tbxy)) == Int32.MaxValue) { correct_input = false;// если неправильный, то выходим из цикла break; } } if (correct_input == false)// ошибка MessageBox.Show("Рассчёт не может быть произведён.\r\nНеверно (неполностью) заполнены поля ввода."); else { btmp = new Bitmap(pictureBox1.Width , pictureBox1.Height);// инициализируем битмап // для правильного отображения сплайна необходимо дважды добавить первую и последнюю точку coords[0] = coords[1]; coords[pnls_num + 1] = coords[pnls_num]; pictureBox1.Refresh();// обновляем pictureBox(стираем старое) graph = Graphics.FromImage(btmp);// инициализируем поверхность для рисования graph.TranslateTransform(pictureBox1.Width/2, pictureBox1.Height/2);// переносим начало координат в центр graph.ScaleTransform(1, -1);// меняем направление оси Y //рисуем координатные оси graph.DrawLine(axis_pen, 0, -pictureBox1.Height / 2, 0, pictureBox1.Height / 2); graph.DrawLine(axis_pen, -pictureBox1.Width / 2, 0,pictureBox1.Width / 2 , 0); Draw_spline();// рисуем сплайн Draw(null, null);// рисуем опорные точки } } public void Draw(object sender, PaintEventArgs e) { for (int i = 1; i < pnls_num+1; i++) graph.FillRectangle(points_brush, coords[i].X - 1, coords[i].Y - 1, 3, 3);// точки рисуются прямоугольниками pictureBox1.BackgroundImage = btmp;// переносим на битмап } public void Draw_spline() { for (int i = 1; i < pnls_num; i++)// в цикле по всем четвёркам точек { Coefficient(i);// считаем коэффициенты PointF[] points=new PointF[100];// создаём массив промежуточных точек for(int j=0;j<100;j++) { float t = (float)((float)j/100);// шаг интерполяции // передаём массиву точек значения по методу beta-spline points[j].X = (a[0] + t * (a[1] + t * (a[2] + t * a[3]))); points[j].Y = (b[0] + t * (b[1] + t * (b[2] + t * b[3]))); } graph.DrawCurve(spline_pen, points,0.1f);// рисуем кривую по полученным точкам } } public void Coefficient(int i)// в функции рассчитываются коэффициенты a0-a3, b0-b3 { a[3] = (-coords[i - 1].X + 3*coords[i].X - 3*coords[i + 1].X + coords[i + 2].X)/6; a[2] = (coords[i - 1].X - 2*coords[i].X + coords[i + 1].X)/2; a[1] = (-coords[i - 1].X + coords[i + 1].X)/2; a[0] = (coords[i - 1].X + 4*coords[i].X + coords[i + 1].X)/6; b[3] = (-coords[i - 1].Y + 3*coords[i].Y - 3*coords[i + 1].Y + coords[i + 2].Y)/6; b[2] = (coords[i - 1].Y - 2*coords[i].Y + coords[i + 1].Y)/2; b[1] = (-coords[i - 1].Y + coords[i + 1].Y)/2; b[0] = (coords[i - 1].Y + 4*coords[i].Y + coords[i + 1].Y)/6; } } }
Прикрепленный файл | Размер |
---|---|
Martyniuk-b_spline.rar | 74.08 кб |
b_spline_program.rar | 10.21 кб |