seashell_model.h

00001 #ifndef SEASHELL_MODEL_H
00002 #define SEASHELL_MODEL_H
00003 
00004 #include <vve.h>
00005 
00006 #include <util/parms.h>
00007 #include <util/palette.h>
00008 
00009 #include <algorithms/solver.h>
00010 #include <util/vector.h>
00011 #include <util/random.h>
00012 #include <cmath>
00013 #include <string.h>
00014 #include <time.h>
00015 #include <vector>
00016 
00017 #include <iostream>
00018 
00019 #ifdef WIN32
00020 #ifndef _MSC_VER
00021 # include <GL/glext.h>
00022 #endif
00023 #endif
00024 
00025 #ifndef VertexAttributes
00026 #  define VertexAttributes
00027 #endif
00028 
00029 #ifndef EdgeAttributes
00030 #  define EdgeAttributes
00031 #endif
00032 
00033 #ifndef ModelInit
00034 #  define ModelInit
00035 #endif
00036 
00037 using util::ran;
00038 typedef util::Palette::Color Color;
00039 
00040 namespace seashell_model
00041 {
00042   using std::cout;
00043   using std::cerr;
00044   using std::endl;
00045 
00046   void reportGLError( const char* id )
00047   {
00048     GLenum error = glGetError();
00049     if(error)
00050     {
00051       cerr << "OpenGL error on " << id << ":" << endl
00052         << gluErrorString(error) << endl;
00053     }
00054   }
00055 
00056   typedef util::Vector<2,double> Point2d;
00057   typedef util::Vector<3,double> Point3d;
00058 
00059   typedef util::Vector<NB_CHEMICALS,double> Chemicals;
00060 
00061   typedef solver::Solver<NB_CHEMICALS> RDSolver;
00062 
00063   typedef RDSolver::tag_t rd_tag_t;
00064 
00065   struct VertexContent
00066   {
00067     Chemicals chems, derivs;
00068     RDSolver::VertexInternals interns;
00069     int pos;       // Position of the vertex
00070     VertexAttributes
00071   };
00072 
00073   struct EdgeContent
00074   {
00075     RDSolver::EdgeInternals interns;
00076     EdgeAttributes
00077   };
00078 
00079   // Class defining your model
00080   // If you change the name, don't forget to change the last line of the file too
00081   class SeashellModel : public Model
00082   {
00083   public:
00084     typedef seashell_model::Chemicals Chemicals;
00085     typedef seashell_model::RDSolver RDSolver;
00086     typedef seashell_model::rd_tag_t rd_tag_t;
00087 
00088     // Type of the graph
00089     typedef VVGraph<VertexContent,EdgeContent> vvgraph;
00090     // Type of a vertex
00091     typedef vvgraph::vertex_t vertex;
00092     // Type of an edge
00093     typedef vvgraph::edge_t edge;
00094 
00095     util::Palette palette;
00096     vvgraph LiveCells;
00097     RDSolver solve;
00098     int nbRows;
00099     int currentTextureHeight;
00100     int currentTextureWidth;
00101     int textureChunk;
00102     int textureMaxSize;
00103 
00104     std::vector<GLuint> texId;
00105 
00106     GLubyte *texture;
00107 
00108     int nbCells;
00109     int nbTextures;
00110     std::vector<int> posTextures;
00111     int cellsPerTextures;
00112     double cellWidth;
00113     double cellHeight;
00114     double time;
00115     double timeDeath;
00116     double timePrint;
00117     double timeDraw;
00118     double printInterval;
00119     double drawInterval;
00120 
00121     double cellDeath;
00122 
00123     int colorLow;             // Color for low concentrations of activator
00124     int colorHigh;            // Color for high concentrations of activator
00125     double maxView;
00126 
00127     double background;
00128 
00129     // Read parameters
00130     int readNbCells;
00131     double readCellDeath;
00132     double readMaxView;
00133     int readTextureChunk;
00134     int randomSeed;
00135 
00136     bool stop;
00137     bool circle;
00138 
00139     void readParms()
00140     {
00141       util::Parms parms("view.v");
00142 
00143       parms("Main", "PrintInterval", printInterval);
00144       parms("Main", "DrawInterval", drawInterval);
00145       parms("Main", "RandomSeed", randomSeed);
00146 
00147       solve.readParms(parms, "Solver");
00148 
00149       // Read seashell parameters
00150       parms("Seashell", "NbCells", readNbCells);
00151       parms("Seashell", "CellWidth", cellWidth);
00152       parms("Seashell", "CellHeight", cellHeight);
00153       parms("Seashell", "CellDeath", readCellDeath);
00154       parms("Seashell", "Circle", circle);
00155 
00156       // View parameters
00157       parms("View", "ColorLow", colorLow);
00158       parms("View", "ColorHigh", colorHigh);
00159       parms("View", "MaxView", readMaxView);
00160       parms("View", "TextureChunk", readTextureChunk);
00161       parms("View", "Background", background);
00162 
00163       readParam( parms );
00164 
00165       if(cellDeath > 0)
00166         solve.MaxDt = std::min(std::min(solve.MaxDt, cellDeath), drawInterval);
00167       else
00168         solve.MaxDt = std::min(std::min(solve.MaxDt, readCellDeath), drawInterval);
00169     }
00170 
00171     // Here, reread the files when they are modified
00172     void modifiedFiles( const std::set<std::string>& filenames )
00173     {
00174       forall(const std::string& fn, filenames)
00175       {
00176         if(fn == "pal.map")
00177           palette.reread();
00178         else if(fn == "view.v")
00179           readParms();
00180       }
00181     }
00182 
00183     ~SeashellModel()
00184     {
00185       if(!texId.empty())
00186         glDeleteTextures(nbTextures, &texId[0]);
00187       if(texture)
00188         delete texture;
00189     }
00190 
00191     virtual void initCell(vertex v) = 0;
00192 
00193     SeashellModel(QObject *parent)
00194       : Model(parent)
00195       , palette("pal.map")
00196       , nbRows(0)
00197       , currentTextureHeight(0)
00198       , currentTextureWidth(0)
00199       , textureChunk(0)
00200       , textureMaxSize(0)
00201       , texture(0)
00202       , nbCells(0)
00203       , cellDeath(0)
00204       , stop(false)
00205       { }
00206 
00207     void init()
00208     {
00209       readParms();
00210       // Registering the configuration files
00211       registerFile("pal.map");
00212       registerFile("view.v");
00213 
00214       if(randomSeed == 0)
00215       {
00216         util::sran(::time(NULL));
00217       }
00218       else
00219       {
00220         util::sran(randomSeed);
00221       }
00222 
00223       nbCells = readNbCells;
00224       cellDeath = readCellDeath;
00225       maxView = readMaxView;
00226       textureChunk = readTextureChunk;
00227 
00228       solve.initialize();
00229 
00230       // Initialize your structure here
00231       time = 0;
00232       timeDeath = 0;
00233       timePrint = printInterval;
00234       timeDraw = 0;
00235 
00236       vertex pv(0);
00237       vertex fv(0);
00238       for(int i = 0 ; i < nbCells ; ++i)
00239       {
00240         vertex v;
00241         v->pos = i;
00242         LiveCells.insert(v);
00243         if(!pv.isNull())
00244         {
00245           LiveCells.insertEdge(v, pv);
00246           LiveCells.insertEdge(pv, v);
00247         }
00248         else
00249         {
00250           fv = v;
00251         }
00252         initCell(v);
00253         pv = v;
00254       }
00255       if(circle)
00256       {
00257         LiveCells.insertEdge(pv, fv);
00258         LiveCells.insertEdge(fv, pv);
00259       }
00260     }
00261 
00262     void createTexture()
00263     {
00264       // Setting up the new texture buffer
00265       int new_height = currentTextureHeight + textureChunk;
00266       // First, test I can create the texture
00267       glTexImage2D(GL_PROXY_TEXTURE_2D, 0, GL_RGBA, currentTextureWidth, new_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture);
00268       reportGLError("glTexImage2D(GL_PROXY_TEXTURE_2D)");
00269       GLint success;
00270       glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &success);
00271       if(!success)
00272       {
00273         cerr << "Texture too big, stopping model" << endl;
00274         stop = true;
00275         return;
00276       }
00277       GLubyte *new_texture = new GLubyte[nbCells*new_height*4];
00278       memset(new_texture, 0, nbCells*new_height*4);
00279       if(texture)
00280       {
00281         memcpy(new_texture, texture, nbCells*currentTextureHeight*4);
00282         delete [] texture;
00283       }
00284       texture = new_texture;
00285       currentTextureHeight = new_height;
00286 
00287       // Setting up the new texture
00288       if(texId.empty())
00289       {
00290         texId.resize(nbTextures);
00291         glGenTextures(nbTextures, &texId[0]);
00292         reportGLError("glGenTextures");
00293       }
00294       for(int i = 0 ; i < nbTextures ; ++i)
00295       {
00296         glBindTexture(GL_TEXTURE_2D, texId[i]);
00297         reportGLError("glBindTexture(texId)");
00298         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
00299         reportGLError("glPixelStorei(GL_UNPACK_ALIGNMENT)");
00300         glPixelStorei(GL_UNPACK_ROW_LENGTH, nbCells);
00301         reportGLError("glPixelStorei(GL_UNPACK_ROW_LENGTH)");
00302         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
00303         reportGLError("glTexParameteri(GL_TEXTURE_MAG_FILTER)");
00304         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
00305         reportGLError("glTexParameteri(GL_TEXTURE_MIN_FILTER)");
00306         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
00307         reportGLError("glTexParameteri(GL_TEXTURE_WRAP_S)");
00308         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
00309         reportGLError("glTexParameteri(GL_TEXTURE_WRAP_T)");
00310         //    cout << "Allocating texture: " << texId << " " << currentTextureWidth << "x" << currentTextureHeight << ": " << (int*)texture << endl;
00311         //    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, currentTextureWidth, currentTextureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture);
00312         //    reportGLError("glTexImage2D");
00313       }
00314       glBindTexture(GL_TEXTURE_2D, 0);
00315       reportGLError("glBindTexture(0)");
00316     }
00317 
00318     virtual Color getColor(const Chemicals& chems)
00319     {
00320       return palette.getColor(std::max(0, int(255*chems[0] / maxView)));
00321     }
00322 
00323     void drawCellsToTexture(int pos)
00324     {
00325       if(pos >= currentTextureHeight)
00326       {
00327         createTexture();
00328         if(stop)
00329           return;
00330       }
00331       int offset = nbCells*pos*4;
00332       GLubyte *texture_line = texture + offset;
00333       forall(const vertex& v, LiveCells)
00334       {
00335         //      Color c = palette.getColor(colorLow, colorHigh, v->chems[0] / maxView);
00336         Color c = getColor(v->chems);
00337         memcpy(texture_line+4*v->pos, c.c_data(), 4);
00338       }
00339       for(int i = 0 ; i < nbTextures ; ++i)
00340       {
00341         glBindTexture(GL_TEXTURE_2D, texId[i]);
00342         offset = posTextures[i]*4;
00343         reportGLError("glBindTexture(texId)");
00344         int width = currentTextureWidth;
00345         if(i == nbTextures-1)
00346         {
00347           width = nbCells - posTextures[i];
00348         }
00349         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, currentTextureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture+offset);
00350         reportGLError("glTexImage2D(GL_TEXTURE_2D)");
00351       }
00352       glBindTexture(GL_TEXTURE_2D, 0);
00353       reportGLError("glBindTexture(0)");
00354     }
00355 
00356     void step()
00357     {
00358       while(timeDraw < drawInterval)
00359       {
00360         if(stop)
00361           return;
00362         solve(LiveCells, *this);
00363         time += solve.dt;
00364         timeDeath += solve.dt;
00365         timePrint += solve.dt;
00366         timeDraw += solve.dt;
00367         if(timeDeath > cellDeath)
00368         {
00369           if(nbRows > textureMaxSize)
00370           {
00371             stop = true;
00372             return;
00373           }
00374           timeDeath -= cellDeath;
00375           drawCellsToTexture(nbRows);
00376           if(stop)
00377             return;
00378           ++nbRows;
00379         }
00380         if(timePrint >= printInterval)
00381         {
00382           timePrint -= printInterval;
00383           Chemicals minC, maxC;
00384           for(int i = 0 ; i < NB_CHEMICALS ; ++i)
00385           {
00386             minC[i] = HUGE_VAL;
00387             maxC[i] = -HUGE_VAL;
00388           }
00389           forall(const vertex& c, LiveCells)
00390           {
00391             for(int i = 0 ; i < NB_CHEMICALS ; ++i)
00392             {
00393               if(minC[i] > c->chems[i])
00394                 minC[i] = c->chems[i];
00395               if(maxC[i] < c->chems[i])
00396                 maxC[i] = c->chems[i];
00397             }
00398           }
00399           cout << "Time: " << time << " - Current dt: " << solve.dt << endl;
00400           for(int i = 0 ; i < NB_CHEMICALS ; ++i)
00401           {
00402             cout << "   Chemical " << i << ": " << minC[i] << " - " << maxC[i] << endl;
00403           }
00404         }
00405       }
00406       timeDraw -= drawInterval;
00407     }
00408 
00409     void updateTextureSizes()
00410     {
00411       if(nbCells > textureMaxSize)
00412       {
00413         //      cerr << "Error, too many cells to render the result as a texture. Setting the number of cells to " << textureMaxSize << endl;
00414         nbTextures = nbCells / (textureMaxSize-2) + 1;
00415         cellsPerTextures = nbCells / nbTextures + 2;
00416         posTextures.resize(nbTextures);
00417         posTextures[0] = 0;
00418 //        cout << "We have " << nbTextures << " textures for " << nbCells << " cells." << endl;
00419 //        cout << "Size of textures: " << cellsPerTextures << endl;
00420         for(int i = 1 ; i < nbTextures ; ++i)
00421         {
00422           posTextures[i] = posTextures[i-1] + cellsPerTextures - 2;
00423 //          cout << "Position of texture " << i << ": " << posTextures[i] << endl;
00424         }
00425       }
00426       else
00427       {
00428         nbTextures = 1;
00429         cellsPerTextures = nbCells;
00430         posTextures.resize(1);
00431         posTextures[0] = 0;
00432       }
00433       currentTextureWidth = cellsPerTextures;
00434     }
00435 
00436     void initDraw( Viewer* viewer )
00437     {
00438       glEnable(GL_TEXTURE_2D);
00439       reportGLError("glEnable(GL_TEXTURE_2D)");
00440       glGetIntegerv(GL_MAX_TEXTURE_SIZE, &textureMaxSize);
00441       reportGLError("glGetIntegerv(GL_MAX_TEXTURE_SIZE)");
00442       updateTextureSizes();
00443       viewer->camera()->setType(Camera::ORTHOGRAPHIC);
00444       glClearColor(background, background, background, 0.0);
00445       if(!texId.empty())
00446       {
00447         glDeleteTextures(nbTextures, &texId[0]);
00448         texId.clear();
00449       }
00450       if(texture)
00451       {
00452         delete [] texture;
00453         texture = 0;
00454         currentTextureHeight = 0;
00455       }
00456     }
00457 
00458     void preDraw()
00459     {
00460       glClearColor(background, background, background, 0.0);
00461     }
00462 
00463     void postDraw()
00464     {
00465     }
00466 
00467     void draw(Viewer* viewer)
00468     {
00469       double width = nbCells*cellWidth;
00470       Vec vmin(0,0,-width/10), vmax(width, (nbRows+1)*cellHeight, width/10);
00471       viewer->setSceneBoundingBox(vmin, vmax);
00472       //if(time == 0)
00473       //  return;
00474       if(!stop)
00475         drawCellsToTexture(nbRows);
00476       for(int i = 0 ; i < nbTextures ; ++i)
00477       {
00478         double texWidth = currentTextureWidth*cellWidth;
00479         if(i == nbTextures-1)
00480         {
00481           texWidth = (nbCells-posTextures[i])*cellWidth;
00482         }
00483         double left = posTextures[i]*cellWidth;
00484         double right = left+texWidth;
00485         double left_tex = 1/texWidth;
00486         double right_tex = 1-1/texWidth;
00487         if(i == 0)
00488         {
00489           left_tex = 0;
00490         }
00491         else
00492         {
00493           left += cellWidth;
00494         }
00495         if(i == nbTextures-1)
00496         {
00497           right_tex=1;
00498         }
00499         else
00500         {
00501           right -= cellWidth;
00502         }
00503         glBindTexture(GL_TEXTURE_2D, texId[i]);
00504         reportGLError("glBindTexture(texId)");
00505         glBegin(GL_QUADS);
00506         glTexCoord2f(left_tex,0);
00507         glVertex3f(left,0,0);
00508         glTexCoord2f(right_tex,0);
00509         glVertex3f(right, 0,0);
00510         glTexCoord2f(right_tex,1);
00511         glVertex3f(right, currentTextureHeight*cellHeight,0);
00512         glTexCoord2f(left_tex,1);
00513         glVertex3f(left,currentTextureHeight*cellHeight,0);
00514         glEnd();
00515       }
00516       glBindTexture(GL_TEXTURE_2D, 0);
00517       reportGLError("glBindTexture(0)");
00518       if(stop)
00519         viewer->stopAnimation();
00520     }
00521 
00522     // Methods for the solver
00523     Chemicals& values(const vertex& v, const rd_tag_t&)
00524     {
00525       return v->chems;
00526     }
00527 
00528     Chemicals& derivatives(const vertex& v, const rd_tag_t&)
00529     {
00530       return v->derivs;
00531     }
00532 
00533     RDSolver::VertexInternals& vertexInternals(const vertex& v, const rd_tag_t&)
00534     {
00535       return v->interns;
00536     }
00537 
00538     RDSolver::EdgeInternals& edgeInternals(const vertex& src, const vertex& tgt, vvgraph& S, const rd_tag_t&)
00539     {
00540       return S.edge(src, tgt)->interns;
00541     }
00542 
00543     virtual void updateDerivatives(const vertex& v, const rd_tag_t&) = 0;
00544 
00545     virtual void initialize() {}
00546 
00547     virtual void readParam( util::Parms& ) {}
00548   };
00549 
00550 }
00551 
00552 #include <QContextMenuEvent>
00553 #include <QAction>
00554 #include <QFileDialog>
00555 #include <QMenu>
00556 #include <QImageWriter>
00557 
00558 namespace seashell_model
00559 {
00560   class SeashellViewer : public Viewer
00561   {
00562     Q_OBJECT
00563   public:
00564       SeashellViewer(QWidget* parent)
00565         : Viewer(parent)
00566         {
00567           saveTextureAction = new QAction("Save texture", this);
00568           connect(saveTextureAction, SIGNAL(activated()), SLOT(saveTexture()));
00569         }
00570       public slots:
00571         void saveTexture()
00572         {
00573           SeashellModel *m = dynamic_cast<SeashellModel*>(model());
00574           QList<QByteArray> sf = QImageWriter::supportedImageFormats();
00575           QStringList supportedFormats;
00576           QStringList formats;
00577           forall(const QByteArray& f, sf)
00578           {
00579             QString qf = QString::fromLocal8Bit(f);
00580             supportedFormats += qf;
00581           }
00582           formats += "All images ( *." + supportedFormats.join(" *.") + " )";
00583           forall(const QString& f, supportedFormats)
00584           {
00585             formats += f.toUpper() + " images ( *." + f + ")";
00586           }
00587           formats += "All files (*.*)";
00588           QString filters = formats.join(";;");
00589           QString filename = QFileDialog::getSaveFileName(this, "Saving texture image", QString(), filters);
00590           if(!filename.isEmpty())
00591           {
00592             QImage img(m->texture, m->nbCells, m->nbRows+1, QImage::Format_RGB32);
00593             img = img.rgbSwapped();
00594             QImageWriter writer(filename);
00595             writer.write(img);
00596           }
00597         }
00598 
00599   protected:
00600 
00601       void contextMenuEvent( QContextMenuEvent* event )
00602       {
00603         QMenu *popup = new QMenu(this);
00604         popup->addAction(saveTextureAction);
00605         popup->popup(event->globalPos());
00606       }
00607 
00608       QAction *saveTextureAction;
00609   };
00610 
00611 }
00612 
00613 #include <seashell_model.moc>
00614 
00615 DEFINE_VIEWER(seashell_model::SeashellViewer);
00616 
00617 #define StartModel \
00618   struct ModelClass : public seashell_model::SeashellModel \
00619   { \
00620     typedef seashell_model::SeashellModel ParentClass; \
00621     ModelClass(QObject *parent) \
00622       : ParentClass(parent) \
00623       ModelInit \
00624     { \
00625       init(); \
00626       initialize(); \
00627     }
00628 
00629 #define EndModel }; DEFINE_MODEL(ModelClass);
00630 
00631 #endif // SEASHELL_MODEL_H
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines
Generated on Fri May 31 15:37:51 2013 for VVE by  doxygen 1.6.3