¿Cómo crear un círculo con curvas de Bézier?


Respuestas:


138

Como ya se dijo: no hay una representación exacta del círculo usando curvas de Bezier.

Para completar las otras respuestas: para la curva de Bezier con nsegmentos, la distancia óptima a los puntos de control, en el sentido de que la mitad de la curva se encuentra en el círculo mismo, es (4/3)*tan(pi/(2n)).

fórmula para n segmentos

Así que por 4 puntos lo es (4/3)*tan(pi/8) = 4*(sqrt(2)-1)/3 = 0.552284749831.

Caso de 4 puntos


2
Por distancia óptima, ¿qué tipo de métricas estás optimizando? Como se muestra en Aproximadamente un círculo con curvas de Bézier cúbicas , la deriva máxima más baja posible se logra con un valor diferente. ¿Puede proporcionar algún enlace que defina qué significa "óptimo" en su caso, o cómo se deriva la fórmula?
Suma

1
@Suma esto no es óptimo para cierta distancia. Es óptimo tener la mitad de la curva en el círculo. Y ciertamente se puede mejorar si pones otro criterio.
Kpym

2
OKAY. Intentaré reformular: "la distancia a los puntos de control de manera que el centro de la curva se encuentre en el círculo mismo". Veo esto como una decisión válida (suficientemente buena y fácil de calcular), pero no la llamaría óptima (al menos no sin escribir en qué sentido es óptima).
Suma

1
Sí, ya que este tiene una desviación máxima de + 0.027% y una desviación mínima de -0 frente al círculo verdadero. Solo es más grande que el círculo real; la mejor aproximación mejorada se realiza moviendo C a la mitad de 0.027%. Sin embargo, si desea los puntos medios en el círculo, esta es sin duda la forma de hacerlo.
Tatarizar

2
@ legends2k Uso LaTeX con TikZ para generar un PDF que luego convierto a PNG.
Kpym

34

Cubierto en comp.graphics.faq

Extracto:

Asunto 4.04: ¿Cómo encajo una curva de Bezier en un círculo?

Curiosamente, las curvas de Bezier pueden aproximarse a un círculo pero no encajar perfectamente en un círculo. Una aproximación común es usar cuatro beziers para modelar un círculo, cada uno con puntos de control a una distancia d = r * 4 * (sqrt (2) -1) / 3 desde los puntos finales (donde r es el radio del círculo), y en una dirección tangente al círculo en los puntos finales. Esto asegurará que los puntos medios de los Beziers estén en el círculo y que la primera derivada sea continua.
El error radial en esta aproximación será de aproximadamente 0.0273% del radio del círculo.

Michael Goldapp, "Aproximación de arcos circulares por polinomios cúbicos" Diseño geométrico asistido por computadora (# 8 1991 pp.227-238)

Tor Dokken y Morten Daehlen, "Buenas aproximaciones de círculos por curvas de Bézier continuas de curvatura" Diseño geométrico asistido por computadora (# 7 1990 pp. 33-41). http://www.sciencedirect.com/science/article/pii/016783969090019N (artículo no gratuito)

Vea también el artículo sin muro de pago en http://spencermortensen.com/articles/bezier-circle/

Navegadores y elemento de lienzo.

Tenga en cuenta que algunos navegadores usan curvas de Bezier para su arco de dibujo de lienzo, Chrome usa (en este momento) un enfoque de 4 sectores y Safari usa un enfoque de 8 sectores, la diferencia es notable solo en alta resolución, debido a ese 0.0273%, y también solo realmente visible cuando los arcos se dibujan en paralelo y fuera de fase, notará que los arcos oscilan desde un círculo verdadero. El efecto también es más notorio cuando la curva se anima alrededor de su centro radial, el radio de 600 px suele ser el tamaño donde marcará la diferencia.

Ciertas API de dibujo no tienen una verdadera representación de arco, por lo que también usan curvas de Bezier, por ejemplo, la plataforma Flash no tiene una API de dibujo de arco, por lo que cualquier marco que ofrezca arcos generalmente utiliza el mismo enfoque de curva de Bezier.

Tenga en cuenta que los motores SVG de los navegadores pueden utilizar un método de dibujo diferente.

Otras plataformas

Independientemente de la plataforma que intente usar, vale la pena verificar cómo se realiza el dibujo del arco, para que pueda predecir errores visuales como este y adaptarse.


Gracias, lo sustituyo.
ocodo

31

Las respuestas a la pregunta son muy buenas, por lo que hay poco que agregar. Inspirado por eso, comencé a hacer un experimento para confirmar visualmente la solución, comenzando con cuatro curvas de Bézier, reduciendo el número de curvas a una. Sorprendentemente, descubrí que con tres curvas Bézier, el círculo se veía lo suficientemente bien para mí, pero la construcción es un poco complicada. De hecho, utilicé Inkscape para colocar la aproximación de Bézier negra de 1 píxel de ancho sobre un círculo rojo de 3 píxeles de ancho (como lo produjo Inkscape). Para aclarar, agregué líneas y superficies azules que muestran los cuadros delimitadores de las curvas de Bézier.

Para verte a ti mismo, te presento mis resultados:

El gráfico de 1 curva (que parece una gota apretada en una esquina, solo para completar):ingrese la descripción de la imagen aquí

El gráfico de 2 curvas:ingrese la descripción de la imagen aquí

El gráfico de 3 curvas:ingrese la descripción de la imagen aquí

El gráfico de 4 curvas: ingrese la descripción de la imagen aquí

(Quería poner el SVG o PDF aquí, pero no es compatible)


1
Por ahora, svg se puede incluir como fragmento de código html. Vea, por ejemplo, esta respuesta: stackoverflow.com/a/32162431
TS

1
@TS: Cuando traté de reemplazar los gráficos con los SVG que tenía, me di cuenta de que los perdí con una memoria USB que me habían robado a principios de este año. Si el tiempo lo permite, intentaré recrearlos pronto. Sin embargo, si se puede agregar SVG como código XML (y no se muestra como gráficos), no tiene mucho sentido aquí.
U. Windl

Si su navegador admite svg, las imágenes se renderizan tan pronto como haga clic en "Ejecutar fragmento de código" (aparentemente ese botón no está disponible en la versión móvil de stackoverflow ...). Vea en la respuesta que vinculé.
TS

1
@TS: Para archivos más largos es demasiado feo en mi humilde opinión.
U. Windl

9

Ya hay muchas respuestas, pero encontré un pequeño artículo en línea con una muy buena aproximación bézier cúbica de un círculo. En términos de círculo unitario c = 0.55191502449 donde c es la distancia desde los puntos de intersección del eje a lo largo de las tangentes a los puntos de control.

Como un solo cuadrante para el círculo unitario con las dos coordenadas del medio como puntos de control. (0,1),(c,1),(1,c),(1,0)

El error radial es solo 0.019608%, así que solo tuve que agregarlo a esta lista de respuestas.

El artículo se puede encontrar aquí Aproximadamente un círculo con curvas de Bézier cúbicas


5
¿Ha leído este excelente tratado sobre las curvas de Bezier de Mike 'Pomax' Kamermans de Stackoverflow ? ¡Vale la pena leerlo! :-)
MarkE

1
@markE Muchas gracias por ese enlace, que es uno de los tratados "más excelentes" que he visto sobre el tema. No puedo esperar a tener la oportunidad de repasarlo en detalle ..: D gracias ...
Blindman67

1
Entonces, con un error de 0.019608%, los gráficos obtendrán 4 píxeles de error cuando el radio supere los 2551 píxeles en un círculo en lugar de ese terrible 0.027253% donde tenemos un medio píxel sólido de error (donde el motor gráfico cambiará el píxel) a 1835 px, lo que provoca un error de 2 píxeles.
Tatarizar

@Tatarize El artículo no especifica cómo se midió el error, dice ¿deriva radial máxima? Supongo que el error se minimiza a lo largo de la curva 0 <= t <= 1 para coincidir con el cuadrante 0 <= pheta <= Pi / 2 en t = 0 = 1/2 = 1 es igual a pheta = 0 = Pi / 4 = Pi / 4 el error es 0.019608% y el error máximo en t = ~ 0.1822 & t = ~ 0.8177 de 0.019608% (¿signos?) Pero en estos puntos t no es igual a pheta ¿El error incluye la deriva angular? . 4 píxeles pueden ser correctos o no. El error puede ser varianza, por lo tanto, error <2pix para r = 2551. Muchas preguntas que necesitarán investigación
Blindman67

Estoy bastante seguro de haber visto la curva de error de que el ajuste dado simplemente mueve el punto hacia abajo lo suficiente como para causar que el error máximo por encima de la línea de arco sea igual al error máximo por debajo de la línea de arco. Es decir, cambiamos la curva un poco hacia abajo para que todo el error no sea positivo. Este ajuste significa que estamos cruzando la línea del arco 4 veces, con 4 puntos de error máximo. Cuando la línea especificada original tenía 2 puntos, es decir, en t = .25 yt = .75. Con los ajustes debería estar en t = .125, t = .375 t = .625 t = .875. Esto supone que estamos usando píxeles sólidos y no suavizados, lo que cambiaría a 14 px.
Tatarizar

8

No es posible. Un Bezier es un cúbico (al menos ... el más utilizado es). Un círculo no se puede expresar exactamente con un cúbico, porque un círculo contiene una raíz cuadrada en su ecuación. Como consecuencia, debe aproximarse.

Para hacer esto, debes dividir tu círculo en n-tantes (por ejemplo, cuadrantes, octantes). Para cada n-tant, usa el primer y último punto como el primero y el último de la curva de Bezier. El polígono de Bezier requiere dos puntos adicionales. Para ser rápido, tomaría las tangentes al círculo para cada punto extremo del n-tant y elegiría los dos puntos como la intersección de las dos tangentes (de modo que básicamente su polígono Bezier sea un triángulo). Aumente el número de n-tantes para adaptarse a su precisión.


4
Es posible, siempre que use un número infinito de curvas Bézier, de longitud cero. Que es básicamente un número infinito de puntos, o más bien una curva de arco.
Tatarizar


7

Para las personas que solo buscan 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>

Esto permite dibujar un círculo que está formado por 4 curvas Bezier. Escrito en JS pero se puede traducir fácilmente a cualquier otro idioma


Eso es muy útil, ¡gracias! ¿Qué hay que cambiar para poner los 4 segmentos en orden? Necesito escribir texto a lo largo de una ruta, pero ahora está disperso en los 4 segmentos
Alexa

1

No estoy seguro de si debería abrir una nueva pregunta ya que se trata de aproximación, pero estoy interesado en la fórmula general para obtener puntos de control para Bezier de cualquier grado y creo que encaja dentro de esta pregunta. Todas las soluciones que encontré en la web son solo para curvas cúbicas o se pagan o ni siquiera entiendo (no soy muy bueno en matemáticas). Así que decidí intentar resolver esto por mi cuenta. Estaba estudiando la distancia del punto de control desde el centro de un círculo dependiendo del ángulo dado y hasta ahora encontré que:

ingrese la descripción de la imagen aquí

Donde Nes el número de puntos de control para una sola curva y αes el ángulo del arco del círculo.

Para una curva cuadrática, se puede simplificar a l ≈ r + r * PI*0.1 * pow(α/90, 2) The PI*0.1es más bien una suposición: no calculé el valor perfecto, pero está bastante cerca. Esto funciona razonablemente bien para la curva con 1-2 puntos de control que dan un error de radio de aproximadamente 0,2% para la curva cúbica. Para curvas de mayor grado, la pérdida de precisión es notable. Con 3 puntos de control, la curva parece similar a la cuadrática, así que obviamente me pierdo algo, pero no puedo resolverlo y este método generalmente se ajusta a mis necesidades por ahora. Aquí está la demostración .


¿Qué software usas para crear esta imagen?
Qian Sijianhao

1
Captura de pantalla de mi demostración + panel de escritura matemática (o como se traduzca el nombre) de win 7 + MS Paint
Paweł Audionysos

0

Lamento traer este de entre los muertos, pero encontré esta publicación muy útil junto con esta página para crear una fórmula expandible.

Básicamente, puede crear un círculo cercano usando una fórmula increíblemente simple que le permite usar cualquier número de curvas Bezier sobre 4: Distance = radius * stepAngle / 3

Donde Distancees la distancia entre un punto de control de Bezier y el extremo más cercano del arco, el radio es el radiusdel círculo y stepAnglees el ángulo entre los 2 extremos del arco representado por 2π / (el número de curvas).

Entonces, para golpearlo de una vez: Distance = radius * 2π / (the number of curves) / 3


1
Ésta no es la mejor aproximación de un círculo. El mejor es Distance = (4/3)*tan(pi/2n). Para un gran número de arcos es casi lo mismo porque tan(pi/2)~pi/2n, pero por ejemplo para n=4(que es el caso más utilizado) su fórmula da Distance=0.5235...pero la óptima es Distance=0.5522... (por lo que tiene un error de ~ 5%).
Kpym

-2

Es una aproximación pesada que se verá razonable o terrible dependiendo de la resolución y precisión, pero yo uso sqrt (2) / 2 x radio como mis puntos de control. Leí un texto bastante largo sobre cómo se deriva ese número y vale la pena leerlo, pero la fórmula anterior es rápida y sucia.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.