Ejemplos java y C/linux

Tutoriales

Enlaces

Licencia

Creative Commons License
Esta obra está bajo una licencia de Creative Commons.
Para reconocer la autoría debes poner el enlace http://www.chuidiang.com

Escalado de gráficos en java

Como vimos en el escalado de gráficos en C, es bastante habitual que queramos hacer un gráfico en el que los ejes x e y no son pixels. Por ejemplo, queremos dibujar una función seno, en el que la x varía de 0 a 2*PI y la y varía de -1 a 1. Nuestra pantalla va en pixels y el área gráfica sobre la que queremos dibujar, va en el eje x de 0 a 500 pixels (por ejemplo) y la y va de 0 a 300 pixels. Mira en el enlace anterior si quieres ver una explicación más detallada de este problema, aunque no hace falta que te metas con detalle con las matemáticas que aparecen allí.

La clase AffineTranform

En C no nos quedaba más remedio que echar unas cuentas. En java tenemos una facilidad adicional, que es la clase AffineTransform. Esta clase se encarga de transformar gráficos en un sistema de coordenadas en otro. En concreto, nos permite, por ejemplo, pasar unos puntos que calculemos como x y sin(x) a coordendas de pixels, de forma que nuestro gráfico de seno quede más o menos ajustado a nuestra área de dibujo.

De hecho es lo que vamos a ver en este ejemplo: cómo podemos transformar nuestra función sin(x) en coordenadas de pixels, de forma que nuestro dibujo quede bien ajustado a nuestra área gráfica.

Nuestro gráfico de seno varía la x de 0 a 2*PI y la y de -1 a 1. Para que esto sea más genérico, diremos que la x varía de xMin a xMax y que la y varía de yMin a yMax. Nuestro área de dibujo puede tener, por ejemplo, 500 pixels de ancho y 300 de alto. Vamos a decir que tiene un ancho y un alto, nuevamente por hacerlo más genérico.

Si pasas de las matemáticas, puedes ir al final y ver directamente el trozo de código que lo hace.

Si leemos la parte de escalado gráfico, veremos que hay que hacer las siguientes transformaciones:

Ya está. Con esto debemos tener nuestro gráfico perfectamente escalado dentro del área gráfica. Ahora sólo hay un pequeño problema secundario. Cuando a una AffineTransform le vamos añadiendo transformaciones, que es nuestro caso, la AffineTransform las hace en orden inverso a como se las hemos ido diciendo, es decir, hace primero la última que hemos añadido y finalmente la primera. Primero hará la translación en el eje y del alto del área gráfica, luego el escalado y finalmente la translación de xMin e yMin.

Afortunadamente tiene fácil solución, basta con escribir todo eso al revés, primero la última instrucción y como última la primera.

    El código final para calcular la AffineTransform quedaría así

AffineTransform t = new AffineTransform();
t.translate (0.0, alto);
t.scale (ancho/(xMax-xMin), - alto/(yMax-yMin));
t.translate (-xMin, -xMax);

Un último detalle a riesgo de ser pelma. Si nuestro Canvas se redimensiona, porque está en una ventana que puede estirarse o cualquier otro motivo, debemos recalcular la AffineTransform, ya que estas transformaciones dependen del alto y ancho de nuestro Canvas.

Transformar los puntos

A la hora de dibujar, primero calculamos los puntos que queremos dibujar. Para que nos resulte más fácil, metemos estos puntos en un array de Point2D.

En el código pongo 300 puntos, que debería ser algo aproximado al ancho del área gráfica, aunque podriamos hacer ancho puntos.  De esta forma tendremos los puntos avanzando de izquierda a derecha de un pixel en un pixel.

El incremento para x para obtener esos 300 puntos se hace con una cuenta que es la que aparece en el código, dividiendo el rango de x entre el número de puntos.

El código resultante sería este

// Rellenamos un array de Point2D con una funcion sin(x)
Point2D.Double puntos = new Point2D.Double (300);
// Calculo de x inicial e incremento de x para calcular la funcion sin(x)
double incremento = (xMax - xMin)/puntos.length;
double x = xMin;

//  se rellena el array
for (int i = 0; i<puntos.length; i++)
{
    puntos[i] = new Point2D.Double (x, Math.sin(x));
    x = x + incremento;
}

Ahora sólo tenemos que transformar estos puntos usando nuestra AffineTransform. Debemos primero crear un array donde vamos a obtener los puntos transformados. Luego bastará llamar al método transform() de AffineTransform

// Creación del array donde obtendremos los puntos en pixles
Point2D [] puntosTransformados = new Point2D[300];
for (int i=0; i<puntosTransformados.length; i++)
    puntosTransformados[i] = new Point2D.Double();
// Tranformación de los puntos
t.transform (puntos, 0, puntosTransformados, 0, puntos.length);

El dibujado te lo dejo a tí. Si tienes dudas con ello puedes ver el ejemplo de gráfico simple en java.

El ejemplo

Aquí tienes unos fuentes de ejemplo. No pongo el Applet porque es una tontería ver un Applet con un seno dibujado y que no hace nada. Toda la chicha está en la clase Lienzo.java, a la que puedes descargar, quitar la extensión .txt y meterla en un subdirectorio adecuado (recuerda que en java debe haber una estructura de directorios paralela a la de package, por lo que el directorio debe ser algo así como chuidiang/ejemplos/graficos/seno). También tienes el Main.java para usarlo como aplicación independiente y el AppletSeno.java por si quieres meterlo dentro de una página html.

Estadísticas y comentarios

Numero de visitas desde el 4 Feb 2007: