martes, 17 de agosto de 2010

Flash: ActionScript 3 - Error al sumar decimales

El problema
Sí, hijos, sí. Una nueva de las de Flash y para la que aún no he encontrado solución. En esta ocasión la gracia graciosa se ha dado en ActionScript 3, pero creo que más bien es un error de Flash al tratar variables flotantes (decimales).

Por todos es sabido, al menos en el universo que nos ocupa y ajustándonos a las leyes físicas y matemáticas que cercan nuestra vida cotidiana, que 2+2=4. Pero ¡ojo!, Flash se atreve a contradecir a los más sabios de la historia y, es más, hace tambalear las bases de las matemáticas actuales, porque para Flash, 2+2 PUEDE que sea 4. Es como si Flash fuera cuántico.

El error me ha surgido al tratar de ampliar un MovieClip utilizando su propiedad scaleX y scaleY. Simplemente, cada vez que pincho un botón, el MovieClip debe incrementar su scaleX y scaleY en 0.2. En ActionScript 3, las propiedades scaleX y scaleY (como también la propiedad alpha) se miden en valores decimales entre 0 y 1, a diferencia de ActionScript 2 en el que estas propiedades se miden en valores entre 0 y 100. De este modo, cada vez que pincho el botón mencionado, incremento las propiedades scaleY y scaleX en 0.2 con un simple movie.scaleX = movie.scaleY += 0.2.

En principio todo aparenta bien. Todo cambia cuando necesitas recuperar el valor de scaleX o scaleY para realizar comprobaciones porque, según Flash, 1+0.2 es igual a 1.2. Correctísimo. Pero no vayas a pensar que todo va a ir igual de bien, porque 1.2+0.2 es igual a 1.4000000001, y de ahí en adelante arrastra el error hasta el infinito y más allá. No es algo perceptible gráficamente, pero como digo entorpece las comprobaciones.

Pero Flash no se queda ahí, en su afán por contradecir el mundo se aventura más allá, poniendo a prueba nuestros conocimientos matemáticos y nuestra paciencia. Inmerso en este problema se me ocurre intentar redondear, y como Flash no dispone de ninguna función de redondeo con decimales (cosa que aún no me explico, será que mi inteligencia no llega a entender las razones puras e íntegras de Flash), me dispongo a redondear manualmente con la sencilla operación ya famosa entre los que han intentado redondear decimales en Flash:

1.400000001 * 10 = 14.00000001 (Bien!)
Math.round(14.00000001) = 14 (Bien!! Estamos cerca!!)
14/10 = 1.40000001 (FJ%·&/"$%!!!!! ¿CÓMO????)

Increíble pero cierto. Las graciosidades graciosas de Flash no conocen límites.

¿Por qué pasa esto?
Amigo mío, los caminos del señor son inescrutables, y además no deberías osar ni preguntarte por las razones de Flash. Simplemente ocurre. Es como un acto de fe.

Intuyo que el problema radica en que Flash recalcula constantemene el valor de las propiedades scaleY y scaleX en función del tamaño adquirido y el tamaño original. Por ejemplo, si tengo un MovieClip a tamaño natural (con scaleX y scaleY a 1) y decido ampliarlo un 20%, tan sólo tengo que establecer estas propiedades a 1.2. Si más tarde intento recuperar el valor de estas propiedades, creo que Flash lo que hace es medir la anchura y altura del MovieClip actualmente y dividirlas por sus correspondientes medidas iniciales. En teoría esto debería devolver el mismo resultado, es decir, 1.2, pero algo me dice que las ampliaciones en pantalla, al realizarse sobre píxeles, en ocasiones no son exactas, y esa inexactitud es la que provoca que el recálculo de Flash dé erróneo. Por ejemplo, si el MovieClip en cuestión mide 1003 píxeles, al aplicarle la ampiación del 20% su anchura sería de 1203.6 píxeles (¡¡¡ERROR!!! ¡¡¡ERROR!!! NO EXISTE EL MEDIO PÍXEL, las pantallas muestran un píxel entero o si no no lo muestran). Entonces ¿qué pasa con los 0.6 píxeles que sobran? "¡Fácil!" - dice Flash - "¡Lo redondeo para no molestar al programador con engorrosos decimales! ¡¡Qué bueno soy con mis programadores!!" En definitiva, mi MovieClip mide ahora 1204 píxeles, y cuando quiero recuperar el valor de scaleX o scaleY Flash divide sus 1204 entre los 1003 originales, y es ahí cuando se presenta el ejército de decimales.

Todo esto son sólo SUPOSICIONES mías. Un intento de explicar los fenómenos que ocurren en Flash, como un astrónomo intenta saber qué pasa dentro de un agujero negro.

He de aclarar que el intento de redondeo puesto arriba no lo he hecho con valores fijos como en el ejemplo, si no que donde puse 1.40000001 en realidad está la propiedad movie.scaleX. Más concretamente el redondeo lo hacía así:
movie.scaleX = movie.scaleY += 0.2; (En este punto scaleX y scaleY valen 1.400000001)
movie.scaleX = movie.scaleY = Math.round(movie.scaleX * 10) / 10;

¿Cómo se soluciona?
Quien lo sepa que me lo diga, por favor. Yo he tenido que acabar recurriendo a otras variables declarados a mano por mí para hacer las comprobaciones que necesito.

Agradecimientos
Siempre al todopoderoso Flash, por enseñarnos una vez más los misterios que lo componen, por mostrarnos que en lo más hondo de su núcleo el espacio-tiempo se comprime y el tiempo y las matemáticas se dilatan.