Saltar al contenido

Precisión decimal y el peso real

Cuántos decimales necesita de verdad una coordenada, cómo se calcula el error visible en píxeles, por qué esta optimización sí sobrevive a la compresión, y dónde no se puede recortar.

Cuántos decimales hacen falta

La cuenta es directa. Si el viewBox tiene V unidades de ancho y el gráfico se presenta con P píxeles de ancho, cada unidad vale P / V píxeles. Un error de una unidad en la última cifra decimal se convierte en:

Texto
error en pixeles = (P / V) * 10^(-decimales)

Con un icono de viewBox="0 0 24 24" presentado a 24 píxeles, cada unidad vale 1 píxel. Un decimal da un error de 0,1 píxeles; dos, de 0,01. Un decimal es suficiente para ese icono, y el segundo ya está por debajo de lo que un antialiasing de 8 bits distingue.

Con el mismo icono presentado a 96 píxeles (un icono de aplicación), cada unidad vale 4 píxeles y un decimal da 0,4 píxeles de error: visible en un borde. Ahí hacen falta dos.

Con un mapa de viewBox="0 0 1000 800" presentado a 800 píxeles, cada unidad vale 0,8 píxeles. Cero decimales dan un error de 0,8 píxeles, que se ve; un decimal da 0,08, que no. Un mapa con seis decimales por coordenada está desperdiciando cinco.

La regla práctica, expresada al revés:

Texto
decimales necesarios ≈ ceil( log10( P / V ) ) + 1

Y con un margen para el zoom del navegador: suma uno más si el gráfico se puede ampliar.

En la mayoría de los casos reales la respuesta es dos decimales, y tres si hay arcos o el gráfico es ampliable. Seis, que es lo que producen muchas herramientas, no lo justifica nada.

El ahorro

Una coordenada con seis decimales ocupa unos 10 caracteres (412.483921). Con dos, unos 6 (412.48). Con la parte entera pequeña y el punto, la diferencia media ronda el 35 o el 40 por ciento de la longitud de la cadena d.

En un icono de línea, la d es la mayor parte del fichero. En un mapa o una ilustración compleja, es prácticamente todo el fichero. Ahí una reducción de precisión del 40 por ciento en la d es una reducción del 35 por ciento del fichero.

Y a diferencia del resto de optimizaciones, esa reducción llega al usuario. Los dígitos de un decimal son, desde el punto de vista de un compresor, información de alta entropía: no se repiten, no siguen un patrón, no se predicen. Brotli no puede hacer nada con ellos. Eliminarlos elimina bytes reales de la transferencia.

Es la razón de que en un fichero de mapa, la precisión sea la única palanca que importa. Todo lo demás (indentación, atributos, nombres) comprime a nada.

Cómo se recorta

En SVGO, con el parámetro de precisión de los plugins numéricos:

JS
export default {  plugins: [    {      name: 'preset-default',      params: {        overrides: {          removeViewBox: false,          cleanupNumericValues: { floatPrecision: 2 },          convertPathData: {            floatPrecision: 2,            transformPrecision: 4,          },        },      },    },  ],};

Dos parámetros distintos y conviene entender la diferencia.

floatPrecision es la precisión de las coordenadas finales. Es el que decide el error visible.

transformPrecision es la precisión de los coeficientes de las matrices de transformación cuando SVGO las aplica a la geometría. Ese número tiene que ser mayor, porque un error en un coeficiente de matriz se multiplica por la coordenada: un error de 0,01 en un factor de escala aplicado a una coordenada de 800 produce un error de 8 unidades. Bajar transformPrecision es la forma más rápida de destrozar un dibujo grande.

Regla: transformPrecision al menos dos unidades por encima de floatPrecision.

Si generas la d desde tu propio código, el redondeo es tuyo:

JS
const r = (n, d = 2) => Number(n.toFixed(d));function ruta(puntos) {  return puntos    .map((p, i) => `${i ? 'L' : 'M'}${r(p.x)} ${r(p.y)}`)    .join('');}

Nótese el Number(): toFixed devuelve una cadena con ceros de relleno (12.50), y convertir a número los elimina. Ese cero final es un byte por coordenada que se suma.

Los tres sitios donde no se puede recortar

Uno: los coeficientes de una matriz de transformación. Ya explicado. El error se multiplica.

Dos: los radios de un arco elíptico. Como se vio en el nivel 17, cuando los extremos de un arco están casi a la distancia máxima que permiten los radios, un redondeo puede empujarlos por encima y disparar la corrección automática de la especificación, que escala los radios. El resultado es un arco ligeramente distinto de sus vecinos, visible como una irregularidad en un gráfico circular. Tres decimales como mínimo en geometría con arcos.

Tres: los valores de stdDeviation, baseFrequency y scale en los filtros. Un baseFrequency="0.008" redondeado a dos decimales se convierte en 0.01, un cambio del 25 por ciento que altera visiblemente el ruido. Los valores pequeños son especialmente sensibles al redondeo absoluto, porque el error relativo se dispara. SVGO no toca los atributos de filtro por defecto; si configuras un recorte global, exclúyelos.