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

Кривая Безье

07.05.2016, 17:10. Показов 15296. Ответов 20
Метки нет (Все метки)

Вооружился статейкой на википедии https://ru.wikipedia.org/wiki/... 1%8C%D0%B5 и использовал формулу, описаную там, в своей программе, и нифига не работает.

Вот функция факториала
C++
1
2
3
4
5
6
7
8
static long Fact (int n)  // факториал
{
    long r=1;
    for (int i = 1; i <=n; i++) {
        r*=i;
    }
    return r;
}
Вот функция базиса
C++
1
2
3
4
5
float getBasis(int i, float t)
{
 
   return (Fact(n)/(Fact(i)*Fact(n-i)))*pow(t,i)*pow(1-t,n-i);
}
Вот главная функция рисования, где p[i][1] = x и p[i][2] = y i-й точки
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void Bezier ()
{
    float step=0.01;
    int x,y;
    float gb;
    //int **res = new int*[n];
    x=p[0][1];
    y=p[0][2];
   for (int i=0; i < n; i++)
   {
 
    for (float t = 0.01; t < 1; t+=step)
        {
           gb=getBasis(i+1, t);//if (t>1)t=1;
           x+=p[i][1]*gb;
           y+=p[i][2]*gb;
           im->LineTo(x,y);
 
        }
    }
}
На выходе получается что чертится линия от первой точки вниз экрана. Возможно я не правильно понял формулу. Подскажите где ошибся.
__________________
Помощь в написании контрольных, курсовых и дипломных работ, диссертаций здесь
0
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
07.05.2016, 17:10
Ответы с готовыми решениями:

Кривая Безье
Добрый день, подскажите пожалуйста, как реализовать отрисовку Кривых Безье. Нашел кучу теории, но...

Кривая Безье по 4 точкам
Доброго времени суток. В общем зада стоит написать програмку где будет рисоваться кривая Безье, но...

Кривая Безье на основе N точек
Доброго времени суток! Вопрос такой, существует n(допустим 500) точек, по ней нужно построить...

кривой Безье
написать проектную процедуру создания кривой Безье, Желательно в С++ Добавлено через 17 часов 16...

20
Практикантроп
4735 / 2641 / 512
Регистрация: 23.09.2011
Сообщений: 5,672
07.05.2016, 17:34 2
Так не подскажешь; здесь не все ясновидящие. Попробуйте значения x и y сохранять ещё и в StringList и по окончании цикла сохраните его в файл. Станет понятно, какие там величины фигурируют.
0
случайный прохожий
2057 / 1326 / 456
Регистрация: 20.07.2013
Сообщений: 3,694
07.05.2016, 17:47 3
Кривая Безье по 4 точкам
Кривая Безье
1
7 / 3 / 3
Регистрация: 07.11.2014
Сообщений: 218
Записей в блоге: 1
08.05.2016, 12:14  [ТС] 4
nick42, да величины должны быть нормальные, так как я пробовал в лоб по полиному для 4-х точек и там все нормально рисуется. А вот с этой универсальной формулой с факториалами ничего не получается. К тому же все значения можно в отладчике посмотреть. Тут главный вопрос в том, что правильно ли я понял алгоритм

Добавлено через 3 минуты
gunslinger, как ни странно, я не находил этих тем, несмотря на то, что даже в гугле вбивал запрос

Добавлено через 17 часов 15 минут
Не хотите вы думать. Попрыгал с бубном около часа и наконец выстроил такую последовательность команд, которая нужна. Ошибки были в главной функции, вот рабочая:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void Bezier ()
{
    float step=0.01;
    int x,y;
    for (float t = 0.01; t <= 1; t+=step)
        {
        x=0;
        y=0;
            for (int i=0; i < n; i++)
            {
 
            x+=p[i][1]*getBasis(i, t);
            y+=p[i][2]*getBasis(i, t);
            }
            im->LineTo(x,y);
        }
}
И да, внутри getBasis нужно использовать n-1, а не n. В остальных случаях не меняется
0
случайный прохожий
2057 / 1326 / 456
Регистрация: 20.07.2013
Сообщений: 3,694
08.05.2016, 23:09 5
Можно так сделать (немного переделал):
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
const count = 10;
TPoint point[count];
//---------------------------------------------------------------------------
unsigned __int64 factor(UINT n)
{
  unsigned __int64 result = 1;
  for (UINT i = 1; i <= n; i++)
    result *= i;
  return result;
}
//---------------------------------------------------------------------------
double basis(UINT i, UINT n, double t)
{
  return factor(n-1) / factor(i) / factor(n-1-i) * pow(t, i) * pow(1-t, n-1-i);
}
//---------------------------------------------------------------------------
TPoint bezier(UINT n, double t)
{
  TPoint b = TPoint(0, 0);
  double temp;
  for (UINT i = 0; i < n; i++)
  {
    temp = basis(i, n, 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, delta = 500;
  TPoint a;
  double step = 0.0001;
  randomize();
  for (UINT i = 0; i < n; i++)
    point[i] = TPoint(random(delta), random(delta));
  Canvas->MoveTo(point[0].x, point[0].y);
  for(double t = step; t <= 1; t += step)
  {
    a = bezier(n, t);
    Canvas->LineTo(a.x, a.y);
  }
}
Миниатюры
Кривая Безье  
2
случайный прохожий
2057 / 1326 / 456
Регистрация: 20.07.2013
Сообщений: 3,694
09.05.2016, 21:54 6
Лучший ответ Сообщение было отмечено BRcr как решение

Решение

Дополнительная "визуализация":
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
void __fastcall TForm1::ButtonClick(TObject *Sender)
{
  Refresh();
  UINT n = count, offset = 500, r = 3;
  TPoint a;
  TColor color = clRed;
  TColor color2 = clBlack;
  double step = 0.0001;
  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 = step; t <= 1; t += step)
  {
    a = bezier(n, t);
    Canvas->LineTo(a.x, a.y);
  }
 
//  Canvas->MoveTo(point[0].x, point[0].y);
//  Canvas->Pen->Color = clBlue;
//  Canvas->PolyBezier(point, n-1);
}
Миниатюры
Кривая Безье   Кривая Безье   Кривая Безье  

Кривая Безье   Кривая Безье   Кривая Безье  

2
377 / 176 / 47
Регистрация: 11.07.2013
Сообщений: 933
16.05.2017, 17:12 7
Повторил ваш код. Всё прекрасно получилось. Спасибо.
Однако кривая Безье, если внимательно присмотреться, получается не ровной, создаётся эффект "смазанности", хотя в
Кривая Безье

Автокаде, например, такое не наблюдается.
Кривая Безье

У вас, если присмотреться, то же самое. Как можно это исправить?
0
377 / 176 / 47
Регистрация: 11.07.2013
Сообщений: 933
16.05.2017, 18:39 8
Решение проблемы найдено.
Пришлось немного изменить функцию TPoint bezier(UINT n, double t). Выполнял на Лазарусе, поэтому, если кому интересно, можете посмотреть код здесь: Как при построении кривой Безье уменьшить разброс точек?
1
случайный прохожий
2057 / 1326 / 456
Регистрация: 20.07.2013
Сообщений: 3,694
16.05.2017, 19:05 9
Насколько помню, проблему решить не особо удалось.
Пробовал уменьшать шаг step, рисовать не линиями, а точками.
Хотя, если увеличить рисунок, вроде немного плавнее получается.
Вот пример общего случая (для которого кривые Безье являются частным случаем): В-сплайновые кривые
Может нужен другой подход при рисовании (для лучшего сглаживания), хотя математически в самом построении кривых вроде все верно.
Миниатюры
Кривая Безье  
0
случайный прохожий
2057 / 1326 / 456
Регистрация: 20.07.2013
Сообщений: 3,694
16.05.2017, 19:46 10
Долго я сообщение писал... Спасибо за решение.

Добавлено через 26 минут
В принципе можно обойтись без итогового округления.
0
377 / 176 / 47
Регистрация: 11.07.2013
Сообщений: 933
16.05.2017, 19:57 11
А есть ли какой-нибудь математический аппарат, позволяющий разбить кривую Безье (или элипс) на кокие-то элементарные фигуры (линии, дуги и пр.)? Суть такая: надо взять Автокадовский DXF-файл и при помощи его управлять положением режущего инструмента станка, при этом как можно ближе к истине отображать на экране положение инструмента. Если отправить сразу координаты узловых точек той же линии Безье, то теряется возможность отслеживать положение инструмента, если отправлять из цикла for(double t = step; t <= 1; t += step)... и одновременно отрисовывать на экране, то слабым местом будет пропускная способность линии связи.
С окружностью и прямыми всё просто: разбивается фигура на отдельные дуги или линии (от 5 до 20 мм), приращения которых передеются в исполнительное устройство. А вот с кривой Безье не всё так просто...
0
случайный прохожий
2057 / 1326 / 456
Регистрация: 20.07.2013
Сообщений: 3,694
16.05.2017, 22:16 12
Если я правильно понимаю, что требуется, то при (для) предварительной примерной оценки положения инструмента можно увеличить шаг step, и, если все устраивает, вернуть исходное значение шага и отрисовывать в "обычном" режиме.
0
377 / 176 / 47
Регистрация: 11.07.2013
Сообщений: 933
16.05.2017, 22:58 13
По идее всё почти так, но не совсем. Шаговые двигатели работают на скорости 20 000 микрошагов в секунду. Интерфейс связи со скоростью 19200 бит/сек. Поэтому, чтобы не останавливались двигатели (это создаст вибрацию) приходится использовать буфер упреждения, глубиной в несколько перемещений. Т.е. из буфера считываются координаты очередного перемещения, двигатели его отрабатывают, компьютер получает уведомление, отрисовывает на экране и отправляет очередные координаты в буфер. Каждая новая точка должна отличаться от предыдущей хотя бы на расстояние, на которое способны переместить двигатели режущий инструмент за время сеанса связи. Вот отсюда, собственно, и вопрос.
Нашёл учебник: Роджерс "Математические основы машинной графики" 2001 г, раздел 5-12 "Разбиение В-сплайнов", но, вероятно, поспешил, т.к. в DXF-файле очень интересная кодировка сплайнов (SPLINE). Нарисовал в AutoCAD-е,
Вложение 833391
а у себя получил
Вложение 833392
(имею ввиду не симметрию по Y, это из-за разных точек начала отсчёта, а несоответствие формы).
0
377 / 176 / 47
Регистрация: 11.07.2013
Сообщений: 933
16.05.2017, 23:01 14
Автокад:
Кривая Безье

Мой результат:
Кривая Безье
0
377 / 176 / 47
Регистрация: 11.07.2013
Сообщений: 933
16.05.2017, 23:51 15
Нашёл интересную статью на тему кривых Безье: Кривые Безье для ваших игр.rar
0
зомбяк
1567 / 1203 / 342
Регистрация: 14.05.2017
Сообщений: 3,909
16.05.2017, 23:52 16
Цитата Сообщение от shyub Посмотреть сообщение
позволяющий разбить кривую Безье (или элипс) на кокие-то элементарные фигуры (линии, дуги и пр.)?
Дуга вообще-то совсем не элементарная, просто в станках её практически всегда реализовывают на нижнем уровне. А разбить на окружности, в принципе, можно (хотя это не будет аппроксимацией, т.к. деление как и на "точки" будет через равные промежутки расстояния). Смысл примерно следующий - берём пару соседних точек кривой, получаем одну касательную к кривой, далее - берём ещё следующую пару точек, получаем касательную к ней, далее по перпендикулярам получаем центр окружности, и её передаём. Математику мож позже напишу, но нужно чтоб оно перед получением радиуса проверяло, не параллельны ли касательные или близки к параллельным (иначе деление на 0 при получении радиуса окружности)
Миниатюры
Кривая Безье  
1
зомбяк
1567 / 1203 / 342
Регистрация: 14.05.2017
Сообщений: 3,909
16.05.2017, 23:55 17
в этом случае вместо дуг передавать линии линии
0
377 / 176 / 47
Регистрация: 11.07.2013
Сообщений: 933
17.05.2017, 00:52 18
Спасибо. Очень понравилась ваша идея.
В последней задаче у меня были прямые и окружности. Их я разбивал на отрезки, координаты очередной точки, куда должен "приехать" рабочий инструмент (а в случае дуги - центр и угол сектора) передавал в станок. В станке использовал ARM-контроллер, который преобразовывал координаты в шаги методом Брезенхема (в случае наклонных и дуг), ну и по мере ответов станка отрисовывал их на экране.

Не по теме. Лет 10 назад увидил рекламу "буржуйского станка": кладут на станину приклад от охотничьего ружъя. Он его просканировал. Затем кладут заготовку, из которой он повторил копию. Вот, в принципе, в чём вся идея. Просканировать, правда, пока ещё не могу, поэтому первоначально использовал CorelDraw и PLT-формат, теперь хочу освоить DXF, а затем, если получится, то и сканирование. Сложности, правда, не в программировании и электронике, а в "доставании железяк" для станка.

Добавлено через 18 минут
Цитата Сообщение от TRam_ Посмотреть сообщение
через равные промежутки расстояния
Не могу оспорить (вернее хочу, но не могу доказать). А если кривая резко на этом промежутке изменяет своё направление... Вот представьте, что станок фрезерует нос Буратино. Одна точка была перед носом, а другая после. Буратино (Пинокио) будет без носа...?
0
зомбяк
1567 / 1203 / 342
Регистрация: 14.05.2017
Сообщений: 3,909
17.05.2017, 01:53 19
Цитата Сообщение от shyub Посмотреть сообщение
А если кривая резко на этом промежутке изменяет своё направление...
тут вопрос не в резком изменении направления, а в том, как точки этой кривой обходятся (т.е. какой шаг между точками). Называется это параметризацией кривой. То что вы описываете - когда "нос" Буратино всего из 3х точек, то естественно предложенный алгоритм её не построит. А вот если точки там будут через примерно равные расстояния, то точки на нос обязательно попадут, в некотором количестве. Нос получится, возможно, менее длинным чем в оригинале, но он таки будет. Если нужно, чтоб "если делаем нос Буратино, то чтоб получился точно, даже в ущерб вибрации инструмента" - то это уже задача аппроксимации, тут шаг по кривой будет переменный, подбирать когда значение аппроксимации будет минимальным, и т.д.

Кое-что в тему - http://dxdy.ru/topic8371.html
По аппроксимации (для случая когда "для кучи точек выбираем максимально близко через них проходящую окружность") - http://prografix.narod.ru/rus_cirap.html, можно например для контроля точности разбиения применить.

И то, что разбиение с автоматически подбирающимся шагом до нас уже делали - http://www.caduser.ru/forum/in... &TID=29238

Добавлено через 9 минут
Да, и в точке "носа" будет меняться направления обхода окружности (с "по часовой" на "против часовой"). Уже и подзабыл, как это в G-кодах...
0
зомбяк
1567 / 1203 / 342
Регистрация: 14.05.2017
Сообщений: 3,909
17.05.2017, 03:25 20
Подумал-подумал, и нашёл ошибку. В общем случае через такие 2 пары точек можно построить только дугу + прямую. Зато считать, наверно, проще - радиус берётся как расстояние от пересечения касательных до ближайшей точки, помноженное на котангенс половины угла между касательными.
Миниатюры
Кривая Безье  
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
17.05.2017, 03:25
Помогаю со студенческими работами здесь

Кривая в C++ Builder
Ребята как по задным иксам и игрекам построить кривую x1=4,y1=-4,x2=5.18,y2=2, x3=6,y3=5;...

Кривая Эрмита
Доброго времени суток =) Не мог бы кто нибудь помочь с написание программы для построение &quot;...

Кривая сортировка пузырьком StringGrid
Есть код сортировки писал сам может что не учел но не могу понять кода жму на кнопку пропадает...

Кривая Безье в сплайн Безье
Здравствуйте. Я по специальности программист и мне нужно генерировать замкнутую кривую не...


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

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2022, CyberForum.ru