Jak stworzyć okrąg z krzywymi Béziera?


Odpowiedzi:


139

Jak już powiedziano: nie ma dokładnej reprezentacji koła za pomocą krzywych Beziera.

Aby uzupełnić pozostałe odpowiedzi: dla krzywej Beziera z nsegmentami optymalna odległość od punktów kontrolnych, w tym sensie, że środek krzywej leży na samym okręgu, wynosi (4/3)*tan(pi/(2n)).

wzór na n segmentów

A więc za 4 punkty (4/3)*tan(pi/8) = 4*(sqrt(2)-1)/3 = 0.552284749831.

4-punktowy przypadek


2
Jakie metryki optymalizujesz według optymalnej odległości? Jak pokazano na Przybliżenie koła z sześciennymi krzywymi Béziera , najmniejszy możliwy maksymalny dryf osiągany jest przez inną wartość. Czy możesz podać link określający, co oznacza „optymalny” w twoim przypadku lub skąd pochodzi formuła?
Suma,

1
@Suma to nie jest optymalne dla niektórych odległości. Optymalne jest, aby środek krzywej znajdował się na okręgu. I na pewno można to ulepszyć, jeśli postawisz inne kryteria.
Kpym

2
DOBRZE. Postaram się przeformułować: „odległość do punktów kontrolnych taka, że ​​środek krzywej leży na samym okręgu”. Postrzegam to jako uzasadnioną decyzję (dostatecznie dobrą i łatwą do obliczenia), ale nie nazwałbym jej optymalną (przynajmniej nie bez napisania, w jakim sensie jest optymalna).
Suma,

1
Tak, ponieważ ten ma maksymalne odchylenie + 0,027% i minimalne odchylenie -0 w stosunku do prawdziwego koła. Jest tylko zawsze większy niż prawdziwy okrąg, tym lepsze przybliżenie uzyskuje się, przesuwając C o połowę z 0,027%. Jeśli jednak chcesz, aby punkty środkowe znajdowały się na okręgu, z pewnością jest to sposób na zrobienie tego.
Tatarize

2
@ legends2k Używam LaTeX z TikZ do generowania pliku PDF, który następnie konwertuję na PNG.
Kpym

35

Omówione w comp.graphics.faq

Fragment:

Przedmiot 4.04: Jak dopasować krzywą Beziera do koła?

Co ciekawe, krzywe Beziera mogą przybliżać okrąg, ale nie idealnie do niego pasują. Powszechnym przybliżeniem jest użycie czterech bezierów do modelowania koła, z których każdy ma punkty kontrolne w odległości d = r * 4 * (sqrt (2) -1) / 3 od punktów końcowych (gdzie r jest promieniem okręgu) i kierunek styczny do okręgu w punktach końcowych. Zapewni to, że punkty środkowe Beziers znajdują się na okręgu, a pierwsza pochodna jest ciągła.
Błąd promieniowy w tym przybliżeniu wyniesie około 0,0273% promienia okręgu.

Michael Goldapp, „Aproksymacja łuków kołowych za pomocą wielomianów sześciennych” Computer Aided Geometric Design (# 8 1991 str. 227-238)

Tor Dokken i Morten Daehlen, "Dobre przybliżenie okręgów za pomocą krzywych Beziera z ciągłą krzywizną" Computer Aided Geometric Design (# 7 1990 str. 33-41). http://www.sciencedirect.com/science/article/pii/016783969090019N (artykuł płatny)

Zapoznaj się również z artykułem nieobjętym płatnymi ścianami dostępnymi pod adresem http://spencermortensen.com/articles/bezier-circle/

Przeglądarki i element Canvas.

Zwróć uwagę, że niektóre przeglądarki używają krzywych Beziera do ich rysowania na płótnie, Chrome używa (obecnie) podejścia 4-sektorowego, a Safari używa podejścia 8-sektorowego, różnica jest zauważalna tylko przy wysokiej rozdzielczości, z powodu tego 0,0273%, a także naprawdę widoczne tylko wtedy, gdy łuki są rysowane równolegle i poza fazą, zauważysz, że łuki oscylują od prawdziwego koła. Efekt jest również bardziej zauważalny, gdy krzywa animuje się wokół jej środka promieniowego, promień 600 pikseli to zwykle rozmiar, w którym będzie miał znaczenie.

Niektóre interfejsy API do rysowania nie mają rzeczywistego renderowania łuków, więc używają również krzywych Beziera, na przykład platforma Flash nie ma interfejsu API do rysowania łuków, więc wszelkie struktury oferujące łuki generalnie używają tego samego podejścia krzywej Beziera.

Należy pamiętać, że silniki SVG w przeglądarkach mogą używać innej metody rysowania.

Inne platformy

Niezależnie od platformy, której próbujesz użyć, warto sprawdzić, jak odbywa się rysowanie łuku, aby móc przewidzieć takie błędy wizualne i dostosować się.


Dzięki, zamienię to.
ocodo

31

Odpowiedzi na to pytanie są bardzo dobre, więc niewiele jest do dodania. Zainspirowany tym zacząłem eksperymentować, aby wizualnie potwierdzić rozwiązanie, zaczynając od czterech krzywych Béziera, zmniejszając liczbę krzywych do jednego. O dziwo dowiedziałem się, że przy trzech krzywych Béziera okrąg wyglądał wystarczająco dobrze, ale konstrukcja jest nieco skomplikowana. Właściwie użyłem Inkscape do umieszczenia czarnego przybliżenia Béziera o szerokości 1 piksela na czerwonym kole o szerokości 3 pikseli (wyprodukowanym przez Inkscape). Dla jasności dodałem niebieskie linie i powierzchnie pokazujące obwiednie krzywych Béziera.

Aby zobaczyć siebie, przedstawiam swoje wyniki:

Wykres 1-krzywej (który wygląda jak kropla wciśnięta w róg, tylko dla kompletności):wprowadź opis obrazu tutaj

Wykres 2-krzywowy:wprowadź opis obrazu tutaj

Wykres 3-krzywowy:wprowadź opis obrazu tutaj

Wykres 4-krzywowy: wprowadź opis obrazu tutaj

(Chciałem umieścić tutaj SVG lub PDF, ale to nie jest obsługiwane)


1
Do tej pory plik svg można dołączyć jako fragment kodu HTML. Zobacz na przykład tę odpowiedź: stackoverflow.com/a/32162431
TS,

1
@TS: Kiedy próbowałem zamienić grafikę na SVG, które miałem, zdałem sobie sprawę, że zgubiłem je na pamięci USB, która została skradziona na początku tego roku. Jeśli czas na to pozwoli, spróbuję je wkrótce odtworzyć. Jednak jeśli SVG można dodać jako kod XML (i nie jest wyświetlany jako grafika), nie ma to tutaj większego sensu.
U. Windl

Jeśli Twoja przeglądarka obsługuje svg, obrazy są renderowane natychmiast po kliknięciu „Uruchom fragment kodu” (najwyraźniej ten przycisk nie jest dostępny w mobilnej wersji stackoverflow ...). Zobacz w odpowiedzi, którą połączyłem.
TS

1
@TS: W przypadku dłuższych plików jest to zbyt brzydkie IMHO.
U. Windl

9

Wiele odpowiedzi już jest, ale znalazłem mały artykuł online z bardzo dobrym sześciennym przybliżeniem beziera koła. Pod względem koła jednostkowego c = 0,55191502449, gdzie c jest odległością od punktów przecięcia osi wzdłuż stycznych do punktów kontrolnych.

Jako pojedyncza ćwiartka dla okręgu jednostkowego z dwoma środkowymi współrzędnymi będącymi punktami kontrolnymi. (0,1),(c,1),(1,c),(1,0)

Błąd promieniowy wynosi zaledwie 0,019608%, więc po prostu musiałem dodać go do tej listy odpowiedzi.

Artykuł można znaleźć tutaj Przybliż koło z sześciennymi krzywymi Béziera


5
Czy czytałeś ten wspaniały traktat o Bezier Curves autorstwa Mike'a „Pomax” Kamermansa ze Stackoverflow . Warto przeczytać! :-)
markE

1
@markE Dziękuję bardzo za ten link, który jest jednym z „najwspanialszych” traktatów, jakie kiedykolwiek widziałem na ten temat. Nie mogę się doczekać, aby mieć szansę na szczegółowe omówienie tego ..: D dzięki ...
Blindman67

1
Więc z błędem 0,019608% grafika otrzyma błąd 4 piksele, gdy promień przekroczy 2551 pikseli w kole, zamiast tego okropnego 0,027253%, gdy jesteśmy stałym półpikselem błędu (gdzie silnik graficzny zmieni piksel) przy 1835 pikselach, co powoduje błąd w 2 pikselach!
Tatarize

@Tatarize W artykule nie określono, w jaki sposób mierzono błąd, jest mowa o maksymalnym dryfcie radialnym? Zakładam, że błąd jest zminimalizowany wzdłuż krzywej 0 <= t <= 1, aby dopasować ćwiartkę 0 <= pheta <= Pi / 2 przy t = 0 = 1/2 = 1 równa się pheta = 0 = Pi / 4 = Pi / 4 błąd wynosi 0,019608%, a maksymalny błąd przy t = ~ 0,1822 & t = ~ 0,8177 wynosi 0,019608% (znaki?), Ale w tych punktach t nie jest równe pheta, czy błąd obejmuje dryf kątowy? . 4piksele mogą być poprawne lub nie. Błąd może być wariancją, stąd błąd <2pix dla r = 2551. Wiele pytań, które będą wymagały zbadania
Blindman67

Jestem prawie pewien, że spojrzałem na krzywą błędu, że dana regulacja po prostu przesuwa punkt w dół o tyle, aby spowodować maksymalny błąd powyżej linii łuku, aby zrównać się z maksymalnym błędem poniżej linii łuku. Oznacza to, że zmieniamy krzywą nieco w dół, więc cały błąd nie jest dodatni. Ta regulacja oznacza, że ​​przekraczamy linię łuku 4 razy, z 4 punktami maksymalnego błędu. Kiedy oryginalna określona linia miała 2 punkty, a mianowicie przy t = 0,25 it = 0,75. Po uwzględnieniu korekt powinien wynosić t = 0,125, t = 0,375 t = 0,625 t = 0,875. Zakłada się, że używamy pełnych pikseli, a nie wygładzania, co zmieniłoby się przy 14 pikselach.
Tatarize

8

To niemożliwe. Bezier to sześcienny (przynajmniej ... najczęściej używany jest). Nie można dokładnie wyrazić koła sześciennym, ponieważ koło zawiera pierwiastek kwadratowy w swoim równaniu. W konsekwencji musisz przybliżać.

Aby to zrobić, musisz podzielić swój krąg na n-tanty (np. Kwadranty, oktanty). Dla każdego n-tanta używasz pierwszego i ostatniego punktu jako pierwszego i ostatniego krzywej Beziera. Wielokąt Beziera wymaga dwóch dodatkowych punktów. Aby być szybkim, wziąłbym styczne do koła dla każdego skrajnego punktu n-tanta i wybrałbym te dwa punkty jako przecięcie dwóch stycznych (tak, aby zasadniczo twój wielokąt Beziera był trójkątem). Zwiększ liczbę n-tantów, aby dopasować swoją precyzję.


4
Jest to możliwe, o ile używasz nieskończonej liczby krzywych beziera o zerowej długości. Jest to w zasadzie nieskończona liczba punktów, a raczej po prostu łukowa krzywa.
Tatarize


7

Dla osób, które tylko szukają kodu:

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>

Pozwala to narysować okrąg złożony z 4 krzywych Beziera. Napisany w JS, ale można go łatwo przetłumaczyć na inny język

Uwaga

Nie używaj krzywych Beziera, jeśli musisz narysować okrąg za pomocą ścieżki SVG, chyba że jest to wymagane. Na ścieżce możesz Arcutworzyć 2 półokręgi.

Rysowanie okręgu ze ścieżką łuku SVG


To jest bardzo pomocne, dzięki! Co należy zmienić, aby uporządkować 4 segmenty? Muszę pisać tekst wzdłuż ścieżki, ale teraz jest on rozproszony wokół 4 segmentów
Alexa

1

Nie jestem pewien, czy powinienem otworzyć nowe pytanie, ponieważ dotyczy to aproksymacji, ale interesuje mnie ogólna formuła, aby uzyskać punkty kontrolne dla Beziera dowolnego stopnia i uważam, że pasuje do tego pytania. Wszystkie rozwiązania, które znalazłem w sieci, dotyczą tylko krzywych sześciennych, są płatne lub nawet nie rozumiem (nie jestem zbyt dobry z matematyki). Postanowiłem więc spróbować rozwiązać to samodzielnie. Zbadałem odległość punktu kontrolnego od środka koła w zależności od danego kąta i do tej pory stwierdziłem, że:

wprowadź opis obrazu tutaj

Gdzie Njest liczbą punktów kontrolnych dla pojedynczej krzywej i αjest kątem łuku koła.

W przypadku krzywej kwadratowej można to uprościć do: l ≈ r + r * PI*0.1 * pow(α/90, 2) To PI*0.1raczej przypuszczenie - nie obliczyłem idealnej wartości, ale jest dość blisko. Działa to dość dobrze dla krzywej z 1-2 punktami kontrolnymi, dając błąd promienia około 0,2% dla krzywej sześciennej. W przypadku krzywych wyższego stopnia zauważalna jest utrata dokładności. Z 3 punktami kontrolnymi krzywa wygląda podobnie do kwadratowej, więc oczywiście brakuje mi czegoś, ale nie mogę tego rozgryźć i ta metoda generalnie pasuje do moich potrzeb. Oto demo .


Jakiego oprogramowania używasz do tworzenia tego obrazu?
Qian Sijianhao

1
Zrzut ekranu z mojego demo + panel pisania Math (lub jakkolwiek nazwa jest przetłumaczona) z win 7 + MS Paint
Paweł Audionysos

0

Przepraszam, że przywracam ten z martwych, ale uważam, że ten post wraz z stroną jest bardzo pomocny w tworzeniu rozszerzalnej formuły.

Zasadniczo możesz utworzyć bliskie koło za pomocą niewiarygodnie prostej formuły, która pozwala na użycie dowolnej liczby krzywych Beziera powyżej 4: Distance = radius * stepAngle / 3

Gdzie Distancejest odległość między punktem kontrolnym Beziera a najbliższym końcem łuku, promień to radiusokrąg, a stepAnglejest to kąt między dwoma końcami łuku reprezentowany przez 2π / (liczba krzywych).

Więc żeby trafić jednym strzałem: Distance = radius * 2π / (the number of curves) / 3


1
To nie jest najlepsze przybliżenie koła. Najlepszy jest Distance = (4/3)*tan(pi/2n). Dla dużej liczby łuków jest prawie to samo, ponieważ tan(pi/2)~pi/2n, ale na przykład dla n=4(który jest najczęściej używanym przypadkiem) twoja formuła daje, Distance=0.5235...ale optymalna jest Distance=0.5522... (więc masz około 5% błędu).
Kpym

-2

To ciężkie przybliżenie, które będzie wyglądało rozsądnie lub okropnie w zależności od rozdzielczości i precyzji, ale używam sqrt (2) / 2 x radius jako moich punktów kontrolnych. Czytałem dość długi tekst, jak wyprowadza się tę liczbę i warto go przeczytać, ale powyższy wzór jest szybki i brudny.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.