Table of Contents
Modificación de CRRCSIM para agregar la funcionalidad de modelado de ráfagas de viento
Elaborado por: Jose Pablo Espinoza Barahona
Introducción
El proyecto consta del estudio del código fuente de un programa open-source para su posterior documentación; como también generar un registro con todo el proceso para lograrlo. Haciendo uso de los resultados anteriores se identificará, él o los sectores en los cuales se le debe de inyectar código para otorgar las funcionalidades de ráfagas de viento al software. Posteriormente se procederá a formular un prototipo del cómo debería de formular el algoritmo que otorgue el nuevo caso de uso.
Objetivos
Objetivo General
Dotar al programa “crrcsim” la funcionalidad de modelado de ráfagas de viento.
Objetivos Específicos
- Estudiar y generar una documentación de “crrcsim” que represente la abstracción de las clases y su interacción entre capas en el programa.
- Definir y estudiar un algoritmo que abstraiga la naturaleza de una ráfaga de viento y su influencia el ambiente.
Justificación
Debido a los deseos del ARCOSLAB de progresar en el campo de los vehículos aéreos controlados remotamente; nace la necesidad de formar más y mejores pilotos de vehículos controlados por radio controles. Por esta razón es importante proveer a los simuladores usados con capacidades de emulación que sobrepasen las condiciones normales y óptimas de vuelo.
Herramientas
Vi(m)
Se seleccionó usar el editor texto Vi(m) ya que es el editor de texto estándar de los sistemas POSIX, por lo que es una herramienta nativa dentro de los sistemas GNU/Linux y no necesita ningún paso adicional para su instalación.
Locate
Herramienta que crea una base de datos interna usado para identificar de manera eficiente archivos dentro del sistema.
sudo apt-get update sudo apt-get install locate
Como locate crea una base de datos se debe de actualizar antes de usarlo:
sudo updatedb
Grep
Herramienta para búsqueda de cadena de caracteres dentro de archivos nativo de los sistemas POSIX.
Marco Teórico
Game Loop/Cycle
A pesar del propósito con el cual fue concebido crrcsim a nivel conceptual no deja de ser un simple videojuego por lo cual debe de seguir muchos conceptos y patrones empleados en el desarrollo de videojuegos.
Una de las características principales de una arquitectura de juego es el Game Loop o ciclo de juego el cual es una estructura iterativa la cual se encarga básicamente de 3 labores:
- Obtener la entrada de datos del usuario (EventHandlers).
- Actualizar el estado del juego (Update).
- Desplegar la nueva pantalla con los nuevos parámetros (Draw).
Por su naturaleza el ciclo se representa por medio de una iteración while y se va a encontrar en la primera estructura que se ejecuta al correr el programa, en Lenguajes descendientes de C esto sería el método main. Conceptualmente los videojuegos son software orientados a la interactividad con el usuario por lo que se necesita recolectar la información dada por el usuario y usarla para modificar el último estado del sistema para generar uno nuevo. Esto se logra por medio de administradores de eventos (handlers) que siempre están escuchando (listeners) las solicitudes del usuario. El producto de esta operación debe ser luego desplegada en pantalla ya sea como una manifestación visual o en el comportamiento del software.
Arquitectura de Tres Capas
La arquitectura de 3 capas del software es un patrón de estructura cliente-servidor la cual busca seccionar y encapsular el software para una mejor asignación de responsabilidades. Estas tres capas ofrecen una modularidad al software la cual le permite ser escalable y brinda una mayor facilidad para la migración a otros sistemas, encontramos:
Capa de Presentación:
Es la encargada de la interacción con el usuario y de validar las excepciones del sistema. Toda la información recolectada la pasa a la capa lógica para obtener el resultado requerido.
Capa Lógica:
Es la encargada de procesar los datos que entraron por medio de la capa lógica y también de solicitar y/o enviar, si fuera necesario, datos a la capa de Datos.
Capa de Datos:
Es la encargada de ofrecer sistemas de manejo, navegación, almacenamiento para todos los datos ya sea para R.A.M. o en un repositorio de datos.
Clases gestores, ayudantes y asociativas
Estas son clases especiales cuyo propósito es asignar apropiadamente las responsabilidades en las relaciones entre objetos en el paradigma orientado a objetos. Podemos notar dos casos especiales:
Clase Gestor:
Son interfaces para la solicitud de recursos lo cual busca es encapsular todos los servicios que la capa lógica ofrece a la capa de presentación.
Clases ayudantes (helper):
Son interfaces que buscan encapsular servicios de una misma semántica y que son de uso frecuente dentro de la capa lógica.
Clases asociativas:
Son clases que buscan representar mejor la interacción entre clases y crear una mejor asignación de responsabilidades. Tratan de solventar cuando existen relaciones de muchos a muchos y existe información generada por el enlace.
El siguiente diagrama sería un ejemplo de la arquitectura de tres capas convencional:
Estudio del Código
Metodología
Para la elaboración del estudio se examinaran las funciones que provee la interfaz gráfica y detectar puntos de interés sobre su funcionamiento. Luego se identificará figuras relevantes como el main y el gestor de capas. En conjunto de los resultados obtenidos de los pasos anteriores se hará un seguimiento de llamados hasta llegar alguna línea que sea relevante/apropiada para la inyección de código para generar ráfagas de viento. Ejemplificando la metodología en el siguiente diagrama de flujo:
Ejemplo:
Caso inicial:
Como no se conoce el archivo a indagar ni ningún elemento en particular procedemos a experimentar con la GUI. Durante la experimentación se encontró:
Que es una pantalla de opciones de viento, pero notamos la palabra “Velocity” que es un string en algún lugar del código, por lo que ya tenemos un elemento por lo que procedemos a realizar un grep
Ahora conocemos el archivo y la locación del elemento por lo que indagamos en dicho código:
de aquí obtenemos varios elementos dentro de ellos cfg y así sucesivamente…
Instalación crrcsim
Crrcsim se encuentra dentro del repositorio de software de Debian y muchas otras distribuciones por lo tanto para su instalación de la forma más simple:
sudo apt-get update sudo apt-get install crrcsim
Descarga del código fuente
Para realizar el estudio necesitamos descargar el código de crrcsim: Solicitud de la versión instalada.
crrcsim -V
Hacemos una búsqueda del código fuente de la versión obtenida, en este caso la versión instalada fue la 0.9.12 y lo descargaremos en nuestro directorio hogar:
wget "downloads.sourceforge.net/project/crrcsim/crrcsim/crrcsim-0.9.12/crrcsim-0.9.12.tar.gz?r=http%3A%2F%2Fsourceforge.net%2Fprojects%2Fcrrcsim%2F&ts=1445806692&use_mirror=tcpdiag" -O crrcsim-0.9.12.tar.gz[/code]
Descomprimir y desempacar:
tar zxvf ~/crrcsim-0.9.12.tar.gz -C ~/
Debido a la inserción de archivos que hemos hecho dentro nuestro sistema debemos actualizar la base de datos de locate:
sudo updatedb
Crrcsim
Indagación por GUI
Iniciamos ejecutando crrcsim desde consola lo cual nos ofrece información durante ejecución:
crrcsim
Interactuando con la GUI encontramos la posibilidad de cambiar valores de la capa lógica desde la capa de presentación.
(Esc >options>Wind, Themals)
Por lo tanto se identificará en donde se encuentra el código correspondiente a 1, 2 y 3 y estudiar lo que sucede a nivel interno.
Se notó que al cambiar los valores y reanudar la partida los cambios son inmediatos entonces podemos deducir que una vez que se reanuda la partida suceden los 3 pasos del ciclo de juego: leer datos, cambiar estado y pintar escenario con los nuevos datos. Esto nos indica que el lugar donde debemos insertarle código está relacionado con esta secuencia.
De los datos impresos en la terminal:
Podemos concluir que cada escenario tiene condiciones predeterminados, pero que siempre va a leer los parámetros establecidos por el usuario del último estado que se haya salvado, debemos notar el uso de xml como repositorio de parámetros.
Haciendo uso del mnemónico 'h' activamos un compás en la GUI de la cual encontramos el significado de los valores del parámetro de dirección donde:
Dominio: [0, 359] Norte: 0 Este: 90 Sur: 180 Oeste: 270
Indagación por código
Listamos todos los elementos de la carpeta crrcsim-0.9.12
ls -R ~/crrcsim-0.9.12
De la salida de este comando notamos muchos archivos de extensión “.h” y “.cpp” esto nos indica que el programa está escrito en C++. Además podemos notar que toda la capa de presentación está contenida dentro de la carpeta ~/crrcsim-0.9.12/src/GUI
Ahora tratamos de encontrar:
- El método main
locate ~/crrcsim-0.9.12/src/*main* OUTPUT: ~/crrcsim-0.9.12/src/crrc_main.cpp
- Línea donde inicia el ciclo de juego
grep -nrw -e "while" ~/crrcsim-0.9.12/src/crrc_main.cpp OUTPUT: 863: while (Global::Simulation->getState() != STATE_EXIT) Exploramos el archivo apartir de la línea 863 vi +863 ~/crrcsim-0.9.12/src/crrc_main.cpp
Identificamos el ciclo de juego y sus elementos:
865 crrc_time→update(); //control del reloj 866 scheduler.Run(); //reloj del juego 988 Video::display(); //pinta el juego
Pero no contamos con datos suficientes para identificar los encargados de recibir la entrada del usuario durante ejecución ni los elementos que cambian el estado del juego. Comúnmente los elementos encargados de interactuar con el usuario son los Listener y Handler por lo que buscamos explícitamente:
grep -nrw -e "Handler" ~/crrcsim-0.9.12/src/GUI/ OUTPUT: ... ~/crrcsim-0.9.12/src/GUI/crrc_gui_main.cpp:300:/** \brief The GUI's key press event handler. ~/crrcsim-0.9.12/src/GUI/crrc_gui_main.cpp:351:/** \brief The GUI's key release event handler. ...
Por lo tanto todos los handlers se encuentran en: ~/crrcsim-0.9.12/src/GUI/crrc_gui_main.cpp Por otro lado no se tienen pruebas suficientes para identificar los mecanismos que manipulan el estado del juego y lo cambian, se seguirá recolectando datos y se abordará nuevamente más adelante.
Buscamos estructuras identificadas en la indagación por GUI:
- (1)Velocidad
grep -nrw -e "Velocity [ft/s]" ~/crrcsim-0.9.12/src/GUI OUTPUT: ~/crrcsim-0.9.12/src/GUI/crrc_windthermal.cpp:95: slider_windVelocity->setLabel(_("Velocity [ft/s]")); vi +95 ~/crrcsim-0.9.12/src/GUI/crrc_windthermal.cpp DOMINIO VELOCIDAD: 97 slider_windVelocity->setMinValue(0); 98 slider_windVelocity->setMaxValue(80); NOTAR: 100 slider_windVelocity->setValue(cfg->wind->getVelocity());
- (2)Dirección
grep -nrw -e "Direction" ~/crrcsim-0.9.12/src/GUI OUTPUT: ~/crrcsim-0.9.12/src/GUI/crrc_windthermal.cpp:106: slider_windDir->setLabel(_("Direction")); ~/crrcsim-0.9.12/src/GUI/crrc_gui_viewwind.cpp:162: slider_direction→setLabel(_("Direction (degrees)")); vi +106 ~/crrcsim-0.9.12/src/GUI/crrc_windthermal.cpp DOMINIO Dirección: 108 slider_windDir->setMinValue(0); 109 slider_windDir->setMaxValue(359); NOTAR: 111 slider_windDir->setValue(cfg->wind->getDirection());
- (3)“OK”
grep -nrw -e "OK" ~/crrcsim-0.9.12/src/GUI ~/crrcsim-0.9.12/src/GUI/crrc_windthermal.cpp:386: // Dialog left by clicking OK ~/crrcsim-0.9.12/src/GUI/crrc_gui_viewwind.cpp:235: // Dialog left by clicking OK ~/crrcsim-0.9.12/src/GUI/crrc_dialog.cpp:109: butOK->setLegend(_("OK"));
No encontramos ningún elemento relevante en estas líneas por lo que indagamos el código de forma manual el archivo crrc_windthermal.cpp y buscamos algo que nos indique la salida y encontramos:
329 // exit dialog (otherwise we would have to regenerate presetsThermal) 330 dlg→setValue(CRRC_DIALOG_OK); BUSQUEDA DE “CRRC_DIALOG_OK”: grep -nrw -e "CRRC_DIALOG_OK" ~/crrcsim-0.9.12/src/ OUTPUT: ~/crrcsim-0.9.12/src/GUI/crrc_mousebutton.cpp:131: if (obj->getIntegerValue() == CRRC_DIALOG_OK) ~/crrcsim-0.9.12/src/GUI/crrc_windthermal.cpp:384: if (obj->getIntegerValue() == CRRC_DIALOG_OK)
vi +384 ~/crrcsim-0.9.12/src/GUI/crrc_windthermal.cpp OUTPUT: 382 void CGUIWindThermalCallback(puObject *obj) 383 { 384 if (obj->getIntegerValue() == CRRC_DIALOG_OK) 385 { 386 // Dialog left by clicking OK … 448 }
De los resultados obtenidos observamos que:
- El objeto cfg es un objeto que sirve de puente entre la capa de presentación y la capa lógica por lo que es una clase gestor.
- Slider es la clase de la GUI que representa al dial de la ventana de configuración de windThermal.
- El método CGUIWindThermalCallback() nos indica que es lo que pasa cuando salimos de la ventana de “Wind, Thermals”, lo cual es conveniente porque nos indica todo lo que debemos hacer cuando actualizamos los valores de velocidad y dirección.
Indagación por objetos
Hacemos una búsqueda para encontrar la iniciación de cfg y saber de qué clase es su instancia.
Iniciación de cfg:
grep -nrw -e "cfg = new" ~/crrcsim-0.9.12/src/ OUTPUT: ~/crrcsim-0.9.12/src/crrc_main.cpp:687: cfg = new T_Config(cfgfile); // This will also set up cfgfile
Usos de cfg:
grep -nrw -e "cfg" ~/crrcsim-0.9.12/src/ OUTPUT: ~/crrcsim-0.9.12/src/crrc_loadair.cpp:122: SimpleXMLTransfer* cfg = XMLModelFile::getConfig(xml); ~/crrcsim-0.9.12/src/crrc_main.h:107:extern T_Config* cfg; ~/crrcsim-0.9.12/src/GUI/crrc_windthermal.cpp:57: SimpleXMLTransfer* thermaldata = cfg->getCurLocCfgPtr(cfgfile)->getChild("thermal"); ~/crrcsim-0.9.12/src/GUI/crrc_windthermal.cpp:100: slider_windVelocity->setValue(cfg->wind->getVelocity()); ~/crrcsim-0.9.12/src/GUI/crrc_windthermal.cpp:111: slider_windDir->setValue(cfg->wind->getDirection()); ~/crrcsim-0.9.12/src/GUI/crrc_windthermal.cpp:389: cfg->wind->setDirection(dlg->slider_windDir->getFloatValue(), cfg); ~/crrcsim-0.9.12/src/GUI/crrc_windthermal.cpp:390: cfg→wind→setVelocity(dlg→slider_windVelocity→getFloatValue());
De lo anterior podemos concluir:
- cfg es una instancia de una clase gestor debido a que está inicializado fuera de la capa de presentación y tiene acceso a métodos de la capa lógica como lo son set/get.
- La instancia del objeto wind es el próximo que debemos de investigar ya que es el que recibe los valores dados desde la GUI.
- Se debe de hacer un estudio del método CGUIWindThermalCallback(), ya que este nos puede dar las pautas a seguir a la hora de escribir el código de ráfagas.
- Direction y Velocity son del tipo Float.
Estudio del objeto wind :
grep -nrw -e "wind* *= new" ~/crrcsim-0.9.12/src/ OUTPUT: ~/crrcsim-0.9.12/src/GUI/crrc_windthermal.cpp:278: SimpleXMLTransfer* wind = new SimpleXMLTransfer(); ~/crrcsim-0.9.12/src/config.cpp:262: wind = new T_Wind(); locate T_Wind OUTPUT: none
No se encontró ningún archivo con este nombre por lo que debe ser una clase anónima, i.e. una clase especificada dentro de otra, probamos con grep:
grep -nrw -e "class T_Wind" ~/crrcsim-0.9.12/src/ OUTPUT: ~/crrcsim-0.9.12/src/config.h:121:class T_Wind vi +121 ~/crrcsim-0.9.12/src/config.h OUTPUT: 144 void setVelocity (double val); 151 void setDirection(double val, T_Config* cfg); 153 float getVelocity () { return(velocity); }; 154 float getDirection() { return(direction); };
grep -nrw -e "getVelocity()" ~/crrcsim-0.9.12/src/ OUTPUT: ~/crrcsim-0.9.12/src/mod_windfield/windfield.cpp:42:// cfg->wind->getVelocity() on update and calculate_wind ~/crrcsim-0.9.12/src/mod_windfield/windfield.cpp:552: float flWindVel = dWindVelVar * cfg→wind→getVelocity(); vi +42 ~/crrcsim-0.9.12/src/mod_windfield/windfield.cpp OUTPUT: 42 // cfg->wind->getVelocity() on update and calculate_wind comentado no nos dice nada vi +552 ~/crrcsim-0.9.12/src/mod_windfield/windfield.cpp OUTPUT: 546 void update_thermals(float flDeltaT) 547 { 548 Thermal *thermal_ptr; 549 float x_motion; // How much has a thermal moved in X in the last timest ep 550 float y_motion; // How much has a thermal moved in Y in the last timest ep 551 float x_wind_velocity,y_wind_velocity; 552 float flWindVel = dWindVelVar * cfg->wind->getVelocity(); …
Ya tenemos el nombre de los métodos responsables de manipular la velocidad y dirección del viento ahora vamos a proceder a identificar los sectores del programa donde son invocados, i.e. seguir la pila de llamados. Como el valor de ambos parámetros son necesarios para crear el nuevo estado del juego escogemos seguir la invocación de los métodos getVelocity().
Ahora buscamos quien invoca update_thermals():
grep -nrw -e "update_thermals" ~/crrcsim-0.9.12/src/ OUTPUT: ~/crrcsim-0.9.12/src/SimStateHandler.cpp:81: update_thermals(Global::dt * multiloop); vi +81 ~/crrcsim-0.9.12/src/SimStateHandler.cpp OUTPUT: 25 /** \file SimStateHandler.cpp 26 * 27 * This file includes the implementation of the class 28 * SimStateHandler, which takes care of the current 29 * simulation state (running, paused, ...) and provides 30 * some handy time values. 31 * 32 * \author J. Reucker 33 */ … 49 void idle(TSimInputs* inputs) 50 { … 81 update_thermals(Global::dt * multiloop); … ver apéndice para código completo
Ahora buscamos quien invoca a idle:
grep -nrw -e "idle" ~/crrcsim-0.9.12/src/ OUTPUT: ~/crrcsim-0.9.12/src/SimStateHandler.cpp:261: if ( Global::testmode.test_mode) idle(in); ~/crrcsim-0.9.12/src/SimStateHandler.cpp:267: idle(in); vi +261 ~/crrcsim-0.9.12/src/SimStateHandler.cpp OUTPUT: 257 void SimStateHandler::doIdle(TSimInputs* in)
Ahora quien invoca doIdle:
grep -nrw -e "doIdle" ~/crrcsim-0.9.12/src/ OUTPUT: ~/crrcsim-0.9.12/src/crrc_main.cpp:880: Global::Simulation->doIdle(&Global::inputs);
Llegamos nuevamente al método main esto quiere decir que este llamado es el encargado del estado del juego.
Ya sabemos cómo cambiar los valores de velocidad de viento y dirección:
cfg->wind->setVelocity(<float>); cfg->wind->setDirection(<float>, cfg);
Sabemos cuál es la secuencia que sigue un ciclo de juego de forma general más no granular pero no es necesario por la encapsulación inherente del paradigma orientado a objetos por lo tanto conocemos lo suficiente como para tomar una decisión de donde insertar el código. Finalmente:
Revisión de llamadas de CGUIWindThermalCallback():
grep -nrw "CGUIWindThermalCallback" ~/crrcsim-0.9.12/src/ OUTPUT: ~/crrcsim-0.9.12/src/GUI/crrc_windthermal.cpp:40:static void CGUIWindThermalCallback(puObject *obj); ~/crrcsim-0.9.12/src/GUI/crrc_windthermal.cpp:228: setCallback(CGUIWindThermalCallback); ~/crrcsim-0.9.12/src/GUI/crrc_windthermal.cpp:292: CGUIWindThermalCallback(dlg); ~/crrcsim-0.9.12/src/GUI/crrc_windthermal.cpp:331: CGUIWindThermalCallback(dlg); ~/crrcsim-0.9.12/src/GUI/crrc_windthermal.cpp:382:void CGUIWindThermalCallback(puObject *obj)
Revisión de llamadas de CGUIThermalNewPresetCallback():
grep -nrw "CGUIThermalNewPresetCallback" ~/crrcsim-0.9.12/ OUTPUT: ~/crrcsim-0.9.12/src/GUI/crrc_windthermal.cpp:44:static void CGUIThermalNewPresetCallback(puObject *obj); ~/crrcsim-0.9.12/src/GUI/crrc_windthermal.cpp:222: buttonTmp->setCallback(CGUIThermalNewPresetCallback); ~/crrcsim-0.9.12/src/GUI/crrc_windthermal.cpp:298:void CGUIThermalNewPresetCallback(puObject *obj)
Resultados
Usando la información recolectada podemos generar los siguientes diagramas que indican el flujo de invocaciones de los métodos de interés.
Estado de Juego:
Caso de Uso - cambiar parametros vía GUI:
Caso de Uso - cambio de parametros de viento:
Conclusiones
Por concepto el lugar idóneo para insertar el códgio es en ciclo de juego antes de que se llame a los métodos correspondientes a reloj y actualización del estado de juego.
Se debe de usar CGUIThermalNewPresetCallback() como una guía a la hora de elaborar el algoritmo debido a que este método realiza todos los pasos necesarios para no romper ninguna dependencia interna.
A pesar que el objetivo de definir un algoritmo para proveer la capacidad de ráfagas de viento, se logró encontrar una secuencia de llamados que permite actualizar el estado del viento el cual se usará en la segunda parte del proyecto.
Apéndice
Codigo fuente:
método idle() :
void idle(TSimInputs* inputs) { static int initialization_time = 0; static int time_after_last_integration = 0; // Time after the last integration of EOMs static double dDeltaT = 0; // Difference between display and EOM time int nDeltaTicks; int multiloop; int current_time; /** * One if the aircraft is outside of the windfield simulation, * zero otherwise. */ int nAircraftOutsideWindfieldSim = 0; current_time = Global::Simulation->getTotalTime(); if (Global::Simulation->getState() == STATE_RESUMING) { initialization_time=current_time; current_time = 0; time_after_last_integration=0; Global::Simulation->setState(STATE_RUN); } else current_time -= initialization_time; // compute ticks since last execution of this code (considering pauses): nDeltaTicks = current_time - time_after_last_integration; time_after_last_integration = current_time; // The flight model should be calculated every dt seconds. multiloop=(int)(nDeltaTicks/1000.0/Global::dt - dDeltaT + 0.5); dDeltaT += multiloop*Global::dt - nDeltaTicks/1000.0; update_thermals(Global::dt * multiloop); Global::aircraft->getFDMInterface()->update(inputs, Global::dt, multiloop); Global::Simulation->incSimSteps(multiloop); if (nAircraftOutsideWindfieldSim) Global::verboseString += " Outside windfield simulation!"; double X_cg_rwy = Global::aircraft->getPos().r[0]; double Y_cg_rwy = Global::aircraft->getPos().r[1]; double H_cg_rwy = -1*Global::aircraft->getPos().r[2]; Global::gameHandler->update(X_cg_rwy,Y_cg_rwy,H_cg_rwy, Global::recorder, Global::robots); Global::recorder->AirplanePosition(Global::dt, multiloop, Global::aircraft->getFDMInterface()->fdm); Global::robots->Update(Global::dt, multiloop); if(! Global::testmode.test_mode)//the camera is still on test_mode Video::UpdateCamera(Global::dt * multiloop); Global::TXInterface->update(Global::dt * multiloop); }
método CGUIThermalNewPresetCallback() :
void CGUIWindThermalCallback(puObject *obj) { if (obj->getIntegerValue() == CRRC_DIALOG_OK) { // Dialog left by clicking OK CGUIWindThermalDialog* dlg = (CGUIWindThermalDialog*)obj; cfg->wind->setDirection(dlg->slider_windDir->getFloatValue(), cfg); cfg->wind->setVelocity(dlg->slider_windVelocity->getFloatValue()); SimpleXMLTransfer* thermaldata = cfg->getCurLocCfgPtr(cfgfile)->getChild("thermal"); thermaldata->setAttributeOverwrite("strength_mean", dlg->slider_thermalStrengthMean->getStringValue()); thermaldata->setAttributeOverwrite("strength_sigma", dlg->slider_thermalStrengthSigma->getStringValue()); thermaldata->setAttributeOverwrite("radius_mean", dlg->slider_thermalRadiusMean->getStringValue()); thermaldata->setAttributeOverwrite("radius_sigma", dlg->slider_thermalRadiusSigma->getStringValue()); thermaldata->setAttributeOverwrite("lifetime_mean", dlg->slider_thermalLifetimeMean->getStringValue()); thermaldata->setAttributeOverwrite("lifetime_sigma", dlg->slider_thermalLifetimeSigma->getStringValue()); thermaldata->setAttributeOverwrite("density", dlg->slider_thermalDensity->getStringValue()); // handle v3 data { int n_v3idx = thermaldata->indexOfChild("v3"); if (dlg->thermalv3data == 0) { if (n_v3idx >= 0) // v3 has to be deleted after loading a preset without v3: { std::cout << "Chosen preset is thermal model v1, so v3 data is removed.\n"; SimpleXMLTransfer* tmp = thermaldata->getChildAt(n_v3idx); thermaldata->removeChildAt(n_v3idx); delete tmp; } }else{ // there is no v3 data yet? if (n_v3idx < 0) { std::cout << "Load data for thermal model v3.\n"; thermaldata->addChild(new SimpleXMLTransfer(dlg->thermalv3data)); }else{ if (dlg->thermalv3data != thermaldata->getChildAt(n_v3idx)) { std::cout << "Chosen preset is thermal model v3 and we currently have v3 which differs from chosen preset. Remove current data.\n"; SimpleXMLTransfer* tmp = thermaldata->getChildAt(n_v3idx); thermaldata->removeChildAt(n_v3idx); delete tmp; std::cout << "Load data for thermal model v3.\n"; thermaldata->addChild(new SimpleXMLTransfer(dlg->thermalv3data)); } } } } // Thermals have to be recreated: clear_wind_field(); cfg->thermal->read(cfgfile, cfg); Init_mod_windfield(); } std::cout.flush(); puDeleteObject(obj); }
Presentación: presentacion.pdf
Referencias
Monteiro Rodrigo (2010, August 17,). Understanding the Game Main Loop, extraido de http://higherorderfun.com/blog/2010/08/17/understanding-the-game-main-loop/
Neil, D. (2012). Practical Vim : edit text at the speed of thought. Dallas, Texas: The Pragmatic Bookshelf.
Lippman, S. , Lajoie, J. & Moo, B.E. (2012, August 16). C++ Premier, 5th Edition. Boston, Massachusetts: Addison-Wesley Professional.