Buscar este blog

Mostrando entradas con la etiqueta Desarrollo. Mostrar todas las entradas
Mostrando entradas con la etiqueta Desarrollo. Mostrar todas las entradas

martes, 15 de marzo de 2011

Versión v0.5

Versión v0.5. Me da miedo ponerle un nombre de versión mayor. Pero la verdad es que ahora la aplicación «da' gusto», ya que , por fin, se construye el árbol de forma dinámica extrayendo datos de la web. ¿De qué web?. Pues lo mejor que tenemos ahora, que yo sepa y que sea público, es Wikispecies. De ahí se va generando el cladograma que se visualiza en la aplicación. Pero luego entraremos en más detalles, ahora, a disfrutar un poco durante los cinco minutos y algo que dura el video:



Los nuevos cambios más importantes añadidos son los siguientes:

  • Cambio importante de diseño, a destacar: clase Strategy, de la que heredan la mayoría de las clases del juego, clase Smooth, para suavizado de movimientos y desacoplado de las distintas funcionalidades del árbol, es decir, clases Tree vs LocTree vs ColorTree.
  • Procesado automático del cladograma: cada taxón/clado tiene su correspondiente artículo en Wikispecies. Éste consta de una primera sección en donde residen los subclados. Con la librería libcurl hacemos consultas al MediaWiki-API de Wikispecies, y con flex la analizamos para extraer cada subclado.
Y la nueva funcionalidad:
  • Exploración del cladograma de wikispecies: haciendo doble click izquierdo en una clado hoja del árbol se expanden sus subclados.
  • Visualización del artículo del clado en wikipedia:haciendo doble click derecho en el nodo correpondiente.
  • Contracción del árbol: haciendo doble click izquierdo en un clado que no sea hoja, el clado se constrae, ésto es, desaparecen sus subclados. Para constraer el árbol «por arriba», en vez de por abajo, es decir, eliminar padres y hermanos que no interesen, hay que hacer Ctrl+doble click izquierdo en el nodo deseado, ese nodo se convierte en nuestra nueva raíz, desapareciendo el resto del árbol.
  • Las teclas →, ↑, ← y ↓ sirven para mover la cámara, y si se mueve el ratón mientras se mantiene pulsado el botón izquierdo del ratón, la cámara también se mueve pero en dirección contraria.
Algunas cosas a corregir/mejorar:
  • La cámara no siempre está centrada, y hay que moverla manualmente.
  • Disminuir la sobrecarga cuando el árbol es muy grande.
  • Construir subespecies (solo se construyen clados y especies).
  • Indicar la categoría taxonómica del clado (filo, órden, etcétera).
  • Búsqueda de clados.
  • Construcción del clado, «hacia arriba»: solo se puede expandir el árbol en dirección padre-hijo.
  • Indicar cuál es la raíz del árbol u, opcionalmente, indicar la dirección padre-hijo en el árbol, ya que con árboles grandes es difícil saber «donde estás».
También tenemos una rama de trabajo, llamada 3ddevelopment (de la versión v0.3, no de la última) en la que hay una visualización en 3D del árbol. Pero no la he querido mergear con la rama principal por que todavía no veo claro cuál será su uso. La visualización y exploración es menos cómoda y la sobrecarga de la máquina es considerable. Es más bonito, pero menos útil «en la práctica». Quiero agradacer eternamente a mi compañero Pepe cullera por su colaboración, que es el que ha desarrollado al completo dicha rama 3D y espero algún dia encontrar alguna justificación al uso del 3D en la aplicación y trasladar el contenido a la rama principal, al menos como opción de visualización, para los que lo deseen.
Leer más...

viernes, 14 de enero de 2011

Versión v0.3

Ya tenemos nueva versión. ¿Qué lo hace distinto de la anterior?, pues principalmente, que ya se integra con un mini-explorador que permite visualizar los distintos clados en wikipedia. Es algo cutre puesto que se usa el nombre del nodo con el nombre del artículo de wikipedia. Si éste no existe, aparecerá la página de wikipedia que te propone crear un nuevo artículo.

He aquí un video:




Para conseguir integrar el explorador, sencillamente he hecho uso del módulo de Qt WebKit. Sí, habeis leido bien, he migrado el proyecto a Qt, y la verdad es que estoy muy satisfecho, casi todo lo que te imagines Qt te lo ofrece: te ahorra el trabajo de controlar los dobleclicks, te permite crear señales entre funciones puesto que contiene un mecanismo interno de signal and slots, búsqueda de contenidos en el código HTML, widgets de openGL, etcétera. Además, está perfectamente documentado. Vamos, toda una delicia de librería.

Por ejemplo, para controlar la animación, tengo un reloj que da pulsos cada 40 milisegundos —y así tenemos 25 fotogramas por segundo—, y ese reloj está conectado con el slot PhyloTree::animate(), de modo que cada pulso es una señal, y la función animate es un callback que la recibe, aunque todo ese proceso es controlado por Qt. El siguiente paso importante a dar es que el software interactúe con el API de wikipedia para obtener la información deseada de forma robusta.

Otra característica de la que me estoy preocupando en mantener es el «smootheado» de todos los elementos animables posibles, así, si el usuario mueve la cámara con el ratón, la posición no cambia inmediatamente, sino de forma suave hasta su posición final. Lo mismo ocurre si cambiamos el tamaño de la ventana, que tanto el viewport como el miniexplorador integrado se mueve a la nueva posición y tamaño de forma progresiva.

Por último, hay un desarrollador y ahora colega que va a experimentar en una nueva rama de git con el paso a 3D del árbol, al que incluso añadirá efectos de desenfoque, y también la posibilidad de ver el árbol con profundidad mediante el uso de gafas cromáticas; todo un lujo.
Leer más...

miércoles, 29 de diciembre de 2010

Versión v0.2


Después de un mes con el proyecto dejado de lado, he aprovechado las navidades para trabajar en él un poco, y la verdad es que los resultados son satisfactorios, por ahora.

Los cambios son sencillos, he programado un algoritmo basado en fuerzas para imprimir el árbol, la cámara se mueve si se mantiene pulsado el ratón, y también hay programado un algoritmo de coloreado inventado por mí. Además, se ven los nombres de los clados que representan los nodos cuando se coloca el ratón encima.

He aquí un video de ejemplo:



Mas técnicamente, los dos algoritmos principales son los siguientes:


Algoritmo basado en resortes


Es el algoritmo usado para calcular la posición de los nodos del árbol. Es un algoritmo de la familia de los algoritmos basados en fuerzas, conocido popularmente como Spring Embedder (resortes embebidos). Este algoritmo consiste en lo siguiente: cada arista es, en realidad, un resorte, —es decir, un muelle— que tiene una longitud dada. Cada pareja de nodos no conectados, se unirán con una arista ficticia que también modelará a un resorte. Las aristas verdaderas serán resortes de atracción, y las aristas ficticias serán resortes de repulsión.



Las posiciones de los nodos se inicializan aleatoriamente, y se unirán con muelles de la forma indicada. Cada resorte intentará volver a su posición inicial, generándose un sistema de fuerzas que buscará una situación de equilibrio. Es como si forzaramos una serie de puntos unidos con muelles, algunos estirados y otros comprimidos, y luego tiraramos el conjunto al aire. El gráfico anterior ilustra bastante bien su comportamiento, dando finalmente una visualización elegante del árbol.


Algoritmo basado en intervalos de colores


Este algoritmo es invención mía, y funciona de la siguiente forma: a cada nodo se le asigna un intervalo de colores, y se colorea con el centro de dicho intervalo. Dicho intervalo se parte en tantos subintervalos como hijos tenga el nodo, y, análogamente, se colorea con su centro. Se procede recursivamente para todo el árbol, siendo el intervalo de la raíz del árbol el propio cubo RGB (y por tanto, la raíz del árbol será de color gris, pues es el centro del cubo).

La forma de partir cada intervalo es el siguiente. Partiendo del cubo RGB asignado a la raíz, primero se elige para particionar la dimensión R —es decir, la del color rojo—. Así, si la raíz tiene n hijos, el cubo se partirá en n rectángulos 3D, donde el ancho en la dimensión R de cada rectángulo 3D será el ancho del nodo raíz dividido entre 'n'. Para el siguiente nivel, cada cubo se partirá en la dimensión G, luego en la B, luego nuevamente en la R, y así sucesivamente.

La idea de éste algoritmo es que cada nodo sea dueño de una región, del cubo RGB de la que ninguno de sus hijos podrá escapar. Así, aunque en los primeros niveles haya saltos en la gama de colores —sobre todo si cada nodo tiene pocos hijos—, a medida que se incrementa la profundidad la gama de colores de nodos relacionados será mas homogénea, y el propio coloreado será un indicador gráfico de la cercanía entre nodos.

Aspectos por cambiar o mejorar

Hay todavía muchas cosas importantes por mejorar en la aplicación. El más importante por ahora es que la aplicación no cuenta con ninguna base de datos externa. El árbol está creado nodo a nodo en el fichero program.cpp, es decir, embebido en el propio código. Este es quizás el siguiente aspecto crucial de la aplicación, tener una base de datos externa, y crear la visualización del árbol leyendo los datos a partir de dicha base de datos. Seguramente usaré posgreeSQL.

Otros aspectos menores son referidos, por ejemplo, al peso de las aristas. Por ahora, cada arista tiene el mismo peso, que viene indicado en el propio algoritmo basado en resortes —en realidad, no existe aún ninguna clase arista que tenga un atributo peso, sencillamente es una pareja de nodos—. Habrá que asignar un peso a cada arista, usando algún criterio relacionado con el árbol filogenético —por ejemplo, proporcional al número de nietos que tenga un hijo—, y adaptar el algoritmo de resortes para que éste cambio no produzca efectos indeseables en la visualización.

Otro aspecto a cambiar es el posicionado del árbol. El árbol entero es movido según el sistema de fuerzas que provoquen los resortes, y habrá que cambiar el algoritmo para que, al menos la raíz, quede fija en un punto dado, porque a veces el árbol se mueve demasiado hacia un extremo y hay que mover la cámara para volver a centrarlo.

Hasta otra ...
Leer más...

sábado, 13 de noviembre de 2010

Mi primer blur

Bueno, tan pronto como traducí mi aplicación a openGL, ya le añadí el blur. Por ahora el resultado es pobre porque solo me queda perfilar los efectos, pero la dinámica de trabajo, que es lo que me interesaba, ya la conozco. Este es el resultado final:




La visualización es pobre por dos motivos: no hago cálculos de qué color corresponde a cada nodo, ya que todavía estoy viendo como pintar árboles, no cuál es el contenido del árbol, cosa de la que depende el color asignado (y cuestión todavía que tengo que definir). Y las aristas tampoco tienen blur aún (y tampoco se han colocado sombras).

Estos aspectos se definirán ya muy a posteriori, mi siguiente prioridad ahora es programar el algoritmo de visualización del grafo (es decir, el que coloca cada nodo en un lugar adecuado para una estética visualización), y luego, pelearme con la impresión de texto.
Leer más...

Adios Ogre

Desde mi última entrada, han ocurrido muchas cosas en mi proyecto, pero qué han tenido muy poca repercusión en su aspecto actual. Como ya adelanté en la presentación, quería que mi proyecto se pareciera al aspecto de Gource por cuestión de elegancia, y todos éstos cambios tienen su origen en la persecución de ésta idea.

Como un grafo, y por tanto un árbol, no es más que nodos y aristas, los modelos 3D con los que hace falta trabajar son muy sencillos. Sumándole ésto a que el tamaño de cada nodo, o de cada arista, puede ir cambiando en tiempo de ejecución, el dibujado, y con ello los modelos también deberían hacerlo. Ésto me llevo directamente a trabajar con el tipo ManualObject de Ogre, que te permite definir los modelos vértice a vértice, definir subobjetos, y cambiarlos en tiempo de ejecución. Es decir, todo lo que necesitaba, aunque precisamente por trabajar con objetos geométricos a bajo nivel veía a Ogre un recurso demasiado disparatado. Ogre está orientado para trabajar con modelos complejos, en escenarios complejos, con cálculos complejos. Yo no necesito muchos efectos gráficos ni de iluminación, y es todo relativamente sencillo. Pero por tal de no empezar a aprender openGL, por cuestión de tiempo, decidí seguir adelante.

La primera idea para simular el efecto de Gource era mediante el uso de una esfera, con una luz en su centro que la reflejara, para que tuviera el aspecto de una estrella brillante. Cómo se nota que era la primera vez que trabajaba en un contexto 3D. Cuándo tienes un objeto, iluminarlo solo cambia la intensidad de la luz en su superficie, en los lugares donde la luz incida, oscureciéndose a medida que la superficie se aleja de la luz, o se hace más innacebile para ella. Una luz no se verá si no hay ningún objeto en el que reflejarla. Y por poner una luz dentro de un objeto, nunca va a tener un aspecto de "bombilla".

Una vez comprendido ésto, cambié de táctica. Relegé la construcción 3D del árbol en un nuevo objeto, usando el patrón estrategia (StrategyMakeTree). Ahora tenía dos construcciones, una mediante esferas (SphereMakeTree), y otra recurriendo al llamado efecto Tyndall (ColloidMakeTree).

El efecto Tyndall es lo que ocurre cuando un rayo de luz entra en una habitación, o cuando el agua se refleja en el mar. El aire de la habitación, o el agua, tienen una serie de impurezas. Cuando un rayo de luz penetra, cada partícula impura refleja la luz que incide en ella, creando una serie de puntos de luz indistinguibles entre sí para el ojo humano, dando la sensación de un continuo iluminado. Luz reflejada en aire o agua pura no es visible en forma de rayo, debido a que no tiene partículas que puedan reflejar la luz incidente en ellas. En la imágen siguiente se ve un láser que atraviesa dos vasos, uno con agua pura, y otro con impurezas, y se observa como el agua pura no muestra al rayo atravesándolo, mientras que las impurezas disueltas en el agua sí que lo permiten.



Un sistema como el aire o el agua con impurezas, son llamados coloides, y mi nueva idea, era, por supuesto, que cada nodo fuera un coloide. Se crearía una nube de puntos con forma esférica, de forma que en su centro hubiera una mayor densidad, y en el límite de su radio poca. Luego, situando una luz en su centro se simularía un coloide, difuminándose la intensidad lumínica a medida que la luz se alejaba de su centro, hasta diluirse con el fondo negro.

Pero por más formas ideadas para implementar un coloide, ninguna tuvo éxito. Para experimentar, comencé construyendo una esfera que tuviera siempre una misma densidad de puntos. Pero si construía una esfera de poca densidad, se veía en vez de un coloide una nube dispersa de puntos. Si la construía con poca densidad. Se veía una esfera sólida en vez de un coloide. Si hacía una esfera de poca densidad, con mucho radio, y alejando mucho la cámara, se volvía a ver una esfera sólida.

Mi siguiente paso fue ver como lo hacía Gource. Me llevé un tiempo estudiando su código fuente, lo que me llevó a aprender bastantes cosas de openGL para comprenderlo. Explorándo el código descubrí, entre otras cosas, que el efecto de destellos de luz cuando se creaba una rama nueva se llama bloom. El desenfocado que buscaba, blur (blur no es más que borroso en inglés). También descubrí que existe una rama de la teoría de grafos que se llama visualización de grafos, y Gource usa un algoritmo de visualización basado en fuerzas, esto es, que el grafo funciona como un sistema gravitatorio, donde cada nodo afecta a la posición de los restantes nodos, hasta alcanzar una posición de equilibrio, que es la posición final de visualización.

Otro detalle interesante es que, como el árbol está cambiando de posición y orientación constantemente, las aristas están curvadas de modo que incrementen la sensación de movimiento, curvándose la aristas en un sentido semejante al sentido de giro del árbol. Para construir estás aristas curvadas, se hace uso de splines.

La forma que tiene gource de dibujar el blur (el desenfoque), es mediante el uso de texturas. Se especifican las coordenadas de un cuadro, se coloca encima una imágen que contiene un círculo difunado que hace las veces de desenfoque, y encima se coloca otra imágen con el nodo en cuestión. El color de la textura de desenfoque se mezcla con el del nodo, y con eso finalmente parece que es el propio nodo el que brilla.

Intenté hacer lo propio con Ogre, creando la clase GourceianMakeTree. Y empezaron los problemas. Por más vueltas que le daba, no conseguía que la textura se renderizara bien. Sobre el funcionamiento interno de ManualObject existe poca documentación, en la lista de correo de la forja tampoco hubo nadie que me pudiera ayudar de forma totalmente satisfactoria. Incluso un desarrollador con el que mantengo contacto por correo, muy familiarizado con Ogre y openGL, con el que aprendí muchísimo, tampoco supo como resolver mi problema. Todo ello demuestra el poco conocimiento, uso o divulgación, de los ManualObject, lo que me convenció más aún que Ogre no estaba hecho para trabajar de esa forma (ya que nadie más lo hacía).

Así que decidí dejar Ogre, y con ello OIS, y cambiarme a openGL, con Glut primero, y con SDL después. Este paso de glut a SDL fue debido a que, aunque glut sea orientado a eventos -cosa que hace más elegante el diseño para este tipo de aplicaciones-, su sintaxis me obligaba a hacer cosas poco elegantes, como tratar con atributos externos o trabajar con referencias a funciones miembro, con sintaxis prefijada, y sin tener control total sobre la función MainLoop, que es la función de Glut que lleva el control de toda la aplicación.

Una vez perdidos unos cuantos dias aprendiendo openGL, pude finalmente reescribir mi aplicación. El diseño prácticamente no ha cambiado. Las clases se organizan de la misma forma, solo que las funciones miembro sí que son distintas y hacen cosas distintas.

Un detalle curioso (que tuve que descrubir yo, y nunca se advierte cuando se habla de SDL + openGL), entre Glut y SDL es que, mientras Glut toma el sistema de coordenadas del escenario centrado en la pantalla, SDL toma el centro del escenario en la coordenada (0, 0) de la pantalla, es decir, la esquina superior izquierda. Además, la pantalla se convierte en el primer cuadrante invertido, ya que la coordenada y de la pantalla crece desplazándose hacia abajo, como de costumbre en SDL. Es decir, que hay que tener en cuenta que SDL no cambia su comportamiento sea una ventana openGL o una pantalla "clásica".

Y respecto al aspecto actual del proyecto, pocas novedades. Todavía no tengo programado el desenfoque, solo pinto un cuadro blanco por cada clado, conectados por aristas. En la siguiente imágen se muestra una captura.



No hay ningún algoritmo "propio" de impresión del árbol. Sencillamente, los hijos los imprimo en columna, con un desplazamiento x constante, por tanto, los hijos de padres distintos podrían solaparse.
Leer más...