Какой алгоритм определяет близость точки к кривой Безье?
Я хочу определить, когда точка (положение мыши) в 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}];
имейте в виду, что я использую базис Бернштейна для удобства, но любой параметрический представление кривой Безье было бы достаточно.
что дает:
теперь, чтобы найти минимальное расстояние до точки (например, {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}]]
. (Вращается ! для экономии места на экране )
(*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}]
этот график является (минимальным) расстоянием от любой точки в пространстве до нашей кривой (конечно, значение по кривой равно нулю):
во-первых, визуализируйте кривую в растровое изображение (черно-белое) с вашим любимым алгоритмом. Затем, когда вам нужно, определите ближайший пиксель к позиции мыши, используя информацию из этот вопрос. Вы можете изменить функцию поиска, чтобы она возвращала расстояние, поэтому вы можете легко сравнить ее с вашими требованиями. Этот метод дает вам расстояние с допуском 1-2 пикселя, что, я думаю, будет делать.
определение: расстояние от точки до отрезка линии = расстояние от исходной точки до ближайшей точки На отрезке.
предположения: известно algo для вычисления расстояния от точки до сегмента (например, вычислить перехват с сегментом нормали к сегменту, проходящему через исходную точку. Если пересечение находится вне сегмента, выберите ближайшую конечную точку сегмента)
- использовать deCasteljau algo и подразделяют ваши кубики до получения достаточно хорошей ромашковой цепи линейных сегментов. дополнительная информация the "сглаживание кривой Безье" раздел
- рассмотрим минимальное расстояние между вашей точкой и результирующими сегментами как расстояние от вашей точки до кривой. Повторите для всех кривых в вашем наборе.
уточнение в точке 2: не вычисляйте фактическое расстояние, а его квадрат, получение минимального квадратного расстояния достаточно хорошо-сохраняет вызов/сегмент sqrt.
расчет усилия: эмпирически кубическая кривая с максимальной степенью (т. е. ограничивающая коробка) 200-300 приводит примерно к 64 сегментам линии при сплющивании до максимального допуска 0,5 (достаточно хорошо для невооруженного глаза).
- каждый шаг deCasteljau требует 12 деления на 2 и 12 дополнений.
- оценка плоскостности - 8 умножений + 4 дополнения (при использовании расстояния такси для оценки расстояния)
- оценка расстояния от точки до сегмента требует при максимальных 12 умножениях и 11 дополнениях-но это будет редкий случай в контексте выравнивания Безье, я бы ожидал в среднем 6 умножений и 9 дополнений.
Итак, предполагая очень плохой случай (100 прямых сегментов/кубических), вы заканчиваете поиск своего расстояния со стоимостью около 2600 умножений + 2500 дополнений за рассмотренный кубический.
отказ от ответственности:
- не просите меня о демонстрации чисел в оценка вычислительных усилий выше, Я отвечу "использовать исходный код" (Примечание: Java-реализации).
- другие подходы могут быть возможными и, возможно, менее дорогостоящими.
с уважением,
Эдриан Colomitchi