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;
00070 VertexAttributes
00071 };
00072
00073 struct EdgeContent
00074 {
00075 RDSolver::EdgeInternals interns;
00076 EdgeAttributes
00077 };
00078
00079
00080
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
00089 typedef VVGraph<VertexContent,EdgeContent> vvgraph;
00090
00091 typedef vvgraph::vertex_t vertex;
00092
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;
00124 int colorHigh;
00125 double maxView;
00126
00127 double background;
00128
00129
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
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
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
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
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
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
00265 int new_height = currentTextureHeight + textureChunk;
00266
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
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
00311
00312
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
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
00414 nbTextures = nbCells / (textureMaxSize-2) + 1;
00415 cellsPerTextures = nbCells / nbTextures + 2;
00416 posTextures.resize(nbTextures);
00417 posTextures[0] = 0;
00418
00419
00420 for(int i = 1 ; i < nbTextures ; ++i)
00421 {
00422 posTextures[i] = posTextures[i-1] + cellsPerTextures - 2;
00423
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
00473
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
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