Довжина дуги кривої Безьє


23

Дивіться також: те саме питання на Math.SE

Як я можу знайти довжину дуги кривої Безьє? Наприклад, лінійна крива Безьє має довжину:

length = sqrt(pow(x[1] - x[0], 2) + pow(y[1] - y[0], 2));

А як щодо квадратичних, кубічних або n-градусних кривих Безьє?

(Моєю метою було заздалегідь оцінити роздільну здатність вибірки, тому мені не доведеться витрачати час на перевірку, якщо наступна точка стосується попередньої точки.)


1
Вам слід переформулювати питання, щоб позначити довжину кривої, тобто набагато простіший (і пошуковий) термін.
Спарр

Я пропоную розмістити це з математики, я впевнений, що якесь розумне обличчя там дасть вам відповідь в одному з них розумних веб-шрифтів: p
Tor Valamo

2
@Так я (вчора), але мені сказали, що це дуже складно і, отже, непрактично. [ math.stackexchange.com/q/12186/2736 ]
Mateen Ulhaq

Імовірно, клотоїдні криві / сплайни є альтернативою безьє і мають вирази довжини дуги, але я про це ще не знаю. (Намагаючись генерувати точки на однаковій відстані вздовж кривої.) Катенери також мають вирази довжини дуги закритої форми?
ендоліт

Відповіді:


9

Простий спосіб для кубічних Безьє - це розділити криву на N сегментів і підсумувати довжини сегментів.

Однак, як тільки вам знадобиться довжина лише частини кривої (наприклад, до точки 30% довжини вздовж), параметризація довжини дуги почне грати. Я розмістив досить довгу відповідь на одне із власних запитань про Безьє, з простим зразком коду.


Я роблю це для LEGO Mindstorms NXT, який має дійсно слабкий процесор (48 МГц), тому мені потрібна максимальна швидкість. Я візьму на себе роздільний підхід, щоб зберегти деяку швидкість, і отримаю його досить точно (для візуалізації в режимі "не в реальному часі"). У мене також є варіант, в якому ви можете встановити значення 1.0/t(називається resolution), так що це для "реального часу" (що в кращому випадку становить 10 кадрів в секунду на повільному NXT). Кожна ітерація, t += resolutionі нова точка / лінія малюється. У будь-якому випадку, дякую за ідею.
Mateen Ulhaq

4

Хоча я згоден з відповідями, які ви вже отримали, я хочу додати простий, але потужний механізм наближення, який ви можете використовувати для будь-яких ступенів кривих Безьє: Ви постійно підрозділяєте криву, використовуючи підрозділ де Кастелау до максимальної відстані контрольних точок. підкривої до базової лінії підкривої знаходиться нижче деякого постійного епсилона . У цьому випадку субкрива може бути апроксимована за її базовою лінією.

Насправді, я вважаю, що такий підхід зазвичай застосовується, коли графічна підсистема повинна провести криву Безьє. Але не цитуйте мене з цього приводу, на даний момент у мене немає посилань.

На практиці це виглядатиме так: (крім мови не має значення)

public static Line[] toLineStrip(BezierCurve bezierCurve, double epsilon) {
    ArrayList<Line> lines = new ArrayList<Line>();

    Stack<BezierCurve> parts = new Stack<BezierCurve>();
    parts.push(bezierCurve);

    while (!parts.isEmpty()) {
        BezierCurve curve = parts.pop();
        if (distanceToBaseline(curve) < epsilon) {
            lines.add(new Line(curve.get(0), curve.get(1)));
        } else {
            parts.addAll(curve.split(0.5));
        }
    }

    return lines.toArray(new Line[0]);
}

Хоча це хороший підхід, я чув про числову нестабільність кривих Безьє високого порядку, для яких потрібна інша ідея: розділення кривих вищого порядку на менші кубічні криві.
Матін Ульхак

Крім того, якщо кінцева мета є точною оцінкою, може бути хорошою ідеєю наблизити квадратику замість ліній, щоб переконатися, що ми не занижуємо нашу оцінку в місцях високої кривизни.
Mateen Ulhaq

2

Довжини дуги для кривих Безьє є лише закритою формою для лінійних та квадратичних. Для кубіків закритий розчин не гарантується. Причина в тому, що довжина дуги визначається радикальним інтегралом, для якого є закриті лише многочлени другого ступеня.

Для довідки: довжина квадратичного Безьє для точок (a, p) (b, q) та (c, r) дорівнює

(a ^ 2 · (q ^ 2 - 2 · q · r + r ^ 2) + 2 · a · (r - q) · (b · (p - r) + c · (q - p)) + ( b · (p - r) + c · (q - p)) ^ 2) · LN ((√ (a ^ 2 - 2 · a · b + b ^ 2 + p ^ 2 - 2 · p · q + q ^ 2) · √ (a ^ 2 + 2 · a · (c - 2 · b) + 4 · b ^ 2 - 4 · b · c + c ^ 2 + (p - 2 · q + r) ^ 2) + a ^ 2 + a · (c - 3 · b) + 2 · b ^ 2 - b · c + (p - q) · (p - 2 · q + r)) / (√ (a ^ 2 + 2 · A · (c - 2 · b) + 4 · b ^ 2 - 4 · b · c + c ^ 2 + (p - 2 · q + r) ^ 2) · √ (b ^ 2 - 2 · b · c + c ^ 2 + q ^ 2 - 2 · q · r + r ^ 2) + a · (b - c) - 2 · b ^ 2 + 3 · b · c - c ^ 2 + (p - 2 · q + r) · (q - r))) / (a ​​^ 2 + 2 · a · (c - 2 · b) + 4 · b ^ 2 - 4 · b · c + c ^ 2 + (p - 2 · Q + r) ^ 2) ^ (3/2) + (√ (a ^ 2 - 2 · a · b + b ^ 2 + p ^ 2 - 2 · p · q + q ^ 2) · (a ^ 2 + a · (c - 3 · b) + 2 · b ^ 2 - b · c + (p - q) · (p - 2 · q + r)) - √ (b ^ 2 - 2 · b · c + c ^ 2 + q ^ 2 - 2 · q · r + r ^ 2) · (a · (b - c) - 2 · b ^ 2 + 3 · b · c - c ^ 2 + (p - 2 · q + r) · (q - r))) / (a ​​^ 2 + 2 · a · (c - 2 · b) + 4 · b ^ 2 - 4 · b · c + c ^ 2 + (p - 2 · Q + r) ^ 2)

Де LN - природний логарифм, а ^ позначає силу і √ квадратний корінь.

Отже, простіше і дешевше наблизити дугу за яким-небудь іншим правилом, як багатокутник або схема інтеграції, як правило Сімпсона, оскільки квадратні корені LN - це дорогі операції.


2

Я опрацював вираз довжини закритої форми для 3-х бального Безьє (внизу). Я не намагався опрацювати закриту форму на 4+ балів. Це, швидше за все, буде складно або складно представляти і впоратися. Однак методика чисельного наближення, така як алгоритм інтеграції Runge-Kutta, спрацювала б досить добре, інтегруючи за допомогою формули довжини дуги . Мої запитання щодо RK45 в MSE можуть допомогти у впровадженні RK45.

Ось деякі Java - код для довжини дуги 3 точки Безьє а, з точками a, bі c.

    v.x = 2*(b.x - a.x);
    v.y = 2*(b.y - a.y);
    w.x = c.x - 2*b.x + a.x;
    w.y = c.y - 2*b.y + a.y;

    uu = 4*(w.x*w.x + w.y*w.y);

    if(uu < 0.00001)
    {
        return (float) Math.sqrt((c.x - a.x)*(c.x - a.x) + (c.y - a.y)*(c.y - a.y));
    }

    vv = 4*(v.x*w.x + v.y*w.y);
    ww = v.x*v.x + v.y*v.y;

    t1 = (float) (2*Math.sqrt(uu*(uu + vv + ww)));
    t2 = 2*uu+vv;
    t3 = vv*vv - 4*uu*ww;
    t4 = (float) (2*Math.sqrt(uu*ww));

    return (float) ((t1*t2 - t3*Math.log(t2+t1) -(vv*t4 - t3*Math.log(vv+t4))) / (8*Math.pow(uu, 1.5)));
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.