2011-02-28 <guravage@petitdru.sen.cwi.nl>
* canvas.cpp (about): Correct citation text.
Resolves issue #4: http://code.google.com/p/virtualleaf/issues/detail?id=4.
2011-01-13 <guravage@petitdru.sen.cwi.nl>
* transporterdialog.cpp (TransporterDialog): Use wn to discern
which cell a wallitem belongs to.
* wallitem.cpp (WallItem): Add a comment explaining how
wallnumber(wn) can be used to tell which cell a wallitem belongs
to.
* VirtualLeaf-install-windows.nsi: Remove previous change that added icon directory.
* canvas.cpp: Instead of using a separate icon directory, compile
leaficon_small.xpm directly into canvas.cpp.
* VirtualLeaf-install-windows.nsi: Add new icons directory
* canvas.cpp (about): Added virtual leaf logo to About message box.
2011-01-12 <guravage@petitdru.sen.cwi.nl>
* transporterdialog.cpp (TransporterDialog): Add a proper title to
the dialog, and place the cell and wall pairs in a seperate labe.
2011-01-10 <guravage@petitdru.sen.cwi.nl>
* transporterdialog.cpp (TransporterDialog): Start numbering Transporter fields at zero.
Resolves issue #1: http://code.google.com/p/virtualleaf/issues/detail?id=1.
* wallitem.cpp (OnClick): Change dialog modality to Qt::WindowModal.
Resolves issue #2: http://code.google.com/p/virtualleaf/issues/detail?id=2.
* canvas.cpp (about): Add link to Plant Physiology paper.
Resolves issue #3: http://code.google.com/p/virtualleaf/issues/detail?id=3.
2010-11-30 <guravage@petitdru.sen.cwi.nl>
* canvas.cpp (snapshot): Preface each image format extension with
an asterisk so files with those extensions to appear in the file
dialogue.
(snapshot): Forgot to add asterisk before our own pdf format.
* VirtualLeaf-install-windows.nsi: Include imageformats folder
alongside executable.
* canvas.cpp (snapshot): PNG is the default image format.
2010-11-29 <guravage@petitdru.sen.cwi.nl>
* canvas.cpp (snapshot): Query and display supported snapshot file
formats.
* VirtualLeaf-install-windows.nsi: To facilitate debugging I added
the debug versions of all the windows DLLs.
2010-10-19 <guravage@caterpie.sen.cwi.nl>
* mainbase.cpp (Save): Use format specified in function prototype.
* VirtualLeaf.cpp (Plot): Render PNG instead of JPEG. Write image
and XML files dependent only on getTime().
2010-10-18 <guravage@caterpie.sen.cwi.nl>
* mainbase.cpp (Save): QDir::toNativeSeparators(fname). Invoke
save with format argument set to zero to force QImage to guess the
format by looking at fileName's suffix.
* VirtualLeaf-install.nsi: Put uninstaller in top directory.
* modelcatalogue.cpp (InstallModel): For all OS-es. Move from "bin" directory to root application folder.
* VirtualLeaf.pro: MAXOSX, look for gpl3.txt in the ../doc directory.
* Makefile (Makefile.libplugin): Removed -makefile qmake option.
* VirtualLeaf.cpp (TIMESTEP): Removed getIterations().
* mesh.h (Mesh): Removed {increment,get}Iterations().
* canvas.cpp (TimeStepWrap): Replaced getIterations() with getTime().
2010-10-15 <guravage@caterpie.sen.cwi.nl>
* mesh.h (Mesh): Added iterations. incrementIterations() and
getIterations().
* VirtualLeaf.cpp (Plot): Replaced local frame counter with
mesh.incrementIterations().
* canvas.cpp (TimeStepWrap): Replaced local counter with
2010-10-14 <guravage@caterpie.sen.cwi.nl>
* VirtualLeaf.pro: Turned debug off to make all profiles must be
consistant.
* VirtualLeaf-install.nsi: Tweaked paths to coincide with UNIX
distribution, i.e. VirtualLeaf.exe, its libraries, models, and the
uninstaller all go in the bin directory. And leaves directory
placed under data directory.
* canvas.cpp (exportCellData): Added a check to inquire before
overwritting an existing file.
2010-10-14 <merks@cwi.nl>
* mesh.cpp: In response to referees' comments, added new parameter
"yield_threshold" instead of fixed parameter '4' for yield
threshold.
* wallitem.cpp: when clicking a wall, both the wall type was
cycled and the transporterdialog popped up. Corrected this - for
wall type cycling, hold the Control button while left
clicking. TransporterDialog only pops up for left click. Also made
sure the wall is redrawn after changing the transporter values.
* cell.cpp (DivideWalls): accomodated for rename of Circumference -> WallCircumference
* hull.h: added an operator< to sort Points
* hull.cpp: added an operator< to sort Points
* cellbase.cpp (ExactCircumference): I added a new function
ExactCircumference, yielding the circumference of the cell along
its wall_elements
* VirtualLeaf.cpp: adjust info_string to accomodate for new name
of function CellBase::Circumference -> CellBase::WallCircumference
* mesh.cpp: corrected Mesh::Compactness, the boundary coordinates
need to be sorted in x,y order for the convex hull algorithm
(thanks Margriet!). I updated CSVExportCellData so it exports the
circumferences of hull and boundary_polygon.
* VirtualLeafpar.tmpl (export_fn_prefix): changed to 'cell.'
* canvas.cpp (TimeStepWrap): Removed TimeStamp(). Cell data
filename now incorporates iteration number.
* canvas.h (MainBase): Removed TimeStamp().
2010-10-08 <guravage@caterpie.sen.cwi.nl>
* pardialog.h:
* pardialog.cpp:
* parameter.h:
* parameter.cpp: Regenerated to include export_interval and export_fn_prefix.
* VirtualLeafpar.tmpl: Appended export_interval and export_fn_prefix.
* canvas.h (MainBase): Declared polymorphic exportCellData() functions.
* canvas.cpp:
(TimeStamp): New private TimeStamp() function.
(TimeStepWrap): Added invocation of exportCellData().
(exportCellData): Created two polymorphic functions: one with a
single QString argument, the other with no argument. The former is
called from TimeStepWrap() while the latter is called from the
"Export cell areas" item in the file menu.
2010-10-07 <guravage@caterpie.sen.cwi.nl>
* canvas.cpp (exportCellData): Added a Q3FileDialog to inquire
where to write the exportCellData.
2010-06-28 <guravage@caterpie.sen.cwi.nl>
* VirtualLeaf-install.nsi: Grab gpl3.txt from doc directory.
* canvas.cpp (gpl): gpl3.txt can be either in an ancestor doc
directory (Linux) or a decedent doc directory (Windows, via the
binary installer).
* VirtualLeaf-install.nsi: Add VirtualLeaf doc directory.
2010-06-25 <guravage@caterpie.sen.cwi.nl>
* gpl3.txt: Moved gpl3.txt from doc to src directory.
* VirtualLeaf.pro: Added -Wno-write-strings and -Wno-unused-parameter to QMAKE_CXXFLAGS.
* libplugin.pro: Ditto.
* parameter.cpp: Result of adding datadir changes to make_parameter_source.pl.
* parameter.h: Ditto.
* output.h: Declared new function (AppendHomeDirIfPathRelative).
* output.cpp (AppendHomeDirIfPathRelative): Added new function.
* canvas.cpp (gpl): Moving gpl3.txt from doc to src obviates the need to docDir.cd("../doc").
* VirtualLeaf-install.nsi: Grab gpl3.txt from src directory.
Add missing libiconv/bin, libxml2bin and libz/bin directories.
Copy libiconv-2.dll, libxml2.dll and zlib1.dll from relative paths.
* VirtualLeaf.pro: copy gpl3.txt as part of QMAKE_POST_LINK.
2010-06-24 <guravage@caterpie.sen.cwi.nl>
* libplugin.pro: Use correct library path.
* VirtualLeaf.pro: Ditto.
* VirtualLeaf.cpp (DrawCell): Iterate over NChem to construct info_string.
2010-06-23 <guravage@caterpie.sen.cwi.nl>
* simitembase.cpp: Removed NULL assignments to unused variables.
* VirtualLeaf.cpp: Ditto.
* apoplastitem.cpp: Ditto.
* canvas.cpp: Ditto.
* cell.cpp: Ditto.
* cellbase.h: Ditto.
* forwardeuler.cpp: Ditto.
* mainbase.h: Ditto.
* nodeitem.cpp: Ditto.
* qcanvasarrow.h: Ditto.
* simitembase.cpp: Ditto.
* Makefile (clean): Add -f Makefile argument to each make invocation.
* VirtualLeaf-install.nsi: New gpl license text.
* VirtualLeaf.pro: Disabled console mode.
* mesh.cpp (Clear): Added parentheses to qDebug statments.
(TestIllegalWalls): Replaced qDebug().
* canvas.cpp (mouseReleaseEvent): Replaced qDebug() with cerr since qDebug complains about *node_set.
* wall.cpp (CorrectWall): Rplaced gDebug() with cerr in transform call and when printing *this.
2010-06-22 <guravage@caterpie.sen.cwi.nl>
* Makefile (tutorials): Add tutorials target.
2010-06-21 <guravage@caterpie.sen.cwi.nl>
* parameter.cpp: Added particular reassignment of datadir.
* canvas.cpp (gpl): Added GPL3 License text. Display detail text only if the source text file exists.
2010-06-18 <guravage@caterpie.sen.cwi.nl>
* canvas.cpp (gpl): Added gpl slot to display GPL license.
* VirtualLeaf.pro: Changed default LIBXML2DIR, LIBICONVDIR and LIBZDIR to corresponding distribution lib directories.
* Makefile (clean): add if stmt not to `touch` on windows.
2010-06-17 <guravage@caterpie.sen.cwi.nl>
* VirtualLeaf.pro: Removed perl references.
2010-06-15 <guravage@caterpie.sen.cwi.nl>
* VirtualLeaf.pro: Removed xmlwritecode.cpp from SOURCES list.
* xmlwrite.cpp (XMLSave): Removed references to XMLWriteLeafSourceCode and XMLWriteReactionsCode.
* xmlwrite.h (XMLIO): Ditto!
* mesh.cpp (findNextBoundaryNode): Initialize Node *next_boundary_node = NULL;
* xmlwrite.cpp (XMLReadSimtime): Removed unused variable cur
(XMLReadWalls): viz_flux need not be declared twice; default value of 0.0.
(XMLReadCells): Removed unused count variable.
(XMLReadSimtime): Removed unused cur variable.
(XMLRead): Removed unused v_str variable.
* simitembase.cpp (userMove): Use assignment merely to obviate compilation warning.
(SimItemBase) Ditto.
* qcanvasarrow.h (QGraphicsLineItem): Use assignment merely to obviate compilation warning.
* output.cpp (OpenWriteFile): Removed unused par variable.
* nodeitem.cpp (paint): Use assignment merely to obviate compilation warning.
* forwardeuler.cpp (odeint): Use assignment merely to obviate compilation warning.
* cell.cpp (DivideOverGivenLine): Use assignment merely to obviate compilation warning.
* canvas.cpp (FigureEditor): Use assignments merely to obviate compilation errors.
(mousePressEvent): Removed unused item variable.
* apoplastitem.cpp
(ApoplastItem): Removed unused par variable.
(OnClick): Use NULL assignment merely to obviate compilation warning.
* mainbase.h (MainBase): Use assignment merely to obviate compilation warning.
* cellbase.h (CellsStaticDatamembers): Use assignment merely to obviate compilation warning.
* cell.cpp: Wrapped diagnostic output in QDEBUG blocks.
* VirtualLeaf.cpp ditto.
* canvas.cpp ditto.
* cell.cpp ditto.
* data_plot.cpp ditto.
* forwardeuler.cpp ditto.
* mesh.cpp ditto.
* mesh.h
* random.cpp ditto.
* wall.cpp ditto.
* wallbase.cpp ditto.
* wallitem.cpp ditto.
2010-06-07 <guravage@caterpie.sen.cwi.nl>
* VirtualLeaf.pro: Removed explicit perl invocation to regerenerate parameter files.
* libplugin.pro: ditto.
2010-06-03 <guravage@caterpie.sen.cwi.nl>
* pardialog.h: Added default versions of this automatically generated file.
* pardialog.cpp: ditto.
* parameter.h: ditto.
* parameter.cpp: ditto.
* VirtualLeaf.pro: delete/generate parameter.{h,cpp}and pardialog.{h,cpp} only if perl is installed.
* libplugin.pro: dito.
* Makefile: Added top-level Makefile
2010-05-10 <guravage@caterpie.sen.cwi.nl>
* VirtualLeaf.pro: Added -fPIC option to QMAKE_CXXFLAGS.
@@ -281,1286 +281,1286 @@ void FigureEditor::mouseReleaseEvent(QMo
#ifdef QDEBUG
qDebug() << "Trying to cut leaf" << endl;
#endif
QPointF sp = intersection_line -> line().p1(); // startpoint
QPointF ep = mapToScene(e->pos());
intersection_line -> setLine( QLineF(sp, ep) );
intersection_line -> show();
vector <CellItem *> intersected_cells = getIntersectedCells();
// no cells selected, do nothing
if (intersected_cells.size()==0) {
qDebug() << "No cells detected :-(" << endl;
return;
}
Vector startpoint = Vector(sp.x(), sp.y()) / Cell::Factor() - Cell::Offset();
Vector endpoint = Vector(ep.x(), ep.y()) / Cell::Factor() - Cell::Offset();
NodeSet *node_set = new NodeSet;
for (vector<CellItem *>::iterator it = intersected_cells.begin(); it != intersected_cells.end(); it++) {
(*it)->setBrush(QBrush("purple"));
Cell &c=(*it)->getCell();
// sometimes the cell hasn't properly divided yet before the
// next division is called? so check for it? let's find a way
// to do this later. Note that this functionality currently
// might result in a segmentation fault for users who are
// quickly dragging and releasing division lines...
scene()->update();
qDebug() << "Dividing Cell " << c.Index() << endl;
c.DivideOverGivenLine( startpoint, endpoint, true, node_set);
node_set->CleanUp();
mesh.AddNodeSet(node_set);
qDebug() << "Done DivideOverGivenLine" << endl;
mesh.TestIllegalWalls();
// Do the actual cutting and removing
if (intersected_cells.size()) {
mesh.CutAwayBelowLine( startpoint, endpoint );
// Correct flags of nodeset
for (NodeSet::iterator i = node_set->begin(); i != node_set->end(); i++) {
(*i)->SetSAM();
(*i)->SetBoundary();
// Make cells attached to nodeset part of the boundary
// This is a temporary solution for the following:
// If a cell attached to a NodeSet divides (with a division plane intersecting the node set),
// we must insert a new node into the node set.
// For now, we add a layer of "virtual cells" inbetween.
list<Cell *> cells_attached_to_nodeset = node_set->getCells();
for ( list<Cell *>::iterator c = cells_attached_to_nodeset.begin(); c != cells_attached_to_nodeset.end(); c++) {
(*c)->SetBoundary(Cell::SAM);
qDebug() << "Done CutAwayBelowLine" << endl;
mesh.RepairBoundaryPolygon();
qDebug() << "Done RepairBoundaryPolygon" << endl;
mesh.CleanUpWalls();
qDebug() << "Done CleanUpWalls" << endl;
dynamic_cast<Main *>(parent())->Plot();
cerr << "NodeSet of cutting line: " << *node_set << endl;
} else
if (e->button()==Qt::RightButton) {
if (intersection_line /* && !angle_line*/) {
QPointF p = mapToScene(e->pos());
QPointF sp = intersection_line->line().p1();
viewport()->setMouseTracking( TRUE );
// returns a vector of pointer to cells colliding with intersection line
vector <CellItem *> FigureEditor::getIntersectedCells(void)
{
vector <CellItem *> colliding_cells;
QList<QGraphicsItem *> l = intersection_line->collidingItems( );
qDebug() << "l.size() = " << l.size() << endl;
for (QList<QGraphicsItem *>::Iterator it=l.begin(); it!=l.end(); ++it) {
qDebug() << typeid(**it).name() << endl;
if ( !strcmp(typeid(**it).name(),"8CellItem") ) {
colliding_cells.push_back(dynamic_cast<CellItem *>(*it));
delete intersection_line;
intersection_line = 0;
return colliding_cells;
void FigureEditor::FullRedraw(void)
QList<QRectF> rl;
rl << sceneRect();
updateScene(rl);
NodeItem *FigureEditor::selectedNodeItem(QList<QGraphicsItem *> graphicItems) const
NodeItem *nodeItem = 0;
// graphicItems is a list of the QgraphicItems under the mouse click event
QList<QGraphicsItem *>::Iterator it = graphicItems.begin();
for (; it != graphicItems.end(); ++it) {
if ( !strcmp(typeid(**it).name(),"8NodeItem")) {
// the object under the mouse click is a Nodeitem
nodeItem = dynamic_cast<NodeItem *>(*it);
// indicate we've selected it
nodeItem->setBrush(dark_red);
break;
return nodeItem;
void FigureEditor::insertNode(QPointF p)
Node *node = new Node(p.x(), p.y(), 0);
mesh.AddNode(node);
scene()->clearSelection();
FullRedraw();
qDebug() << "Node: " << p << endl;
static uint mainCount = 0;
Main::Main(QGraphicsScene& c, Mesh &m, QWidget* parent, const char* name, Qt::WindowFlags f) :
Q3MainWindow(parent,name,f),
MainBase(c,m),
mesh(m)
editor = new FigureEditor(canvas,mesh, this);
qDebug() << "Interactive = " << editor->isEnabled();
working_dir = 0;
QObject::connect( editor, SIGNAL(MousePressed()), this, SLOT(PauseIfRunning()));
QObject::connect( editor, SIGNAL(MouseReleased()), this, SLOT(ContIfRunning()));
QMenuBar* menu = menuBar();
Q3PopupMenu* file = new Q3PopupMenu( menu );
file->insertItem("&Read leaf", this, SLOT(readStateXML()));
file->insertItem("&Save leaf", this, SLOT(saveStateXML()));
file->insertItem("Snapshot", this, SLOT(snapshot()), Qt::CTRL+Qt::SHIFT+Qt::Key_S);
file->insertSeparator();
file->insertItem("Read next leaf", this, SLOT(readNextStateXML()), Qt::Key_PageDown);
file->insertItem("Read previous leaf", this, SLOT(readPrevStateXML()), Qt::Key_PageUp);
file->insertItem("Read last leaf", this, SLOT(readLastStateXML()), Qt::Key_End);
file->insertItem("Read first leaf", this, SLOT(readFirstStateXML()), Qt::Key_Home);
file->insertItem("Export cell data", this, SLOT(exportCellData()));
file->insertItem("&Print...", this, SLOT(print()), Qt::CTRL+Qt::Key_P);
file->insertItem("E&xit", qApp, SLOT(quit()), Qt::CTRL+Qt::Key_Q);
menu->insertItem("&File", file);
Q3PopupMenu* edit = new Q3PopupMenu( menu );
edit->insertItem("Reset Chemicals and Transporters", this, SLOT( CleanMesh()), Qt::CTRL+Qt::Key_R );
edit->insertItem("Reset Chemicals", this, SLOT( CleanMeshChemicals()) );
edit->insertItem("Reset Transporters", this, SLOT( CleanMeshTransporters()) );
edit->insertItem("Randomize PIN1 Transporters", this, SLOT( RandomizeMesh()) );
edit->insertItem("Cut away SAM", this, SLOT( CutSAM() ));
menu->insertItem("&Edit", edit);
run = new Q3PopupMenu( menu );
running = false;
paused_id = run->insertItem("&Simulation paused", this, SLOT(togglePaused()), Qt::Key_S);
run->setItemChecked(paused_id, FALSE);
menu->insertItem("&Run", run);
view = new Q3PopupMenu( menu );
view->insertItem("&Zoom in", this, SLOT(zoomIn()), Qt::CTRL+Qt::Key_Equal);
view->insertItem("Zoom &out", this, SLOT(zoomOut()), Qt::CTRL+Qt::Key_Minus);
view->insertSeparator();
com_id = view->insertItem("Show cell ¢ers", this, SLOT(toggleShowCellCenters()));
view->setItemChecked(com_id, FALSE);
mesh_id = view->insertItem("Show &nodes", this, SLOT(toggleShowNodes()), Qt::CTRL+Qt::SHIFT+Qt::Key_N);
view->setItemChecked(mesh_id, TRUE);
node_number_id = view->insertItem("Show node numbers", this, SLOT(toggleNodeNumbers()), Qt::CTRL+Qt::SHIFT+Qt::Key_M);
view->setItemChecked(node_number_id, FALSE);
cell_number_id = view->insertItem("Show cell numbers", this, SLOT(toggleCellNumbers()));
view->setItemChecked(cell_number_id, FALSE);
hide_cells_id = view->insertItem("Hide cells", this, SLOT(toggleHideCells()));
view->setItemChecked(hide_cells_id, FALSE);
border_id = view->insertItem("Show &border cells", this, SLOT(toggleShowBorderCells()));
view->setItemChecked(border_id, FALSE);
cell_axes_id = view->insertItem("Show cell &axes", this, SLOT(toggleCellAxes()));
cell_strain_id = view->insertItem("Show cell &strain", this, SLOT(toggleCellStrain()));
view->setItemChecked(cell_axes_id, FALSE);
fluxes_id = view->insertItem("Show &fluxes", this, SLOT(toggleShowFluxes()));
view->setItemChecked(fluxes_id, FALSE);
cell_walls_id = view->insertItem("Show transporters", this, SLOT(toggleShowWalls()));
view->setItemChecked(cell_walls_id, FALSE);
// apoplasts_id = view->insertItem("Show apoplasts", this, SLOT(toggleShowApoplasts()));
// view->setItemChecked(apoplasts_id, FALSE);
only_boundary_id = view->insertItem("Show only leaf &boundary", this, SLOT(toggleLeafBoundary()));
movie_frames_id = view->insertItem("Start saving movie &frames", this, SLOT(toggleMovieFrames()));
view->setItemChecked(movie_frames_id, par.movie);
view->setItemChecked(only_boundary_id, FALSE);
menu->insertItem("&View", view);
options = new Q3PopupMenu( menu );
dyn_cells_id = options->insertItem("Cell growth", this, SLOT(toggleDynCells()));
options->setItemChecked(dyn_cells_id, true);
options->insertItem("Edit ¶meters", this, SLOT(EditParameters()), Qt::CTRL+Qt::Key_E);
rotation_mode_id = options->insertItem("Rotate leaf", this, SLOT(EnterRotationMode()), Qt::CTRL + Qt::SHIFT + Qt::Key_R);
options->setItemChecked(rotation_mode_id, false);
menu->insertItem("&Options",options);
// Menu of models
modelmenu = new QMenu( menu );
menu->insertItem("&Models", modelmenu);
menu->insertSeparator();
helpmenu = new Q3PopupMenu( menu );
tooltips_id = helpmenu->insertItem("Show Cell&Info", this, SLOT(Refresh()));
helpmenu->setItemChecked(tooltips_id, true);
helpmenu->insertSeparator();
//helpmenu->insertSeparator();
helpmenu->insertItem("&LICENSE", this, SLOT(gpl()) );
helpmenu->insertItem("About", this, SLOT(about()) ); //, Key_F1);
menu->insertItem("&Help",helpmenu);
statusBar();
setCentralWidget(editor);
printer = 0;
init();
// Start timer which repetitively invokes
// a simulation time step
timer = new QTimer( this );
connect( timer, SIGNAL(timeout()), SLOT(TimeStepWrap()) );
stopSimulation();
statusBar()->addWidget(new QLabel("Ready."));
setCaption(caption);
gifanim = 0;
infobar = new InfoBar();
addDockWindow(infobar);
void Main::RefreshInfoBar(void)
infobar->SetText(mesh.ModelID());
void Main::UserMessage(QString message, int timeout)
statusBar()->showMessage(message, timeout);
void Main::init()
clear();
static int r=24;
srand(++r);
mainCount++;
Main::~Main()
delete printer;
if ( !--mainCount ) {
//EndGifAnim();
void Main::newView()
// Open a new view... have it delete when closed.
Main *m = new Main(canvas, mesh, 0, 0, Qt::WDestructiveClose);
qApp->setMainWidget(m);
m->show();
qApp->setMainWidget(0);
void Main::EditParameters()
ParameterDialog *pardial = new ParameterDialog(this, "stridediag");
// Make sure the values in the parameter dialog are updated after a file is read
// each method changing the parameters (reading XML or PAR files) should
// emit this signal
QObject::connect( this, SIGNAL( ParsChanged() ), pardial, SLOT( Reset() ) );
void Main::savePars()
Q3FileDialog *fd = new Q3FileDialog( this, "file dialog", TRUE );
fd->setMode( Q3FileDialog::AnyFile );
fd->setFilter( "Parameter files (*.par)");
fd->setDir(par.datadir);
QString fileName;
if ( fd->exec() == QDialog::Accepted ) {
fileName = fd->selectedFile();
ofstream parfile((const char *)fileName);
par.Write(parfile);
startSimulation();
void Main::readPars()
fd->setMode( Q3FileDialog::ExistingFile );
par.Read((const char *)fileName);
emit ParsChanged();
void Main::saveStateXML()
fd->setFilter( "LeafML files (*.xml)");
// extract extension from filename
QFileInfo fi(fileName);
QString extension = fi.suffix();
if (extension.isEmpty()) {
extension = "xml";
fileName += ".";
fileName += extension;
if ( QFile::exists( fileName ) &&
QMessageBox::question(
this,
tr("Overwrite File? -- Leaf Growth"),
tr("A file called %1 already exists."
" Do you want to overwrite it?")
.arg( fileName ),
tr("&Yes"), tr("&No"),
QString::null, 1, 1 ) ) {
return saveStateXML();
} else {
mesh.XMLSave((const char *)fileName, XMLSettingsTree());
QString status_message;
status_message = QString("Wrote LeafML to %1").arg(fileName);
cerr << status_message.toStdString() << endl;
statusBar()->showMessage(status_message);
void Main::snapshot()
qDebug() << "Supported Image Formats: " << QImageWriter::supportedImageFormats() << endl;
Q3FileDialog *fd = new Q3FileDialog( this, "Save snapshot", TRUE );
QString supported_file_formats = " *.pdf";
foreach (QString format, QImageWriter::supportedImageFormats()){
supported_file_formats += (" *." + format);
fd->setFilter("Image files (" + supported_file_formats + " )");
extension = "png";
tr("Overwrite file? -- Cell data export"),
" Do you want to overwrite it?").arg( fileName ),
QMessageBox::Yes, QMessageBox::No
) == QMessageBox::No
) {
return snapshot();
// Save bitmaps at 1024x768
if (Save((const char *)fileName, extension, 1024, 768)==0) {
status_message = QString("Wrote snapshot to %1").arg(fileName);
status_message = QString("Error writing snapshot to %1").arg(fileName);
void Main::readPrevStateXML()
// if we have already read a file, read the next file
if (!currentFile.isEmpty() && working_dir) {
QString next_file;
QStringList xml_files = working_dir->entryList("*.xml");
QString currentFile_nopath = currentFile.section( '/', -1 );
QString currentFile_path = currentFile.section( '/', 0, -2 );
QList<QString>::iterator f = xml_files.find( currentFile_nopath );
if (f == xml_files.end()) {
if (f==xml_files.begin()) {
QMessageBox mb( "Read previous leaf",
"No more files",
QMessageBox::Information,
QMessageBox::Ok | QMessageBox::Default,
QMessageBox::NoButton,
QMessageBox::NoButton);
mb.exec();
next_file = *(--f);
next_file = currentFile_path+"/"+next_file;
readStateXML((const char *)next_file);
int Main::readStateXML(const char *filename, bool geometry, bool pars, bool simtime)
try {
xmlNode *settings;
mesh.XMLRead((const char *)filename, &settings, geometry, pars, simtime);
qDebug() << "Reading done."<< endl;
XMLReadSettings(settings);
xmlFree(settings);
Cell::SetMagnification(1);
Cell::setOffset(0,0);
FitLeafToCanvas();
currentFile = QString(filename);
Plot();
QString status_message = QString("Successfully read leaf from file %1. Time is %2 h.").arg(currentFile).arg(mesh.getTimeHours().c_str());
setCaption(caption_with_file.arg(filename));
statusBar()->message(status_message);
qDebug() << "Done. Returning 0." << endl;
return 0;
} catch (const char *error_message) {
QMessageBox mb( "Read leaf from XML file",
QString(error_message),
QMessageBox::Critical,
Qt::NoButton,
Qt::NoButton);
return 1;
void Main::readNextStateXML()
++f;
if (f==xml_files.end()) {
QMessageBox mb( "Read next leaf",
next_file = *f;
readStateXML((const char*)next_file);
void Main::readLastStateXML()
next_file = xml_files.back();
void Main::readFirstStateXML()
next_file = xml_files.front();
QDir Main::GetLeafDir(void) {
QDir LeafDir(QApplication::applicationDirPath());
QStringList plugin_filters; // filter for plugins, i.e "*.dll", "*.dylib"
#if defined(Q_OS_WIN)
if (LeafDir.dirName().toLower() =="debug"
||LeafDir.dirName().toLower() =="release")
LeafDir.cdUp();
//plugin_filters << "*.dll";
#elif defined(Q_OS_MAC)
if (LeafDir.dirName() =="MacOS"){
// for all OS-es. Move from "bin" directory to root application folder.
if (LeafDir.dirName() == "bin") {
LeafDir.cd("data/leaves");
/* if (!LeafDir.cd("data/leaves")) {
QString status_message = QString("No directory data/leaves");
return LeafDir;
*/
void Main::readStateXML()
// extern Mesh mesh;
qDebug() << "Trying to open an OptionFileDialog" << endl;
OptionFileDialog *fd = new OptionFileDialog( this, "read dialog", TRUE );
fd->setMode( OptionFileDialog::ExistingFile );
if (working_dir) {
fd->setDir(*working_dir);
delete working_dir;
working_dir = fd->dir();
if (readStateXML((const char *)fileName,fd->readGeometryP(), fd->readParametersP()) )
return readStateXML(); // user can try again
void Main::clear()
editor->clear();
void Main::about()
static QMessageBox* about = new QMessageBox
( "VirtualLeaf V1.0",
"<h3>VirtualLeaf V1.0</h3>\
<p>\
An Open Source framework for cell-based modeling of plant\
tissue growth and development.\
</p>\
<p>(c) 2005-2008, Roeland Merks <i>et al.</i>\
<a href=\"http://www.psb.vib-ugent.be\">VIB Department Plant Systems Biology</a>,\
Ghent, Belgium.\
<p>(c) 2008-2010,\
<a href=\"http://www.cwi.nl/~merks\">Roeland Merks <i>et al.</i></a>\
<a href=\"http://www.cwi.nl\">Centrum Wiskunde & Informatica (CWI)</a> and\
<a href=\"http://www.ncsb.nl\"> Netherlands Consortium for Systems Biology (NCSB)</a>,\
Amsterdam, the Netherlands.\
VirtualLeaf is free software: you can redistribute it and/or\
modify it under the terms of the GNU General Public License as\
published by the Free Software Foundation, either version 3 of the\
License, or (at your option) any later version.\
If you use this code for your projects, please cite our paper in\
<a href=\"http://www.plantphysiol.org\">Plant Physiology</a>, ‘\
Roeland M. H. Merks, Michael Guravage, Dirk Inze, and Gerrit T.S. Beemster,\
<a href=\"http://www.plantphysiol.org/cgi/content/short/pp.110.167619?keytype=ref&ijkey=YTmfxrHG5QCsa8k\">\
Roeland M. H. Merks, Michael Guravage, Dirk Inzé, and Gerrit T.S. Beemster,\
<a href=\"http://www.plantphysiol.org/cgi/reprint/155/2/656.pdf\">\
VirtualLeaf: an Open Source framework for cell-based modeling of plant tissue growth and development</a>,<br>\
Plant Physiology 2011: pp.110.167619v1-pp.110.167619.\
Plant Physiology 2011: 155:656-666.\
Please share your model plugins and extensions at\
<a href=\"http://virtualleaf.googlecode.com\">http://virtualleaf.googlecode.com</a>.\
</p>",
QMessageBox::Information, 1, 0, 0, this, 0, FALSE );
about->setButtonText( 1, "Dismiss" );
about->setIconPixmap(QPixmap( leaficon_small ));
about->show();
void Main::gpl()
static QMessageBox* gpl = new QMessageBox ( "GPL License", "",
QDir docDir(QApplication::applicationDirPath());
docDir.cd("../doc"); // Where Linux expects gpl3.txt
QString path = docDir.filePath("gpl3.txt");
if (!docDir.exists("gpl3.txt")){
docDir = QApplication::applicationDirPath();
docDir.cd("doc"); // Where Windows expects gpl3.txt
path = docDir.filePath("gpl3.txt");
// At this point path points either to the linux variant, which
// exists, or the windows variant, which may exist. Testing the
// ifstream object will determine whether we've found gpl3.txt.
std::ifstream file(path.toStdString().c_str());
std::string str;
if (file) {
file.seekg(0, std::ios::end);
str.reserve(file.tellg());
file.seekg(0, std::ios::beg);
str.assign((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
gpl->setDetailedText(QString(str.c_str()));
gpl->setText(QString( "<h3>GNU GENERAL PUBLIC LICENSE</h3>"
"<p>Version 3, 29 June 2007</p>"
"<p>Copyright © 2007 Free Software Foundation, Inc. "
"<<a href=\"http://fsf.org/\">http://fsf.org/</a>></p><p>"
"Everyone is permitted to copy and distribute verbatim copies "
"of this license document, but changing it is not allowed.</p>"
"<h2>GNU GENERAL PUBLIC LICENSE</h2>"));
gpl->setButtonText( 1, "Dismiss" );
gpl->show();
void Main::aboutQt(){
QMessageBox::aboutQt( this, "Virtual Leaf" );
void Main::toggleShowCellCenters()
void Main::toggleShowWalls()
void Main::toggleShowApoplasts()
void Main::toggleShowNodes()
void Main::toggleNodeNumbers(void)
void Main::toggleCellNumbers(void)
void Main::toggleCellAxes(void)
void Main::toggleCellStrain(void)
void Main::toggleShowFluxes(void)
void Main::toggleShowBorderCells()
void Main::toggleHideCells(void)
editor->FullRedraw();
void Main::toggleMovieFrames(){}
void Main::toggleLeafBoundary(){}
void Main::toggleDynCells() {}
void Main::startSimulation(void)
timer->start( 0 );
statusBar()->message("Simulation started");
running = true;
void Main::stopSimulation(void)
timer->stop();
cerr << "Stopping simulation" << endl;
statusBar()->message("Simulation paused");
void Main::togglePaused()
bool s = run->isItemChecked(paused_id);
if (s) {
cerr << "Calling start simulation" << endl;
cerr << "Calling stop simulation" << endl;
void Main::setFluxArrowSize(int size)
flux_arrow_size = size/100.;
void Main::enlarge()
canvas.setSceneRect( QRectF( 0,0, canvas.width()*4./3., canvas.height()*4./3.) );
void Main::shrink()
canvas.setSceneRect( QRectF( 0,0, canvas.width()*3/4, canvas.height()*3/4) );
void Main::scale(double factor)
QMatrix m = editor->matrix();
m.scale(factor, factor);
editor->setMatrix( m );
void Main::zoomIn()
m.scale( 1.1, 1.1 );
void Main::zoomOut()
m.scale( 0.9, 0.9 );
void Main::print()
if ( !printer ) printer = new QPrinter;
if ( printer->setup(this) ) {
Vector bbll,bbur;
mesh.BoundingBox(bbll,bbur);
qDebug() << "bbll = " << bbll << endl;
qDebug() << "bbur = " << bbur << endl;
double cw = (bbur.x - bbll.x);
double ch = (bbur.y - bbll.y);
QPainter pp(printer);
QRect vp=pp.viewport();
qDebug() << "Paper width = " << vp.width() << " x " << vp.height() << endl;
// Note that Cell is also translated...
/* pp.translate(-bbur.x,-bbur.y);
if (cw>ch) {
pp.scale(vp.width()/(2*cw*Cell::Magnification()), vp.width()/(2*cw*Cell::Magnification()));
pp.scale(vp.height()/(2*ch*Cell::Magnification()), vp.height()/(2*ch*Cell::Magnification()));
}*/
canvas.render(&pp);//, QRectF(), QRectF(0.,0.,canvas.width(),canvas.height()));
void Main::TimeStepWrap(void)
static int t;
stringstream fname;
TimeStep();
t = (int)mesh.getTime();
if ((par.export_interval > 0) && !(t%par.export_interval)){
fname << par.datadir << "/" << par.export_fn_prefix;
fname.fill('0');
fname.width(6);
fname << t << ".csv";
this->exportCellData(QString(fname.str().c_str()));
// check number of timesteps
if (t == par.nit) {
emit SimulationDone();
void Main::RestartSim(void)
if ( QMessageBox::question(
tr("Restart simulation?"),
tr("Restart simulation.\n"
"Are you sure?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::NoButton ) == QMessageBox::Yes ) {
cerr << "Restarting simulation" << endl;
mesh.Clear();
Init();
//startSimulation();
void Main::FitCanvasToWindow(void)
double scale_factor_x = (double)editor->width()/(double)canvas.width();
double scale_factor_y = (double)editor->height()/(double)canvas.height();
double scale_factor = scale_factor_x > scale_factor_y ? scale_factor_x : scale_factor_y;
qDebug() << "editor->width() = " << editor->width() << endl;
qDebug() << "editor->height() = " << editor->height() << endl;
qDebug() << "scale_factor = " << scale_factor << endl;
qDebug() << "scale_factor_x = " << scale_factor_x << endl;
qDebug() << "scale_factor_y = " << scale_factor_y << endl;
m.scale( scale_factor, scale_factor );
editor->show();
void Main::PauseIfRunning(void)
if (running) {
void Main::ContIfRunning(void)
void Main::FitLeafToCanvas(void)
Vector ll,ur;
mesh.BoundingBox(ll, ur);
ll*=Cell::Magnification(); ur*=Cell::Magnification();
// give the leaf some space
Vector border = ((ur-ll)/5.);
QRectF bb( ll.x - border.x, ll.y - border.y, ur.x-ll.x + 2*border.x, ur.y-ll.y + 2*border.y );
// cerr << ur << ", " << ll << endl;
// editor->fitInView(bb, Qt::KeepAspectRatio);
editor->ensureVisible(bb);
canvas.setSceneRect(bb);
//editor->setTransform(viewport);
void Main::CleanMesh(void)
vector<double> clean_chem(Cell::NChem());
vector<double> clean_transporters(Cell::NChem());
for (int i=0;i<Cell::NChem();i++) {
clean_chem[i]=par.initval[i];
clean_transporters[i]=0.;
mesh.CleanChemicals(clean_chem);
mesh.CleanTransporters(clean_transporters);
mesh.setTime(0);
void Main::CleanMeshChemicals(void)
void Main::CleanMeshTransporters(void)
void Main::RandomizeMesh(void)
vector<double> max_chem(Cell::NChem());
vector<double> max_transporters(Cell::NChem());
max_transporters[i]=0.;
max_chem[i]=par.initval[i];
// Amount of PIN1 at walls
max_transporters[1] = 0.01;
mesh.RandomizeChemicals(max_chem, max_transporters);
void Main::XMLReadSettings(xmlNode *settings)
MainBase::XMLReadSettings(settings);
view->setItemChecked(com_id, showcentersp);
view->setItemChecked(mesh_id, showmeshp);
view->setItemChecked(border_id, showbordercellp);
view->setItemChecked(node_number_id, shownodenumbersp);
view->setItemChecked(cell_number_id, showcellnumbersp);
view->setItemChecked(cell_axes_id, showcellsaxesp);
view->setItemChecked(cell_strain_id, showcellstrainp);
view->setItemChecked(movie_frames_id, movieframesp);
view->setItemChecked(only_boundary_id, showboundaryonlyp);
view->setItemChecked(fluxes_id, showfluxesp);
view->setItemChecked(hide_cells_id, hidecellsp);
options->setItemChecked(dyn_cells_id, dynamicscellsp);
view->setItemChecked( cell_walls_id, showwallsp);
// view->setItemChecked( apoplasts_id, showapoplastsp);
editor->setTransform(viewport);
xmlNode *Main::XMLSettingsTree(void)
showcentersp = view->isItemChecked(com_id);
showmeshp = view->isItemChecked(mesh_id);
showbordercellp = view->isItemChecked(border_id);
shownodenumbersp = view->isItemChecked(node_number_id);
showcellnumbersp = view->isItemChecked(cell_number_id);
showcellsaxesp = view->isItemChecked( cell_axes_id );
showcellstrainp = view->isItemChecked( cell_strain_id );
movieframesp = view->isItemChecked(movie_frames_id);;
showboundaryonlyp = view->isItemChecked(only_boundary_id);
showfluxesp = view->isItemChecked(fluxes_id);
dynamicscellsp = options->isItemChecked(dyn_cells_id);
showwallsp = view->isItemChecked( cell_walls_id);
//showapoplastsp = view->isItemChecked( apoplasts_id);
hidecellsp = view->isItemChecked( hide_cells_id);
xmlNode *settings = MainBase::XMLSettingsTree();
QTransform viewport(editor->transform());
xmlAddChild(settings, XMLViewportTree(viewport));
return settings;
void Main::exportCellData(QString fileName) {
qDebug() << "exportCellData fileName: " << fileName << endl;
QFile file(fileName);
if ( file.open( IO_WriteOnly ) ) {
QTextStream stream( &file );
mesh.CSVExportCellData(stream);
mesh.CSVExportWallData(stream);
mesh.CSVExportMeshData(stream);
file.close();
void Main::exportCellData() {
extension = "csv";
if (extension!="csv" && extension!="CSV") {
if (
tr("Change extension? -- Cell data export"),
tr("VirtualLeaf can only export data to CSV format (Excel-compatible)."
"Do you want to change the file extension to \".csv\"?"),
QMessageBox::Yes, QMessageBox::No ) == QMessageBox::No
return exportCellData();
fileName.replace(extension,"csv");
extension="csv";
exportCellData(fileName);
QString status_message = QString("Wrote data file to %1").arg(fileName);
/* finis */
Status change: