Archive

Archive for the ‘Tutorials’ Category

Keep screen aspect ratio with different resolutions using libGDX

February 5, 2012 7 comments

There’s something in that “Screen Resolution” game menu that possesses me. I’ve always fancied making a game with different screen resolutions, but the task is far from trivial. These notes are the result of a weekend spent looking for the solution (with help from the JGO community). You can download the source code from here.

Problem

Imagine you are developing a game and start supporting the 480×320 resolution because it fits nice in your smartphone. You align the menus, place the sprites, and do some nasty hacks (that we all have done sometimes) to make your game look pretty. In the end, you have a game that has been developed, literally, for your own phone! (or phone screen resolution). It will look distorted in other phones with different screen resolutions :-(

What do you want to is to support multiple screen resolutions without hardcoding all the layout for every single screen resolution that exists (there are lots of them).

(My) Solution

The solution I’ve found it’s not TEH solution, but it works good enough for me.

I’m working with libGDX. This library has a OrthographicCamera class (doc, code) that fits nice for 2D games. This class is responsible to 1) define the volume of the game scene (which in OpenGL argot is called frustum) and 2) to project it orthographically into a plane: the scene image. In addition, libGDX also provides a wrapper to the OpenGL function glViewport(), which transforms the scene image obtained with the camera class into the device screen.

The plan is the following:

  1. Define a virtual resolution to work with (align menus, place sprites, etc.).
  2. Set the camera to use the virtual resolution.
  3. Use glViewport() to adjust our scene image to the physical resolution of the device screen (keeping the aspect ratio of course).

To define the virtual resolution, it is fine to define static final fields in your AplicationListener game class (I’m using libGDX argot). The camera, a Rectangle defining our viewport, and the SpriteBatch, which all of them we will be using later, are also (non-static) fields of the class.

public class MyAwesomeGame implements ApplicationListener
{
    private static final int VIRTUAL_WIDTH = 480;
    private static final int VIRTUAL_HEIGHT = 320;
    private static final float ASPECT_RATIO = (float)VIRTUAL_WIDTH/(float)VIRTUAL_HEIGHT;

    private Camera camera;
    private Rectangle viewport;
    private SpriteBatch sb;

When our game starts, it will first execute the method create() and then resize(int, int) with the width and height of the window as input parameters. In create() we should initialize all the fields required further. In particular, we will initialize the camera and the SpriteBatch (canvas of each frame).

    @Override
    public void create()
    {
        sb = new SpriteBatch();
        camera = new OrthographicCamera(VIRTUAL_WIDTH, VIRTUAL_HEIGHT);
    }

In resize() we should setup the Rectangle that we will be using later to set the viewport. And here it is the trick. Let’s see this function slowly. First we declare and initialize some local variables.

    @Override
    public void resize(int width, int height)
    {
        // calculate new viewport
        float aspectRatio = (float)width/(float)height;
        float scale = 1f;
        Vector2 crop = new Vector2(0f, 0f);

They are quite intuitive, for instance, aspectRatio holds the ratio width/height of the device screen (physical resolution), scale is the factor to which scale our scene image, and crop (do not confuse with crap) is the amount of pixels to be cropped from the viewport in order to keep the aspect ratio of the scene image.

Now, if aspectRatio is greater than the virtual aspect ratio it is because the physical resolution is wider (proportionally) than the virtual resolution. Therefore, we should match the height of both resolutions (virtual and physical) and crop in the X direction since our virtual scene image wont fill the whole screen. Conversely, if aspectRatio is lesser than ASPECT_RATIO then we should match the width of both resolutions and crop in the Y direction.

        if(aspectRatio > ASPECT_RATIO)
        {
            scale = (float)height/(float)VIRTUAL_HEIGHT;
            crop.x = (width - VIRTUAL_WIDTH*scale)/2f;
        }
        else if(aspectRatio < ASPECT_RATIO)
        {
            scale = (float)width/(float)VIRTUAL_WIDTH;
            crop.y = (height - VIRTUAL_HEIGHT*scale)/2f;
        }
        else
        {
            scale = (float)width/(float)VIRTUAL_WIDTH;
        }

        float w = (float)VIRTUAL_WIDTH*scale;
        float h = (float)VIRTUAL_HEIGHT*scale;
        viewport = new Rectangle(crop.x, crop.y, w, h);
    }

Finally, we just have to modify the render() method (which is used to render our scene, of course) to update the camera, set the viewport, and draw our objects/entities.

    @Override
    public void render()
    {
        // update camera
        camera.update();
        camera.apply(Gdx.gl10);

        // set viewport
        Gdx.gl.glViewport((int) viewport.x, (int) viewport.y,
                          (int) viewport.width, (int) viewport.height);

        // clear previous frame
        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

        // DRAW EVERYTHING
    }

And that’s it. Let’s see it in action.

Some images

To illustrate this tips I’m rendering a scene that consists on two rectangles. One green that fills all the scene (just to know where exactly our scene image is), and one square red just to detect visually aspect ratio violations. We use 480×320 as our virtual resolution, as our smartphone uses it natively. Therefore, in our phone we should see everything and without distortion, just as this screenshot I just took:

Now imagine I send this awesome game to my friend @notch (any similarity with real characters/persons is fictional) which is really rich and has a smartphone with greater resolution. He will see this flawed game:

Notice that the square has been distorted into another rectangle (non-squared). My friend is loosing part of the feeling of my game! And most important, the artist that is making such awesome graphics is really pissed off…

Using the method of this tutorial he will just get the right game:

Ok, it is true. He’s not using his whole smartphone screen (btw, who told him to spend that much money in a fancy new smartphone in the first place) but at least the aspect ratio is correct and the game graphics artist is happy again.

Further approximation to perfection

I have discovered nothing new, but at least I won’t doubt again how to perform this tedious but mandatory task. You must know that there are, for sure, better approaches to solve the resolution problem. For instance, I just came up with the idea of having two/three versions of the game with different aspects ratios (say 4:3, 16:9, and 16:10). Then, you viewport the layout corresponding to the aspect ratio that is closer to the physical aspect ratio, and hence, minimizing the ugly black bands.

If you have any comment/suggestion/praise/curse, do not hesitate to leave a comment here or say something in Twitter.

Compilar Assimp 2.0 en VC++ 2010 Express

July 19, 2011 1 comment

Assimp (a.k.a. Open Asset Import Library) son unas librerías multiplataforma y de código abierto para importar modelos 3D de diferentes formatos (MD2, MD5, COLLADA, etc.). Compilar estas librerías usando VC++ 2010 Express puede ser muy complicado si no se siguen unos pasos concretos.

Satisfacer las dependencias: Boost y DirectX SDK

La compilación de Assimp depende de las librerías Boost. Además, el visor de modelos Assimp View necesita las librerías de DirectX d3d9.lib y d3dx9.lib.

Boost

Boost se puede descargar desde su página principal de descargas (aquí). Simplemente habrá que descomprimir y desempaquetar la versión más reciente (a día de hoy la versión 1.47.0) en un directorio conocido. En nuestro caso utilizaremos

C:\dev\boost\1.47.0

y nos refereriremos a esa ruta en el resto de este tutorial como $(BOOST_PATH).

DirectX SDK

DirectX son unas APIs (application programming interface) de Microsoft especialmente preparadas para crear juegos. El visor de Assimp View está construido utilizando dichas librerías, y por tanto, tenemos que tenerlas instaladas en el ordendor para poder compilarlo.

Para ello simplemente hay que descargarse el SDK (software development kit) más moderno de su página de descargas (aquí) e instalarlo haciendo doble clic. En nuestro caso es la versión de Junio 2010 y la instalamos en la ruta

C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)

que a partir de ahora llamaremos $(DIRECTX_PATH).

Descargar del código fuente

El código fuente de Assimp se puede descargar de su página principal de descargas (aquí). A día de hoy, la que nos interesa es de assimp 2.0 (de noviembre de 2010), el fichero “Any platform: ZIP archive, full source code” (no pongo enlace porque no puedo garantizar su persistencia).

Descomprimir y desempaquetar el código fuente

Hay que descomprimir y desempaquetar el fichero descargado en un directorio conocido. En este caso utilizaremos

C:\dev\assimp\2.0.863

A partir de ahora nos refereriremos a esta ruta como $(ASSIMP_PATH). Si utilizas una ruta diferente, asegúrate de cambiar todas las demás rutas que dependan de ésta. Como comprobación adicional deberías tener los archivos INSTALL, LICENSE y README en el directorio $(ASSIMP_PATH).

Modificar los proyectos de VC9

De momento Assimp soporta ser compilado por VC9 a través de una solución (.sln) y unos proyectos (.vcproj) incluidos en el directorio $(ASSIMP_PATH)\workspaces\vc9. La forma de compilarlo con VC10 es convertir la solución para VC9.

Sin embargo, cuando se hace esto directamente aparece un problema con la arquitectura x64, concretamente el mensaje de error “Failed to upgrade platform ‘x64′. Please make sure you have it installed under ‘%vctargetspath%\platforms\x64′“. Probablemente esto se deba a que la versión de MVC++ 2010 Express, que es gratuita, no permite compilar para la arquitectura x64.

Para solucionarlo hay que modifcar a mano, con un editor externo (por ejemplo el excelente Notepad++), el archivo de solución y los de proyectos.

Este paso es muy tedioso si no sabes automatizarlo (yo no sé). Así que os dejo los ficheros de la versión VC9 sin los bloques de “x64″ para que os podáis saltar este paso. Simplemente reeemplaza los archivos de tu ordenador con los descargados.

En caso de que seáis valientes, seguid los siguientes pasos:

Archivo de solución assimp.sln

Hay que buscar la palabra “x64″ y eliminar todas las líneas donde aparezcan en el archivo assimp.sln. En total hay 144 ocurrencias. Las hay de dos tipos:

debug|x64 = debug|x64
{B17B959B-BB8A-4596-AF0F-A8C8DBBC3C5E}.debug|x64.ActiveCfg = debug|x64

Con eso habremos terminado con este fichero.

Archivos de proyectos

De forma similar hay que encontrar los bloques que contengan la palabra “x64″ de los archivos de proyecto (assimp.vcproj, assimp_cmd.vcproj, assimp_view.vcproj y UnitTest.vcproj). Hay un total de 171 ocurrencias. Los hay de varios tipos. En el principio siempre hay un bloque con la forma:

<Platform
 Name="x64"
 />

Otros más largos son de la forma:

<Configuration
    Name="debug|x64"
    OutputDirectory="./../../bin/$(ProjectName)_$(ConfigurationName)_$(PlatformName)"
    IntermediateDirectory="./../../obj/$(ProjectName)_$(ConfigurationName)_$(PlatformName)"
    ConfigurationType="1"
    InheritedPropertySheets=".\shared\UnitTest.vsprops"
    CharacterSet="1"
    >
    <Tool
        Name="VCPreBuildEventTool"
    />
    // ETC ...
</Configuration>

Por último hay otro tipo:

<FileConfiguration
    Name="debug|x64"
    >
    <Tool
        Name="VCCLCompilerTool"
        UsePrecompiledHeader="0"
    />
</FileConfiguration>

Una vez quitados todos los bloques que incluyan la palabra “x64″ ya estamos listos para importar el proyecto a VC10.

Convertir el proyecto a VC10

Ahora hay que ejecutar VC++ 2010. Al pinchar en el menú “Archivo”->”Abrir”->”Proyecto o solución”, aparecerá un diálogo donde hay que seleccionar la solución (.sln) que se encuentra en $(ASSIMP_PATH)\workspaces\vc9\ (la que acabamos de modificar). Se abrirá un “Asistente para conversión de Visual Studio”. Pinchando en siguiente, siguiente y finalizar, abriremos una solución con cuatro proyectos: assimp, assimpcmd, assimpview y unit.

En caso de que este paso no te funcione por alguna razón, dejo colgados los ficheros de la solución y los proyectos para VC10.

Modificar el código (pequeño parche)

Por alguna razón, VC10 incluye el archivo de cabecera stdint.h desde una cabecera estándar. Este archivo es imcompatible con pstdint.h que se proporciona con Assimp. Por lo tanto hay que asegurarse de que Assimp no incluye pstdint.h o saltarán las alarmas.

Leyendo los foros de Assimp (aquí) se propuso el siguiente parche: en la línea 59 del fichero AssimpPCH.h cambiar

#ifdef _MSC_VER

por

#if defined(_MSC_VER) && (_MSC_VER < 1600)

Con eso ya podemos compilar Assimp con la seguridad de que pstdint.h no se incluirá cuando usemos VC10.

Configurar los proyectos

El último paso es configurar los proyectos para que encuentren las rutas de las dependencias (Boost y DirectX) y para solucionar un error con el fichero de salida.

Para incluir las rutas adecuadas hay que abrir las propiedades de cada uno de los cuatro proyectos. Hay que elegir en la parte superior izquierda “Todas las conf.” y después, en el árbol de configuraciones, hay que pinchar en el elemento “Propiedades de configuración”->”Directorios de VC++”. Ahí hay que cambiar las propiades “Directorios de archivos de inclusión” y “Directorio de archivos de bibliotecas” para que incluyan las cabeceras y librerías de Boost y DirectX.

Los directorios de inclusión en nuestro caso son:

$(BOOST_PATH)
$(DIRECTX_PATH)\Include

Mientras que los de librerías son:

$(BOOST_PATH)\lib
$(DIRECTX_PATH)\Lib

Recuerda cambiar cada variable $(*_PATH) por el directorio que corresponda en cada caso.

Esto es suficiente para que encuentre todas las dependencias de Boost y DirectX. Sin embargo, el nombre de los proyectos no coincide con el nombre que el enlazador espera a la salida, por lo que falla al generar las librerías compartidas (los .DLL) y las estáticas (las .LIB).

Para solucionar este problema hay que abrir las propiedades del proyecto assimp y para la configuración “debug-dll” (parte superior izquierda) hay que pinchar en la configuración “Propiedades de configuración”->”General”. La entrada “Nombre de destino” debe cambiarse a “Assimp32d” (sin las comillas y ojo con la ‘d’ final). La misma entrada para la configuración “release-dll” debe cambiarse por “Assimp32″ (ojo con la ‘d’ del final).

Y con eso ya está listo todo para compilarse. Las librerías de “debug-dll”, “release-dll”, “debug-st” y “release-st” aparecerán el el directorio $(ASSIMP_PATH)\lib.

Como prueba de que todo ha salido bien puedes intentar compilar Assimp View y cargar un modelo. Por ejemplo, puedes cargar $(ASSIMP_PATH)\test\models\Collada\duck.dae y obtener algo parecido a esto :-)

Categories: Tutorials Tags: , ,

Ejecutar aplicaciones a través de SSH

March 8, 2011 Leave a comment

ssh!¿Te ha pasado alguna vez el tener que ejecutar una aplicación que se encuentra en el ordenador del trabajo?

Con un poco de suerte, te basta con abrir una terminal, hacer una conexión SSH, ejecutar la aplicación y esperar el resultado. Sin embargo, en otras ocasiones necesitas que el programa se siga ejecutando cuando te desconectes del ordenador, o, peor aún, que quieras manipular la interfaz gráfica de la aplicación.

Como estoy harto de buscar en Google cómo hacer cada cosa, me voy a hacer una pequeña receta con los tres casos, a ver si te sirve a ti también.

Ejecutar aplicación en consola

En este caso lo que se quiere hacer es:

  1. Conectarse al ordenador remoto vía SSH.
  2. Ejecutar una aplicación.
  3. Esperar que finalice la aplicación.
  4. Desconectarse del ordenador remoto.

Esto es bastante sencillo y buscando por Internet (y leyendo las páginas del manual del comando “ssh”) es fácil encontrar la solución.

Para conectarte al ordenador remoto basta con hacer una llamada del estilo:

localhost$ ssh usuario@servidor.es

donde ‘usuario’ es el nombre de usuario que quieres utilizar en el ordenador identificado por su nombre de dominio ‘servidor.es’. Después de introducir tu password cuando te lo pida, te mostrará un terminal en el ordenador remoto. Ahí podrás ejecutar la aplicación que desees:

servidor.es$ uname -srv
Linux 2.6.37-ARCH #1 SMP PREEMPT Fri Feb 25 07:53:43 CET 2011

Una vez terminada la ejecución del programa podemos introducir ‘exit’ o pulsar control+D para salir.

Ejecutar una aplicación persistente en consola

Este caso es muy parecido al anterior. Sólo se diferencia en que ahora no existe el paso 3., es decir, queremos que la aplicación se siga ejecutando después de habernos desconectado del ordenador remoto. Para ello utilizamos el comando POSIX “nohup” en el ordenador remoto.

servidor.es$ nohup make &
servidor.es$ ^D

Eso nos garantiza que la aplicación se seguirá ejecutando aún cuando le llegue la señal hangup (HUP), que indica que el usuario que ejecutó la orden se ha desconectado de la máquina. Este comando es especialmente útil para compartir ficheros por redes P2P (con un programa torrent por consola) o para hacer grandes actualizaciones remotamente.

Ejecutar una aplicación X

El último caso es cuando queremos ejecutar un comando en el ordenador remoto que cree una ventana en nuestro ordenador local. Por tanto, tendremos que iniciar una sesión X en el ordenador local, lo que en la mayoría de casos significa que debes haber iniciado sesión en Gnome, KDE, PekWM o el entorno de escritorio que utilices.

Los pasos a seguir son muy similares. Primero habrá que conectarse por SSH como en el primer caso, pero añadiendo la opción ‘-X’:

localhost$ ssh -X usuario@servidor.es

El resultado será el mismo, tendremos una terminal remota en el servidor ‘servidor.es’. Todo igual, salvo que al ejecutar una aplicación gráfica todas las llamadas al servidor X se hacen a la máquina local (donde estás tú) no a la máquina remota (donde ejecutas la aplicación).

Por ejemplo, si ejecuto la aplicación ‘kcalc’ (la calculadora del escritorio KDE) en mi máquina wigner (ordenador del trabajo):

[alejandro@wigner ~]$ kcalc

en maxwell (mi ordenador personal) aparece la siguiente ventanita:

Kcalc @ wignerLas tres recetas las he obtenido de diferentes sitios en Internet que ya no recuerdo. El último caso lo aprendí aquí, en un tutorial de Van Emery.

Categories: Tutorials Tags: , , , ,

Ecuaciones en tus presentaciones

November 14, 2010 Leave a comment

Foto tomada por tkamenick.

A veces es imprescindible incorporar expresiones matemáticas en tus presentaciones hechas con Powerpoint, Keynote, OpenOffice Impress o Google Presentations. Pero no queda claro qué programa usar para hacerlo.

Por un lado, el editor de ecuaciones de MathType es muy útil y es una solución a tener en cuenta cuando trabajemos con Powerpoint. Sin embargo, las ecuaciones generadas con él no pueden ser exportadas a otros programas (al menos que yo sepa).

Por otro lado, para Mac existe un software llamado LaTeXiT que usé durante mucho tiempo y que funciona muy bien. Lo que hace (por detrás de la interfaz gráfica) es básicamente dos cosas: 1) crea un archivo TeX mínimo a partir del cual se imprime la ecuación en DVI y 2) exporta este DVI a un formato que sea soportado por el programa de presentaciones (en mi caso me conformo con PNG). Si usas Mac lo tienes hecho. Si usas GNU/Linux no (de nuevo que yo sepa). Así que lo que propongo es un script que hace a manubrio lo que hace LaTeXiT de forma bonita.

Lo primero es tener una plantilla de LaTeX que sea mínima. Como sólo vamos a crear ecuaciones, incluiremos en el los paquete necesarios para algunos símbolos (de la AMS).

\documentclass{article}
\usepackage{amsmath,amsthm,amssymb}
\pagestyle{empty}

\begin{document}
\begin{equation*}
	% Aquí va la ecuación que queremos incorporar a la presentación
\end{equation*}
\end{document}

Por otro lado, tendremos que tener el siguiente script que convierte a cada ecuación en una imagen PNG:

#!/bin/bash

ELATEX=/usr/bin/latex
EDVIPS=/usr/bin/dvips
ECONVERT=/usr/bin/convert

RES=600

$ELATEX $1.tex
$EDVIPS $1 -o
$ECONVERT -units PixelsPerInch -density $RES -trim -transparent \#000080 \
          -type PaletteMatte +repage $1.ps $1.png

rm $1.dvi $1.ps

Del script, lo único que hay que tocar son las rutas a los ejecutables latex, dvips y convert (latex y dvips suelen venir en tu distribución de LaTeX preferida, y convert es una utilidad de ImageMagick) y la resolución de la imagen generada, RES. Para ejecutarlo hay que guardarlo (yo elegí el nombre latex2png) y para usarlo sobre un archivo llamado prueba.tex hay que usar `latex2png prueba` sin las comillas simples.

Con el anterior script la forma de trabajar que a mí me funciona es un editor de texto abierto para cambiar la fórmula, una consola para ejecutar el script y un navegador de archivos (dolphin de KDE) para arrastrar el PNG a la presentación. En caso de que uses Emacs puedes ahorrarte la consola si defines un macro para ejecutar el script sobre el archivo que estás editando. Y si eres un master-programming, ¿por qué no te curras una interfaz gráfica para el anterior script al estilo LaTeXiT?

Para muestra, un botón. Este es el resultado de la ecuación:

\mathfrak{F}^\alpha\left[f\right]\left(\mathbf{p}\right)
= \iint_{\mathbb{R}^2}\mathrm{d}\mathbf{r}\ f\left(\mathbf{r}\right)K^\alpha
\left(\mathbf{r},\mathbf{p}\right)

Categories: Tutorials Tags: , , ,

Error típico al cambiar la variable PS1

September 20, 2010 Leave a comment

Después de un rato buscando por Internet he conseguido arreglar este error bobo que ocurre al cambiar el prompt (¿cómo lo traduces?) de tu terminal de GNU/Linux.

Os pongo en situación. En mi archivo ~/.bashrc exporto la variable PS1 para modificar la “moñiga” que aparece en la línea de comandos y que indica dónde puedo empezar a introducir órdenes. Hasta ahí en muchos tutoriales te explican de forma muy clara lo que hay que hacer y qué diferentes opciones tienes (ejemplo). Lo que se me pasó (por leer en diagonal) es que los caracteres de escape (que no se verán en la terminal) también cuentan en ciertas ocasiones. Por ejemplo, utilizó el PS1:

export PS1='\e[32;1m\w\e[0m$ '

que básicamente te indica el directorio actual en verde negrita:

~/Documentos$ 

cuando vuelvo para atrás en el historial de bash y escribo un comando que supera los 17 caracteres de la variable PS1, el prompt se va al carajo. Si fuera a ejecutar el comando 'apt-get install power-to-me' y luego el comando 'nano ~/.me' en la terminal vería:

~$ apt-get install power-to-me
~$ apt-get instalnano ~/.me

Fíjate que los 17 primeros caracteres pertenecen al comando anterior y el resto es el comando de nano.

Esto ocurre porque para calcular la distancia del prompt cuenta todos los caracteres de la variable PS1, incluidos los caracteres de escape de colores. La solución estaba todo el tiempo delante de mis narices, porque en todos los tutoriales sobre la modificación del PS1 te indican cómo hacerlo. Tan simple como utilizar las secuencias de escape especiales '\[' y '\]' (que indican que no hay que contar los caracteres que encierran) alrededor de los caracteres de cambio de color. Así, el prompt quedaría:

export PS1='\[\e[32;1m\]\w\[\e[0m\]$ '

y tan amigos todos.

Categories: Tutorials Tags: , , , ,

Mostrar figuras 2D en Matlab

September 20, 2010 Leave a comment

Para muchas aplicaciones es necesario mostrar el resultado de una matriz en Matlab. Esta entrada no es más que un recordatorio/chuleta para cuando se me olvide cómo hacerlo, aunque puede que sea útil para otras personas.

Lo primero es crear la figura donde se añadirán los ejes, títulos, subfiguras, etc. Para ello basta con utilizar el comando:

figure('color', 'white');

El argumento de color y white hacen que el fondo de la figura sea blanco, lo cual mejora el aspecto de la misma (en mi opinión).

Sobre la recién creada figura mostraremos la representación gráfica de la matriz img. El comando imagesc() es todo lo que necesitamos, y su funcionamiento es:

imagesc(img);

Esto mostrará la imagen dentro de unos ejes que van de 1 a Nr para las filas y de 1 a Nc para las columnas. Si tuviéramos un vector con los valores físicos de cada eje podríamos decirle a Matlab que los utilizara añadiéndolos a la llamada de imagesc():

imagesc(x, y, img);

Es importante asegurarse que x e y tienen exactamente Nc y Nr elementos. De tener menos elementos Matlab te avisaría con un error, y de tener más Matlab los ajustaría de forma que sólo salieran los Nc (o Nr) centrados, lo cual es un comportamiento inesperado e indeseado.

De querer ver la escala de valores de la imagen (la traducción de color a valor numérico) sólo habría que ejecutar después el comando

colorbar;

Por defecto establece unos colores de forma que el valor más bajo es de color azul oscuro y el más alto es rojo oscuro granate: el colormap ‘Jet’. A mí este me parece el mejor para representación de matrices que van de un valor máximo a uno mínimo. Sin embargo, para representaciones de matrices que tienen valores cíclicos esto no es así. Para cambiarlo sólo hay que ver las opciones de mapas de colores que tenemos (ejecutando help colormap o visitando la página web del manual) y una vez elegido aplicarlo

colormap('Jet');

Otra de las cosas importantes que hay que tener en cuenta es si los elementos de la matriz tienen la misma altura que anchura. Por ejemplo, imagina una matriz que representa una distribución de probabilidad con ejes x e y con el mismo paso. En dicho caso se representaría de forma de que cada eje se ajuste al tamaño de la figura, sin conservar la proporción entre lados.  Esto es indeseado cuando se quiere saber “a ojo” si una distribución de valores tiene simetría polar o del estilo. Para forzar a la imagen a que mantenga la proporción entre sus lados hay que usar el comando

axis image;

Además, por lo general Matlab considera que en el eje Y, cuanto menor sea el valor más alto debe aparecer, lo que provoca que la imagen esté volteada verticalmente con respecto a lo que estamos acostumbrados a ver. Para solucionarlo podemos forzar a Matlab a que ponga los valores más altos del eje y arriba con

axis xy;

Y si queremos hacer las dos cosas, que mantenga la proporción de lados y que voltee verticalmente la imagen, podemos juntarlos en un mismo comando:

axis image xy;

Como muestra, esta figura de una distribución gaussiana simétrica pero desplazada 0,3 unidades en la dirección positiva del eje y.

Figura de ejemplo 1

La figura se ha generado (incluyendo la distribución gaussiana desplazada) con el siguiente código:

% Creación de la señal
N = 1024;
w = 0.2;
x = -1:1/(N/2-1/2):1;
[xx yy] = meshgrid(x);
img = exp(-(xx.^2+ (yy-0.3).^2)/w);

% Creación de la figura
figure('color', 'white');
imagesc(x, x, img);
axis image xy;
colorbar;
colormap('Hot');

Es importante darse cuenta que aunque esta figura tiene ya bastantes detalles de estilo, sigue siendo inaceptable para una publicación en un medio serio. Por ejemplo, el que el eje Y tenga diferente intervalo entre las etiquetas que el eje X aún teniendo el mismo rango es un fallo. Lo mismo ocurre con la barra de color: es evidente que va de 0 a 1, pero no aparece ni el uno ni el otro.

Para solucionar estos problemas tenemos que usar varios comandos que sirven para indicar en qué posiciones de los ejes X, Y y de color hay que poner etiquetas y qué etiquetas poner en cada sitio.

El comando ‘set’ permite modificar las propiedades de ciertos objetos relacionados con la figura. El primer argumento es el identificador del objeto y el resto son pares de valores, siendo el primero una cadena de caracteres que identifica a la propiedad y el segundo el valor nuevo de dicha propiedad. En concreto nosotros estaremos interesados en las propiedades ‘XTick’, ‘XTickLabel’, ‘YTick’ e ‘YTickLabel’ en los objetos de ejes (axes) y de la barra de color (colorbar).

Con las propiedades ‘?Tick’ y ‘?TickLabel’ podremos definir dónde poner las etiquetas y qué etiquetas poner en cada uno de los ejes, respectivamente. Así por ejemplo, si queremos poner cinco etiquetas en los ejes X e Y que vayan de -1 a 1, los comandos que habría que introducir son:

set(gca, 'XTick', [-1 -0.5 0.0 0.5 1], ...
         'XTickLabel', {'-1.0', '-0.5', '0.0', '0.5', '1.0'});
set(gca, 'YTick', [-1 -0.5 0.0 0.5 1], ...
         'YTickLabel', {'-1.0', '-0.5', '0.0', '0.5', '1.0'});

donde ‘gca’ es una función que devuelve el identificador del objeto axes que actualmente se está utilizando.

Para el caso de la barra de colores primero tenemos que conseguir el identificador. Esto es sencillo ya que la función ‘colorbar’ te lo devuelve como argumento de salida. Así que los comandos para modificar la barra de color son:

cbh = colorbar;
set(cbh, 'YTick', [0.001 0.25 0.5 0.75 0.999], ...
         'YTickLabel', {'0', '0.25', '0.5', '0.75', '1'});

Es importante darse cuenta que en el colorbar he puesto 0.001 y 0.999 como el primer y último valor de la propiedad ‘YTick’. Esto es un truco porque muchas veces el colobar no llega exactamente a 1 sino que se queda en un valor muy próximo pero menor que 1. En dicho caso la etiqueta no aparece puesto que está fuera del rango que estamos mirando. Esto, con ciertas modificaciones en cada caso, permite evitar este efecto indeseado.

Al final el resultado es la siguiente figura:

Figura de ejemplo 2

Esto ya tiene mucha mejor pinta. El último retoque que le falta es poner títulos a los ejes y a la figura. Para ello utilizaremos los comandos ‘xlabel’, ‘ylabel’ y ‘title’. Son autoexplicativos, así que directamente pongo la figura y el código fuente utilizado para su creación.

Figura de ejemplo final

% Creación de la señal
N = 1024;
w = 0.2;
x = -1:1/(N/2-1/2):1;
[xx yy] = meshgrid(x);
img = exp(-(xx.^2+ (yy-0.3).^2)/w);

% Creación de la figura
figure('color', 'white');
imagesc(x, x, img);
axis image xy;
cbh = colorbar;
colormap('Hot');
set(gca, 'XTick', [-1 -0.5 0.0 0.5 1], ...
         'XTickLabel', {'-1.0', '-0.5', '0.0', '0.5', '1.0'});
set(gca, 'YTick', [-1 -0.5 0.0 0.5 1], ...
         'YTickLabel', {'-1.0', '-0.5', '0.0', '0.5', '1.0'});
set(cbh, 'YTick', [0.001 0.25 0.5 0.75 0.999], ...
         'YTickLabel', {'0', '0.25', '0.5', '0.75', '1'});
title('Distribución gaussiana desplazada');
xlabel('Posición (mm)');
ylabel('Posición (mm)');
Categories: Tutorials Tags: , ,

Máquina virtual usando KVM en Debian «Squeeze» para 64 bits

September 17, 2010 Leave a comment

Logotipo de KVM

Desde hace tiempo utilizo software de virtualización de máquinas para acceder a esos programas que son necesarios y en GNU/Linux no existe una alternativa de suficiente calidad. Básicamente lo uso para dos cosas: 1) Adobe Illustrator porque las alternativas libres (Inkscape y compañía) no funcionan tan bien y para la publicación en revistas es necesario que las figuras sean de la máxima calidad, y 2) Microsoft Office para cuando me mandad algún archivo .DOCX y similares. Para el resto de aplicaciones me basta y me sobra con GNU/Linux.

Hasta ahora he utilizado el VirtualBox de Oracle para la virtualización. Sin embargo, recientemente he probado KVM (Kernel-based Virtual Machine) que lo ponían como bueno y me ha gustado. Para completar la lista tengo que decir que también existe el software de VMware que es gratis pero no libre, y por tanto no me convence. Tanto VirtualBox como KVM son de código abierto. Del que voy a hablar aquí es de KVM, que es el que uso actualmente. En concreto explicaré los pasos seguidos para la instalación de kvm en Debian «Squeeze» en arquitectura de 64 bits y la creación de un sistema virtual con Windows 7 y Abode Creative Suite 4.

Según lo entiendo yo (que no necesariamente es cierto) KVM carga unos módulos del kernel de Linux que sirven para compartir los recursos de hardware entre la máquina virtual (la que crearemos ahora) y el sistema huésped (el sistema operativo donde se ejecuta esto). Cuando ejecutas KVM lo que hace es cargar una BIOS básica y emular una máquina (ordenador) que realmente no existe. Es realmente como The Matrix: el mundo real es tu ordenador y ahí cargas un programa como Matrix que permite instalar otro sistema operativo como si Matrix fuera el mundo real. El sistema operativo virtual no es consciente de que realmente es un subprograma en otro sistema operativo, para él sólo existe Matrix :-)

La instalación de KVM sigue el procedimiento típico de cualquier distribución con administrador de paquetes. En mi caso simplemente ejecuté el siguiente comando como superusuario:

# apt-get install kvm

Esto funciona en Debian testing (squeeze) y probablemente en *Ubuntu 10.04. Si utilizas Fedora tendrás que usar yum y si usas openSuse zypper. Esto te instala todos los paquetes necesarios para usar KVM, incluyendo los módulos del kernel. Nótese que la explicación de esta entrada es para la versión 0.12.5 de kvm. Otras versiones pueden tener diferente sintaxis o características.

Una vez termina la instalación, lo primero es crear el disco duro de la máquina virtual. Para ello podemos utilizar las herramientas que proporciona KVM, en concreto, el comando:

$ kvm-img create -f qcow2 MyVirtualHDD.qcow 30G

Éste crea en el directorio actual una imagen en formato qcow2 con tamaño de 30 gygabytes: lo que será el disco duro virtual. Podemos cambiar el tamaño modificando el último parámetro del comando anterior. Para más información lee el manual ejecutando `info kvm-img`.

Con eso ya tenemos todo lo necesario para que arranque nuestra máquina virtual. Con el siguiente comando se arranca usando un gygabyte de memoria RAM y el disco duro recién creado:

$ kvm -m 1G -hda MyVirtualHDD.qcow

Si lo ejecutamos lo que obtenemos es un bonito error del tipo: No Bootable Devices Found.

Fallo de arranque en KVM

Esto es porque nuestro ordenador no tiene sistema operativo (en concreto lo que no tiene es medio de arranque). Por lo tanto, el siguiente paso es introducir un dvd virtual para la instalación del sistema operativo.

Existen dos formas de hacerlo: 1) o bien tenemos el dvd físico del sistema operativo o 2) tenemos una imagen .iso del mismo. En ambos casos es lo mismo y lo único que cambia es un parámetro. En mi caso tengo el dvd de instalación de Windows 7 64 bits. Identificando el dispositivo del lector de dvd, éste se encuentra en /dev/dvd. En otros casos puede variar. Ahora, el comando de ejecución incluye un parámetro para que cargué el dvd en el arranque:

$ kvm -m 1G -hda MyVirtualHDD.qcow -cdrom /dev/dvd -boot d

El parámetro ‘-cdrom’ indica dónde está el lector de dvd y ‘-boot’ indica que debe arrancar desde dicho lector. Si tenemos una imagen .iso podemos usarla como imagen del dvd cambiando ‘/dev/dvd’ por ‘/ruta/dvd.iso’.

Si ejecutamos el anterior comando con el dvd de Windows 7 en la unidad, se iniciará el instalador en la máquina virtual. La instalación es exactamente igual que como sería en tu ordenador real. Al final de la misma tendremos Windows 7 instalado en el disco duro virtual, por lo que el arranque por dvd ya no es necesario. Iniciar tu sistema virtual recién instalado es tan sencillo como ejecutar:

$ kvm -m 1G -hda MyVirtualHDD.qcow -cdrom /dev/dvd -boot c

Windows 7 recién instalado en KVM

Ahora es el turno de instalar el Adobe Create Suite 4, que viene en 3 dvds. La pregunta es, ¿cómo lo instalo a partir de tres dvds si sólo puedo especificar un dvd cada vez que arranca el sistema virtual? Afortunadamente KVM tiene depende en algunos aspectos de qemu, y qemu tiene un “monitor” del sistema (una sala de control en la analogía con Matrix) donde podemos cambiar algunas propiedades de la máquina virtual de forma dinámica. En concreto tiene una opción para expulsar un dispositivo de bloques (dvd) y cambiarlo por otro (enlace).

Para entrar en el modo monitor hay que pulsar la combinación de teclas control+alt+2. Eso nos llevará a una consola de comandos. Para obtener información de los dispositivos de bloque que hay en la máquina virtual tenemos que ejecutar:

(qemu-monitor) info blocks

Esto producirá una salida parecida a la siguiente:

kvm-monitor: salida de info blocksAhí podemos ver que el identificador del dvd es ‘ide1-cd0′. Sobre él podremos ahora hacer dos operaciones básicas: expulsar su contenido y cambiar la ruta a su imagen. La primera hace en Matrix el equivalente a extraer el dvd de la unidad lectora. La segunda es el equivalente a cambiar el dvd por otro. Así, los pasos a seguir para cambiar el dvd en la máquina virtual utilizando el lector de dvd (/dev/dvd) de la máquina huésped son los siguientes:

  1. Extrae en Windows el medio: botón derecho en la unidad de dvd y pulsa expulsar.
  2. Ejecutar un `info blocks` en el kvm monitor para comprobar si la unidad ha sido extraída.
  3. (opcional) Si la unidad no ha sido extraída ejecutar:
  4. (qemu) eject ide1-cd0
  5. Extraer el dvd del lector físico y cambiarlo por el otro que queramos.
  6. Recargar el medio virtual ejecutando:
  7. (qemu) change ide1-cd0 /dev/dvd

Siguiendo esos pasos es inmediato instalar el Adobe CS4 y cualquier otro software que necesite ser instalado a partir de varios dvds.

Instalación del Adobe CS4 (disco 2 de 3)

Y ya está, el sistema virtual completamente instalado y listo para usarse. Sólo para recordar, el comando para ejecutarlo es:

kvm -m 1G -hda MyVirtualHDD.qcow -cdrom /dev/dvd -boot c

Por último he de reconocer que todo esta información viene de Internet en general y en concreto de estas fuentes:

  • La página oficial de KVM (enlace).
  • La documentación oficial de qemu (enlace).
  • El wiki de Debian.org (enlace).
  • Los maravillosos tutoriales de Sean Kenefick (tutorial 1, tutorial 2).
Follow

Get every new post delivered to your Inbox.