miércoles, 28 de noviembre de 2012

Arquitectura - removiendo las bases

Arquitectura - removiendo las bases

En muchas ocasiones, he visto o he estado en aplicaciones donde una modificación de los requisitos, te puede trastocar o simplemente no poder implementarse con la arquitectura con la que ha sido construida la aplicación.

Siempre que ha querido hacer una analogía entre la construcción de un edificio con la construcción software. Vamos a intentar hacer lo mismo para el caso de "remover la arquitectura por los requisitos de usuario".

Imaginemos que estamos desarrollando una aplicación 3d, donde el terreno 3d, se forma através de un fichero .jpg o .bmp donde el código de color es una altura. La arquitectura tiene encuenta este hecho y se construye la malla de del terreno, conforme al grid del .jpg. Es decir, internamente se trabajo con coordenadas del tipo grid [i][j]  para acceder a la altura. Y todo funciona a la perfección!!!

Hasta que el cliente llega y dice..."Vamos a trabajar con una empresa donde tiene archivos con curvas de nivel en vez de con .jpg. Entonces, la aplicación nos gustaría que importase y trabajase con archivos de curva de nivel".

Y va el ing. sw, o el desarrollador y se queda a cuadros. Y se queda por que sabe que tendrá que modificar toda la arquitectura de la aplicación, pero ¿cómo explicarlo?

Imaginemos que estamos construyendo un bloque (esto le paso a un arquitecto que realizo un diseño para un cliente. Eran un bloque de pisos en una pequeña ladera).

El arquitecto empezo por diseñar los pilares, los cuales sustentaran todo el peso y de hay subir pisos. Es decir, con los pilares hizo el esquelo del edificio, la arquitectura.
Cuando termino los planos, se los enseño al cliente:
- "bien..bien..pero...yo quería garaje.."
El arquitecto se quedo a cuadros también, claro esta. Y se quedo por que como el garaje va debajo de los pisos, tendría que haber una separación entre los pilares para crear las plazas de garaje, con lo cual, tendría que modificar la arquitectura de los pilares para encajar las plazas de garaje.



Aquí, en este ejemplo, se ve claramente como se modifica la arquitectura.
En el caso del software, la dependencia de que cada i,j tiene su altura, hizo que cuando este supuesto no es cierto, todo se cae a bajo, como un castillo de naipes.
Algo como, hallar el dato de altura más cercano a punto i,j podría haber sido una arquitectura más resistente los cambios de requisitos.

Como punto final, la diferencia entre el aquitecto del garaje y el arquitecto del terreno 3d, es que el primero, solo tuvo que hacer las modificaciones en un papel, el otro es sobre código, sobre algo ya construido.
Es como si el arquitecto del edificio, despues de haber consstruido los pilares, según la distrubición que le ha marco un programa de calculo de estructuras, llega el arquitecto y manda a los albañiles a mover los pilares para dejar espacio a las plazas de garaje. Da miedo, no?.

happy coding!


domingo, 8 de abril de 2012

Interprete de Ecuaciones

Desde hacía mucho tiempo, tiempos donde compilaba con el borland c++, me llamaba mucho la atención en poder representar gráficamente una fórmula matemática:

y = x*x+5
y= x*cos(x/2)

Pero tenía un problema, una vez escribía la función:

double eq1(double x)
{
    return (x*x+5)
}

Ya no podía cambiar. El usuario, no podía escribir la ecuación que quisiera ver representada en la pantalla.

Hasta que me decidí a investigar este campo un poco y también a arrepentirme un poco más de no haber elegido la asignatura de libre elección "compiladores e interpretes" en segundo de la carrera. A si qué, empeze a mirar libros como "Engineering a Compiler" y "Modern Compiler Implementation in C". Ahí ví las gramáticas por la izquierda, que son las gramáticas más facil de implementar, con lo cual, mejor empezar por algo fácil. Y implemente está gramática:


S => E$
E => TERMINAL Eprima
Eprima => +TERMINAL Eprima
Eprima => -TERMINAL Eprima
Eprima => cos(E)
Eprima =>


TERMINAL => FACTOR Tprima
Tprima =>*FACTOR Tprima
Tprima =>/FACTOR Tprima
Tprima =>


FACTOR => NUM
FACTOR => ( E )
FACTOR => VARIABLE
FACTOR => cos( E )
FACTOR => sin( E )


Como se puede ver en el video, se pueden escribir variables (x, a,...) que a la hora de realizar la gráfica el programa, las va dando valores de un rango de -50 a 50 para realizar la gráfica.
Admite ecuaciones de dos variables, pero esto sería ya para realizar una visualización en tres dimensiones.




La implementación de este tipo de algoritmos, permite al usuario obrar con más libertad a la hora de interactuar con cualquier aplicación. En este por ejemplo, se permite experimentar con visualizar casi todo tipo de equaciones (que permita este simple gramática), pero en aplicaciones grandes, se podría por medio de comandos variables...es decir,..un mini-lenguaje, hacer más productiva una aplicación.

domingo, 29 de enero de 2012

Uso de Nueva Tecnología


Uso de Nueva Tecnología


Al iniciar un proyecto uno de las cosas que hay que decidir es la tecnologia con la que se va desarrollar el software. Hay muchas fuerzas por las cuales puede ser elegida uno y no otra. Puede ser que la empresa cliente imponga la tecnología por que luego se quedará con el código fuente y la tecnología que conoce. O por que el equipo de desarrollo quiere aprender una nueva tecnologia. O por que una tecnología permite hacer determinadas cosas con un par de lineas de código.


En el caso concreto de WPF es una tecnología que te permite hacer muchas cosas con un par de líneas de código pero si estás acostumbrado a programar al estilo de MFC o winforms, habrá cosas en las que uno tendrá que perder un poco el tiempo buscando la forma de hacerlo.


- No se puede hacer una copia de un objeto. En wpf hay que serializarlo, y luego deserializar para crear otro. ¿y que pasa si hay una clase wpf que no se puede serializar? es algo en lo habría uno que investigar.
- El repintado no se hace con un updateall sino llamando a un Invoke en wpf.


O por ejemplo, otro caso, el elegir los sockets de toda la vida o usar las diversas tecnologías para comunicaciones (.net remoting, WCF, ...). Que cuando uno sea más o menos un experto en .net remoting saldrá a la luz una nueva tecnología para comunicaciones.


Con esto quiero decir, que a veces, cuando eliges una tecnología para ir más rápido, el hecho de no conocerla a fondo hace que unas cosas se avanza rápido en un proyecto y otras tengas que parar a investigar. Cosas que con tecnologías que ya conocias, era a más rápido.


Aunque claro está, este conocimiento que se va adquiriendo en un primer proyecto con una tecnología nueva (como dicen en el libro Software Project Secrets), en un segundo proyecto que use dicha tecnología irá mucho más rápido el proyecto. Pero, cuando surja ese segundo proyecto, es muy posible que ya exista otra tecnología que sustituya a la anterior y volvamos al principio.


Pero una de las cosas más peligrosas a mi entender para el éxito de un proyecto es el realizar un proyecto novedoso con tecnología novedosa.




lunes, 15 de agosto de 2011

Geo typical texture

Geo typical texture

Hace un tiempo, en una entrevista me preguntarón por si conocía lo que era el geo type texturing. Basicamente es realizar la textura dependiendo del tipo de objeto que se encuentre en el terreno. Por ejemplo, si queremos realizar la textura de una ciudad, tendremos que saber que tipo de objetos hay en el terreno para aplicar la textura (edificios, parques,..)

Pues bien, al igual que me ocurrio con el simulador de tráfico que en su día me comento mi profesor de programación en cuando hice Equipos Informáticos, y desarrolle uno usando lógica difusa, he creado un pequeño proyecto, citytex, donde el algoritmo realiza lo siguiente:

- Genera un esqueleto de una ciudad en función del número de calles horizontales y verticales.
Aleatoriamente, las calles se inclinan en un sentido o en otro para darle un poco de realismo y no sea una cuadricula perfecta.
Y finalmente se insertan un número de edificios.

- Genera un bitmap con con la información de los objetos que se encuentran. En este paso, solo sabemos las carreteras (en azul) y los edificios(en amarillo).

- Despues, se pinta con el raton las zonas donde queremos que exista una acera, o un parque. Esto se podría sustituir con un algoritmo de rellenado de zonas, pero eso sería otro proyecto.

En el transcurso de su implementación, me he dado cuenta de algunas cosas que ya desarrollare en siguientes post, pero como adelanto, se refiere a la liberación de memoria y al pseudo-código.

La dificultad de este proyecto a radicado a la hora de texturizar las carreteras y generar el grafo interno. Se ha pensado de esta forma, ya que así, representándolo con un grafo, se podría con algoritmos de grafos, determinar que zonas podrían ser rellenadas con unos rascacielos y otras con casas más bajas. Los nodos mejor comunicados estarían rodeados de rascacielos y los de a las afueras, con texturas de viviendas.

Aunque mirando la textura que genera, posiblemente no sea muy realista, pero podría servir de base para futuras versiones. Por ejemplo, que el algoritmo tomase la altura de cada punto y la tomase encuenta para crear la textura.

Uno de los problemas de usar fotos aereas como texturas es su resolución, que debe ser muy alta. Cuando en un simulador de vuelo se está cerca del terreno, la textura, si tiene poca resolución se empieza a pixelar y aparece como no real, quejándose así los pilotos. Una solución para esto, es la creación de texturas proceduralmente, como en este ejemplo, aunque claro, queda mucho, mucho por mejorar, pero ha estado bien e interesante aproximarse un poco a la creación de este tipo de texturas.

Para bajar el programa aquí.




algunos links sobre este tema.

http://www.helistart.com/addons.aspx

http://ict.usc.edu/projects/military_terrain_for_games_pipeline/

PROCEDURAL TEXTURING
Most input datasets generally contain only geometry information, while some may contain rudimentary textures. The MTGP pipeline improves upon the dataset, procedurally applying geo-typical textures (dirt or asphalt, brick or stone sand, grass or concrete) to incoming geometry (roads, buildings and terrain).

jueves, 7 de julio de 2011

El Valor del Software

El Valor del Software

En todos los desarrollos software, hay un tira y afloja con el usuario sobre lo que tiene que hacer el sistema. Una de estas fuentes, de estos tiras y aflojas, son la complejidad de los requisitos software que pide el usuario.

En estas situaciones, en mi opinión, hay que tener cuidado, ya que, si se tira mucho por parte del equipo de desarrollo sobre un requisito, es posible que el usuario, devalue la aplicación tanto, que no quede contento.
Si el usuario está solicitando un requisito que para el es de un gran valor para su trabajo, y finalmente, se modifica tanto ese requisito. Ese requisito no va a portar el valor que esperaba el usuario.
Esto me recuerda a un programilla que desarrolle para una amiga abogada. Este programa contaba a partir de una fecha y un plazo, el día en que vencía ese plazo (dies ad quem). Programar esto, es fácil, pero el valor para el usuario era grande. Pero al poco de ver como la aplicación calculaba el día que vencía el plazo, quiso que el programa se comunicara con su calendario de Google, de forma que le enviase un aviso por correo un día antes de que cumpliera.

Esto se vio pronto que era de un gran valor añadido, ya que ahorraba tiempo, y así, sería más díficil que un plazo se pasase. Pero había una complejidad técnica. Tenía que mirar la API de google. Entonces, tenía dos opciones (al menos en ese momento pensé):

a) Convencer al usuario para que la herramienta  implementara un envio de correo a su cuenta de correo y asi, lo tendría almacenado. La complejidad técnica es menor, ya que en .net esto se hace con unas cuantas lineas.

b) Mirar la API de google y tratar de comunicar el programa, CuentaPlazos, con el calendario de Google.
La opción a) deja al usuario sin centralidad en su calendario toda su actividad. Corriendo el riesgo de bajar el valor de la aplicación, y por consiguiente su uso.

La opción b) , aunque cuesta algo más de tiempo, añade valor a la aplicación, y así lo percibe el usuario.
Con este ejemplo creo que el identificar y mantener los requisitos con más valor para el usuario son fundamentales en el desarrollo software, ya que si no, es posible que no use el futuro software.

En el caso del CuentaPlazos, es posible, que si muestro un calendario con colorines de forma muy vistosa, pero luego, no se comunica con el calendario de google, la aplicación perdería valor para el usuario ya que no se ha implementado un requisito para él fundamental.

Y es que, en definitiva, un software que pide un cliente, debe de satisfacer a dicho cliente, y en el desarrollo habrá que poner los medios para alcanzar dicho objetivo.


sábado, 2 de julio de 2011

Codificando para depurar

Codificando para depurar


En la codificación es muy normal al codificar una función o método de una clase, esperar que funcione como debe de funcionar. Creo que esto es debido a que cuando implementamos un método, sabemos desde donde va a ser llamado y sabemos perfectamente cual será el escenario de esa llamada. Por ejemplo, si estoy implementando.

void CTriangle::create(Point *pPoints);

Desde donde será llamada, siempre el puntero pPoints será distinto de null, y los tres puntos serán distintos. Ya que algoritmo de creación de triangulos, si funciona al 100% correctamente, pasará siempre un array de puntos correctos.


¿Pero que pasa, si ese algoritmo tiene algún bug? crearemos algún triangulo incorrectamente y entonces, en pantalla o no se verán o veremos cosas raras en la pantalla.


Aquí, en este punto, es donde es el motivo de este post. El codificar para depurar. Podemos tener, una función de chequeo dentro del método Create para que inmediatamente que se cree un triangulo, comprobar que es correcto, y si no lo es, hacer saltar un assert. De esta forma, se va, SIN perdida de tiempo, al fallo y no gastar un tiempo precioso en depurar. 
Cierto, esto ralentiza la ejecución, pero después, cuando no salta ningún assert, estás comprobaciones, se pueden comentar en código.


Un ejemplo de ello. Cuando estaba implementando la "Triangulación Restringida de Delaunay", tenía este método que llamaba cada vez que creaba un objeto de la clase CTriangle.



// cheque que el triangulo está construido correctamente
// true si tiene algun error.
bool CTriangle::checkError(void)
{
bool bError = false;


if (isRepeatVertex())
bError = true;
if (isRepeatVertexPoint())
bError= true;
if (checkAdyacents())
{
bError = true;
}


if ( !bError )
{
//TRACE("---------------- ERROR TRIANGULO ------------\n");
//debugView();
}



return bError;
}


checkError comprobaba que el triangulo estaba bien construido, tanto en sus indices al array de puntos, como que los puntos eran distintos entre sí, y en sus triangulo adyacentes. De esta forma, al saltar este chequeo, era un signo de que algo mal estaba en el algoritmo de Triangulación.

Otra forma que ayuda para depurar, es identificar de forma única a cada objeto que se crea. De esta forma, puedes trazar la evolución de un objeto y centrarte en el solamente. Por ejemplo, en CTriangle tenía:

int m_id;

Así, si saltaba el método checkError, con el id del triangulo, podía centrarme en como se creaba este triangulo y su relación con sus adyacentes.


Con todo esto, creo que no debemos de pensar al codificar, que todo saldrá bien, sino debemos pensar, que algo irá mal y tendremos que depurar. Entonces, tendremos que codificar para que dicha depuración sea más fácil, eficaz y productiva.






Aquí, os dejo una demo de un programa que utiliza dicho algoritmo

jueves, 30 de junio de 2011

Rendimiento managed to native code

Rendimiento managed to native code

Leyendo un libro sobre c++/cli, c++/cli in Action, donde el autor hacía referencia de que realizar muchas transiciones entre código manejado y código no manejado (código nativo), haría a la aplicación más lenta.

Para comprobar esto, realize el siguente programilla que tiene dos funciones, callfromUnManaged y callfromManaged. Las dos funciones tienen un bucle que llaman a funciones nativas. CallfromUnManaged no hará ninguna transición, ya que es una función nativa,
pero callfromManaged si tendrá que realizar transiciones.

Visto lo anterior, parece de cajón que callfromManaged será más lenta. Pues sí y no!. Depende de la opciones del compilador y la complejidad de las transición (luego explico esto)
configuración 1:
En relase si tenemos el C++/C ==> Optimization a 'disabled', la función más rápida será callfromManaged.
En relase si tenemos el C++/C ==> Optimization a 'Maximize Speed (/O2)', la función más rápida será callfromUnManaged.
¿Cual sería la explicación? es posible que por defecto, el código IL esté optimizado y el código nativo no,  con lo cual para que optimize el código nativo hay que configurarlo explicitamente.

Lo de la complejidad de la transición de código manejado a nativo, fue por que si solamente se pasan enteros, algunos resultados me daban como que era más rápido el código manejado, pero cuando añadi como parametros el paso de punteros de los dos objetos Student y Teacher para la función:

int calculateSquareRoot(float dataValue,int nTimes,Student *pStudent,Teacher *pTeacher)
Esto hizo que se complicara un poco el código que el compilador tenía que generar para pasar de código manejado a nativo, dando como resultado que ahora, cuando se optimiza el código ('Maximize Speed (/O2)), el método 'callfromUnmanaged' es más rápido.

Unos resultados parecidos se pueden encontrar aquí:

http://www.grimes.demon.co.uk/dotnet/man_unman.htm


Aquí dejo el código fuente: