Форум программистов, компьютерный форум, киберфорум
C++ Builder
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.81/36: Рейтинг темы: голосов - 36, средняя оценка - 4.81
 Аватар для Gubila_2000
7 / 3 / 3
Регистрация: 07.11.2014
Сообщений: 218
Записей в блоге: 1

В-сплайновые кривые

10.05.2016, 12:59. Показов 7667. Ответов 17
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
дана формула
https://www.cyberforum.ru/cgi-bin/latex.cgi?r(t)=\sum_{k=0}^{n} N_{k}^{q}(t)*P_k

где
https://www.cyberforum.ru/cgi-bin/latex.cgi?t_{min} \leq t \leq t_{max}
https://www.cyberforum.ru/cgi-bin/latex.cgi?1 \leq q \leq n+1

https://www.cyberforum.ru/cgi-bin/latex.cgi?N_{k}^1(t)=\left\{\begin{1} [if]  t_{min} \leq t \leq t_{0} \\ 0 [if] 1 \leq q \leq n+1\right.

https://www.cyberforum.ru/cgi-bin/latex.cgi?N_{k}^q(t)=\frac{t-t_k}{t_{k+q-1}-t_k}*N_{k}^{q-1}(t)+\frac{t_{k+q}-t}{t_{k+q}-t_{k+1}}*N_{k+1}^{q-1}(t)

Напишите мне на с++ функцию, которая считает https://www.cyberforum.ru/cgi-bin/latex.cgi?N_{k}^q(t) и возвращает значение.
Я над кривой Безье очень долго думал, а тут вообще темный лес. Нужно только эту функцию, я просто не могу понять нижнюю формулу, что есть что
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
10.05.2016, 12:59
Ответы с готовыми решениями:

Кривые на С++
Добрый день. Есть такая задача: имеются 4 точки на плоскости, их координаты известны. При условии, что точки всегда расположены в виде...

Кривые Серпинского
Мучаюсь третий день! Не могу сделать так, чтобы порядок нужно было вводить с клавиатуры, не константой. Выдает кучу бредовых ошибок. Это...

Кривые Серпинсого
На рисунке изображены кривые Серпинского 1 и 2-го порядков. Составить программу построения кривых 1, 2, 3, 4 и 5-го порядков, так чтобы...

17
place status here
 Аватар для gunslinger
3186 / 2220 / 640
Регистрация: 20.07.2013
Сообщений: 6,013
10.05.2016, 19:51
Это вся имеющаяся информация?
Откуда формулы взяты?

Замечания:
В предпоследней формуле второе условие выполняется всегда. Зачем тогда первое условие? И верно ли оно (как выглядит в общем случае)?
На сколько частей разбит интервал изменения параметра t?
Как N зависит от k в предпоследней формуле?
Что такое https://www.cyberforum.ru/cgi-bin/latex.cgi?{P}_{k}? Точки?
0
 Аватар для Gubila_2000
7 / 3 / 3
Регистрация: 07.11.2014
Сообщений: 218
Записей в блоге: 1
10.05.2016, 19:58  [ТС]
gunslinger, формулы взял из лекций мгту https://www.google.ru/url?sa=t... 1273,d.bGg

P_k я так понимаю, что опорные точки ломаной
0
10.05.2016, 20:06

Не по теме:

Гляну на досуге. Но обещать результат пока не буду.

0
place status here
 Аватар для gunslinger
3186 / 2220 / 640
Регистрация: 20.07.2013
Сообщений: 6,013
12.05.2016, 02:00
Лучший ответ Сообщение было отмечено Gubila_2000 как решение

Решение

Выкладываю, что получилось, а получилось не очень.
Насколько понимаю, нужно еще выбирать значения узлов t (тут внимательней надо теорию просматривать).
В любом случае, может часть кода тебе пригодится, а я на данный момент пас.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
 
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------
const count = 4;
TPoint point[count];
double tmin = 0., tmax = 200.;
double step = count-1. + count + 1;
 
double N(UINT q, UINT k, double t)
{
  if (q == 1)
  {
    if (t >= tmin + step * k && t <= tmin + step * (k + 1))
      return 1;
    else
      return 0;
  }
  else
    return (t - tmin - step * k) / (tmin + step * (k + q + 1) - tmin - step * k) * N(q-1, k, t) +
           (tmin + step * (k + q) - t) / (tmin + step * (k + q) - tmin - step * (k + 1)) * N(q-1, k+1, t);
}
//---------------------------------------------------------------------------
TPoint R(UINT q, double t)
{
  TPoint b = TPoint(0, 0);
  double temp;
  for (UINT i = 0; i <= count; i++)
  {
    temp = N(q, i, t);
    b.x += point[i].x * temp;
    b.y += point[i].y * temp;
  }
  return b;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ButtonClick(TObject *Sender)
{
  Refresh();
  UINT n = count-1, offset = 500, r = 3;
  TPoint a;
  TColor color = clRed;
  TColor color2 = clBlack;
  double step = 0.001;
  randomize();
  for (UINT i = 0; i <= n; i++)
    point[i] = TPoint(random(offset), random(offset));
 
  Canvas->Pen->Color = color2;
  Canvas->TextOutW(point[0].x, point[0].y, 1);
  Canvas->Ellipse(point[0].x - r, point[0].y - r, point[0].x + r, point[0].y + r);
  for (UINT i = 1; i <= n; i++)
  {
    Canvas->TextOutW(point[i].x, point[i].y, i+1);
    Canvas->MoveTo(point[i-1].x, point[i-1].y);
    Canvas->LineTo(point[i].x, point[i].y);
    Canvas->Ellipse(point[i].x - r, point[i].y - r, point[i].x + r, point[i].y + r);
  }
 
  Canvas->MoveTo(point[0].x, point[0].y);
  Canvas->Pen->Color = color;
  for(double t = tmin + step; t <= tmax; t += step)
  {
    a = R(n, t);
    if (fabs(a.x) > 10 && fabs(a.y) > 10)
      Canvas->LineTo(a.x, a.y);
  }
 
//  Canvas->MoveTo(point[0].x, point[0].y);
//  Canvas->Pen->Color = clBlue;
//  Canvas->PolyBezier(point, n-1);
}
//---------------------------------------------------------------------------
Миниатюры
В-сплайновые кривые  
2
place status here
 Аватар для gunslinger
3186 / 2220 / 640
Регистрация: 20.07.2013
Сообщений: 6,013
14.05.2016, 14:11
Подправил немного (но до "истины" еще далеко):
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
//---------------------------------------------------------------------------
 
#include <vcl.h>
#pragma hdrstop
 
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------
const count = 4;
TPoint point[count];
double tmin = 0., tmax = 6.;
double step = (tmax-tmin) / (count-1 + count-1);
 
double N(UINT q, UINT k, double t)
{
  if (q == 1)
  {
    if (t >= tmin + step * k && t <= tmin + step * (k + 1))
      return 1;
    else
      return 0;
  }
  else
    return (t - tmin - step * k) / (tmin + step * (k + q + 1) - tmin - step * k) * N(q-1, k, t) +
           (tmin + step * (k + q) - t) / (tmin + step * (k + q) - tmin - step * (k + 1)) * N(q-1, k+1, t);
}
//---------------------------------------------------------------------------
TPoint R(UINT q, double t)
{
  TPoint b = TPoint(0, 0);
  double temp;
  for (UINT i = 0; i < count; i++)
  {
    temp = N(q, i, t);
    b.x += point[i].x * temp;
    b.y += point[i].y * temp;
  }
  return b;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ButtonClick(TObject *Sender)
{
  Refresh();
  UINT n = count-1, q = n, offset = 500, r = 3;
  TPoint a;
  TColor color = clRed;
  TColor color2 = clBlack;
  double st = 0.00001*1;
  randomize();
  for (UINT i = 0; i < count; i++)
    point[i] = TPoint(random(offset), random(offset));
 
  Canvas->Pen->Color = color2;
  Canvas->TextOutW(point[0].x, point[0].y, 1);
  Canvas->Ellipse(point[0].x - r, point[0].y - r, point[0].x + r, point[0].y + r);
  for (UINT i = 1; i <= n; i++)
  {
    Canvas->TextOutW(point[i].x, point[i].y, i+1);
    Canvas->MoveTo(point[i-1].x, point[i-1].y);
    Canvas->LineTo(point[i].x, point[i].y);
    Canvas->Ellipse(point[i].x - r, point[i].y - r, point[i].x + r, point[i].y + r);
  }
 
  Canvas->Pen->Color = color;
  for(double t = tmin + 0*step; t <= tmax - 0*step; t += st)
  {
    a = R(q, t);
    if (t <= tmin + 2*step || t > tmax - 2*step)
    {
      Canvas->MoveTo(a.x, a.y);
//    Canvas->Pen->Color = color2;
    }
    else
    {
      Canvas->LineTo(a.x, a.y);
//    Canvas->Pen->Color = color;
    }
  }
}
//---------------------------------------------------------------------------
Замечание по коду: если st выбирать равным step, то получим следующее


если же уменьшать st, то


Я почти что уверен, что проблема по крайней мере в выборе точек https://www.cyberforum.ru/cgi-bin/latex.cgi?{t}_{k}.
Возможно, они должны быть не равномерно распределены в диапазоне от https://www.cyberforum.ru/cgi-bin/latex.cgi?{t}_{min} до https://www.cyberforum.ru/cgi-bin/latex.cgi?{t}_{max}, а выбраны исходя из соображений на стр. 14 документа из поста №3 (либо см. вложение).
Миниатюры
В-сплайновые кривые  
1
 Аватар для Gubila_2000
7 / 3 / 3
Регистрация: 07.11.2014
Сообщений: 218
Записей в блоге: 1
14.05.2016, 14:13  [ТС]
gunslinger,

В первоначальном виде все работает нормално, но стоило мне просто добавить свой ввод, как програма стала вылетать с ошибкой деления на ноль. Из-за чего это может быть?
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
TForm3 *Form3;
//---------------------------------------------------------------------------
__fastcall TForm3::TForm3(TComponent* Owner)
    : TForm(Owner)
{
#define ic Image1->Canvas
}
int count, flag=0;
TPoint point[100];
double tmin = 0., tmax = 200.;
double step = count-1. + count + 1;
 
double N(UINT q, UINT k, double t)
{
  if (q == 1)
  {
    if (t >= tmin + step * k && t <= tmin + step * (k + 1))
      return 1;
    else
      return 0;
  }
  else
    return (t - tmin - step * k) / (tmin + step * (k + q + 1) - tmin - step * k) * N(q-1, k, t) +
           (tmin + step * (k + q) - t) / (tmin + step * (k + q) - tmin - step * (k + 1)) * N(q-1, k+1, t);
}
//---------------------------------------------------------------------------
TPoint R(UINT q, double t)
{
  TPoint b = TPoint(0, 0);
  double temp;
  for (UINT i = 0; i < count; i++)
  {
    temp = N(q, i, t);
    b.x += point[i].x * temp;
    b.y += point[i].y * temp;
  }
  return b;
}
//---------------------------------------------------------------------------
void __fastcall TForm3::Button1Click(TObject *Sender)
{
 
    if (flag) {
    Refresh();
    UINT n = count-1;
    TPoint a;
 
    double step = 0.001;
 
     for(double t = tmin + step; t <= tmax; t += step)
    {
        a = R(n, t);
        if (a.x > 10 && a.y > 10)
          ic->LineTo(a.x, a.y);
     }
     flag=0;
} else count = 0;
flag=1;
}
//---------------------------------------------------------------------------
void __fastcall TForm3::Image1MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift,
          int X, int Y)
{
ic->Pen->Width=10;
ic->Pen->Color=clBlack;
point[count]= TPoint(X,Y);
ic->MoveTo(X,Y);
ic->LineTo(X,Y);
if (count) {
   ic->Pen->Width=3;
   ic->Pen->Color=clSilver;
   ic->MoveTo(point[count-1].X, point[count-1].Y);
   ic->LineTo(X,Y);
}
count++;
}
0
place status here
 Аватар для gunslinger
3186 / 2220 / 640
Регистрация: 20.07.2013
Сообщений: 6,013
14.05.2016, 14:21
В документе (стр. 4, внизу) написано по этому поводу - если знаменатель равняется 0, то нужно считать выражение (дробь), содержащее этот знаменатель, равным нулю.
Смотри функцию N().
P.S.: в твоем коде строке №53 не обязательна, я использовал ее для теста.
Можно убрать либо заменить 10 на 0. Лучше первый вариант.
0
 Аватар для Gubila_2000
7 / 3 / 3
Регистрация: 07.11.2014
Сообщений: 218
Записей в блоге: 1
14.05.2016, 14:25  [ТС]
gunslinger, просто странно, что в расчетах я вообще ничего не менял, даже ставил 4 точки, все равно выбивает с ошибкой, а когда count была константой этой ошибки небыло
0
place status here
 Аватар для gunslinger
3186 / 2220 / 640
Регистрация: 20.07.2013
Сообщений: 6,013
14.05.2016, 14:34
И step у меня изначально считался неправильно.
P.S.: значит, ты что-то поменял, что влияет.
Нужно смотреть.

Добавлено через 6 минут
Я сейчас глянуть не могу, только если позже.
0
 Аватар для Gubila_2000
7 / 3 / 3
Регистрация: 07.11.2014
Сообщений: 218
Записей в блоге: 1
14.05.2016, 19:05  [ТС]
gunslinger, нет проблем. Если сам разберусь, то отпишусь

Добавлено через 3 часа 30 минут
Понятия не имею что я изменил, но программа перестала ругаться на деление на ноль и стала рисовать, однако программа стала капризничать и не выполнять некоторые команды. Например не хочет менять цвет и толщину линии, несмотря на то, что я эти команды поместил в тело основной функции. Во время отрисовки сплайна все настройки сбрасывается и рисуется черная линия толщиной 1. И рисовать начинает из левого верхнего угла (1,1).

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
TForm3 *Form3;
//---------------------------------------------------------------------------
__fastcall TForm3::TForm3(TComponent* Owner)
    : TForm(Owner)
{
#define ic Image1->Canvas
}
int  flag=0, nc=0;
int count=4;
TPoint point[100];
double tmin = 0., tmax = 6.;
double step = (tmax-tmin) / (count-1 + count-1);
 
double N(UINT q, UINT k, double t)
{
  if (q == 1)
  {
    if (t >= tmin + step * k && t <= tmin + step * (k + 1))
      return 1;
    else
      return 0;
  }
  else
    return (t - tmin - step * k) / (tmin + step * (k + q + 1) - tmin - step * k) * N(q-1, k, t) +
           (tmin + step * (k + q) - t) / (tmin + step * (k + q) - tmin - step * (k + 1)) * N(q-1, k+1, t);
}
//---------------------------------------------------------------------------
TPoint R(UINT q, double t)
{
  TPoint b = TPoint(0, 0);
  double temp;
  for (UINT i = 0; i < count; i++)
  {
    temp = N(q, i, t);
    b.x += point[i].x * temp;
    b.y += point[i].y * temp;
  }
  return b;
}
//---------------------------------------------------------------------------
void __fastcall TForm3::Button1Click(TObject *Sender)
{
 
    if (flag)
     {
        Refresh();
        UINT n = count-1;
        TPoint a;
        double st = 0.0001;
        double step = 0.001;
        ic->MoveTo(point[0].x,point[0].y);
        ic->Pen->Color=clRed;
        ic->Pen->Width=3;
        for(double t = tmin + step*0; t <= tmax - step*0; t += st)
        {
 
            a = R(n, t);
           /*   if (t <= tmin + 0*step || t > tmax - 0*step)
            {
                 Canvas->MoveTo(a.x, a.y);
 
            }
            else
            { */
                Canvas->LineTo(a.x, a.y);
 
            //}
        }
        flag=0;
     } else {count = 0; flag=1; ic->MoveTo(1,1);}
 
}
//---------------------------------------------------------------------------
void __fastcall TForm3::Image1MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift,
          int X, int Y)
{
ic->Pen->Width=10;
ic->Pen->Color=clBlack;
point[count]= TPoint(X,Y);
ic->MoveTo(X,Y);
ic->LineTo(X,Y);
if (count) {
   ic->Pen->Width=3;
   ic->Pen->Color=clSilver;
   ic->MoveTo(point[count-1].X, point[count-1].Y);
   ic->LineTo(X,Y);
}
 
count++;
}
Но даже если не смотреть на это кривая выглядит чересчур страшной)

Добавлено через 47 минут
Вот на шарпе готовая программа, но тут много лишнего
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
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;
        }
    }
}
http://grafika.me/node/408

Добавлено через 6 минут
0
place status here
 Аватар для gunslinger
3186 / 2220 / 640
Регистрация: 20.07.2013
Сообщений: 6,013
15.05.2016, 03:41
Лучший ответ Сообщение было отмечено BRcr как решение

Решение

Нашел хорошее описание алгоритма (коротко и ясно): http://mathworld.wolfram.com/B-Spline.html

Код на его основе:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
const count = 7, m = 9;
TPoint point[count];
double tmin = 0, tmax = 1, t[m+1] = {tmin, 0, 0, 0.2, 0.4, 0.6, 0.8, 1, 1, tmax};
int n = count - 1, p = m - n - 1;
 
double N(UINT i, UINT j, double T)
{
  if (j == 0)
  {
    if (T >= t[i] && T < t[i+1])  // && t[i+1] > t[i])
      return 1;
    else
      return 0;
  }
  else
  {
    double memb1 = (t[i+j] - t[i]) ? (T - t[i]) / (t[i+j] - t[i]) : 0,
           memb2 = (t[i+j+1] - t[i+1]) ? (t[i+j+1] - T) / (t[i+j+1] - t[i+1]) : 0;
    return memb1 * N(i, j-1, T) + memb2 * N(i+1, j-1, T);
  }
}
//---------------------------------------------------------------------------
TPoint C(UINT p, double T)
{
  TPoint b = TPoint(0, 0);
  double temp;
  for (int i = 0; i <= n; i++)
  {
    temp = N(i, p, T);
    b.x += point[i].x * temp;
    b.y += point[i].y * temp;
  }
  return b;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ButtonClick(TObject *Sender)
{
  Refresh();
  UINT offset = 500, r = 3;
  double step = 0.0001;
  TPoint a;
  TColor color = clRed;
  TColor color2 = clBlack;
  randomize();
  for (int i = 0; i <= n; i++)
    point[i] = TPoint(random(offset), random(offset));
 
  Canvas->Pen->Color = color2;
  Canvas->TextOutW(point[0].x, point[0].y, 1);
  Canvas->Ellipse(point[0].x - r, point[0].y - r, point[0].x + r, point[0].y + r);
  for (int i = 1; i <= n; i++)
  {
    Canvas->TextOutW(point[i].x, point[i].y, i+1);
    Canvas->MoveTo(point[i-1].x, point[i-1].y);
    Canvas->LineTo(point[i].x, point[i].y);
    Canvas->Ellipse(point[i].x - r, point[i].y - r, point[i].x + r, point[i].y + r);
  }
 
  for(double T = tmin; T <= tmax; T += step)
  {
    a = C(p, T);
    Canvas->Pixels[a.x][a.y] = color;
  }
}


Замечание: при некоторых значениях параметра p (обычно когда он > 2), сплайн проходит через левый верхний угол.
Причину пока выяснить не удалось.
Формулу (p = m - n - 1) менять не стоит, можешь изменять count и/или m, а также точки t. Но учти, что они (точки) должны быть неубывающей последовательностью.

3
place status here
 Аватар для gunslinger
3186 / 2220 / 640
Регистрация: 20.07.2013
Сообщений: 6,013
16.05.2016, 05:50
Лучший ответ Сообщение было отмечено Gubila_2000 как решение

Решение

Выяснил причину. Нужно было соответствующим образом выбирать узловые точки https://www.cyberforum.ru/cgi-bin/latex.cgi?{t}_{i}.
Теперь можно строить сплайны вплоть до степени p = 4. Хотя третьей степени вроде достаточно.
Можешь поэкспериментировать.
ехе для случая p = 3 во вложении.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
const count = 7, m = 10;
TPoint point[count];
double tmin = 0, tmax = 1, t[m+1] = {tmin, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, tmax};
int n = count - 1, p = m - count;  // p = m - n - 1;
 
double N(UINT i, UINT j, double T)
{
  if (j == 0)
  {
    if (T >= t[i] && T < t[i+1])  // && t[i+1] > t[i])
      return 1;
    else
      return 0;
  }
  else
  {
    double memb1 = (t[i+j] - t[i]) ? (T - t[i]) / (t[i+j] - t[i]) : 0,
           memb2 = (t[i+j+1] - t[i+1]) ? (t[i+j+1] - T) / (t[i+j+1] - t[i+1]) : 0;
    return memb1 * N(i, j-1, T) + memb2 * N(i+1, j-1, T);
  }
}
//---------------------------------------------------------------------------
TPoint C(UINT p, double T)
{
  TPoint b = TPoint(0, 0);
  double temp;
  for (int i = 0; i <= n; i++)
  {
    temp = N(i, p, T);
    b.x += point[i].x * temp;
    b.y += point[i].y * temp;
  }
  return b;
}
//---------------------------------------------------------------------------
void init()
{
  for (int i = 1; i <= p; i++)
  {
    t[i] = tmin;
    t[m-i] = tmax;
  }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ButtonClick(TObject *Sender)
{
  Refresh();
  init();
  UINT offset = 25, range = 500 - 2 * offset, r = 3;
  double step = 1e-5;
  TPoint a;
  TColor color = clRed;
  TColor color2 = clGray;
  randomize();
  for (int i = 0; i <= n; i++)
    point[i] = TPoint(offset + random(range), offset + random(range));
 
  Canvas->Pen->Color = color2;
  Canvas->TextOutW(point[0].x, point[0].y, 1);
  Canvas->Ellipse(point[0].x - r, point[0].y - r, point[0].x + r, point[0].y + r);
  for (int i = 1; i <= n; i++)
  {
    Canvas->TextOutW(point[i].x, point[i].y, i+1);
    Canvas->MoveTo(point[i-1].x, point[i-1].y);
    Canvas->LineTo(point[i].x, point[i].y);
    Canvas->Ellipse(point[i].x - r, point[i].y - r, point[i].x + r, point[i].y + r);
  }
 
  if (p * 2 <= m - 1)
    for(double T = tmin; T <= tmax; T += step)
    {
      a = C(p, T);
      Canvas->Pixels[a.x][a.y] = color;
    }
  else
    MessageBox(Application->Handle, String("Должно выполняться условие\n\nm-1 >= 2p\n\nПри m=" + String(m) + ", p=" + String(p) + " оно не соблюдается.").c_str(), L"Ошибка", 0);
}
Миниатюры
В-сплайновые кривые  
Вложения
Тип файла: zip bspline.zip (1.36 Мб, 98 просмотров)
1
 Аватар для Gubila_2000
7 / 3 / 3
Регистрация: 07.11.2014
Сообщений: 218
Записей в блоге: 1
16.05.2016, 17:37  [ТС]
gunslinger, Насколько я понял в ходе подбора, если count не константа, а изменяется, то m изменяется по правилу m=count*2-1;
0
place status here
 Аватар для gunslinger
3186 / 2220 / 640
Регистрация: 20.07.2013
Сообщений: 6,013
16.05.2016, 18:01
m никак не зависит от count.
m+1 - количество узловых точек.
count - количество контрольных точек.
n = count - 1, p = m - count.



Функция init() дублирует точки для правильного построения сплайна.

Можно условие
C++
1
if (p * 2 <= m - 1)
заменить на
C++
1
if (p < 5 && p > 0)
Тут добавлено условие, что p должно быть положительным числом.

Тогда строка
C++
1
MessageBox(Application->Handle, String("Должно выполняться условие\n\nm-1 >= 2p\n\nПри m=" + String(m) + ", p=" + String(p) + " оно не соблюдается.").c_str(), L"Ошибка", 0);
меняется на
C++
1
MessageBox(Application->Handle, String("Должно выполняться условие\n\np > 0 и p < 5\n\nПри p=" + String(p) + " оно не соблюдается.").c_str(), L"Ошибка", 0);
Изображения
 
0
place status here
 Аватар для gunslinger
3186 / 2220 / 640
Регистрация: 20.07.2013
Сообщений: 6,013
16.05.2016, 18:18
Что касается твоего утверждения, то ты не до конца понял - просто должно выполняться условие
m <= count*2-1 (или m < count*2).
Плюс m > count (или m >= count+1).
Либо двойное условие для p постом выше (p < 5 && p > 0).
Повторю еще раз, зависимости между m и count в любом случае никакой нет.
0
place status here
 Аватар для gunslinger
3186 / 2220 / 640
Регистрация: 20.07.2013
Сообщений: 6,013
16.05.2016, 21:57
Сделал на основе вышеприведенного кода программу-заставку.
Для больных эпилепсией просьба использовать на свой страх и риск, так как смена фона может быть достаточно резкой, хоть и происходит не с большой частотой.
Миниатюры
В-сплайновые кривые  
Вложения
Тип файла: zip bspline_scr.zip (1.38 Мб, 33 просмотров)
0
place status here
 Аватар для gunslinger
3186 / 2220 / 640
Регистрация: 20.07.2013
Сообщений: 6,013
16.05.2016, 22:20
Немного подправил смену цвета.
Вложения
Тип файла: zip bspline_scr.zip (1.38 Мб, 72 просмотров)
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
16.05.2016, 22:20
Помогаю со студенческими работами здесь

Кривые расчеты
Есть прога зарплаты. Начисляю отпускные одному человеку появляется огромная сумма непойми откуда. Если любому другому, все нормально....

Кривые Безье
Добрый вечер! Помогите, пожалуйста! Никак не могу разобраться с кривыми Безье :cry: Мне нужно как-то сделать программу, которая будет...

Эллиптические кривые
Не подскажете, что можно почитать про эллиптические кривые на средне-популярном уровне? Скажем, на уровне &quot;библиотечки Кванта&quot;

Эллиптические кривые
Доброго времени суток! Нуждаюсь в помощи людей, которые разбираются в этом хоть чуточку лучше меня. К большому сожалению преподаватель дала...

полиномиальные кривые
Здравствуйте! Задание: Z-функции (zmf), PI-функции (pimf) и S-функции (smf). Функция zmf представляет собой асимметричную...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
18
Ответ Создать тему
Новые блоги и статьи
SDL3 для Web (WebAssembly): Работа со звуком через SDL3_mixer
8Observer8 09.02.2026
Содержание блога Пошагово создадим проект для загрузки звукового файла и воспроизведения звука с помощью библиотеки SDL3_mixer. Звук будет воспроизводиться по клику мышки по холсту на Desktop и по. . .
SDL3 для Web (WebAssembly): Основы отладки веб-приложений на SDL3 по USB и Wi-Fi, запущенных в браузере мобильных устройств
8Observer8 07.02.2026
Содержание блога Браузер Chrome имеет средства для отладки мобильных веб-приложений по USB. В этой пошаговой инструкции ограничимся работой с консолью. Вывод в консоль - это часть процесса. . .
SDL3 для Web (WebAssembly): Обработчик клика мыши в браузере ПК и касания экрана в браузере на мобильном устройстве
8Observer8 02.02.2026
Содержание блога Для начала пошагово создадим рабочий пример для подготовки к экспериментам в браузере ПК и в браузере мобильного устройства. Потом напишем обработчик клика мыши и обработчик. . .
Философия технологии
iceja 01.02.2026
На мой взгляд у человека в технических проектах остается роль генерального директора. Все остальное нейронки делают уже лучше человека. Они не могут нести предпринимательские риски, не могут. . .
SDL3 для Web (WebAssembly): Вывод текста со шрифтом TTF с помощью SDL3_ttf
8Observer8 01.02.2026
Содержание блога В этой пошаговой инструкции создадим с нуля веб-приложение, которое выводит текст в окне браузера. Запустим на Android на локальном сервере. Загрузим Release на бесплатный. . .
SDL3 для Web (WebAssembly): Сборка C/C++ проекта из консоли
8Observer8 31.01.2026
Содержание блога Если вы откроете примеры для начинающих на официальном репозитории SDL3 в папке: examples, то вы увидите, что все примеры используют следующие четыре обязательные функции, а. . .
SDL3 для Web (WebAssembly): Установка Emscripten SDK (emsdk) и CMake для сборки C и C++ приложений в Wasm
8Observer8 30.01.2026
Содержание блога Для того чтобы скачать Emscripten SDK (emsdk) необходимо сначало скачать и уставить Git: Install for Windows. Следуйте стандартной процедуре установки Git через установщик. . . .
SDL3 для Android: Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 29.01.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами. Версия v3 была полностью переписана на Си, в. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru