El arco elíptico: A y sus dos banderas
Los siete parámetros del comando A, qué significan large-arc-flag y sweep-flag, la corrección automática de radios fuera de rango, y por qué un círculo completo necesita dos arcos.
Los siete parámetros
<path d="A rx ry rotacion-eje-x bandera-arco-grande bandera-barrido x y" />En orden: los dos radios de la elipse; la rotación del eje x de la elipse en grados; las dos banderas, cada una 0 o 1; y el punto final del arco.
Lo primero que hay que asimilar es que A es un comando de punto final, no de centro y ángulos. No dices «dibuja 90 grados de una circunferencia de radio 40 centrada aquí»; dices «llega hasta este punto siguiendo una elipse de estos radios». El motor calcula el centro. Esa parametrización, que se llama endpoint parameterization, es la que hace que A se pueda encadenar con el resto de comandos sin romper el modelo del punto actual, y es también la razón de las dos banderas.
El problema geométrico es este: dados dos puntos y dos radios, hay en general cuatro arcos elípticos distintos que los unen. Dos elipses candidatas (una a cada lado de la recta que une los puntos), y en cada una, dos maneras de ir de A a B (por el trozo corto o por el largo). Las banderas eligen entre esos cuatro.
large-arc-flag: 0 elige el arco de menos de 180 grados, 1 el de más de 180.
sweep-flag: 0 recorre en sentido antihorario (ángulo decreciente), 1 en sentido horario (ángulo creciente). En el sistema de coordenadas de SVG, con la y hacia abajo, «horario» es lo que se ve horario en pantalla.
<svg viewBox="0 0 240 130" width="480"> <g fill="none" stroke-width="3"> <path d="M 30 100 A 50 50 0 0 0 110 100" stroke="#89b4fa" /> <path d="M 30 100 A 50 50 0 0 1 110 100" stroke="#a6e3a1" /> <path d="M 30 100 A 50 50 0 1 0 110 100" stroke="#f9e2af" /> <path d="M 30 100 A 50 50 0 1 1 110 100" stroke="#f38ba8" /> </g></svg>Cuatro d idénticas salvo por dos dígitos, cuatro arcos completamente distintos. Cuando un arco te sale «al revés» o «por el otro lado», el arreglo son esos dos dígitos, y probar las cuatro combinaciones cuesta diez segundos.
La rotación del eje x solo tiene efecto si rx y ry son distintos: rotar una circunferencia no hace nada. Con radios distintos, inclina la elipse. Se mide en grados y en el sentido positivo del sistema de coordenadas, es decir, horario en pantalla.
Radios fuera de rango
La especificación define qué hacer cuando los valores no tienen sentido geométrico, y esas reglas son la razón de que A casi nunca falle del todo.
Regla uno: si rx o ry es cero, el arco se trata como un segmento recto hasta el punto final. Ni error ni forma degenerada: una L. Esto es útil de verdad, porque permite generar una d con un radio variable y que el caso cero produzca la esquina recta sin ramificar el código.
Regla dos: los radios negativos se toman en valor absoluto. Un -40 es un 40.
Regla tres: si los radios son demasiado pequeños para que exista una elipse que pase por los dos puntos, se escalan hacia arriba de forma uniforme hasta que exista exactamente una solución. La especificación da la fórmula exacta en su apéndice de notas de implementación. En la práctica esto significa que un A 1 1 0 0 1 100 0 no falla: dibuja el semicírculo de radio 50 que sí cabe. Es la explicación de por qué un arco pequeño «no obedece» a los radios que le pones: está en el régimen de corrección.
Regla cuatro: si el punto final coincide con el punto inicial, el arco se omite por completo. No se dibuja nada, y el comando se descarta como si no estuviera.
Esa última regla es la respuesta a la pregunta que todo el mundo se hace al menos una vez.
Por qué un círculo necesita dos arcos
Un círculo completo empieza y acaba en el mismo punto. Por la regla cuatro, un solo A con inicio y fin coincidentes se descarta y no dibuja nada. La solución estándar son dos arcos de media vuelta:
<!-- Circulo de radio 40 centrado en (50, 50) --><path d="M 10 50 A 40 40 0 1 0 90 50 A 40 40 0 1 0 10 50 Z" />De (10, 50) a (90, 50) por arriba, y de vuelta por abajo. Nótese que ambos usan large-arc-flag a 1, aunque cada uno sea exactamente medio círculo: cuando los puntos son diametralmente opuestos, los dos arcos posibles miden 180 grados y la bandera del arco grande deja de discriminar, así que da igual el valor. Lo que sí importa es que sweep-flag sea el mismo en los dos, para que el recorrido sea coherente.
Esto tiene una consecuencia directa en gráficos de tarta: un sector que abarca los 360 grados completos no se puede dibujar con la misma función que los demás. Si una categoría representa el cien por cien, la fórmula genérica produce inicio igual a fin y el sector desaparece. Hay que detectar ese caso y emitir un circle, o dos arcos. Es un bug clásico que solo aparece cuando alguien filtra los datos hasta dejar una sola categoría.
Un sector de anillo completo
Esta es la construcción que justifica todo el nivel: el trozo de donut, con radio interior y exterior, que es la unidad básica de media docena de gráficos.
// Sector de anillo. Angulos en radianes, 0 = tres en punto, creciente horario.function sectorAnillo(cx, cy, rInt, rExt, a0, a1) { const p = (r, a) => [cx + r * Math.cos(a), cy + r * Math.sin(a)]; const grande = Math.abs(a1 - a0) > Math.PI ? 1 : 0; const [x0, y0] = p(rExt, a0); const [x1, y1] = p(rExt, a1); const [x2, y2] = p(rInt, a1); const [x3, y3] = p(rInt, a0); return [ `M ${x0} ${y0}`, `A ${rExt} ${rExt} 0 ${grande} 1 ${x1} ${y1}`, `L ${x2} ${y2}`, `A ${rInt} ${rInt} 0 ${grande} 0 ${x3} ${y3}`, 'Z', ].join(' ');}// Un sector del 30 por ciento empezando arribaconst inicio = -Math.PI / 2;sectorAnillo(100, 100, 40, 70, inicio, inicio + 0.3 * 2 * Math.PI);Fíjate en los dos detalles que hacen que funcione. El sweep-flag del arco exterior es 1 y el del interior es 0, porque el recorrido de vuelta va en sentido contrario; si pones el mismo en los dos, la forma se cruza sobre sí misma y sale una mariposa. Y el large-arc-flag se calcula una vez y se usa en ambos, porque los dos arcos abarcan el mismo ángulo.