Какой алгоритм определяет близость точки к кривой Безье?

Я хочу определить, когда точка (положение мыши) в on или рядом с кривой, определенной серией контрольных точек B-сплайна.

информация, которую я буду иметь для B-сплайна, - это список из n контрольных точек (в координатах x,y). Список контрольных точек может быть любой длины (>=4) и определять B-сплайн, состоящий из (n-1)/3 кубических кривых Безье. Кривые Безье все кубические. Я хочу установить параметр k (в пикселях) расстояния, определенного как "рядом" с кривой. Если положение мыши находится в пределах K пикселей кривой тогда мне нужно вернуть true, иначе false.

есть ли алгоритм, который дает мне эту информацию. Любое решение не должно быть точным - я работаю с допуском в 1 пиксель (или координату).

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

положение точки относительно кривой Безье

пересечение кривой Безье и отрезка линии

изменить: Пример кривой:

 e, 63.068, 127.26   
    29.124, 284.61   
    25.066, 258.56   
    20.926, 212.47   
        34, 176  
    38.706, 162.87  
    46.556, 149.82  
    54.393, 138.78 

описание формата: "каждому краю присваивается атрибут pos, который состоит из списка из 3n + 1 местоположений. Это контрольные точки B-сплайна: точки p0, p1, p2, p3-первый сплайн Безье, p3, p4, p5, p6 являются вторыми и т. д. Точки представлены двумя целыми числами, разделенными запятой, представляющими координаты X и Y местоположения, указанного в точках (1/72 дюйма). В атрибуте pos списку контрольных точек может предшествовать начальная точка ps и / или конечная точка pe. Они имеют обычное представление позиции с префиксом" s "или" e", соответственно."

EDIT2: дальнейшее объяснение точки" Е " (и s, Если присутствует).

в атрибут pos, список контрольных точек может предшествовать запуску точка ps и / или конечная точка pe. Они имеют обычное представление позиции с a приставка" s "или" e", соответственно. Начальная точка присутствует, если есть стрелка на p0. В этом случае стрелка находится от p0 до ps, где ps фактически находится на границе узла. Длина и направление наконечника стрелы задается вектором (ps-p0). Если есть нет стрелки, p0 находится на границе узла. Аналогично, точка pe обозначает - стрелка на другом конце ребра, соединяющаяся с последней точкой сплайна.

3 ответов


вы можете сделать это analitically, но нужно немного математики.

кривая Безье может быть выражена в терминах Основы Бернштейн. Здесь я буду использовать Mathematica, что обеспечивает хорошую поддержку математике.

так что если у вас есть вопросы:

pts = {{0, -1}, {1, 1}, {2, -1}, {3, 1}};  

эквалайзер. для кривой Безье:

f[t_] := Sum[pts[[i + 1]] BernsteinBasis[3, i, t], {i, 0, 3}];

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

что дает:

alt text

теперь, чтобы найти минимальное расстояние до точки (например, {3, -1}), вы должны минимизировать функцию:

d[t_] := Norm[{3, -1} - f[t]];  

для этого вам нужен алгоритм минимизации. У меня есть один под рукой, так что:

NMinimize[{d[t], 0 <= t <= 1}, t]  

выдает:

 {1.3475, {t -> 0.771653}}  

и это все.

HTH!

редактировать относительно вашего редактирования "B-сплайн с состоящими из (n-1)/3 кубических кривых Безье."

если вы построили кусочное представление B-сплайна, вы должны перебирать все сегменты, чтобы найти минимумы. Если вы объединили части по непрерывному параметру, то этот же подход будет делать.

редактировать

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

я решил его с помощью стандартных Bsplines вместо функций mathematica, ради ясности.

Clear["Global`*"];
(*first define the points *)
pts = {{
        29.124, 284.61}, {
        25.066, 258.56}, {
        20.926, 212.47}, {
        34, 176}, {
        38.706, 162.87}, {
        46.556, 149.82}, {
        54.393, 138.78}};

(*define a bspline template function *)

b[t_, p0_, p1_, p2_, p3_] :=
                  (1-t)^3 p0 + 3 (1-t)^2 t p1 + 3 (1-t) t^2 p2 + t^3 p3;

(* define two bsplines *)
b1[t_] := b[t, pts[[1]], pts[[2]], pts[[3]], pts[[4]]];
b2[t_] := b[t, pts[[4]], pts[[5]], pts[[6]], pts[[7]]];

(* Lets see the curve *)

Show[Graphics[{Red, Point[pts], Green, Line[pts]}, Axes -> True], 
 ParametricPlot[BSplineFunction[pts][t], {t, 0, 1}]]

. (Вращается ! для экономии места на экране )

alt text

(*Now define the distance from any point u to a point in our Bezier*)
d[u_, t_] := If[(0 <= t <= 1), Norm[u - b1[t]], Norm[u - b2[t - 1]]];

(*Define a function that find the minimum distance from any point u \
to our curve*)
h[u_] := NMinimize[{d[u, t], 0.0001 <= t <= 1.9999}, t];

(*Lets test it ! *)
Plot3D[h[{x, y}][[1]], {x, 20, 55}, {y, 130, 300}]

этот график является (минимальным) расстоянием от любой точки в пространстве до нашей кривой (конечно, значение по кривой равно нулю):

alt text


во-первых, визуализируйте кривую в растровое изображение (черно-белое) с вашим любимым алгоритмом. Затем, когда вам нужно, определите ближайший пиксель к позиции мыши, используя информацию из этот вопрос. Вы можете изменить функцию поиска, чтобы она возвращала расстояние, поэтому вы можете легко сравнить ее с вашими требованиями. Этот метод дает вам расстояние с допуском 1-2 пикселя, что, я думаю, будет делать.


определение: расстояние от точки до отрезка линии = расстояние от исходной точки до ближайшей точки На отрезке.

предположения: известно algo для вычисления расстояния от точки до сегмента (например, вычислить перехват с сегментом нормали к сегменту, проходящему через исходную точку. Если пересечение находится вне сегмента, выберите ближайшую конечную точку сегмента)

  1. использовать deCasteljau algo и подразделяют ваши кубики до получения достаточно хорошей ромашковой цепи линейных сегментов. дополнительная информация the "сглаживание кривой Безье" раздел
  2. рассмотрим минимальное расстояние между вашей точкой и результирующими сегментами как расстояние от вашей точки до кривой. Повторите для всех кривых в вашем наборе.

уточнение в точке 2: не вычисляйте фактическое расстояние, а его квадрат, получение минимального квадратного расстояния достаточно хорошо-сохраняет вызов/сегмент sqrt.

расчет усилия: эмпирически кубическая кривая с максимальной степенью (т. е. ограничивающая коробка) 200-300 приводит примерно к 64 сегментам линии при сплющивании до максимального допуска 0,5 (достаточно хорошо для невооруженного глаза).

  1. каждый шаг deCasteljau требует 12 деления на 2 и 12 дополнений.
  2. оценка плоскостности - 8 умножений + 4 дополнения (при использовании расстояния такси для оценки расстояния)
  3. оценка расстояния от точки до сегмента требует при максимальных 12 умножениях и 11 дополнениях-но это будет редкий случай в контексте выравнивания Безье, я бы ожидал в среднем 6 умножений и 9 дополнений.

Итак, предполагая очень плохой случай (100 прямых сегментов/кубических), вы заканчиваете поиск своего расстояния со стоимостью около 2600 умножений + 2500 дополнений за рассмотренный кубический.

отказ от ответственности:

  1. не просите меня о демонстрации чисел в оценка вычислительных усилий выше, Я отвечу "использовать исходный код" (Примечание: Java-реализации).
  2. другие подходы могут быть возможными и, возможно, менее дорогостоящими.

с уважением,

Эдриан Colomitchi