ความยาวส่วนโค้ง Bezier


23

ดูเพิ่มเติมที่: คำถามเดียวกันกับ Math.SE

ฉันจะค้นหาความยาวของเส้นโค้ง Bezier ได้อย่างไร ตัวอย่างเช่นเส้นโค้ง Bezier เชิงเส้นมีความยาว:

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

แต่สิ่งที่เกี่ยวกับเส้นโค้งกำลังสองลูกบาศก์หรือ n- องศา Bezier

(เป้าหมายของฉันคือการประเมินความละเอียดของการสุ่มตัวอย่างล่วงหน้าดังนั้นฉันไม่ต้องเสียเวลาตรวจสอบว่าจุดต่อไปแตะที่จุดก่อนหน้า)


1
คุณควรตั้งคำถามใหม่เพื่ออ้างอิงความยาวของเส้นโค้งซึ่งเป็นคำที่ตรงไปตรงมามากขึ้น (และสามารถค้นหาได้)
Sparr

ฉันขอแนะนำให้โพสต์สิ่งนี้ทางคณิตศาสตร์ฉันแน่ใจว่าบางหน้าที่ฉลาดจะให้คำตอบในหนึ่งในเว็บแบบอักษรฉลาด: p
Tor Valamo

2
@Tor ฉันทำ (เมื่อวาน) แต่ฉันบอกว่ามันซับซ้อนมากและไม่น่าปฏิบัติ [ math.stackexchange.com/q/12186/2736 ]
Mateen Ulhaq

เส้นโค้ง / เส้นโค้ง clothoid คาดคะเนเป็นทางเลือกสำหรับ beziers และมีการแสดงออกความยาวของรูปแบบปิด แต่ฉันยังไม่ทราบอะไรเกี่ยวกับเรื่องนี้มากนัก (พยายามที่จะสร้างจุดระยะทางเท่ากันตามเส้นโค้ง) Catenaries ยังมีการแสดงออกความยาวส่วนโค้งแบบปิด?
endolith

คำตอบ:


9

วิธีที่ง่ายสำหรับลูกบาศก์เบซิเยร์คือการแบ่งส่วนโค้งออกเป็นส่วน N และรวมความยาวของส่วน

อย่างไรก็ตามทันทีที่คุณต้องการความยาวของเส้นโค้งเพียงบางส่วน (เช่นสูงสุด 30% ของความยาวตามแนว) การแปรค่าความยาวส่วนโค้งจะเข้าสู่การเล่น ฉันโพสต์คำตอบที่ค่อนข้างยาวสำหรับหนึ่งในคำถามของตัวเองเกี่ยวกับเบซิเอร์ด้วยรหัสตัวอย่างง่ายๆ


ฉันกำลังทำสิ่งนี้กับ LEGO Mindstorms NXT ซึ่งมีโปรเซสเซอร์ที่อ่อนแอมาก (48Mhz) ดังนั้นฉันจึงต้องการความเร็วมากที่สุดเท่าที่จะเป็นไปได้ ฉันจะใช้วิธีการหารเพื่อประหยัดความเร็วและทำให้แม่นยำพอ (สำหรับการเรนเดอร์ "แบบไม่เรียลไทม์") ฉันยังมีตัวเลือกที่คุณสามารถกำหนดค่าของ1.0/t(เรียกว่าresolution) ดังนั้นสำหรับ "เรียลไทม์" (ซึ่งเป็น 10fps ที่ดีที่สุดสำหรับ NXT ที่ช้า) ทุกการทำซ้ำt += resolutionและจุด / บรรทัดใหม่จะถูกวาด อย่างไรก็ตามขอบคุณสำหรับความคิด
Mateen Ulhaq

4

ในขณะที่ฉันพร้อมกับคำตอบที่คุณได้รับแล้วฉันต้องการเพิ่มกลไกการประมาณค่าแบบง่าย ๆ แต่ทรงพลังซึ่งคุณสามารถใช้สำหรับองศาBézierใด ๆ : คุณแบ่งเส้นโค้งอย่างต่อเนื่องโดยใช้ส่วนย่อย de Casteljau จนกว่าระยะทางสูงสุดของจุดควบคุม ของย่อยโค้งพื้นฐานย่อยโค้งเป็นด้านล่างคงepsilon ในกรณีนั้นเส้นโค้งย่อยสามารถประมาณได้โดยเส้นฐาน

ในความเป็นจริงฉันเชื่อว่านี่เป็นวิธีที่มักใช้เมื่อระบบย่อยกราฟิกต้องวาดเส้นโค้งเบซิเยร์ แต่อย่าอ้างฉันในเรื่องนี้ฉันไม่ได้มีการอ้างอิงอยู่ในขณะนี้

ในทางปฏิบัติมันจะมีลักษณะดังนี้: (ยกเว้นภาษาที่ไม่เกี่ยวข้อง)

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]);
}

ในขณะที่วิธีนี้เป็นวิธีที่ดีฉันได้ยินความไม่แน่นอนเชิงตัวเลขที่เส้นโค้ง Bezier ที่มีลำดับสูงซึ่งต้องการแนวคิดอื่น: การแยกเส้นโค้งลำดับที่สูงขึ้นเป็นเส้นโค้งลูกบาศก์ขนาดเล็ก
Mateen Ulhaq

นอกจากนี้หากเป้าหมายสุดท้ายคือการประมาณการที่แม่นยำอาจเป็นความคิดที่ดีที่จะประมาณด้วย quadratics แทนเส้นเพื่อให้แน่ใจว่าเราไม่ได้ประมาณค่าประมาณการของเราที่ตำแหน่งที่มีความโค้งสูง
Mateen Ulhaq

2

ความยาวส่วนโค้งของเส้นโค้ง Bezier เป็นเพียงรูปแบบปิดสำหรับเส้นตรงและกำลังสอง สำหรับ cubics จะไม่รับประกันว่าจะมีวิธีปิด เหตุผลก็คือความยาวส่วนโค้งถูกกำหนดโดยอินทิกรัลอินทิกรัลซึ่งปิดสำหรับพหุนามระดับ 2 เท่านั้น

สำหรับการอ้างอิง: ความยาวของ Bezier กำลังสองสำหรับคะแนน (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 - 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 คือลอการิทึมธรรมชาติและ ^ หมายถึงพลังงานและ square รากที่สอง

ดังนั้นจึงควรง่ายกว่าและถูกกว่าการประมาณส่วนโค้งโดยกฎอื่น ๆ เช่นรูปหลายเหลี่ยมหรือรูปแบบการรวมเช่นกฎของ Simpson เพราะรากที่สอง LN มีการดำเนินการที่มีราคาแพง


2

ฉันทำงานจากการแสดงออกของความยาวแบบปิดสำหรับ Bezier 3 จุด (ด้านล่าง) ฉันไม่ได้พยายามหารูปแบบปิดสำหรับ 4 คะแนนขึ้นไป สิ่งนี้น่าจะยากหรือซับซ้อนในการเป็นตัวแทนและจัดการ อย่างไรก็ตามเทคนิคการประมาณตัวเลขดังกล่าวเป็นขั้นตอนวิธีการรวม Runge-Kutta จะทำงานได้ค่อนข้างดีโดยการบูรณาการโดยใช้สูตรความยาวส่วนโค้ง คำถาม & คำตอบของฉันเกี่ยวกับRK45บน MSE อาจช่วยในการปรับใช้ RK45

นี่คือบางส่วนรหัส Java สำหรับความยาวส่วนโค้งของ 3 จุด Bezier กับจุดa, และbc

    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)));
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.