Кривая Безье через три точки

Я читал подобные темы, чтобы найти решение, но безуспешно. Я пытаюсь сделать инструмент таким же, как в CorelDraw, под названием Pen Tool. Я сделал это, соединив кубические кривые Безье, но все же упустил одну функцию, которая заключается в перетаскивании кривой (не контрольной точки) для редактирования ее формы.

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

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

Итак, как я могу пересчитать положения контрольных точек при перетаскивании кривой?


person Rasa    schedule 29.01.2011    source источник
comment
Вы предполагаете, что все осведомлены о поведении CorelDraw...   -  person Dr. belisarius    schedule 29.01.2011
comment
Я думал, что достаточно ясно описал поведение CorelDraw PenTool, связанное с вопросом. Однако вы можете игнорировать тот факт, что PenTool принадлежит CorelDraw. Просто сосредоточьтесь на упомянутом поведении.   -  person Rasa    schedule 29.01.2011
comment
Недостаточно ясно для меня. Если Безье определяется через его контрольные точки, любая операция над ним должна выполняться путем изменения контрольных точек. Вы объясняете другой вид перетаскивания, но непонятно, что вы перетаскиваете. Кривая представляет собой уравнение, единственными параметрами которого являются t и контрольные точки.   -  person Dr. belisarius    schedule 29.01.2011
comment
Я перетаскиваю кривую в положение t.   -  person Rasa    schedule 29.01.2011


Ответы (3)


Я только что посмотрел исходники Inkspace и нашел такой код, может быть, он вам поможет:

// Magic Bezier Drag Equations follow!
// "weight" describes how the influence of the drag should be distributed
// among the handles; 0 = front handle only, 1 = back handle only.
double weight, t = _t;
if (t <= 1.0 / 6.0) weight = 0;
else if (t <= 0.5) weight = (pow((6 * t - 1) / 2.0, 3)) / 2;
else if (t <= 5.0 / 6.0) weight = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
else weight = 1;

Geom::Point delta = new_pos - position();
Geom::Point offset0 = ((1-weight)/(3*t*(1-t)*(1-t))) * delta;
Geom::Point offset1 = (weight/(3*t*t*(1-t))) * delta;

first->front()->move(first->front()->position() + offset0);
second->back()->move(second->back()->position() + offset1);

В вашем случае "first->front()" и "second->back()" будут означать две контрольные точки

person Anton Semenov    schedule 29.01.2011
comment
Большое тебе спасибо! Я реализовал эту процедуру на Java, и она сразу заработала. Не уверен, работает ли это так же, как в CorelDraw, но пока выглядит очень похоже. Проверим его поведение позже. В очередной раз благодарим за помощь. - person Rasa; 30.01.2011

Кривая Безье — это не что иное, как два полинома: X(t), Y(t).

Кубический:

x = ax*t^3 + bx*t^2 + cx*t + dx
                               0 <= t <= 1
y = ay*t^3 + by*t^2 + cy*t + dy

Итак, если у вас есть кривая - у вас есть поли коэффициенты. Если вы перемещаете свою точку и знаете ее параметр t, то вы можете просто пересчитать коэффициенты полигона - это будет система из 6 линейных уравнений для коэффициентов (для каждой точки). Система разбита на две системы (x и y) и может быть решена точно или с использованием некоторых численных методов - они тоже не сложные.

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

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

Кубическая кривая через контрольные точки:

B(t) = (1-t)^3*P0 + 3(1-t)^2*t*P1 + 3(1-t)*t^2*P2 + t^3*P3

Все, что вам нужно сделать, это привести стандартную полиномиальную форму (просто раскройте скобки) и приравнять коэффициенты. Это обеспечит окончательную систему контрольных точек!

person Andrew    schedule 30.01.2011
comment
Я не уверен, что ты прав. Видите ли, у меня есть P0, P3, t и P (точка на кубической кривой, соответствующая t). Эти данные дают мне семейство кубических кривых. Единого решения не существует, если только не задействованы некоторые дополнительные ограничения. - person Rasa; 30.01.2011
comment
Да, ты прав. В качестве дополнительного условия вы можете использовать что-то вроде этого. Возьмите дополнительную точку на кривой, она тоже будет перемещена. Например, если t ‹ 0,5 в выбранной пользователем точке, вы можете взять точку при t1 = t/2 и переместить ее с половинной скоростью точки пользователя. Если t > 0,5, то t1 = t + 0,5*(1 - t); - person Andrew; 30.01.2011
comment
Я имел это в виду, но это не решит проблему, потому что нужно определить точное положение другой точки; его t-позиции просто недостаточно. Итак, предлагаемое решение от Inkscape достаточно хорошее. Я пробовал. - person Rasa; 30.01.2011
comment
У вас есть четыре очка - этого достаточно. Четыре - потому что P0, P3, точка с параметром t0, этот пользователь перемещается, а точка с параметром t1 перемещается в соответствии с точкой пользователя, как я уже сказал - person Andrew; 30.01.2011
comment
Предположим, пользователь взял точку в момент времени t=1/5. Вы говорите, что точки в t1=1/10 или t1=3/5 должны иметь одинаковую скорость. то есть половина скорости точки, выбранной пользователем? - person Rasa; 30.01.2011
comment
если t = 1/5, я думаю, что хорошо взять точку t1 = 0,2 + 0,5 * (1-0,2) = 0,6 и переместить ее тоже с некоторой скоростью - например, 1/2 точки пользователя. Думаю, будет хорошо, если эта скорость будет зависеть от параметра t. Я не знаю, как именно будет выглядеть результат, но вы должны понимать, что если пользователь перемещает одну точку на кривой, нет определенного решения, как должны двигаться все контрольные точки. Это потому, что пользователи взаимодействуют с 2 параметрами (x, y точки), а две контрольные точки предоставляют 4 параметра. Так что в любом случае вам нужно дополнительное условие. Я только что предложил вам один из них - person Andrew; 30.01.2011

Когда вы нажимаете на кривую, вы уже знаете положение текущей контрольной точки. Таким образом, вы можете рассчитать смещение X и смещение Y от этой точки до положения мыши. В случае перемещения мыши вы сможете пересчитать новую контрольную точку с помощью смещений X/Y.

Извините за мой английский

person Anton Semenov    schedule 29.01.2011
comment
Я не уверен, что указатель мыши останется на кривой, когда я пересчитаю положения контрольных точек. Указатель мыши не должен улетать за пределы кривой при перетаскивании. - person Rasa; 29.01.2011
comment
Я думаю, это было бы правильно. В моем сценарии вы будете двигать не кривую, а контрольную точку (с помощью смещений), и в случае использования двойного предвидения это даст отличные результаты. Конечно, очень сложно расположить указатель мыши прямо на излечении (я полагаю, у вас есть кривая шириной 1 пиксель), и в большинстве случаев мышь не будет находиться прямо на излечении, так что все будет в порядке! :-) - person Anton Semenov; 29.01.2011
comment
Есть 2 контрольные точки. То, что вы советуете, может быть в порядке только для t = 0,5, но не для других значений. - person Rasa; 29.01.2011
comment
Извините, я думал, вы используете квадратичную кривую Безье. В случае Cubic вы можете перемещать только ближайшую контрольную точку или обе вместе. Анализируя поведение coreldraw, я думаю, что он перемещает ближайшую точку - person Anton Semenov; 29.01.2011
comment
Я только что проверил это и не смог найти никаких конкретных пропорций, которые могли бы быть действительными между смещением точки кривой, перетаскиваемой пользователем, и ближайшей контрольной точкой. - person Rasa; 29.01.2011
comment
Родные идеи вышли. Но я просто смотрю исходники Inkspace и нахожу эту функцию: - person Anton Semenov; 30.01.2011