จะสร้างวงกลมด้วยเส้นโค้งเบเซียร์ได้อย่างไร?


101

เรามีจุดเริ่มต้น (x, y) และรัศมีวงกลม นอกจากนี้ยังมีเครื่องยนต์ที่สามารถสร้างเส้นทางจากจุดโค้งBézier

ฉันจะสร้างวงกลมโดยใช้เส้นโค้งเบเซียร์ได้อย่างไร


ที่เกี่ยวข้องอย่างใกล้ชิด: Geometrical Arc ถึง Bezier Curve
ตำนาน 2k

คำตอบ:


141

ดังที่ได้กล่าวไปแล้ว: ไม่มีการแสดงวงกลมโดยใช้เส้นโค้งเบเซียร์อย่างแน่นอน

ให้เสร็จสมบูรณ์คำตอบอื่น ๆ : สำหรับโค้ง Bezier กับnกลุ่มที่ดีที่สุด(4/3)*tan(pi/(2n))ระยะทางไปยังจุดควบคุมในความรู้สึกที่ตรงกลางของเส้นโค้งการโกหกในวงกลมที่ตัวเองเป็น

สูตรสำหรับ n เซ็กเมนต์

ดังนั้นสำหรับ 4 (4/3)*tan(pi/8) = 4*(sqrt(2)-1)/3 = 0.552284749831จุดที่มันเป็น

กรณี 4 จุด


2
ตามระยะทางที่เหมาะสมคุณกำลังเพิ่มประสิทธิภาพเมตริกประเภทใด ดังที่แสดงในวงกลมโดยประมาณที่มีเส้นโค้งลูกบาศก์เบซิเอร์ค่าดริฟท์สูงสุดต่ำสุดที่เป็นไปได้จะได้มาจากค่าที่ต่างกัน คุณช่วยระบุลิงค์ที่ระบุความหมายที่ "เหมาะสมที่สุด" ในกรณีของคุณได้หรือไม่หรือได้รับสูตรมาอย่างไร
Suma

1
@ ซูมานี่ไม่เหมาะสำหรับระยะทางหนึ่ง มันเป็นที่ดีที่สุดที่จะมีตรงกลางของเส้นโค้งบนวงกลม และสามารถทำให้ดีขึ้นได้อย่างแน่นอนหากคุณวางเกณฑ์อื่น
Kpym

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

1
ใช่เนื่องจากค่านี้มีค่าเบี่ยงเบนสูงสุดที่ + 0.027% และค่าเบี่ยงเบนต่ำสุด -0 เทียบกับวงกลมจริง มันใหญ่กว่าวงกลมจริงเท่านั้นการประมาณที่ดีขึ้นทำได้โดยการเลื่อน C เข้าไปครึ่งหนึ่งของ 0.027% หากคุณต้องการจุดกึ่งกลางบนวงกลมนี่เป็นวิธีที่แน่นอน
Tatarize

2
@ legends2k ฉันใช้ LaTeX กับ TikZ เพื่อสร้าง PDF ที่ฉันแปลงเป็น PNG แล้ว
Kpym

36

ครอบคลุมใน comp.graphics.faq

ข้อความที่ตัดตอนมา:

เรื่อง 4.04: ฉันจะปรับเส้นโค้งเบเซียร์ให้เป็นวงกลมได้อย่างไร

ที่น่าสนใจก็คือเส้นโค้ง Bezier สามารถประมาณวงกลมได้ แต่ไม่พอดีกับวงกลม การประมาณโดยทั่วไปคือการใช้ beziers สี่ตัวเพื่อสร้างแบบจำลองวงกลมโดยแต่ละจุดควบคุมมีระยะทาง d = r * 4 * (sqrt (2) -1) / 3 จากจุดสิ้นสุด (โดยที่ r คือรัศมีวงกลม) และใน ทิศทางแทนเจนต์ไปยังวงกลมที่จุดสิ้นสุด เพื่อให้แน่ใจว่าจุดกึ่งกลางของ Beziers อยู่บนวงกลมและอนุพันธ์แรกนั้นต่อเนื่องกัน
ความคลาดเคลื่อนของรัศมีในการประมาณนี้จะอยู่ที่ประมาณ 0.0273% ของรัศมีของวงกลม

Michael Goldapp "การประมาณส่วนโค้งวงกลมโดยพหุนามลูกบาศก์" การออกแบบทางเรขาคณิตโดยใช้คอมพิวเตอร์ช่วย (# 8 1991 pp.227-238)

Tor Dokken และ Morten Daehlen, "การประมาณที่ดีของวงกลมโดยเส้นโค้ง Bezier ที่มีความโค้งต่อเนื่อง" Computer Aided Geometric Design (# 7 1990 pp. 33-41) http://www.sciencedirect.com/science/article/pii/016783969090019N (ไม่ใช่บทความฟรี)

ดูบทความที่ไม่ใช่ paywalled ได้ที่http://spencermortensen.com/articles/bezier-circle/

เบราว์เซอร์และองค์ประกอบผ้าใบ

โปรดทราบว่าเบราว์เซอร์บางตัวใช้เส้นโค้ง Bezier กับส่วนโค้งการวาดผ้าใบของตน Chrome ใช้ (ในปัจจุบัน) วิธีการ 4 ภาคส่วน Safari ใช้วิธีการ 8 ภาคส่วนความแตกต่างจะสังเกตเห็นได้เฉพาะที่ความละเอียดสูงเท่านั้นเนื่องจาก 0.0273% และยัง มองเห็นได้อย่างแท้จริงก็ต่อเมื่อส่วนโค้งถูกวาดในแนวขนานและอยู่นอกเฟสคุณจะสังเกตเห็นส่วนโค้งที่แกว่งจากวงกลมจริง นอกจากนี้เอฟเฟกต์ยังสังเกตเห็นได้ชัดเจนยิ่งขึ้นเมื่อเส้นโค้งเคลื่อนไหวรอบ ๆ ศูนย์กลางรัศมีโดยปกติแล้วรัศมี 600px จะเป็นขนาดที่จะสร้างความแตกต่าง

API การวาดภาพบางตัวไม่มีการเรนเดอร์ส่วนโค้งจริงดังนั้นจึงใช้เส้นโค้ง Bezier เช่นแพลตฟอร์ม Flash ไม่มี API การวาดส่วนโค้งดังนั้นเฟรมเวิร์กใด ๆ ที่มีส่วนโค้งโดยทั่วไปจะใช้วิธีเส้นโค้ง Bezier เดียวกัน

โปรดทราบว่าเอ็นจิ้น SVG ภายในเบราว์เซอร์อาจใช้วิธีการวาดที่แตกต่างกัน

แพลตฟอร์มอื่น ๆ

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


ขอบคุณฉันจะเปลี่ยนให้
ocodo

31

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

เพื่อดูตัวเองฉันกำลังนำเสนอผลลัพธ์ของฉัน:

กราฟ 1 เส้นโค้ง (ซึ่งดูเหมือนว่ามีการลดลงที่มุมเพียงเพื่อความสมบูรณ์):ป้อนคำอธิบายภาพที่นี่

กราฟ 2 เส้นโค้ง:ป้อนคำอธิบายภาพที่นี่

กราฟ 3 เส้นโค้ง:ป้อนคำอธิบายภาพที่นี่

กราฟ 4 โค้ง: ป้อนคำอธิบายภาพที่นี่

(ฉันต้องการใส่ SVG หรือ PDF ที่นี่ แต่ไม่รองรับ)


1
ถึงตอนนี้ svg สามารถรวมเป็นข้อมูลโค้ด html ได้ ดูตัวอย่างคำตอบนี้: stackoverflow.com/a/32162431
TS

1
@TS: เมื่อฉันพยายามแทนที่กราฟิกด้วย SVGs ที่ฉันมีฉันรู้ว่าฉันทำสิ่งเหล่านั้นหายไปพร้อมกับแท่ง USB ที่ถูกขโมยไปเมื่อต้นปีนี้ ถ้ามีเวลาฉันจะพยายามสร้างขึ้นใหม่ในไม่ช้า อย่างไรก็ตามหากสามารถเพิ่ม SVG เป็นโค้ด XML (และไม่แสดงเป็นกราฟิก) ก็ไม่สมเหตุสมผลที่นี่
U. Windl

หากเบราว์เซอร์ของคุณรองรับ svg รูปภาพจะแสดงผลทันทีที่คุณคลิก "Run Code Snippet" (ดูเหมือนว่าปุ่มนั้นจะไม่มีใน stackoverflow เวอร์ชันมือถือ ... ) ดูในคำตอบที่ฉันเชื่อมโยง
TS

1
@TS: สำหรับไฟล์ที่ยาวกว่านั้น IMHO น่าเกลียดเกินไป
U. Windl

9

มีคำตอบมากมายแล้ว แต่ฉันพบบทความออนไลน์ขนาดเล็กที่มีค่าประมาณของวงกลมลูกบาศก์ที่ดีมาก ในแง่ของวงกลมหน่วย c = 0.55191502449 โดยที่ c คือระยะห่างจากจุดตัดแกนตามเส้นสัมผัสไปยังจุดควบคุม

เป็นรูปสี่เหลี่ยมเดียวสำหรับวงกลมหน่วยที่มีพิกัดกลางสองจุดเป็นจุดควบคุม (0,1),(c,1),(1,c),(1,0)

ข้อผิดพลาดในแนวรัศมีเป็นเพียง 0.019608% ดังนั้นฉันจึงต้องเพิ่มลงในรายการคำตอบนี้

สามารถอ่านบทความได้ที่นี่โดยประมาณวงกลมที่มีเส้นโค้งลูกบาศก์เบซิเอร์


5
คุณเคยอ่านบทความที่ยอดเยี่ยมนี้เกี่ยวกับ Bezier Curves โดยMike 'Pomax' ของ Stackoverflowหรือไม่ คุ้มค่าแก่การอ่าน! :-)
markE

1
@markE ขอบคุณมากสำหรับลิงค์นั้นเป็นบทความที่ "ยอดเยี่ยมที่สุด" ที่ฉันเคยเห็นในเรื่องนี้ แทบรอไม่ไหวแล้วที่จะไปดูแบบละเอียด .. : D ขอบคุณ ...
Blindman67

1
ดังนั้นด้วยข้อผิดพลาด 0.019608% กราฟิกจะได้รับข้อผิดพลาด 4 พิกเซลเมื่อรัศมีเกินกว่า 2551 พิกเซลในวงกลมแทนที่จะเป็น 0.027253% ที่น่ากลัวซึ่งเรามีข้อผิดพลาดครึ่งพิกเซลที่มั่นคง (โดยที่เอ็นจิ้นกราฟิกจะเปลี่ยนพิกเซล) ที่ 1835 พิกเซลทำให้ 2 พิกเซลผิดพลาด!
Tatarize

@Tatarize บทความไม่ได้ระบุว่ามีการวัดข้อผิดพลาดอย่างไรกล่าวว่าการดริฟท์รัศมีสูงสุด? ฉันคิดว่าข้อผิดพลาดถูกย่อให้เล็กสุดตามเส้นโค้ง 0 <= t <= 1 เพื่อให้ตรงกับกำลังสอง 0 <= pheta <= Pi / 2 ที่ t = 0 = 1/2 = 1 เท่ากับ pheta = 0 = Pi / 4 = Pi / 4 ข้อผิดพลาดคือ 0.019608% และข้อผิดพลาดสูงสุดที่ t = ~ 0.1822 & t = ~ 0.8177 จาก 0.019608% (สัญญาณ?) แต่ที่จุดเหล่านี้ t ไม่เท่ากับ pheta ข้อผิดพลาดรวมถึงการดริฟท์เชิงมุมหรือไม่ . 4 พิกเซลอาจถูกต้องหรือไม่ก็ได้ ข้อผิดพลาดอาจเป็นความแปรปรวนดังนั้นข้อผิดพลาด <2pix สำหรับ r = 2551 คำถามมากมายที่ต้องตรวจสอบ
Blindman67

ฉันค่อนข้างแน่ใจว่าได้ดูเส้นโค้งข้อผิดพลาดแล้วว่าการปรับที่กำหนดนั้นเพียงแค่เลื่อนจุดลงมามากพอที่จะทำให้เกิดข้อผิดพลาดสูงสุดเหนือเส้นโค้งเพื่อให้เท่ากับข้อผิดพลาดสูงสุดที่อยู่ใต้เส้นโค้ง กล่าวคือเราเปลี่ยนเส้นโค้งลงเล็กน้อยดังนั้นข้อผิดพลาดทั้งหมดจึงไม่เป็นบวก การปรับนี้หมายความว่าเรากำลังข้ามเส้นโค้ง 4 ครั้งโดยมีข้อผิดพลาดสูงสุด 4 จุด เมื่อเส้น spec'ed เดิมมี 2 จุดคือที่ t = .25 และ t = .75 ด้วยการปรับเปลี่ยนควรอยู่ที่ t = .125, t = .375 t = .625 t = .875 สิ่งนี้ถือว่าเราใช้พิกเซลทึบและไม่ได้ต่อต้านนามแฝงซึ่งจะเปลี่ยนที่ 14px
Tatarize

8

มันเป็นไปไม่ได้. Bezier คือลูกบาศก์ (อย่างน้อย ... ที่ใช้กันมากที่สุดคือ) วงกลมไม่สามารถแสดงด้วยลูกบาศก์ได้อย่างแน่นอนเนื่องจากวงกลมมีรากที่สองในสมการ ดังนั้นคุณต้องประมาณ

ในการทำเช่นนี้คุณต้องแบ่งวงกลมของคุณเป็น n-tants (egquadrants, octants) สำหรับแต่ละ n-t คุณใช้จุดแรกและจุดสุดท้ายเป็นจุดแรกและจุดสุดท้ายของเส้นโค้ง Bezier รูปหลายเหลี่ยม Bezier ต้องการจุดเพิ่มเติมสองจุด เพื่อให้เร็วฉันจะนำเส้นสัมผัสไปยังวงกลมสำหรับจุดสุดขั้วแต่ละจุดของ n-tant และเลือกจุดสองจุดเป็นจุดตัดของเส้นสัมผัสทั้งสอง (เพื่อให้โดยพื้นฐานแล้วรูปหลายเหลี่ยม Bezier ของคุณเป็นรูปสามเหลี่ยม) เพิ่มจำนวน n-tants ให้พอดีกับความแม่นยำของคุณ


4
เป็นไปได้ตราบเท่าที่คุณใช้เส้นโค้งเบเซียร์จำนวนอนันต์โดยมีความยาวเป็นศูนย์ ซึ่งโดยพื้นฐานแล้วคือจำนวนจุดที่ไม่สิ้นสุดหรือเป็นแค่เส้นโค้ง
Tatarize

7

คำตอบอื่น ๆ ครอบคลุมความจริงที่ว่าวงกลมที่แท้จริงเป็นไปไม่ได้ ไฟล์ SVG นี้เป็นการประมาณโดยใช้เส้นโค้ง Quadratic Bezier และเป็นสิ่งที่ใกล้เคียงที่สุดที่คุณจะได้รับ: http://en.wikipedia.org/wiki/File:Circle_and_quadratic_bezier.svg

นี่คือเส้นโค้ง Cubic Bezier: http://en.wikipedia.org/wiki/File:Circle_and_cubic_bezier.svg


7

สำหรับผู้ที่กำลังมองหาโค้ด:

https://jsfiddle.net/nooorz24/2u9forep/12/

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");

function drawBezierOvalQuarter(centerX, centerY, sizeX, sizeY) {
    ctx.beginPath();
    ctx.moveTo(
        centerX - (sizeX),
        centerY - (0)
    );
    ctx.bezierCurveTo(
        centerX - (sizeX),
        centerY - (0.552 * sizeY),
        centerX - (0.552 * sizeX),
        centerY - (sizeY),
        centerX - (0),
        centerY - (sizeY)
    );
    ctx.stroke();
}

function drawBezierOval(centerX, centerY, sizeX, sizeY) {
    drawBezierOvalQuarter(centerX, centerY, -sizeX, sizeY);
    drawBezierOvalQuarter(centerX, centerY, sizeX, sizeY);
    drawBezierOvalQuarter(centerX, centerY, sizeX, -sizeY);
    drawBezierOvalQuarter(centerX, centerY, -sizeX, -sizeY);
}

function drawBezierCircle(centerX, centerY, size) {
    drawBezierOval(centerX, centerY, size, size)
}

drawBezierCircle(200, 200, 64)
<canvas id="myCanvas" width="400" height="400" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>

ช่วยให้สามารถวาดวงกลมที่สร้างจากเส้นโค้ง Bezier 4 เส้น เขียนด้วย JS แต่สามารถแปลเป็นภาษาอื่นได้อย่างง่ายดาย

บันทึก

อย่าใช้เส้นโค้ง Bezier หากคุณต้องการวาดวงกลมโดยใช้เส้นทาง SVG เว้นแต่จะต้องทำเช่นนั้น ในเส้นทางคุณสามารถใช้Arcเพื่อสร้างครึ่งวงกลม 2 วง

การวาดวงกลมด้วยเส้นทางโค้งของ SVG


ที่เป็นประโยชน์มากขอบคุณ! สิ่งที่ต้องเปลี่ยนแปลงเพื่อนำ 4 เซ็กเมนต์มาเรียงลำดับ? ฉันต้องเขียนข้อความตามเส้นทาง แต่ตอนนี้มันกระจัดกระจายไปทั่วทั้ง 4 ส่วน
Alexa

1

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

ป้อนคำอธิบายภาพที่นี่

ที่ไหนNคือจำนวนของจุดควบคุมโค้งเดียวและαเป็นมุมโค้งวงกลม

สำหรับเส้นโค้งกำลังสองสามารถทำให้ง่ายขึ้นเป็นl ≈ r + r * PI*0.1 * pow(α/90, 2) The PI*0.1ค่อนข้างเดาได้ - ฉันไม่ได้คำนวณค่าที่สมบูรณ์แบบ แต่มันค่อนข้างใกล้เคียง สิ่งนี้ทำงานได้ดีพอสมควรสำหรับเส้นโค้งโดยมีจุดควบคุม 1-2 จุดที่ให้รัศมีคลาดเคลื่อนประมาณ 0.2% สำหรับเส้นโค้งลูกบาศก์ สำหรับเส้นโค้งระดับที่สูงขึ้นจะสูญเสียความแม่นยำ ด้วยเส้นโค้งจุดควบคุม 3 จุดมีลักษณะคล้ายกับกำลังสองดังนั้นเห็นได้ชัดว่าฉันพลาดอะไรบางอย่าง แต่ฉันคิดไม่ออกและวิธีนี้มักจะเหมาะกับความต้องการของฉันในตอนนี้ นี่คือการสาธิต


คุณใช้ซอฟต์แวร์อะไรในการสร้างภาพนี้?
Qian Sijianhao

1
สกรีน
ช็อต

0

ขออภัยที่นำอันนี้กลับมาจากความตาย แต่ฉันพบว่าโพสต์นี้มีประโยชน์มากพร้อมกับหน้านี้ในการสร้างสูตรที่ขยายได้

โดยทั่วไปคุณสามารถสร้างวงกลมใกล้โดยใช้สูตรง่ายๆอย่างไม่น่าเชื่อที่ให้คุณใช้เส้นโค้ง Bezier จำนวนเท่าใดก็ได้มากกว่า 4: Distance = radius * stepAngle / 3

Distanceระยะห่างระหว่างจุดควบคุม Bezier และจุดสิ้นสุดที่ใกล้ที่สุดของส่วนโค้งอยู่ที่ไหนรัศมีคือradiusวงกลมและstepAngleเป็นมุมระหว่างปลายทั้ง 2 ของส่วนโค้งซึ่งแสดงด้วย2π / (จำนวนเส้นโค้ง)

ดังนั้นการตีในนัดเดียว: Distance = radius * 2π / (the number of curves) / 3


2
นี่ไม่ใช่การประมาณที่ดีที่สุดของวงกลม Distance = (4/3)*tan(pi/2n)ที่ดีที่สุดคือ สำหรับส่วนโค้งจำนวนมากมันเกือบจะเท่ากันเพราะtan(pi/2)~pi/2nแต่ตัวอย่างเช่นn=4(ซึ่งเป็นกรณีที่ใช้บ่อยที่สุด) สูตรของคุณให้Distance=0.5235...แต่ค่าที่ดีที่สุดคือDistance=0.5522... (คุณมีข้อผิดพลาด ~ 5%)
Kpym

-2

มันเป็นการประมาณที่หนักซึ่งจะดูสมเหตุสมผลหรือแย่มากขึ้นอยู่กับความละเอียดและความแม่นยำ แต่ฉันใช้รัศมี sqrt (2) / 2 xเป็นจุดควบคุมของฉัน ฉันอ่านข้อความที่ค่อนข้างยาวว่าตัวเลขนั้นได้มาอย่างไรและมันก็คุ้มค่าที่จะอ่าน แต่สูตรข้างต้นเป็นวิธีที่รวดเร็วและสกปรก

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