Buscando el arco de circunferencia de un segmento con curva cuadrática o cúbica de Bezier

O=(x, y) Q=(c, d) P=(a,b) αα/2M
Figura. Buscando el arco de circunferencia para un segmento usando Bezier

El siguiente problema es como dibujar un arco de circunferencia entre dos puntos P y Q. En la Figura sólo conocemos las coordenadas de esos puntos y tenemos que dotar de puntos de control para una cuadrática o bien cúbica de Bezier. En los temas anteriores vimos como calcular la distancia de control y el ángulo de control para conformar una circunferencia que se aproxime a una cuyo radio es el del polígono y con un determinado número de lados. Recordamos que con 5 lados podíamos obtener una buena aproximación con cuadráticas y, por supuesto, mejor aproximación con las cúbicas.

Siendo n el número de lados del polígono teníamos que α = 2π/n, de tal forma que con un radio r para el polígono, lo que habíamos obtenido para las cuadráticas eran las siguientes:

distancia control = r (2-cos(α/2)) ángulo control = 0

Y para las cúbicas las siguientes:

distancia control = r (1 + (4/3 tan(α/4))2)1/2 ángulo control = α/2 - tan-1(4/3 tan(α/4))

Por lo tanto conociendo el radio y el número de lados es suficiente para ubicar los puntos de control. Vamos a aprovechar esto para dibujar un arco de circunferencia entre dos puntos sin necesidad de dibujar el resto de puntos del polígono.

Para conocer el radio primero debemos buscar el punto O = (x, y) donde estará ubicado el centro del polígono. PQ es el segmento cuyos puntos P = (a, b) y Q = (c, d) son conocidos. De ese segmento, que se corresponde con una arista del polígono, conocemos su pendiente m = (c-a)/(d-b). El punto medio del segmento será M = ((a+c)/2, (b+d)/2). La recta que pasa por OM es perpendicular a PQ y por tanto con pendiente -m, con la siguiente ecuación:

y = -((c-a)/(d-b)) (x-((a+c)/2)) + ((b+d)/2)    [1]

La otra condición que imponemos surge del ángulo α/2. En la Figura vemos que el triángulo rectángulo OMP es tal que tan(α/2) = PM / OM = (PQ/2) / OM así que:

PQ/2 = 1/2 (((a-c)2+(b-d)2)1/2 OM = ((x-(a+c)/2)2+(y-(b+d)/2)2))1/2

que tras quitar raíces, elevar al cuadrado la tangente y usando α = 2π/n nos queda así:

tan2(π/n) = 1/4 (((a-c)2+(b-d)2) / ((x-(a+c)/2)2+(y-(b+d)/2)2))    [2]

Resolvemos [1] y [2] encontrando el punto O = (x, y):

x = 1/4 (2 (a + c) + (b - d) tan(π/(2 n)) + (d - b) cot(π/(2 n))) y = 1/2 ((a - c) cot(π/n) + b + d)

donde cot es la cotangente, es decir, cot = 1/tan.

Resolver con Wolframalpha

Utilizamos Wolframalpha para la resolución usando la siguiente entrada:

{n>=3,
y=-((c-a)/(d-b)) (x-((a+c)/2)) + ((b+d)/2),
tan^2(PI/n) = 1/4 (((a-c)^2+(b-d)^2) / ((x-(a+c)/2)^2+(y-(b+d)/2)^2))}
        

Agregamos n>=3 para obtener un resultado general para polígonos de 3 o más lados. Se obtienen 4 resultados, pero realmente son dos, pues el primero y cuarto son iguales y el segundo y tercero también. Tras probarlos hemos elegido el primer resultado:

x = 1/4 tan(π/(2 n)) (2 a cot(π/(2 n)) - b cot^2(π/(2 n)) + b + 2 c cot(π/(2 n)) + d cot^2(π/(2 n)) - d)
y = 1/4 tan(π/(2 n)) (a cot^2(π/(2 n)) - a + 2 b cot(π/(2 n)) - c cot^2(π/(2 n)) + c + 2 d cot(π/(2 n)))
        

Simplificamos quedando finalmente así:

x = 1/4 (2 (a + c) + (b - d) tan(π/(2 n)) + (d - b) cot(π/(2 n)))
y = 1/2 ((a - c) cot(π/n) + b + d)
        

Obtenemos el radio usando alguna de las dos distancias OP o OQ, por ejemplo esta última:

r = ((c-x)2 + (d-y)2)1/2

Usando un polígono de 5 lados, entonces con n=5 tendremos un valor para el radio y para el ángulo α = 2π/n, tras lo cual procedemos a calcular la distancia de control y, en su caso, también el ángulo de control.

Comprobando el arco de circunferencia con curvas de Bezier

Figura. Arco de circunferencia en una arista de un polígono de 5 lados

Probaremos lo visto en el apartado anterior poniendo dos puntos aleatoriamente sobre el plano del editor SVG, el primero en (6.95, 3.9) y el segundo en (10.45, 6.4), tal como se observa en la Figura. Asignaremos una curva cuadrática de Bezier en el punto final, calculando el punto de control a una distancia que supone la aproximación a un arco de circunferencia. Tomaremos para los cálculos un polígono de 5 lados.

Tras calcular con el botón obtenemos el punto de control (x1, y1) = (9.51, 4.01). Como información adicional obtenida al calcular se expone el centro en (6.98, 7.56) con un radio de 3.66 unidades. Tras aplicar el arco en el SVG, el siguiente paso es construir un polígono de 5 lados con arcos de circunferencia y comprobar que los arcos coinciden.

Figura. Polígono de 5 lados con arcos de circunferencia

En la Figura puede ver el arco que construimos antes en color azul claro y trazo grueso para resaltarlo y observarlo con mayor claridad. Ahora pasamos a construir el polígono con arcos de circunferencia con los datos obtenidos antes, centro en (6.98, 7.56) y diámetro el doble del radio 3.66. Observamos ya que ambos arcos se superponen.

Figura. Arco circular

Tras aplicar los cambios y ponerle al primer arco un trazo fino azul más oscuro, vemos en la Figura como coincide perfectamente con la circunferencia en rojo que construimos con el polígono de 5 lados. Cuando en este apartado y el siguiente decimos que hay coincidencia entre arcos nos referimos a que la aproximación al arco de circunferencia es bastante grande sin aplicar zoom, pues ya vimos en los temas anteriores que siempre son aproximaciones.

Aunque no afecta a la coincidencia de arcos, observamos en la Figura 3 que no coinciden los puntos inicial y final del arco azul con un arco entre dos puntos del polígono. Esto es porque el ángulo al construir el polígono tiene valor cero por defecto. El primer punto del polígono con número 0 está bajo el punto 5 y se ubica con un ángulo de 0 grados respecto al centro. Pero se podría buscar un valor para ese ángulo donde ambos arcos coincidarían también en sus puntos inicial y final.

Arcos con Bezier cúbicas y polígonos regulares para construirlos

Figura. Arcos de circunferencia con Bezier cúbicas con polígonos regulares de 3, 4 y 5 lados

Partiendo de un segmento horizontal entre los puntos (6, 7) y (16, 7) usamos el editor SVG para construir tres arcos de circunferencia con curvas Bezier cúbicas aplicando la utilidad anterior. El primer arco en color azul y trazo continuo que se observa en la Figura se corresponde con una Bezier cúbica calculando el centro y radio con un polígono de 3 lados. En trazo azul discontinuo está el arco de circunferencia que también generamos con un polígono de 3 lados que centramos y damos radio con los datos obtenidos al crear el arco. Se observa como coinciden.

El siguiente arco en color verde es el correspondiente a un polígono de 4 lados y el de color rojo de 5 lados. A medida que incrementamos el número de lados, la circunferencia se hace mayor y el segmento de arco se irá aproximando a al segmento de recta entre los dos puntos, es decir, se aproximará a la arista del polígono.

Puede insertar el código siguiente en el editor SVG para analizarlo con más detalle. Los tres primeros <path> son las circunferencias con trazo discontinuo que están dibujadas al fondo. Los tres últimos son los arcos con trazo continuo en el orden 3, 4 y 5 lados.

<svg viewBox="0 0 24 24" width="480" height="480">
    <path d="M19.51 13.88C19.51 17.57 17.14 20.83 13.63 21.97C10.12 23.11 6.28 21.86 4.12 18.88C1.95 15.9 1.95 11.86 4.12 8.88C6.28 5.9 10.12 4.65 13.63 5.79C17.14 6.93 19.51 10.19 19.51 13.88z" stroke="rgb(255,0,0)" fill="none" stroke-width="0.1" stroke-dasharray="0.2"></path>
    <path d="M18.07 12C18.07 15.9 14.9 19.07 11 19.07C7.1 19.07 3.93 15.9 3.93 12C3.93 8.1 7.1 4.93 11 4.93C14.9 4.93 18.07 8.1 18.07 12z" stroke="rgb(0,128,0)" fill="none" stroke-width="0.1" stroke-dasharray="0.2"></path>
    <path d="M16.77 9.89C16.77 14.33 11.96 17.11 8.12 14.89C4.27 12.67 4.27 7.11 8.11 4.89C11.96 2.67 16.77 5.45 16.77 9.89z" stroke="rgb(0,0,255)" fill="none" stroke-width="0.1" stroke-dasharray="0.2"></path>
    <path d="M6 7C8.22 3.15 13.78 3.15 16 7" stroke="rgb(0,0,255)" fill="none" stroke-width="0.1"></path>
    <path d="M6 7C8.76 4.24 13.24 4.24 16 7" stroke="rgb(0,128,0)" fill="none" stroke-width="0.1"></path>
    <path d="M6 7C8.98 4.83 13.02 4.83 16 7" stroke="rgb(255,0,0)" fill="none" stroke-width="0.1"></path>
</svg>