Кривая Безье всегда одинаковой длины - PullRequest
8 голосов
/ 23 ноября 2011

Я работаю над игрой на холсте HTML5.

Я хочу нарисовать S-образную кубическую кривую Безье между двумя точками, но я ищу способ вычислить координаты элемента управленияуказывает на то, что сама кривая всегда имеет одинаковую длину независимо от того, насколько близки эти точки, пока не достигнет точки, где кривая станет прямой линией.

Ответы [ 3 ]

2 голосов
/ 23 ноября 2011

Действительно ли необходимо, чтобы кривая была кривой Безье? Установить две круглые дуги, общая длина которых постоянна, гораздо проще. И вы всегда получите S-образную форму.

Подгонка двух дуг окружности:

Fitting two circles

Пусть D - евклидово расстояние между конечными точками. Пусть C будет постоянной длиной, которую мы хотим. Я получил следующее выражение для b (нарисовано на картинке):

b = sqrt(D*sin(C/4)/4 - (D^2)/16)

Я не проверял, если это правильно, поэтому, если кто-то получает что-то другое, оставьте комментарий.

РЕДАКТИРОВАТЬ: Вы должны учитывать отрицательное решение, которое я получаю при решении уравнения и проверить, какое из них является правильным.

b = -sqrt(D*sin(C/4)/4 - (D^2)/16)
2 голосов
/ 23 ноября 2011

Это разрешимо численно.Я предполагаю, что у вас есть кубический Безье с 4 контрольными точками.на каждом шаге у вас есть первая (P0) и последняя (P3) точки, и вы хотите вычислить P1 и P2 так, чтобы общая длина была постоянной.

Добавление этого ограничения удаляет одну степень свободы, поэтому мы имеем1 слева (началось с 4, определены конечные точки (-2), а постоянная длина равна еще одному -1).Поэтому вам нужно определиться с этим.

Кривая Безье - это многочлен, определенный между 0 и 1, вам нужно интегрировать по квадратному корню из суммы элементов (2d?).для кубического Безье это означает квадрат 6-градусного полинома, который Вольфрам не знает, как решить.Но если у вас есть все другие известные контрольные точки (или известные до зависимости от какого-либо другого ограничения), вы можете иметь таблицу сохранения предварительно рассчитанных значений для этого ограничения.

0 голосов
/ 23 ноября 2011

Вот рабочий пример в SVG, который закрыть для исправления:
http://phrogz.net/svg/constant-length-bezier.xhtml

enter image description here

Я экспериментально определил, что когда конечные точки включеныдруг над другом ручки должны находиться на расстоянии
требуемая длина × cos (30 °)
от ручек;и (конечно), когда конечные точки находятся на самом большом расстоянии, ручки должны находиться друг над другом.Построение всех идеальных точек выглядит как вроде как эллипс:

Graph showing actual points compared to ellipse

Синяя линия - это фактическое идеальное уравнение, а красная линия сверху - аппроксимирующая эллипсидеал.Использование уравнения для эллипса (как в моем примере выше) позволяет линии получить на 9% длиннее посередине.

Вот соответствующий код JavaScript:

// M is the MoveTo command in SVG (the first point on the path)
// C is the CurveTo command in SVG:
//   C.x is the end point of the path
//   C.x1 is the first control point
//   C.x2 is the second control point
function makeFixedLengthSCurve(path,length){
  var dx   = C.x - M.x, dy = C.y - M.y;
  var len  = Math.sqrt(dx*dx+dy*dy);
  var angle = Math.atan2(dy,dx);
  if (len >= length){
    C.x  = M.x + 100 * Math.cos(angle);
    C.y  = M.y + 100 * Math.sin(angle);
    C.x1 = M.x; C.y1 = M.y;
    C.x2 = C.x; C.y2 = C.y;
  }else{
    // Ellipse of major axis length and minor axis length*cos(30°)
    var a = length, b = length*Math.cos(30*Math.PI/180);
    var handleDistance = Math.sqrt( b*b * ( 1 - len*len / (a*a) ) ); 
    C.x1 = M.x + handleDistance * Math.sin(angle);
    C.y1 = M.y - handleDistance * Math.cos(angle);
    C.x2 = C.x - handleDistance * Math.sin(angle);
    C.y2 = C.y + handleDistance * Math.cos(angle);
  }
}
...