Como criar um círculo com curvas de Bézier?


Respostas:


138

Como já foi dito: não existe uma representação exata do círculo usando as curvas de Bézier.

Para completar as outras respostas: para a curva de Bézier com nsegmentos, a distância ótima aos pontos de controle, no sentido de que o meio da curva está no próprio círculo, é (4/3)*tan(pi/(2n)).

fórmula para n segmentos

Então é por 4 pontos (4/3)*tan(pi/8) = 4*(sqrt(2)-1)/3 = 0.552284749831.

Caso de 4 pontos


2
Por distância ideal, que tipo de métrica você está otimizando? Conforme mostrado em Aproximar um círculo com curvas de Bézier cúbicas , a menor deriva máxima possível é alcançada por um valor diferente. Você pode fornecer algum link definindo o que "ótimo" significa no seu caso, ou como a fórmula é derivada?
Suma,

1
@Suma isso não é ideal para alguma distância. É ideal ter o meio da curva do círculo. E certamente pode ser melhorado se você colocar outros critérios.
Kpym

2
ESTÁ BEM. Tentarei reformular: "a distância aos pontos de controle de forma que o meio da curva fique no próprio círculo". Vejo isso como uma decisão válida (boa o suficiente e fácil de calcular), mas não a chamaria de ótima (pelo menos não sem escrever em que sentido é ótima).
Suma,

1
Sim, já que este tem um desvio máximo de + 0,027% e um desvio mínimo de -0 em relação ao círculo verdadeiro. Ele só é maior do que o círculo real; a aproximação melhorada é feita movendo C pela metade de 0,027%. Se você quiser os pontos médios do círculo, esta é certamente a maneira de fazê-lo.
Tatarize em

2
@ legends2k Eu uso o LaTeX com o TikZ para gerar um PDF que eu converto para PNG.
Kpym

34

Abrangido no comp.graphics.faq

Excerto:

Assunto 4.04: Como faço para ajustar uma curva de Bezier a um círculo?

Curiosamente, as curvas de Bézier podem se aproximar de um círculo, mas não se encaixam perfeitamente em um círculo. Uma aproximação comum é usar quatro beziers para modelar um círculo, cada um com pontos de controle a uma distância d = r * 4 * (sqrt (2) -1) / 3 dos pontos finais (onde r é o raio do círculo), e em uma direção tangente ao círculo nos pontos finais. Isso garantirá que os pontos médios dos Béziers estejam no círculo e que a primeira derivada seja contínua.
O erro radial nesta aproximação será de cerca de 0,0273% do raio do círculo.

Michael Goldapp, "Aproximação de arcos circulares por polinômios cúbicos" Computer Aided Geometric Design (# 8 1991 pp.227-238)

Tor Dokken e Morten Daehlen, "Good Approximations of circles by curvature-contínua Bezier curves" Computer Aided Geometric Design (# 7 1990 pp. 33-41). http://www.sciencedirect.com/science/article/pii/016783969090019N (artigo não gratuito)

Consulte também o artigo sem acesso pago em http://spencermortensen.com/articles/bezier-circle/

Navegadores e elemento Canvas.

Observe que alguns navegadores usam curvas de Bezier para desenhar o arco de tela, o Chrome usa (atualmente) uma abordagem de 4 setores e o Safari usa uma abordagem de 8 setores, a diferença é perceptível apenas em alta resolução, por causa disso 0,0273%, e também apenas verdadeiramente visível quando os arcos são desenhados em paralelo e fora de fase, você notará que os arcos oscilam a partir de um círculo verdadeiro. O efeito também é mais perceptível quando a curva está animando em torno de seu centro radial, o raio de 600px geralmente é o tamanho em que fará a diferença.

Determinadas APIs de desenho não têm renderização de arco verdadeiro, então também usam curvas de Bezier, por exemplo, a plataforma Flash não tem API de desenho de arco, portanto, quaisquer estruturas que oferecem arcos geralmente usam a mesma abordagem de curva de Bezier.

Observe que os mecanismos SVG nos navegadores podem usar um método de desenho diferente.

Outras plataformas

Seja qual for a plataforma que você está tentando usar, vale a pena verificar como o desenho do arco é feito, para que você possa prever erros visuais como este e se adaptar.


Obrigado, vou substituí-lo.
ocodo

31

As respostas à pergunta são muito boas, então há pouco a acrescentar. Inspirado nisso, comecei a fazer um experimento para confirmar visualmente a solução, começando com quatro curvas de Bézier, reduzindo o número de curvas para uma. Surpreendentemente, descobri que com três curvas de Bézier o círculo parecia bom o suficiente para mim, mas a construção é um pouco complicada. Na verdade, usei o Inkscape para colocar a aproximação Bézier preta de 1 pixel de largura sobre um círculo vermelho de 3 pixels de largura (como produzido pelo Inkscape). Para esclarecimento, adicionei linhas e superfícies azuis mostrando as caixas delimitadoras das curvas de Bézier.

Para se ver, estou apresentando meus resultados:

O gráfico de 1 curva (que parece uma gota espremida em um canto, apenas para completar):insira a descrição da imagem aqui

O gráfico de 2 curvas:insira a descrição da imagem aqui

O gráfico de 3 curvas:insira a descrição da imagem aqui

O gráfico de 4 curvas: insira a descrição da imagem aqui

(Eu queria colocar o SVG ou PDF aqui, mas isso não é compatível)


1
Agora, o svg pode ser incluído como um trecho de código html. Veja por exemplo esta resposta: stackoverflow.com/a/32162431
TS

1
@TS: Quando tentei substituir os gráficos pelos SVGs que tinha, percebi que os perdi com um pen drive que havia sido roubado no início deste ano. Se o tempo permitir, tentarei recriá-los em breve. No entanto, se o SVG pode ser adicionado como código XML (e não é exibido como gráfico), não faz muito sentido aqui.
U. Windl

Se o seu navegador suportar svg, as imagens serão renderizadas assim que você clicar em "Executar snippet de código" (aparentemente, esse botão não está disponível na versão móvel do stackoverflow ...). Veja na resposta que liguei.
TS

1
@TS: Para arquivos mais longos, é IMHO muito feio.
U. Windl

9

Muitas respostas já, mas encontrei um pequeno artigo online com uma aproximação Bezier cúbica muito boa de um círculo. Em termos de círculo unitário c = 0,55191502449 onde c é a distância dos pontos de interceptação do eixo ao longo das tangentes aos pontos de controle.

Como um único quadrante para o círculo unitário com as duas coordenadas do meio sendo os pontos de controle. (0,1),(c,1),(1,c),(1,0)

O erro radial é de apenas 0,019608%, então eu só tive que adicioná-lo a esta lista de respostas.

O artigo pode ser encontrado aqui Aproximar um círculo com curvas de Bézier cúbicas


5
Você já leu este excelente tratado sobre as Curvas de Bezier, de Mike 'Pomax' Kamermans de Stackoverflow . Vale a pena ler! :-)
markE de

1
@markE Muito obrigado por esse link, que é um dos "mais excelentes" tratados que já vi sobre o assunto. Não posso esperar para ter uma chance de repassar isso em detalhes ..: D obrigado ...
Blindman67

1
Assim, com 0,019608% de erro, os gráficos obterão 4 pixels de erro quando o raio ultrapassar 2551 pixels em um círculo, em vez dos terríveis 0,027253% onde somos um meio pixel sólido de erro (onde o mecanismo gráfico mudará o pixel) em 1835 px causando 2 pixels com erro!
Tatarize em

@Tatarize O artigo não especifica como o erro foi medido, ele diz desvio radial máximo? Presumo que o erro seja minimizado ao longo da curva 0 <= t <= 1 para corresponder ao quadrante 0 <= feta <= Pi / 2 em t = 0 = 1/2 = 1 igual a feta = 0 = Pi / 4 = Pi / 4 o erro é 0,019608% e o erro máximo em t = ~ 0,1822 & t = ~ 0,8177 de 0,019608% (sinais?), Mas nesses pontos t não é igual a pheta, o erro inclui a deriva angular? . 4 pixels podem ou não estar corretos. O erro pode ser variância, portanto, erro <2pix para r = 2551. Muitas questões que precisarão de investigação
Blindman67

Tenho certeza de que, tendo olhado para a curva de erro, o ajuste dado simplesmente move o ponto para baixo o suficiente para fazer com que o erro máximo acima da linha do arco seja igual ao erro máximo abaixo da linha do arco. O que significa que mudamos a curva um pouco para baixo para que todo o erro não seja positivo. Esse ajuste significa que estamos cruzando a linha do arco 4 vezes, com 4 pontos de erro máximo. Quando a linha original especificada tinha 2 pontos, a saber, em t = 0,25 e t = 0,75. Com os ajustes, deve estar em t = 0,125, t = 0,375 t = 0,625 t = 0,875. Isso pressupõe que estamos usando pixels sólidos e não anti-alias, que mudaria em 14px.
Tatarize em

8

Não é possível. Um Bézier é um cúbico (pelo menos ... o mais comumente usado é). Um círculo não pode ser expresso exatamente com uma cúbica, porque um círculo contém uma raiz quadrada em sua equação. Como consequência, você deve aproximar.

Para fazer isso, você deve dividir seu círculo em n-tants (por exemplo, quadrantes, octantes). Para cada n-tant, você usa o primeiro e o último ponto como o primeiro e o último da curva de Bézier. O polígono de Bézier requer dois pontos adicionais. Para ser rápido, eu pegaria as tangentes ao círculo para cada ponto extremo da n-tante e escolheria os dois pontos como a interseção das duas tangentes (de modo que basicamente seu polígono de Bezier seja um triângulo). Aumente o número de n-tants para se ajustar à sua precisão.


4
É possível, desde que você use um número infinito de curvas de Bezier, de comprimento zero. Que é basicamente um número infinito de pontos, ou melhor, apenas uma curva de arco.
Tatarize em


7

Para pessoas que estão apenas procurando por código:

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>

Isso permite desenhar um círculo que é feito de 4 curvas de Bezier. Escrito em JS, mas pode ser facilmente traduzido para qualquer outro idioma


Isso é muito útil, obrigado! O que precisa ser mudado para colocar os 4 segmentos em ordem? Preciso escrever um texto ao longo de um caminho, mas agora ele está espalhado pelos 4 segmentos
Alexa

1

Não tenho certeza se devo abrir uma nova questão, pois se trata de aproximação, mas estou interessado na fórmula geral para obter pontos de controle para Bézier de qualquer grau e acredito que se encaixa nesta questão. Todas as soluções que encontrei na web são só para curvas cúbicas ou são pagas ou nem entendo (não sou muito bom em matemática). Então decidi tentar resolver isso sozinho. Eu estudei a distância do ponto de controle do centro de um círculo dependendo do ângulo dado e até agora descobri que:

insira a descrição da imagem aqui

Onde Né o número de pontos de controle para uma única curva e αé o ângulo do arco do círculo.

Para a curva quadrática, pode ser simplificado para l ≈ r + r * PI*0.1 * pow(α/90, 2) The PI*0.1é um pouco um palpite - eu não calculei o valor perfeito, mas está bem próximo. Isso funciona razoavelmente bem para curva com 1-2 pontos de controle, dando erro de raio de cerca de 0,2% para curva cúbica. Para curvas de grau mais alto, a perda de precisão é perceptível. Com 3 pontos de controle, a curva é semelhante à quadrática, então obviamente estou perdendo algo, mas não consigo descobrir e esse método geralmente se adapta às minhas necessidades por enquanto. Aqui está a demonstração .


Qual software você usa para criar esta imagem?
Qian Sijianhao

1
Captura de tela do meu painel de escrita de demonstração + Matemática (ou como o nome foi traduzido) do win 7 + MS Paint
Paweł Audionysos

0

Desculpe por trazer este de volta dos mortos, mas eu achei este post muito útil junto com esta página em chegar a uma fórmula expansível.

Basicamente, você pode criar um círculo próximo usando uma fórmula incrivelmente simples que permite usar qualquer número de curvas de Bezier acima de 4: Distance = radius * stepAngle / 3

Onde Distanceé a distância entre um ponto de controle de Bézier e a extremidade mais próxima do arco, raio é o radiusdo círculo e stepAngleé o ângulo entre as 2 extremidades do arco, representado por 2π / (o número de curvas).

Então, para acertar em um tiro: Distance = radius * 2π / (the number of curves) / 3


1
Esta não é a melhor aproximação de um círculo. O melhor é Distance = (4/3)*tan(pi/2n). Para um grande número de arcos é quase o mesmo porque tan(pi/2)~pi/2n, mas por exemplo para n=4(que é o caso mais usado) sua fórmula dá, Distance=0.5235...mas o ideal é Distance=0.5522... (então você tem ~ 5% de erro).
Kpym

-2

É uma aproximação pesada que parecerá razoável ou terrível dependendo da resolução e precisão, mas eu uso sqrt (2) / 2 x radius como meus pontos de controle. Eu li um texto bastante longo sobre como esse número é derivado e vale a pena ler, mas a fórmula acima é rápida e suja.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.