Changeset - 9a40ab737a73
[Not reviewed]
default
0 14 2
Roeland Merks - 15 years ago 2010-09-08 17:31:50
merks@cwi.nl
* Added functionality for numerical output (CSV) format. See functions starting with "CSV" in mesh.cpp. External code hull.cpp is responsible for convex hull calculations.
- ToDo: adopt menu item "File->export cell areas" so user can choose a file name or set up periodic output of numerical data.
- ToDo: add parameters re: periodic numeric output to "<settings>" block of LeafML-file, so it becomes possible to set up numeric output interactively, then write the LeafML file and run the program in batch mode to retrieve periodic numeric output.

* Added new functionality to plugin interface. It becomes possible to define a default LeafML init file for a plugin. It is read whenever the model plugin is loaded.
- ToDo: define default LeafML files for all plugins, according to the LeafML files mentioned in the Plant Phys. tutorial.


user: Roeland Merks <merks@cwi.nl>
branch 'default'
added src/hull.cpp
added src/hull.h
changed src/TutorialCode/Tutorial0/tutorial0.pro
changed src/VirtualLeaf.cpp
changed src/VirtualLeaf.pro
changed src/build_models/auxingrowthplugin.cpp
changed src/build_models/auxingrowthplugin.h
changed src/canvas.cpp
changed src/canvas.h
changed src/mainbase.h
changed src/mesh.cpp
changed src/mesh.h
changed src/modelcatalogue.cpp
changed src/simplugin.cpp
changed src/simplugin.h
changed src/xmlwrite.cpp
16 files changed with 331 insertions and 11 deletions:
0 comments (0 inline, 0 general)
src/TutorialCode/Tutorial0/tutorial0.pro
Show inline comments
 
#
 
# $Id$
 
#
 
#  This file is part of the Virtual Leaf.
 
#
 
#  The Virtual Leaf 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.
 
#
 
#  The Virtual Leaf is distributed in the hope that it will be useful,
 
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
#  GNU General Public License for more details.
 
#
 
#  You should have received a copy of the GNU General Public License
 
#  along with the Virtual Leaf.  If not, see <http://www.gnu.org/licenses/>.
 
#
 
#  Copyright 2010 Roeland Merks.
 
#
 

	
 

	
 
TARGET = tutorial0
 
VLEAFHOME = ../../..
 

	
 
CONFIG += release
 
CONFIG -= debug
 
CONFIG += plugin
 

	
 
BINDIR = $${VLEAFHOME}/bin
 
LIBDIR = $${VLEAFHOME}/lib
 
INCDIR = $${VLEAFHOME}/src
 
DEFINES = QTGRAPHICS # VLEAFPLUGIN
 
DESTDIR = $${BINDIR}/models
 
HEADERS = $${TARGET}.h 
 
HEADERS = $${TARGET}.h $${INCDIR}/simplugin.h
 
INCLUDEPATH += $${INCDIR}	
 

	
 
QMAKE_CXXFLAGS += -fexceptions #-I$${INCDIR}
 
QMAKE_CXXFLAGS_DEBUG += -g3
 
QMAKE_CXXFLAGS_DEBUG += -DQDEBUG
 
QT += qt3support
 
SOURCES = $${TARGET}.cpp
 
TEMPLATE = lib 
 

	
 
unix {
 
 LIBS += -L$${LIBDIR} -lvleaf
 
 QMAKE_CXXFLAGS += -fPIC -I/usr/include/libxml2
 
 QMAKE_LFLAGS += -fPIC
 
}
 

	
 
win32 {
 
 LIBXML2DIR = $${LIBDIR}\libxml2
 
 LIBICONVDIR = $${LIBDIR}\libiconv
 
 LIBZDIR = $${LIBDIR}\libz
 
 LIBS += -L$${LIBDIR} -lvleaf
 
 QMAKE_CXXFLAGS += -DLIBXML_STATIC
 
 QMAKE_CXXFLAGS += -I$${LIBXML2DIR}\include -I$${LIBICONVDIR}\include -I$${LIBZDIR}\include
 

	
 
}
 

	
 
# finish
src/VirtualLeaf.cpp
Show inline comments
 
/*
 
 *
 
 *  This file is part of the Virtual Leaf.
 
 *
 
 *  The Virtual Leaf 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.
 
 *
 
 *  The Virtual Leaf is distributed in the hope that it will be useful,
 
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 *  GNU General Public License for more details.
 
 *
 
 *  You should have received a copy of the GNU General Public License
 
 *  along with the Virtual Leaf.  If not, see <http://www.gnu.org/licenses/>.
 
 *
 
 *  Copyright 2010 Roeland Merks.
 
 *
 
 */
 

	
 
#include <string>
 
#include <fstream>
 
#include <sstream>
 
#include <cstring>
 
#include <functional> 
 
#include <getopt.h>
 
#include <cerrno>
 
#include "mesh.h"
 
#include "parameter.h"
 
#include "random.h"
 
#include "pi.h"
 
#include "cellitem.h"
 
#include "canvas.h"
 
#include "cell.h"
 
#include "output.h"
 
#include <qwidget.h>
 
#include <q3process.h>
 
#include <qapplication.h>
 
#include <QDesktopWidget>
 
#include <QGraphicsScene>
 
#include <QMessageBox>
 
//Added by qt3to4:
 
#include <QMouseEvent>
 

	
 
#include <unistd.h>
 
#include <q3textstream.h> 
 

	
 
#ifdef HAVE_QWT
 
#include "data_plot.h"
 
#endif
 
#include <QPalette>
 
#include <QBrush>
 
#include <QToolTip>
 
#include "simplugin.h"
 
#include <QPluginLoader>
 
#include <QDir>
 
#include "modelcatalogue.h"
 

	
 
static const std::string _module_id("$Id$");
 

	
 
extern Parameter par;
 

	
 
MainBase *main_window = 0;
 

	
 
#ifdef XFIGGRAPHICS
 
#define TIMESTEP double Graphics::TimeStep(void)
 
#endif
 

	
 
class PrintNode {
 
public:
 
  void operator() (const Node &n) const 
 
  {
 
    cerr << n.Index() << ": " << n <<  endl;
 
  }
 
};
 

	
 

	
 
class EdgeSource {
 

	
 
public:
 
  void operator() (Cell &c) {
 

	
 
    if (c.AtBoundaryP()) {
 
      cerr << "Cell " << c.Index() << " is a source cell.\n";
 
      c.SetSource(0,par.source);
 
    } else {
 
      cerr << "Cell " << c.Index() << " is _not_ a source cell.\n";
 
    }
 
  }
 
};
 

	
 

	
 

	
 
class CellInfo {
 
public:
 
  void operator() (Cell &c,std::ostream &os) const {
 
    os << "Cell " << c.index << " says: " << endl;
 
    os << "c.nodes.size() = " << c.nodes.size() << endl;
 
    for (list<Node *>::iterator i=c.nodes.begin(); i!=c.nodes.end(); i++) {
 
      cerr << (*i)->Index() << " ";
 
    }
 
    cerr << endl;
 
  }
 
};
 

	
 
double PINSum(Cell &c) {
 
  return c.Chemical(1) + c.SumTransporters(1);// + c.ReduceCellAndWalls<double>( complex_PijAj );
 
}
 

	
 

	
 
class DrawCell {
 
public:
 
  void operator() (Cell &c,QGraphicsScene &canvas, MainBase &m) const {
 
    if (m.ShowBorderCellsP() || c.Boundary()==Cell::None) {
 
      if (!m.ShowBoundaryOnlyP() && !m.HideCellsP()) {
 
	if (m.ShowToolTipsP()) {
 
	  QString info_string=QString("Cell %1, chemicals: ( %2, %3, %4, %5, %6)\n %7 of PIN1 at walls.\n Area is %8\n PIN sum is %9\n Circumference is %10\n Boundary type is %11").arg(c.Index()).arg(c.Chemical(0)).arg(c.Chemical(1)).arg(c.Chemical(2)).arg(c.Chemical(3)).arg(c.Chemical(4)).arg(c.SumTransporters(1)).arg(c.Area()).arg(PINSum(c)).arg(c.Circumference()).arg(c.BoundaryStr());
 

	
 
	  info_string += "\n" + c.printednodelist();
 
	  c.Draw(&canvas, info_string);
 
	} else {
 
	  c.Draw(&canvas);
 
	}
 
      }
 
      if (m.ShowCentersP()){
 
	c.DrawCenter(&canvas);
 
      }
 
      if (m.ShowFluxesP()){
 
	c.DrawFluxes(&canvas, par.arrowsize);
 
      }
 
    }
 
  }
 
};
 

	
 
Mesh mesh;
 
bool batch=false;
 

	
 
void MainBase::Plot(int resize_stride)
 
{
 

	
 
  clear();
 

	
 
  static int count=0;
 
  if (resize_stride) {
 
    if ( !((++count)%resize_stride) ) {
 
      FitLeafToCanvas();
 
    }
 
  }
 

	
 
  mesh.LoopCells(DrawCell(),canvas,*this);
 

	
 
  if (ShowNodeNumbersP()) 
 
    mesh.LoopNodes( bind2nd (mem_fun_ref ( &Node::DrawIndex), &canvas ) ) ;
 
  if (ShowCellNumbersP()) 
 
    mesh.LoopCells( bind2nd (mem_fun_ref ( &Cell::DrawIndex), &canvas ) ) ;
 

	
 
  if (ShowCellAxesP()) 
 
    mesh.LoopCells( bind2nd (mem_fun_ref ( &Cell::DrawAxis), &canvas ) );
 

	
 
  if (ShowCellStrainP()) 
 
    mesh.LoopCells( bind2nd (mem_fun_ref ( &Cell::DrawStrain), &canvas ) );
 

	
 
  if (ShowWallsP())
 
    mesh.LoopWalls( bind2nd( mem_fun_ref( &Wall::Draw ), &canvas ) );
 

	
 
  if (ShowApoplastsP()) 
 
    mesh.LoopWalls( bind2nd( mem_fun_ref( &Wall::DrawApoplast ), &canvas ) );
 

	
 
  if (ShowMeshP()) 
 
    mesh.DrawNodes(&canvas);
 

	
 
  if (ShowBoundaryOnlyP()) 
 
    mesh.DrawBoundary(&canvas);
 

	
 
  if ( ( batch || MovieFramesP() )) {
 

	
 
    static int frame = 0;
 
    // frame numbers are sequential for the most frequently written file type.
 
    // for the less frequently written file type they match the other type
 
    if (!(count%par.storage_stride) )  {
 

	
 
      stringstream fname;
 
      fname << par.datadir << "/leaf.";
 
      fname.fill('0');
 
      fname.width(6);
 

	
 
      fname << frame << ".jpg";
 
      if (par.storage_stride <= par.xml_storage_stride) {
 
	frame++;
 
      }
 

	
 
      // Write high-res JPG snapshot every plot step
 
      Save(fname.str().c_str(), "JPEG",1024,768);
 
    }
 

	
 
    if (!(count%par.xml_storage_stride)) {
 
      stringstream fname;
 
      fname << par.datadir << "/leaf.";
 
      fname.fill('0');
 
      fname.width(6);
 
      fname << frame << ".xml";
 

	
 
      if (par.xml_storage_stride < par.storage_stride) {
 
	frame++;
 
      }
 
      // Write XML file every ten plot steps
 
      mesh.XMLSave(fname.str().c_str(), XMLSettingsTree());
 
    }
 
  }
 
}
 

	
 

	
 

	
 
INIT {
 

	
 
  //mesh.SetSimPlugin(plugin);
 
  if (leaffile) { 
 
    xmlNode *settings;
 
    mesh.XMLRead(leaffile, &settings);
 

	
 
    main_window->XMLReadSettings(settings);
 
    xmlFree(settings);
 
    main_window->UserMessage(QString("Ready. Time is %1").arg(mesh.getTimeHours().c_str()));
 

	
 
  } else {
 
    mesh.StandardInit();
 
  }
 
  
 
  Cell::SetMagnification(1);
 
  Cell::setOffset(0,0);
 
  
 
  FitLeafToCanvas();
 
  Plot();
 

	
 
}
 

	
 
TIMESTEP {
 

	
 
  static int i=0;
 
  static int t=0;
 
  static int ncells;
 

	
 
  if (!batch) {
 
    UserMessage(QString("Time: %1").arg(mesh.getTimeHours().c_str()),0);
 
  }
 

	
 
  ncells=mesh.NCells();
 

	
 

	
 
  double dh;
 

	
 
  if(DynamicCellsP()) {
 
    dh = mesh.DisplaceNodes();
 

	
 
    // Only allow for node insertion, cell division and cell growth
 
    // if the system has equillibrized
 
    // i.e. cell wall tension equillibrization is much faster
 
    // than biological processes, including division, cell wall yielding
 
    // and cell expansion
 
    mesh.InsertNodes(); // (this amounts to cell wall yielding)
 

	
 
    if ( (-dh) < par.energy_threshold) {
 

	
 
      mesh.IncreaseCellCapacityIfNecessary();
 
      mesh.DoCellHouseKeeping();
 
      //mesh.LoopCurrentCells(mem_fun(&plugin->CellHouseKeeping)); // this includes cell division
 

	
 
      // Reaction diffusion	
 
      mesh.ReactDiffuse(par.rd_dt);
 
      t++;
 
      Plot(par.resize_stride);
 
    }
 
  } else {
 
    mesh.ReactDiffuse(par.rd_dt);
 
    Plot(par.resize_stride);
 
  }
 
  i++;
 
  return mesh.getTime();
 
}
 

	
 

	
 

	
 
/* Called if a cell is clicked */
 
void Cell::OnClick(QMouseEvent *e){}
 

	
 

	
 
/* Custom message handler - Default appends a newline character to the end of each line. */ 
 
void vlMessageOutput(QtMsgType type, const char *msg)
 
{
 
  switch (type) {
 
  case QtDebugMsg:
 
    //fprintf(stderr, "Debug: %s\n", msg);
 
    cerr << msg << flush;
 
    break;
 
  case QtWarningMsg:
 
    //fprintf(stderr, "Warning: %s\n", msg);
 
    cerr << "Warning: " << msg << flush;
 
    break;
 
  case QtCriticalMsg:
 
    fprintf(stderr, "Critical: %s\n", msg);
 
    cerr << "Critical: " << msg << flush;
 
    break;
 
  case QtFatalMsg:
 
    //fprintf(stderr, "Fatal: %s\n", msg);
 
    cerr << "Fatal: " << msg << flush;
 
    abort();
 
  }
 
}
 

	
 

	
 
Parameter par;
 

	
 
int main(int argc,char **argv) {
 

	
 
  try {
 
    int c;
 
    char *leaffile=0;
 
    char *modelfile=0;
 

	
 
    while (1) {
 

	
 
      //int this_option_optind = optind ? optind : 1;
 
      int option_index = 0;
 
      static struct option long_options[] = {
 
	{"batch", no_argument, NULL, 'b'},
 
	{"leaffile", required_argument, NULL, 'l'},
 
	{"model", required_argument, NULL, 'm'} 
 
      };
 

	
 
      // short option 'p' creates trouble for non-commandline usage on MacOSX. Option -p changed to -P (capital)
 
      static char *short_options = "blm";
 
      c = getopt_long (argc, argv, "bl:m:",
 
		       long_options, &option_index);
 
      if (c == -1)
 
	break;
 

	
 

	
 
      if (c==0) {
 
	printf ("option %s", long_options[option_index].name);
 
	if (optarg)
 
	  printf (" with arg %s", optarg);
 
	printf ("\n");
 

	
 
	c = short_options[option_index];
 
      }
 

	
 
      switch (c) {
 
      case 'b':
 
	cerr << "Running in batch mode\n";
 
	batch=true;
 
	break;
 

	
 
      case 'l':
 
	leaffile=strdup(optarg);
 
	if (!leaffile) {
 
	  throw("Out of memory");
 
	}
 
	printf("Reading leaf state file '%s'\n", leaffile);
 
	break;
 

	
 
      case 'm':
 
	modelfile=strdup(optarg);
 
	if (!modelfile) {
 
	  throw("Out of memory");
 
	}
 
	break;
 

	
 
      case '?':
 
	break;
 

	
 
      default:
 
	printf ("?? getopt returned character code 0%o ??\n", c);
 
      }
 
    }
 

	
 

	
 
    if (optind < argc) {
 
      printf ("non-option ARGV-elements: ");
 
      while (optind < argc)
 
	printf ("%s ", argv[optind++]);
 
      printf ("\n");
 
    }
 

	
 
    bool useGUI = !batch;
 
    qInstallMsgHandler(vlMessageOutput); // custom message handler
 
    QApplication app(argc,argv,useGUI);
 

	
 

	
 

	
 
    QPalette tooltippalette = QToolTip::palette();
 
    QColor transparentcolor = QColor(tooltippalette.brush(QPalette::Window).color());
 

	
 
    tooltippalette.setBrush (QPalette::Window, QBrush (transparentcolor) );
 
    QToolTip::setPalette( tooltippalette );
 

	
 
    QGraphicsScene canvas(0,0,8000,6000);
 

	
 

	
 
    if (useGUI) {
 
      main_window=new Main(canvas, mesh);
 
      if ( QApplication::desktop()->width() > ((Main *)main_window)->width() + 10
 
	   && QApplication::desktop()->height() > ((Main *)main_window)->height() +30 ) {
 

	
 
	((Main *)main_window)->show();
 
	((Main *)main_window)->resize( ((Main *)main_window)->sizeHint());
 
      } else {
 
        ((Main *)main_window)->showMaximized();
 
      }
 
      
 
      // show "About" window at start up
 
      ((Main *)main_window)->about();
 
    } else {
 
      main_window=new MainBase(canvas, mesh);
 
    }
 

	
 
    canvas.setSceneRect(QRectF());
 
    if (!batch) {
 
      QObject::connect( qApp, SIGNAL(lastWindowClosed()), qApp, SLOT(quit()) );
 
    }
 

	
 
    //    main_window->Init(leaffile);
 

	
 
    // Install model or read catalogue of models
 
    ModelCatalogue model_catalogue(&mesh, useGUI?(Main *)main_window:0,modelfile);
 

	
 

	
 
    if (useGUI)
 
      model_catalogue.PopulateModelMenu();
 
    model_catalogue.InstallFirstModel();
 

	
 
    if (leaffile) 
 
      main_window->Init(leaffile);
 
    
 

	
 
    Cell::SetMagnification(1);
 
    /*    Cell::SetMagnification(1);
 
    Cell::setOffset(0,0);
 

	
 
    main_window->FitLeafToCanvas();
 

	
 
    main_window->Plot();
 

	
 
    */
 
    if (batch) {
 
      double t=0.;
 
      do {
 
	t = main_window->TimeStep();
 
      } while (t < par.maxt);
 
    } else
 
      return app.exec();
 

	
 
  } catch (const char *message) {
 
    if (batch) { 
 
      cerr << "Exception caught:" << endl;
 
      cerr << message << endl;
 
      abort();
 
    } else {
 
      QString qmess=QString("Exception caught: %1").arg(message);
 
      QMessageBox::critical(0, "Critical Error", qmess);
 
      abort();
 
    }
 
  } catch (ios_base::failure) {
 
    stringstream error_message;
 
    error_message << "I/O failure: " << strerror(errno);
 
    if (batch) {
 
      cerr << error_message.str() <<endl;
 
      abort();
 
    } else {
 
      QString qmess(error_message.str().c_str());
 
      QMessageBox::critical(0, "I/O Error", qmess );
 
      abort();
 
    }
 
  }
 
}
 

	
 
/* finis */
src/VirtualLeaf.pro
Show inline comments
 
#
 
#  $Id$
 
#
 
#  This file is part of the Virtual Leaf.
 
#
 
#  The Virtual Leaf 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.
 
#
 
#  The Virtual Leaf is distributed in the hope that it will be useful,
 
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
#  GNU General Public License for more details.
 
#
 
#  You should have received a copy of the GNU General Public License
 
#  along with the Virtual Leaf.  If not, see <http://www.gnu.org/licenses/>.
 
#
 
#  Copyright 2010 Roeland Merks.
 
#
 

	
 
CONFIG += release
 
CONFIG -= debug
 
CONFIG -= release
 
CONFIG += debug
 
CONFIG += qt
 

	
 
QMAKE_CXXFLAGS += -fexceptions
 
QMAKE_CXXFLAGS_DEBUG += -g3
 
QMAKE_CXXFLAGS_DEBUG += -DQDEBUG
 

	
 
#REACTIONS = reactions_auxin_growth.h 
 
#REACTIONS = reactions_meinhardt.h
 
#REACTIONS = reactions_pce_growth.h
 
DEFINES += QTGRAPHICS
 
DEFINES += REACTIONS_HEADER=$${REACTIONS}
 
DEFINES += REACTIONS_HEADER_STRING=\"$${REACTIONS}\"
 
DEFINES += FLEMING
 

	
 
BINDIR = ../bin
 
DESTDIR = $$BINDIR
 
TARGET = VirtualLeaf
 
TEMPLATE = app
 
PARTMPL = $${TARGET}par.tmpl
 
MAINSRC = $${TARGET}.cpp
 
QT -= network sql xml
 
QT += qt3support
 

	
 
!win32 {
 
 GRAPHICS = qt #qwt
 
}
 

	
 
win32 {
 
 CONFIG -= console
 
 LIBXML2DIR = ..\lib\libxml2
 
 LIBICONVDIR = ..\lib\libiconv
 
 LIBZDIR = ..\lib\libz
 
 GRAPHICS = qt 
 
 RC_FILE = VirtualLeaf.rc
 
 QMAKE_CXXFLAGS += -DLIBXML_STATIC
 
 QMAKE_CXXFLAGS += -I$${LIBXML2DIR}\include -I$${LIBICONVDIR}\include -I$${LIBZDIR}\include
 
 LIBS += -L$${LIBXML2DIR}\lib -lxml2 -L$${LIBICONVDIR}\lib -L$${LIBZDIR}\lib  -lz -lm -lwsock32 -liconv
 
}
 

	
 

	
 
macx:release {
 
 LIBS+= -dead_strip
 
}
 

	
 
unix {
 
 CC = /usr/bin/gcc 
 
 QWTDIR = /ufs/guravage/opt/qwt-5.2.1-svn
 
 QMAKE_LIBDIR += $$QWTDIR/lib 
 
 QMAKE_CXXFLAGS += -fPIC -I/usr/include/libxml2
 
 QMAKE_LFLAGS += -fPIC
 
 LIBS += -lxml2 -lz -lm 
 
}
 

	
 
# Input
 
HEADERS += \
 
 apoplastitem.h \
 
 canvas.h \
 
 cellbase.h \
 
 cell.h \
 
 cellitem.h \
 
 forwardeuler.h \
 
       hull.h \ 
 
 infobar.h \
 
 mainbase.h \
 
 mainbase.h \
 
 matrix.h \
 
 mesh.h \
 
 miscq.h \
 
 modelcatalogue.h \
 
 Neighbor.h \
 
 node.h \
 
 nodeitem.h \
 
 nodeset.h \
 
 OptionFileDialog.h \
 
 output.h \
 
 parameter.h \
 
 pardialog.h \
 
 parse.h \
 
 pi.h \
 
 qcanvasarrow.h \
 
 random.h \
 
 rungekutta.h \
 
 simitembase.h \
 
 simplugin.h \
 
 sqr.h \
 
 tiny.h \
 
 transporterdialog.h \
 
 UniqueMessage.h \
 
 vector.h \
 
 wallbase.h \
 
 wall.h \
 
 wallitem.h \
 
 warning.h \
 
 xmlwrite.h \
 
 $${PARTMPL}
 

	
 
SOURCES += \
 
 apoplastitem.cpp \
 
 canvas.cpp \
 
 cellbase.cpp \
 
 cell.cpp \
 
 cellitem.cpp \
 
 forwardeuler.cpp \
 
 hull.cpp \
 
 mainbase.cpp \
 
 matrix.cpp \
 
 mesh.cpp \
 
 miscq.cpp \
 
 modelcatalogue.cpp \
 
 Neighbor.cpp \
 
 node.cpp \
 
 nodeitem.cpp \
 
 nodeset.cpp \
 
 OptionFileDialog.cpp \
 
 output.cpp \
 
 parameter.cpp \
 
 pardialog.cpp \
 
 parse.cpp \
 
 random.cpp \
 
 rungekutta.cpp \
 
 simitembase.cpp \
 
 transporterdialog.cpp \
 
 UniqueMessage.cpp \
 
 vector.cpp \
 
 wallbase.cpp \
 
 wall.cpp \
 
 wallitem.cpp \
 
 warning.cpp \
 
 xmlwrite.cpp \
 
 $$MAINSRC
 

	
 
contains( TARGET, leaf_fleming ) {
 
 DEFINES += FLEMING	
 
}
 

	
 
contains(GRAPHICS, qwt) {
 
 #macx:LIBS += -L$$QWTDIR/lib -lqwt
 
 #win32:LIBS += -L$$QWTDIR/lib -lqwt5
 
 #LIBS += -L$$QWTDIR/lib -lqwt
 
 INCLUDEPATH += $$QWTDIR/include
 
 DEFINES += HAVE_QWT
 
 HEADERS += data_plot.h
 
 SOURCES += data_plot.cpp
 
}
 

	
 
contains( GRAPHICS, qt ) {
 
 message( "Building Qt executable" )
 
 QMAKE_CXXFLAGS += -DQTGRAPHICS # -fpermissive
 
}
 

	
 
contains( GRAPHICS, xfig ) {
 
 message("Building Xfig executable (background runnable).")
 
 QMAKE_CXXFLAGS += -DXFIGGRAPHICS
 
}
 

	
 
contains( GRAPHICS, x11 ) {
 
 !unix {
 
  error("X11 graphics only available on Unix systems.")
 
 }
 
 message("Building X11 executable")
 
 SOURCES += x11graph.cpp
 
 HEADERS += x11graph.h
 
 QMAKE_CXXFLAGS += -DX11GRAPHICS
 
 CONFIG -= qt
 
 CONFIG += x11
 
 unix:LIBS += -lpng
 
}
 

	
 
#contains( GRAPHICS, qwt ) {
 
# 	QMAKE_POST_LINK = "install_name_tool -change libqwt.5.dylib $$QWTDIR/lib/libqwt.dylib $${TARGET}.app/Contents/MacOS/$${TARGET}; \
 
#}
 

	
 
# MACOSX packaging
 
macx {
 
ICON = leaficon.icns
 
QMAKE_POST_LINK = "\
 
	#install_name_tool -change libqwt.5.dylib $$QWTDIR/lib/libqwt.dylib $${TARGET}.app/Contents/MacOS/$${TARGET}; \
 
	cp leaficon.icns $${DESTDIR}/$${TARGET}.app; \
 
	mkdir $${DESTDIR}/$${TARGET}.app/Contents/Frameworks; \
 
	cp -R /Library/Frameworks/QtCore.framework $${DESTDIR}/$${TARGET}.app/Contents/Frameworks; \
 
	cp -R /Library/Frameworks/QtGui.framework $${DESTDIR}/$${TARGET}.app/Contents/Frameworks; \
 
	cp -R /Library/Frameworks/Qt3Support.framework $${DESTDIR}/$${TARGET}.app/Contents/Frameworks; \
 
	#cp /usr/local/qwt/lib/libqwt.dylib $${DESTDIR}/$${TARGET}.app/Contents/Frameworks; \
 
	#install_name_tool -id @executable_path/../Frameworks/libqwt.dylib $${DESTDIR}/$${TARGET}.app/Contents/Frameworks/libqwt.dylib; \
 
	install_name_tool -change /usr/local/qwt/lib/libqwt.dylib @executable_path/../Frameworks/libqwt.dylib $${DESTDIR}/$${TARGET}.app/Contents/MacOS/$${TARGET};\
 
	install_name_tool -id @executable_path/../Frameworks/QtCore.framework/Versions/Current/QtCore $${DESTDIR}/$${TARGET}.app/Contents/Frameworks/QtCore.framework/Versions/4.0/QtCore; \
 
	install_name_tool -id @executable_path/../Frameworks/QtGui.framework/Versions/Current/QtGui $${DESTDIR}/$${TARGET}.app/Contents/Frameworks/QtGui.framework/Versions/4.0/QtGui; \
 
	install_name_tool -id @executable_path/../Frameworks/Qt3Support.framework/Versions/Current/Qt3Support $${DESTDIR}/$${TARGET}.app/Contents/Frameworks/Qt3Support.framework/Versions/4.0/Qt3Support; \
 
	install_name_tool -change Frameworks/QtCore.framework/Versions/Current/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4.0/QtCore $${DESTDIR}/$${TARGET}.app/Contents/MacOS/$${TARGET}; \
 
	install_name_tool -change Frameworks/QtGui.framework/Versions/Current/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4.0/QtGui $${DESTDIR}/$${TARGET}.app/Contents/MacOS/$${TARGET}; \
 
	install_name_tool -change Frameworks/Qt3Support.framework/Versions/Current/Qt3Support @executable_path/../Frameworks/Qt3Support.framework/Versions/4.0/Qt3Support $${DESTDIR}/$${TARGET}.app/Contents/MacOS/$${TARGET}; \
 
	install_name_tool -change Frameworks/QtCore.framework/Versions/Current/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4.0/QtCore $${DESTDIR}/$${TARGET}.app/Contents/Frameworks/Qt3Support.framework/Versions/4.0/Qt3Support; \
 
	install_name_tool -change Frameworks/QtGui.framework/Versions/Current/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4.0/QtGui $${DESTDIR}/$${TARGET}.app/Contents/Frameworks/Qt3Support.framework/Versions/4.0/Qt3Support; \
 
	install_name_tool -change Frameworks/QtCore.framework/Versions/Current/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4.0/QtCore $${DESTDIR}/$${TARGET}.app/Contents/Frameworks/Qt3Support.framework/Versions/4.0/Qt3Support; "
 
}
 

	
 
# finis
src/build_models/auxingrowthplugin.cpp
Show inline comments
 
/*
 
 *
 
 *  This file is part of the Virtual Leaf.
 
 *
 
 *  The Virtual Leaf 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.
 
 *
 
 *  The Virtual Leaf is distributed in the hope that it will be useful,
 
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 *  GNU General Public License for more details.
 
 *
 
 *  You should have received a copy of the GNU General Public License
 
 *  along with the Virtual Leaf.  If not, see <http://www.gnu.org/licenses/>.
 
 *
 
 *  Copyright 2010 Roeland Merks.
 
 *
 
 */
 

	
 
#include <QObject>
 
#include <QtGui>
 
#include "../simplugin.h"
 

	
 
#include "parameter.h"
 

	
 
#include "wallbase.h"
 
#include "cellbase.h"
 
#include "auxingrowthplugin.h"
 

	
 
#include "far_mem_5.h"
 

	
 
static const std::string _module_id("$Id$");
 

	
 

	
 
// To be executed after cell division
 
void AuxinGrowthPlugin::OnDivide(ParentInfo *parent_info, CellBase *daughter1, CellBase *daughter2)
 
{
 

	
 
  // Auxin distributes between parent and daughter according to area
 
  double area1 = daughter1->Area(), area2 = daughter2->Area();
 
  double tot_area = area1 + area2;
 
	
 
  daughter1->SetChemical(0,daughter1->Chemical(0)*(area1/tot_area));
 
  daughter2->SetChemical(0,daughter2->Chemical(0)*(area2/tot_area));
 
	
 
  // After divisions, parent and daughter cells get a standard stock of PINs.
 
  daughter1->SetChemical(1, par->initval[1]);
 
  daughter2->SetChemical(1, par->initval[1]);
 
	
 
	
 
  // Reset transporter values of parent and daughter
 
  QList<WallBase *> walls;
 
  foreach(WallBase *w, walls) { 
 
    w->setTransporter(daughter1, 1, 0.);
 
  }
 
}
 

	
 
void AuxinGrowthPlugin::SetCellColor(CellBase *c, QColor *color)
 
{ 
 

	
 
  // Red: PIN1
 
  // Green: Auxin
 
  color->setRgb(c->Chemical(1)/(1+c->Chemical(1)) * 255.,(c->Chemical(0)/(1+c->Chemical(0)) * 255.), 0);
 
}
 

	
 

	
 

	
 
void AuxinGrowthPlugin::CellHouseKeeping(CellBase *c)
 
{
 
  if (c->Boundary()==CellBase::None) {
 
    if (c->Area() > par->rel_cell_div_threshold * c->BaseArea() ) {
 
      c->SetChemical(0,0);
 
      c->Divide();
 
    }		
 
    // expand according to auxin concentration
 
    c->EnlargeTargetArea(par->auxin_dependent_growth?(c->Chemical(0)/(1.+c->Chemical(0)))*par->cell_expansion_rate:par->cell_expansion_rate);
 
  }  
 
}
 

	
 
void AuxinGrowthPlugin::CelltoCellTransport(Wall *w, double *dchem_c1, double *dchem_c2)
 
{
 

	
 
  // leaf edge is const source of auxin
 
  // (Neumann boundary condition: we specify the influx)
 
  if (w->C2()->BoundaryPolP()) {
 
    if (w->AuxinSource()) {
 
      double aux_flux = par->leaf_tip_source * w->Length();
 
      dchem_c1[0]+= aux_flux;
 
      return;
 
    } else {
 
      return;
 
    }
 
  }
 
	
 
  if (w->C1()->BoundaryPolP()) {
 
		
 
    if (w->AuxinSource()) {
 
      double aux_flux = par->leaf_tip_source * w->Length();
 
      dchem_c2[0] += aux_flux;
 
      return;
 
    } else {
 
			
 
      if (w->AuxinSink()) {
 
				
 
	// efflux into Shoot Apical meristem
 
	// we assume all PINs are directed towards shoot apical meristem
 
	dchem_c2[0] -= par->sam_efflux * w->C2()->Chemical(0) / (par->ka + w->C2()->Chemical(0));
 
				
 
	return;
 
      } else 
 
	return;
 
    }
 
  }
 
	
 
    
 
  // Passive fluxes (Fick's law)
 
  // only auxin flux now
 
  // flux depends on edge length and concentration difference
 
  int c=0;
 
  double phi = w->Length() * ( par->D[c] ) * ( w->C2()->Chemical(c) - w->C1()->Chemical(c) );
 
  dchem_c1[c] += phi; 
 
  dchem_c2[c] -= phi;
 
	
 
  // Active fluxes (PIN1 mediated transport)
 
	
 
  // (Transporters measured in moles, here)
 
  // efflux from cell 1 to cell 2
 
  double trans12 = ( par->transport * w->Transporters1(1) * w->C1()->Chemical(0) / (par->ka + w->C1()->Chemical(0)) );
 
	
 
  // efflux from cell 2 to cell 1
 
  double trans21 = ( par->transport * w->Transporters2(1) * w->C2()->Chemical(0) / (par->ka + w->C2()->Chemical(0)) );
 
    
 
  dchem_c1[0] += trans21 - trans12;
 
  dchem_c2[0] += trans12 - trans21;
 
	
 
}
 

	
 
void AuxinGrowthPlugin::WallDynamics(Wall *w, double *dw1, double *dw2)
 
{
 
  // Cells polarize available PIN1 to Shoot Apical Meristem
 
  if (w->C2()->BoundaryPolP()) {
 
    if (w->AuxinSink()) {
 
			
 
      dw1[0] = 0.; dw2[0] = 0.;
 
            
 
      // assume high auxin concentration in SAM, to convince PIN1 to polarize to it
 
      // exocytosis regulated0
 
      double nb_auxin = par->sam_auxin;
 
      double receptor_level = nb_auxin * par->r / (par->kr + nb_auxin);
 
			
 
      dw1[1] = par->k1 * w->C1()->Chemical(1) * receptor_level /( par->km + w->C1()->Chemical(1) ) - par->k2 * w->Transporters1(1);
 
			
 
      dw2[1] = 0.;
 
      return;
 
			
 
    } else {
 
      dw1[0]=dw2[0]=dw1[1]=dw2[1];
 
      return;
 
    }
 
  }
 
    
 
  if (w->C1()->BoundaryPolP()) {
 
    if (w->AuxinSink())  {
 
			
 
      dw1[0] = 0.; dw2[0] = 0.;
 
			
 
      // assume high auxin concentration in SAM, to convince PIN1 to polarize to it
 
      // exocytosis regulated
 
      double nb_auxin = par->sam_auxin;
 
      double receptor_level = nb_auxin * par->r / (par->kr + nb_auxin);
 
      dw2[1] = par->k1 * w->C2()->Chemical(1) * receptor_level /( par->km + w->C2()->Chemical(1) ) - par->k2 * w->Transporters2(1);
 
			
 
      dw1[1] = 0.;
 
      return;
 
			
 
    }  else {
 
      dw1[0]=dw2[0]=dw1[1]=dw2[1];
 
      return;
 
    }
 
  }
 
    
 
    
 
    
 
  // PIN1 localization at wall 1
 
  // Note: chemical 0 is Auxin (intracellular storage only)
 
  // Chemical 1 is PIN1 (walls and intracellular storage)
 
  //! \f$ \frac{d Pij/dt}{dt} = k_1 A_j \frac{P_i}{L_ij} - k_2 P_{ij} \f$
 
  // Note that Pij is measured in term of concentration (mol/L)
 
  // Pi in terms of quantity (mol)
 
	
 
  double dPijdt1=0., dPijdt2=0.;
 
    
 
  // normal cell
 
  double  auxin2 = w->C2()->Chemical(0);
 
  double receptor_level1 = auxin2 * par->r / (par->kr + auxin2);
 
    
 
  dPijdt1 = 
 
    // exocytosis regulated
 
    par->k1 * w->C1()->Chemical(1) * receptor_level1 / ( par->km + w->C1()->Chemical(1) ) - par->k2 * w->Transporters1(1);
 
	
 
  double  auxin1 = w->C1()->Chemical(0);
 
  double receptor_level2 = auxin1 * par->r / (par->kr + auxin1);
 
    
 
  // normal cell
 
  dPijdt2 = 
 
	
 
    // exocytosis regulated
 
    par->k1 * w->C2()->Chemical(1) * receptor_level2 / ( par->km + w->C2()->Chemical(1) ) - par->k2 * w->Transporters2(1);
 
    
 
  /* PIN1 of neighboring vascular cell inhibits PIN1 endocytosis */
 
    
 
  dw1[0] = 0.; dw2[0] = 0.;
 
  dw1[1] = dPijdt1;
 
  dw2[1] = dPijdt2;
 
}
 

	
 

	
 
double AuxinGrowthPlugin::complex_PijAj(CellBase *here, CellBase *nb, Wall *w)
 
{ 
 
  // gives the amount of complex "auxinreceptor-Pin1"  at the wall (at QSS) 
 
  //return here.Chemical(1) * nb.Chemical(0) / ( par->km + here.Chemical(1));
 
	
 
  double nb_aux = (nb->BoundaryPolP() && w->AuxinSink()) ? par->sam_auxin : nb->Chemical(0);
 
  double receptor_level = nb_aux * par->r / (par->kr + nb_aux);
 
	
 
  return here->Chemical(1) * receptor_level / ( par->km + here->Chemical(1));
 
	
 
}
 

	
 

	
 
void AuxinGrowthPlugin::CellDynamics(CellBase *c, double *dchem)
 
{
 
  // Note: Pi and Pij measured in numbers of molecules, not concentrations		
 
  double dPidt = 0.;
 
		
 
  double sum_Pij = c->SumTransporters( 1 );
 
		
 
  // exocytosis regulated:
 
	
 
  dPidt = -par->k1 * c->ReduceCellAndWalls<double>( far_3_arg_mem_fun( *this, &AuxinGrowthPlugin::complex_PijAj ) ) + par->k2 * sum_Pij;
 
	
 
  // production of PIN depends on auxin concentration
 
  dPidt +=  (c->AtBoundaryP()?par->pin_prod_in_epidermis:par->pin_prod) * c->Chemical(0) - c->Chemical(1) * par->pin_breakdown;
 
	
 
  // no PIN production in SAM
 
  if (c->Boundary() == CellBase::SAM) {
 
    dchem[1]=0.;
 
    dchem[0]= - par->sam_auxin_breakdown * c->Chemical(0);
 
  } else {
 
		
 
    dchem[1] = dPidt;
 
		
 
    // source of auxin
 
    dchem[0] = par->aux_cons - par->aux_breakdown * c->Chemical(0);
 
  }
 
}
 

	
 

	
 

	
 

	
 
Q_EXPORT_PLUGIN2(auxingrowthplugin, AuxinGrowthPlugin)
 

	
 
/* finis */
src/build_models/auxingrowthplugin.h
Show inline comments
 
/*
 
 *  $Id$
 
 *
 
 *  This file is part of the Virtual Leaf.
 
 *
 
 *  The Virtual Leaf 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.
 
 *
 
 *  The Virtual Leaf is distributed in the hope that it will be useful,
 
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 *  GNU General Public License for more details.
 
 *
 
 *  You should have received a copy of the GNU General Public License
 
 *  along with the Virtual Leaf.  If not, see <http://www.gnu.org/licenses/>.
 
 *
 
 *  Copyright 2010 Roeland Merks.
 
 *
 
 */
 

	
 
#ifndef _AUXINGROWTHPLUGIN_H_
 
#define _AUXINGROWTHPLUGIN_H_
 

	
 
#include <QObject>
 
#include <QtGui>
 
#include <QString>
 
#include "../simplugin.h"
 

	
 

	
 
class AuxinGrowthPlugin : public QObject, SimPluginInterface {
 
  Q_OBJECT
 
  Q_INTERFACES(SimPluginInterface);
 

	
 
 public:
 
  virtual QString ModelID(void) { return QString( "Auxin accumulation and growth" ); }
 
	
 
  // Executed after the cellular mechanics steps have equillibrized
 
  virtual void CellHouseKeeping (CellBase *c);
 
  // Differential equations describing transport of chemicals from cell to cell
 
  virtual void CelltoCellTransport(Wall *w, double *dchem_c1, double *dchem_c2);
 
    
 
  // Differential equations describing chemical reactions taking place at or near the cell walls
 
  // (e.g. PIN accumulation)
 
  virtual void WallDynamics(Wall *w, double *dw1, double *dw2);
 
	
 
  // Differential equations describing chemical reactions inside the cells
 
  virtual void CellDynamics(CellBase *c, double *dchem);
 
	
 
  // to be executed after a cell division
 
  virtual void OnDivide(ParentInfo *parent_info, CellBase *daughter1, CellBase *daughter2);
 
	
 
  // to be executed for coloring a cell
 
  virtual void SetCellColor(CellBase *c, QColor *color);	
 
  // return number of chemicals
 
  virtual int NChem(void) { return 2; }
 
	
 
  virtual QString DefaultLeafML(void) { return QString("auxin_growth.xml"); }
 
 
 
 private:
 
  double complex_PijAj(CellBase *here, CellBase *nb, Wall *w);
 
};
 

	
 
#endif
 

	
 
/* finis */
src/canvas.cpp
Show inline comments
 
@@ -96,768 +96,769 @@ static QColor dark_red("darkRed");
 
static const int imageRTTI = 984376;
 
extern Parameter par;
 
const QString Main::caption("Virtual leaf");
 
const QString Main::caption_with_file("Virtual leaf: %1");
 

	
 
FigureEditor::FigureEditor(
 
			   QGraphicsScene& c, Mesh &m, QWidget* parent,
 
			   const char* name, Qt::WindowFlags f) :
 
  QGraphicsView(&c,parent), mesh(m)
 
{
 
  intersection_line = 0;
 
  //angle_line = 0;
 
  setInteractive(true);
 
  moving = 0;  
 
  rotation_mode = false;
 
}
 

	
 

	
 
void FigureEditor::clear()
 
{
 
  QList<QGraphicsItem *> list = scene()->items();
 
  QList<QGraphicsItem *>::Iterator it = list.begin();
 
  for (; it != list.end(); ++it) {
 
    delete *it;
 
  }
 
}
 

	
 
void FigureEditor::wheelEvent(QWheelEvent *event)
 
{
 
  scaleView(pow((double)2, -event->delta() / 240.0));
 
}
 

	
 

	
 
void FigureEditor::scaleView (qreal scaleFactor)
 
{
 
  qreal factor = matrix().scale(scaleFactor, scaleFactor).mapRect(QRectF(0, 0, 1, 1)). width();
 
  if (factor < 0.07 || factor > 100) return;
 
  scale (scaleFactor, scaleFactor);
 
}
 

	
 
void FigureEditor::Save(const char *fname, const char *format, int sizex, int sizey)
 
{
 

	
 
  QImage *image = new QImage(sizex, sizey, QImage::Format_RGB32);
 
  image->fill(QColor(Qt::white).rgb());
 
  QPainter *painter=new QPainter(image);
 

	
 
  render(painter);
 

	
 
  image->save(QString(fname),format);
 
  delete painter;
 
  delete image;
 
}
 

	
 
void FigureEditor::mousePressEvent(QMouseEvent* e)
 
{
 
  static QList<Node*> selected;
 
  emit MousePressed();
 

	
 
  //QPointF p = matrix().inverted().map(e->pos());
 
  QPointF p = mapToScene(e->pos());
 

	
 
#ifdef QDEBUG  
 
  qDebug() << endl << "MousePressEvent location: (" << p.x() << "," << p.y() << ")." << endl;
 
  qDebug() << "Magnification:  " << Cell::Magnification() << endl;
 
  qDebug() << "Offsets:  " << Cell::Offset() << endl;
 
#endif
 

	
 

	
 
  QList<QGraphicsItem *> l=scene()->items(p);
 

	
 
#ifdef QDEBUG  
 
  qDebug() << "MousePressEvents, items: " << l.size() << endl;
 
  qDebug() << "Mouse button modifier: " << e->modifiers() << endl;
 
#endif
 

	
 
  if (e->button()==Qt::RightButton || l.size()==0) {
 

	
 
    //cerr << "Drawing an intersection line from " << p.x() << ", " << p.y() << endl;
 
    intersection_line = new QGraphicsLineItem( 0, scene() );
 
    intersection_line->setPen( QPen( QColor("red"), 3, Qt::DashLine ) );
 
    intersection_line->setLine( QLineF(p,p) );
 
    intersection_line->setZValue( 100 );
 
    intersection_line->show();
 
  }
 

	
 
  for (QList<QGraphicsItem *>::Iterator it=l.begin(); it!=l.end(); ++it) {
 
#ifdef QDEBUG
 
    qDebug() << typeid(**it).name() << endl;
 
#endif
 

	
 
    if ( !strcmp(typeid(**it).name(),"8NodeItem")) {
 

	
 
      stringstream data_strstream;
 
      data_strstream << (dynamic_cast<NodeItem*>(*it))->getNode();
 
      dynamic_cast<Main *>(parent())->UserMessage(QString(data_strstream.str().c_str()));
 

	
 
      (dynamic_cast<NodeItem*>(*it))->OnClick(e->button());
 
    }
 
    else 
 
      if ( !strcmp(typeid(**it).name(),"8CellItem") ) {
 

	
 
	Cell &c=((dynamic_cast<CellItem *>(*it))->getCell());      
 
	// OnClick to be defined in end-user code
 
	c.OnClick(e);
 
      } else {
 
	if ( !strcmp(typeid(**it).name(),"8WallItem") ) {
 
	  (dynamic_cast<WallItem *>(*it))->OnClick(e);
 
	} 
 
      }
 
  }
 

	
 
  FullRedraw();
 
  moving = 0;
 
}
 

	
 
void FigureEditor::mouseMoveEvent(QMouseEvent* e)
 
{
 

	
 
  // User chooses "rotation mode" and we can rotate the object around its center of mass
 
  if (dynamic_cast<Main *>(parent())->RotationModeP()) {
 

	
 
    QPointF p = mapToScene(e->pos());
 
    p.setX(p.x() / Cell::Magnification());
 
    p.setY(p.y() / Cell::Magnification());
 

	
 

	
 
    // get object's center of mass
 
    QPointF rotation_midpoint = mesh.Centroid()*Cell::Factor() - Cell::Offset();
 

	
 

	
 
    // calculate rotation angle
 
    double dy = (rotation_midpoint.y() - p.y());
 
    double dx = (rotation_midpoint.x() - p.x());
 
    double new_rot_angle = atan2(dx, dy);
 
    double d_alpha = new_rot_angle - rot_angle;
 
    rot_angle = new_rot_angle;
 

	
 
    mesh.Rotate(d_alpha, ( Vector(rotation_midpoint) + Cell::Offset() ) / Cell::Factor() );
 

	
 
    dynamic_cast<Main *>(parent())->Plot(0);
 
    FullRedraw();
 
    return;
 
  }
 
 
 
  
 
  if ( moving ) {
 

	
 
    QPointF p = mapToScene(e->pos());
 
    moving->userMove(p.x() - moving_start.x(),
 
		     p.y() - moving_start.y());
 
    moving_start = p;
 
    scene()->update();
 

	
 
  }
 

	
 
  //cerr << "event";
 

	
 
  // keep track of intersection line to interactively cut a growing leaf
 

	
 
  if ( intersection_line ) {
 

	
 
    QPointF sp = intersection_line -> line().p1(); // startpoint
 
    QPointF ep = mapToScene(e->pos()); // endpoint
 
    intersection_line -> setLine( QLineF(sp, ep) ); 
 
    scene()->update();
 
    // Need this for Mac
 
    FullRedraw();
 
  }
 
}
 

	
 
//void FigureEditor::contentsMouseReleaseEvent(QMouseEvent* e)
 
void FigureEditor::mouseReleaseEvent(QMouseEvent* e)
 
{
 

	
 
  emit MouseReleased();
 
  // intersection line for leaf was finished now.
 

	
 
  if (e->button()==Qt::LeftButton) { 
 
    if (intersection_line ) {
 
#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) {
 
#ifdef QDEBUG
 
	qDebug() << "No cells detected :-(" << endl;
 
#endif
 
	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();
 

	
 
#ifdef QDEBUG
 
	qDebug() << "Dividing Cell " << c.Index() << endl;
 
#endif
 

	
 
	c.DivideOverGivenLine( startpoint, endpoint, true, node_set);
 
      }
 

	
 
      node_set->CleanUp();
 
      mesh.AddNodeSet(node_set);
 

	
 
#ifdef QDEBUG
 
      qDebug() << "Done DivideOverGivenLine" << endl;
 
#endif
 

	
 
      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);
 
	}
 

	
 

	
 

	
 
#ifdef QDEBUG
 
	qDebug() << "Done CutAwayBelowLine" << endl;
 
#endif
 
	mesh.TestIllegalWalls();
 
	mesh.RepairBoundaryPolygon();
 
#ifdef QDEBUG
 
	qDebug() << "Done RepairBoundaryPolygon" << endl;
 
#endif
 
	mesh.TestIllegalWalls();
 
	mesh.CleanUpWalls();
 
#ifdef QDEBUG
 
	qDebug() << "Done CleanUpWalls" << endl;
 
#endif
 
	mesh.TestIllegalWalls();
 
      }
 

	
 
      dynamic_cast<Main *>(parent())->Plot();
 

	
 
#ifdef QDEBUG
 
      cerr << "NodeSet of cutting line: " << *node_set << endl;
 
#endif
 
    }
 
  } 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( );
 

	
 
#ifdef QDEBUG
 
  qDebug() <<  "l.size() = " << l.size() << endl;
 
#endif
 

	
 
  for (QList<QGraphicsItem *>::Iterator it=l.begin(); it!=l.end(); ++it) {
 

	
 
#ifdef QDEBUG
 
    qDebug() << typeid(**it).name() << endl;
 
#endif
 

	
 
    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();
 
  dynamic_cast<Main *>(parent())->Plot();
 
  FullRedraw();
 
#ifdef QDEBUG  
 
  qDebug() << "Node: " << p << endl;
 
#endif
 
}
 

	
 
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);
 

	
 
#ifdef QDEBUG
 
  qDebug() << "Interactive = " << editor->isEnabled();
 
#endif
 

	
 
  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 areas", this, SLOT(exportCellData()));
 

	
 
  file->insertSeparator();
 
  file->insertItem("&Print...", this, SLOT(print()), Qt::CTRL+Qt::Key_P);
 
  file->insertSeparator();
 
  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 &centers", 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);
 
  view->insertSeparator();
 
  only_boundary_id = view->insertItem("Show only leaf &boundary", this, SLOT(toggleLeafBoundary()));
 
  view->insertSeparator();
 
  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 &parameters", 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->insertItem("&About", this, SLOT(about()) ); //, Key_F1);
 
  helpmenu->insertSeparator();
 
  helpmenu->insertItem("&LICENSE", this, SLOT(gpl()) );
 
  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()
 
{
 

	
 
  stopSimulation();
 

	
 
  Q3FileDialog *fd = new Q3FileDialog( this, "file dialog", TRUE );
 
  fd->setMode( Q3FileDialog::AnyFile );
 
  fd->setFilter( "Parameter files (*.par)");
 

	
 
  QString fileName;
 
  if ( fd->exec() == QDialog::Accepted ) {
 
    fileName = fd->selectedFile();
 
    ofstream parfile((const char *)fileName);
 
    par.Write(parfile);
 
  }
 

	
 
  startSimulation();
 
}
 

	
 
void Main::readPars()
 
{
 

	
 
  stopSimulation();
 

	
 
  Q3FileDialog *fd = new Q3FileDialog( this, "file dialog", TRUE );
 
  fd->setMode( Q3FileDialog::ExistingFile );
 
  fd->setFilter( "Parameter files (*.par)");
 

	
 
  QString fileName;
 
  if ( fd->exec() == QDialog::Accepted ) {
 
    fileName = fd->selectedFile();
 
    par.Read((const char *)fileName);
 
  }
 

	
 
  emit ParsChanged();
 
}
 

	
 

	
 
void Main::saveStateXML()
 
{
 

	
 
  stopSimulation();
 
  Q3FileDialog *fd = new Q3FileDialog( this, "file dialog", TRUE );
 
  fd->setMode( Q3FileDialog::AnyFile );
 
  fd->setFilter( "XML (*.xml)");
 
  QString fileName;
 

	
 
  if ( fd->exec() == QDialog::Accepted ) {
 
    fileName = fd->selectedFile();
 
    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());
 

	
 
    }
 
  }
 
}
 

	
 

	
 

	
 
void Main::snapshot()
 
{
 

	
 

	
 
  stopSimulation();
 
  Q3FileDialog *fd = new Q3FileDialog( this, "Save snapshot", TRUE );
 
  fd->setMode( Q3FileDialog::AnyFile );
 

	
 
  QString fileName;
 

	
 
  if ( fd->exec() == QDialog::Accepted ) {
 
    fileName = fd->selectedFile();
 
    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 snapshot();
 

	
 
    } else {
 

	
 
      // extract extension from filename
 
      QString extension = getExtension(fileName);
 

	
 
      // Save bitmaps at 1024x768
 
      Save((const char *)fileName, extension, 1024, 768);
 
    }
 
  }
 
}
 

	
 

	
 

	
 
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()) {
 
      return;
 
    }
 

	
 
    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();
 
      return;
 
    }
 
    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);
 
#ifdef QDEBUG
 
    qDebug() << "Reading done."<< endl;
 
#endif
 
    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());
 
    cerr << status_message.toStdString() << endl;
 
    setCaption(caption_with_file.arg(filename));
 
    statusBar()->message(status_message);
 
    emit ParsChanged();
 
#ifdef QDEBUG
 
    qDebug() << "Done. Returning 0." << endl;
 
#endif
 
    return 0;
 
  } catch (const char *error_message) {
 
    QMessageBox mb( "Read leaf from XML file",
 
		    QString(error_message),
 
		    QMessageBox::Critical,
 
		    QMessageBox::Ok | QMessageBox::Default,
 
		    Qt::NoButton,
 
		    Qt::NoButton);
 
    mb.exec();
 
    return 1;
 
  }
 
}
 

	
 

	
 
void Main::readNextStateXML()
 
{
 

	
 
  // 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()) {
 
      return;
 
    }
 

	
 
    ++f;
 
    if (f==xml_files.end()) {
 
      QMessageBox mb( "Read next leaf",
 
		      "No more files",
 
		      QMessageBox::Information,
 
		      QMessageBox::Ok | QMessageBox::Default,
 
		      QMessageBox::NoButton,
 
		      QMessageBox::NoButton);
 
      mb.exec();
 
      return;
 
    }
 
    next_file = *f;
 
    next_file = currentFile_path+"/"+next_file;
 

	
 
    readStateXML((const char*)next_file);
 
  }
 
}
 

	
 
void Main::readLastStateXML()
 
{
 

	
 
  // 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 );
 

	
 

	
 
@@ -972,385 +973,399 @@ void Main::gpl()
 

	
 
  gpl->setText(QString( "<h3>GNU GENERAL PUBLIC LICENSE</h3>"
 
			"<p>Version 3, 29 June 2007</p>"
 
			"<p>Copyright &copy; 2007 Free Software Foundation, Inc. "
 
			"&lt;<a href=\"http://fsf.org/\">http://fsf.org/</a>&gt;</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()
 
{
 
  Plot();
 
}
 

	
 
void Main::toggleShowWalls()
 
{
 
  Plot();
 
}
 

	
 
void Main::toggleShowApoplasts()
 
{
 
  Plot();
 
}
 

	
 
void Main::toggleShowNodes()
 
{
 
  Plot();
 
}
 

	
 
void Main::toggleNodeNumbers(void)
 
{
 
  Plot();
 
}
 

	
 
void Main::toggleCellNumbers(void)
 
{
 
  Plot();
 
}
 

	
 
void Main::toggleCellAxes(void)
 
{
 
  Plot();
 
}
 

	
 
void Main::toggleCellStrain(void)
 
{
 
  Plot();
 
}
 

	
 
void Main::toggleShowFluxes(void)
 
{
 
  Plot();
 
}
 

	
 
void Main::toggleShowBorderCells()
 
{
 
  Plot();
 
}
 

	
 
void Main::toggleHideCells(void)
 
{
 
  Plot();
 
  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");
 
  running = false;
 
}
 

	
 
void Main::togglePaused()
 
{
 
  bool s = run->isItemChecked(paused_id);
 
  if (s) {
 
    cerr << "Calling start simulation" << endl;
 
    startSimulation();
 
  } else {
 
    cerr << "Calling stop simulation" << endl;
 
    stopSimulation();
 
  }
 
}
 

	
 
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()
 
{
 
  QMatrix m = editor->matrix();
 
  m.scale( 1.1, 1.1 );
 
  editor->setMatrix( m );
 
}
 

	
 
void Main::zoomOut()
 
{
 
  QMatrix m = editor->matrix();
 
  m.scale( 0.9, 0.9 );
 
  editor->setMatrix( m );
 
}
 

	
 

	
 
void Main::print()
 
{
 
  if ( !printer ) printer = new QPrinter;
 

	
 
  if ( printer->setup(this) ) {
 

	
 
    //    extern Mesh mesh;
 
    Vector bbll,bbur;
 
    mesh.BoundingBox(bbll,bbur);
 

	
 
#ifdef QDEBUG
 
    qDebug() << "bbll = " << bbll << endl;
 
    qDebug() << "bbur = " << bbur << endl;
 
#endif
 
    double cw = (bbur.x - bbll.x);
 
    double ch = (bbur.y - bbll.y);
 
    QPainter pp(printer);
 
    QRect vp=pp.viewport();
 
#ifdef QDEBUG
 
    qDebug() << "Paper width = " << vp.width() << " x " << vp.height() << endl;
 
#endif
 

	
 
    // 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()));
 
    } else {
 
      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=0;
 
  TimeStep();
 
  t++;
 
  // check number of timesteps
 
  if (t==par.nit) {
 
    emit SimulationDone();
 
  }
 
}
 

	
 

	
 
void Main::RestartSim(void)
 
{
 

	
 
  stopSimulation();
 
  if ( QMessageBox::question(
 
			     this,
 
			     tr("Restart simulation?"),
 
			     tr("Restart simulation.\n"
 
				"Are you sure?"),
 
			     QMessageBox::Yes | QMessageBox::No, QMessageBox::NoButton ) == QMessageBox::Yes ) {
 

	
 
    cerr << "Restarting simulation" << endl;
 
    //    extern Mesh mesh;
 
    mesh.Clear();
 
    Init();
 
    Plot();
 
    editor->FullRedraw();
 
  } 
 
  //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;
 
  QMatrix m = editor->matrix();
 

	
 
#ifdef QDEBUG  
 
  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;
 
#endif
 
  m.scale( scale_factor, scale_factor );
 
  editor->setMatrix( m );
 
  editor->show();
 
}
 

	
 
void Main::PauseIfRunning(void)
 
{
 
  if (running) {
 
    timer->stop();
 
  }
 
}
 

	
 
void Main::ContIfRunning(void)
 
{
 
  if (running) {
 
    timer->start( 0 );
 
  }
 
}
 

	
 
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);
 
  //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);
 
  Plot();
 

	
 
  editor->FullRedraw();
 
}
 

	
 
void Main::CleanMeshChemicals(void) 
 
{
 

	
 
  vector<double> clean_chem(Cell::NChem());
 

	
 
  for (int i=0;i<Cell::NChem();i++) {
 
    clean_chem[i]=par.initval[i];
 
  }
 

	
 
  mesh.CleanChemicals(clean_chem);
 
  mesh.setTime(0);
 
  Plot();
 

	
 
  editor->FullRedraw();
 
}
 

	
 
void Main::CleanMeshTransporters(void) 
 
{
 
  vector<double> clean_transporters(Cell::NChem());
 
  for (int i=0;i<Cell::NChem();i++) {
 
    clean_transporters[i]=0.;
 
  }
 

	
 
  mesh.CleanTransporters(clean_transporters);
 

	
 
  mesh.setTime(0);
 
  Plot();
 

	
 
  editor->FullRedraw();
 
}
 

	
 
void Main::RandomizeMesh(void) 
 
{
 

	
 
  vector<double> max_chem(Cell::NChem());
 
  vector<double> max_transporters(Cell::NChem());
 

	
 
  for (int i=0;i<Cell::NChem();i++) {
 
    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);
 

	
 
  Plot();
 
}
 

	
 
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(void) {
 

	
 
  // perhaps make this more general: a popup window were user selects data he wants to export
 
  // can go to "settings" section of XML file as well so this can also be measured off-line
 
  // mesh.CSVExport would take an QMap or so to record all options
 
  // first line gives legenda
 
  QFile file("areas.csv");
 
  if ( file.open( IO_WriteOnly ) ) {
 
    QTextStream stream( &file );
 
    mesh.CSVExportCellData(stream);
 
    mesh.CSVExportMeshData(stream);
 
    file.close();
 
  }
 
}
 
/* finis */
src/canvas.h
Show inline comments
 
/*
 
 *
 
 *  $Id$
 
 *
 
 *  This file is part of the Virtual Leaf.
 
 *
 
 *  The Virtual Leaf 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.
 
 *
 
 *  The Virtual Leaf is distributed in the hope that it will be useful,
 
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 *  GNU General Public License for more details.
 
 *
 
 *  You should have received a copy of the GNU General Public License
 
 *  along with the Virtual Leaf.  If not, see <http://www.gnu.org/licenses/>.
 
 *
 
 *  Copyright 2010 Roeland Merks.
 
 *
 
 */
 

	
 
#ifndef _CANVAS_H_
 
#define _CANVAS_H_
 

	
 
#include <q3popupmenu.h>
 
#include <q3mainwindow.h>
 
#include <q3intdict.h>
 
#include <QGraphicsScene>
 
#include <QGraphicsView>
 
#include <QList>
 

	
 
#include <string>
 
#include <sstream>
 

	
 
//Added by qt3to4:
 
#include <QMouseEvent>
 
#include <vector>
 
#include "simitembase.h"
 
#include "mainbase.h"
 
#include "cellitem.h"
 
#include "infobar.h"
 

	
 
#include "nodeitem.h"
 
#include "cell.h"
 

	
 
#ifdef HAVE_QWT
 
#include "data_plot.h"
 
#endif
 

	
 
#include <libxml/xpath.h>
 
#include <libxml/xmlreader.h>
 

	
 
#if defined(Q_OS_MAC)
 
#define PREFIX "cmd"
 
#else
 
#define PREFIX "crtl"
 
#endif
 

	
 

	
 
class QFile;
 
class QDir;
 
class ModelCatalogue;
 
class InfoBar;
 

	
 
class FigureEditor : public QGraphicsView {
 
  Q_OBJECT
 

	
 
    friend class Main;
 
 public:
 
  FigureEditor(QGraphicsScene&, Mesh&, QWidget* parent=0, const char* name=0, Qt::WFlags f=0);
 
  void clear();
 
  void Save(const char *fname, const char *format, int sizex=640, int sizey=480);
 
  void FullRedraw(void);
 

	
 
 protected:
 
  void mousePressEvent(QMouseEvent*);
 
  void mouseMoveEvent(QMouseEvent*);
 
  void mouseReleaseEvent(QMouseEvent*);
 
  void wheelEvent(QWheelEvent *event);
 
  void scaleView(qreal scaleFactor);
 

	
 
  vector <CellItem *> getIntersectedCells(void);
 
  void insertNode(QPointF p);
 
  NodeItem *selectedNodeItem(QList<QGraphicsItem *> l) const;  
 

	
 
 signals:
 
  void status(const QString&);
 
  void MousePressed(void);
 
  void MouseReleased(void);
 
 protected:
 
  Mesh &mesh;
 

	
 
 private:
 
  //NodeItem* moving;
 
  SimItemBase *moving;
 
  QPointF moving_start;
 

	
 
  QGraphicsLineItem *intersection_line;
 
  bool rotation_mode;
 
  bool insert_mode;
 
  double rot_angle;
 
};
 

	
 
class Main : public Q3MainWindow, public MainBase {
 
  Q_OBJECT
 
    friend class ModelCatalogue; // needs to populate menu item models
 
 public:
 
  Main(QGraphicsScene&, Mesh&, QWidget* parent=0, const char* name=0, Qt::WFlags f=0);
 
  ~Main();
 
  virtual bool ShowCentersP(void) {return view->isItemChecked(com_id);}
 
  virtual bool ShowMeshP(void) {return view->isItemChecked(mesh_id);}
 
  virtual bool ShowBorderCellsP(void) {return view->isItemChecked(border_id);}
 
  virtual bool PausedP(void) {return run->isItemChecked(paused_id);}
 
  virtual bool ShowNodeNumbersP(void) {return view->isItemChecked(node_number_id);}
 
  virtual bool ShowCellNumbersP(void) {return view->isItemChecked(cell_number_id);}
 
  virtual bool ShowCellAxesP(void) {return view->isItemChecked(cell_axes_id);}
 
  virtual bool ShowCellStrainP(void) {return view->isItemChecked(cell_strain_id);}
 
  virtual bool MovieFramesP(void) {return view->isItemChecked(movie_frames_id);}
 
  virtual bool ShowBoundaryOnlyP(void) {return view->isItemChecked(only_boundary_id);}
 
  virtual bool ShowWallsP(void) {return view->isItemChecked(cell_walls_id);}
 
  virtual bool ShowApoplastsP(void) { return view->isItemChecked(apoplasts_id);}
 
  virtual bool ShowFluxesP(void) { return view->isItemChecked(fluxes_id); }
 
  virtual bool DynamicCellsP(void) { return options->isItemChecked(dyn_cells_id); }
 
  virtual bool RotationModeP(void) { return options->isItemChecked(rotation_mode_id); }
 
  virtual bool InsertModeP(void) { return options->isItemChecked(insert_mode_id); }
 
  virtual bool ShowToolTipsP(void) { return helpmenu->isItemChecked(tooltips_id); }
 
  virtual bool HideCellsP(void) { return view->isItemChecked(hide_cells_id); }
 
  void scale(double factor); 
 
  virtual double getFluxArrowsize(void)
 
  {
 
    return flux_arrow_size;
 
  }
 

	
 
  void FitCanvasToWindow();
 
  void FitLeafToCanvas(void);
 

	
 

	
 
  public slots:
 

	
 
  void about();
 
  void gpl();
 
  void TimeStepWrap();
 
  void togglePaused();
 
  void setFluxArrowSize(int size);
 
  void RestartSim(void);
 
  void toggleShowCellCenters(void);
 
  void toggleShowNodes(void);
 
  void toggleShowBorderCells(void);
 
  void toggleShowFluxes(void);
 
  void toggleNodeNumbers(void);
 
  void toggleCellNumbers(void);
 
  void toggleCellAxes(void);
 
  void toggleCellStrain(void);
 
  void toggleShowWalls(void);
 
  void toggleShowApoplasts(void);
 
  void toggleDynCells(void);
 
  void toggleMovieFrames(void);
 
  void toggleLeafBoundary(void);
 
  void toggleHideCells(void);
 
  void print();
 
  void startSimulation(void);
 
  void stopSimulation(void);
 
  void RefreshInfoBar(void);
 

	
 
  void EnterRotationMode(void)
 
  {
 

	
 
    UserMessage("Rotation mode. Click mouse to exit.");
 
    if (editor) {
 
      editor->rot_angle = 0. ; 
 

	
 
      // Exit rotation mode if mouse is clicked
 
      connect(editor, SIGNAL(MousePressed()), this, SLOT(ExitRotationMode()));
 
      
 
      editor->setMouseTracking(true);
 

	
 
    }
 

	
 
  }
 
  void ExitRotationMode(void)
 
  { 
 
    UserMessage("Exited rotation mode.",2000);
 

	
 
    options->setItemChecked(rotation_mode_id, false); 
 
    if (editor)
 
      disconnect(editor, SIGNAL(MousePressed()), this, SLOT(ExitRotationMode()));
 
    editor->setMouseTracking(false);
 
 
 
  }
 

	
 
  virtual void UserMessage(QString message, int timeout=0);
 
  void Refresh(void) { Plot(); }
 
  void PauseIfRunning(void);
 
  void ContIfRunning(void);
 
  virtual void XMLReadSettings(xmlNode *settings);
 

	
 
  private slots:
 
  void aboutQt();
 
  void newView();
 
  void EditParameters();
 
  void readStateXML();
 
  int readStateXML(const char *filename, bool geometry = true, bool pars=true, bool simtime = true);
 
  void readNextStateXML();
 
  void readPrevStateXML();
 
  void readFirstStateXML();
 
  void readLastStateXML();
 
  void exportCellData();
 
  void saveStateXML();
 
  void snapshot();
 
  void savePars();
 
  void readPars();
 
  void clear();
 
  void init();
 
  virtual void CutSAM() { MainBase::CutSAM(); Refresh();}
 

	
 
  void enlarge();
 
  void shrink();
 
  void zoomIn();
 
  void zoomOut();
 

	
 
  void CleanMesh();
 
  void CleanMeshChemicals(void);
 
  void CleanMeshTransporters(void);
 

	
 
  void RandomizeMesh();
 

	
 
 signals:
 
  void SimulationDone(void);
 
  void ParsChanged(void);
 

	
 
 protected:
 
  Mesh &mesh;
 

	
 
 private:
 
  NodeSet *node_set;
 
  FigureEditor *editor;
 
  Q3PopupMenu* options;
 
  Q3PopupMenu *view;
 
  Q3PopupMenu *run;
 
  QMenu *modelmenu;
 
  Q3PopupMenu *helpmenu;
 

	
 
  QPrinter* printer;
 
  const QDir *working_dir;
 
  QString currentFile;
 
  //  toggle item states 
 
  int dbf_id; // options->Double Buffer
 
  int com_id; // view->Show centers
 
  int mesh_id; // view->Show mesh
 
  int node_number_id; // view->Show Node numbers
 
  int cell_number_id; // view->Show Cell numbers
 
  int border_id; // view->Show border cells
 
  int paused_id; // run->Simulation paused
 
  int cell_axes_id; // view->Show cell axes
 
  int cell_strain_id; // view->Show cell strain
 
  int only_boundary_id; // view ->Show only leaf boundary
 
  int cell_walls_id; // view -> Show transporters
 
  int apoplasts_id; // view -> Show apoplasts
 
  int tooltips_id; // help -> Show Cell Info
 
  int hide_cells_id; // view->Hide Cells
 
  double flux_arrow_size;
 
  int movie_frames_id;
 
  int fluxes_id;
 
  int dyn_cells_id;
 
  int rotation_mode_id;
 
  int insert_mode_id;
 
  QTimer *timer;
 
  QFile *gifanim;
 
  bool running;
 
  virtual xmlNode *XMLSettingsTree(void);
 
  static const QString caption;
 
  static const QString caption_with_file;
 
  InfoBar *infobar;
 
};
 

	
 
#endif
 

	
 
/* finis*/
src/hull.cpp
Show inline comments
 
new file 100644
 
// Copyright 2001, softSurfer (www.softsurfer.com)
 
// This code may be freely used and modified for any purpose
 
// providing that this copyright notice is included with it.
 
// SoftSurfer makes no warranty for this code, and cannot be held
 
// liable for any real or imagined damage resulting from its use.
 
// Users of this code must verify correctness for their application.
 

	
 
// Assume that a class is already given for the object:
 
//    Point with coordinates {float x, y;}
 
//===================================================================
 

	
 
// isLeft(): tests if a point is Left|On|Right of an infinite line.
 
//    Input:  three points P0, P1, and P2
 
//    Return: >0 for P2 left of the line through P0 and P1
 
//            =0 for P2 on the line
 
//            <0 for P2 right of the line
 
//    See: the January 2001 Algorithm on Area of Triangles
 

	
 
#include "hull.h"
 

	
 

	
 
inline float
 
isLeft( Point P0, Point P1, Point P2 )
 
{
 
    return (P1.x - P0.x)*(P2.y - P0.y) - (P2.x - P0.x)*(P1.y - P0.y);
 
}
 
//===================================================================
 
 
 

	
 
// chainHull_2D(): Andrew's monotone chain 2D convex hull algorithm
 
//     Input:  P[] = an array of 2D points
 
//                   presorted by increasing x- and y-coordinates
 
//             n = the number of points in P[]
 
//     Output: H[] = an array of the convex hull vertices (max is n)
 
//     Return: the number of points in H[]
 
int
 
chainHull_2D( Point* P, int n, Point* H )
 
{
 
    // the output array H[] will be used as the stack
 
    int    bot=0, top=(-1);  // indices for bottom and top of the stack
 
    int    i;                // array scan index
 

	
 
    // Get the indices of points with min x-coord and min|max y-coord
 
    int minmin = 0, minmax;
 
    float xmin = P[0].x;
 
    for (i=1; i<n; i++)
 
        if (P[i].x != xmin) break;
 
    minmax = i-1;
 
    if (minmax == n-1) {       // degenerate case: all x-coords == xmin
 
        H[++top] = P[minmin];
 
        if (P[minmax].y != P[minmin].y) // a nontrivial segment
 
            H[++top] = P[minmax];
 
        H[++top] = P[minmin];           // add polygon endpoint
 
        return top+1;
 
    }
 

	
 
    // Get the indices of points with max x-coord and min|max y-coord
 
    int maxmin, maxmax = n-1;
 
    float xmax = P[n-1].x;
 
    for (i=n-2; i>=0; i--)
 
        if (P[i].x != xmax) break;
 
    maxmin = i+1;
 

	
 
    // Compute the lower hull on the stack H
 
    H[++top] = P[minmin];      // push minmin point onto stack
 
    i = minmax;
 
    while (++i <= maxmin)
 
    {
 
        // the lower line joins P[minmin] with P[maxmin]
 
        if (isLeft( P[minmin], P[maxmin], P[i]) >= 0 && i < maxmin)
 
            continue;          // ignore P[i] above or on the lower line
 

	
 
        while (top > 0)        // there are at least 2 points on the stack
 
        {
 
            // test if P[i] is left of the line at the stack top
 
            if (isLeft( H[top-1], H[top], P[i]) > 0)
 
                break;         // P[i] is a new hull vertex
 
            else
 
                top--;         // pop top point off stack
 
        }
 
        H[++top] = P[i];       // push P[i] onto stack
 
    }
 

	
 
    // Next, compute the upper hull on the stack H above the bottom hull
 
    if (maxmax != maxmin)      // if distinct xmax points
 
        H[++top] = P[maxmax];  // push maxmax point onto stack
 
    bot = top;                 // the bottom point of the upper hull stack
 
    i = maxmin;
 
    while (--i >= minmax)
 
    {
 
        // the upper line joins P[maxmax] with P[minmax]
 
        if (isLeft( P[maxmax], P[minmax], P[i]) >= 0 && i > minmax)
 
            continue;          // ignore P[i] below or on the upper line
 

	
 
        while (top > bot)    // at least 2 points on the upper stack
 
        {
 
            // test if P[i] is left of the line at the stack top
 
            if (isLeft( H[top-1], H[top], P[i]) > 0)
 
                break;         // P[i] is a new hull vertex
 
            else
 
                top--;         // pop top point off stack
 
        }
 
        H[++top] = P[i];       // push P[i] onto stack
 
    }
 
    if (minmax != minmin)
 
        H[++top] = P[minmin];  // push joining endpoint onto stack
 

	
 
    return top+1;
 
}
 

	
src/hull.h
Show inline comments
 
new file 100644
 
// Class point needed by 2D convex hull code
 
class Point {
 

	
 
public:
 
  Point(float xx, float yy) {
 
    x=xx;
 
    y=yy;
 
  }
 
  Point(void) {
 
    x=0; y=0;
 
  }
 
  float x,y;
 

	
 
};
 

	
 
int chainHull_2D( Point* P, int n, Point* H );
 

	
src/mainbase.h
Show inline comments
 
/*
 
 *
 
 *  $Id$
 
 *
 
 *  This file is part of the Virtual Leaf.
 
 *
 
 *  The Virtual Leaf 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.
 
 *
 
 *  The Virtual Leaf is distributed in the hope that it will be useful,
 
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 *  GNU General Public License for more details.
 
 *
 
 *  You should have received a copy of the GNU General Public License
 
 *  along with the Virtual Leaf.  If not, see <http://www.gnu.org/licenses/>.
 
 *
 
 *  Copyright 2010 Roeland Merks.
 
 *
 
 */
 

	
 

	
 
#ifndef _MAINBASE_H_
 
#define _MAINBASE_H_
 

	
 
#include <QGraphicsScene>
 
#include <qpixmap.h>
 
#include <q3picture.h>
 
#include <qpainter.h>
 
#include <qwidget.h>
 
#include <iostream>
 
#include <QGraphicsItem>
 
#include <QPrinter>
 
#include "mesh.h"
 
#include "warning.h"
 

	
 
using namespace std;
 

	
 
/*! Implement these functions in your main application */
 
class MainBase  {
 

	
 
 public:
 
 MainBase(QGraphicsScene &c, Mesh &m) : mesh(m), canvas(c) {
 

	
 
    // Standard options for batch version
 
    showcentersp =  false;
 
    showmeshp =  false;
 
    showbordercellp =  false;
 
    shownodenumbersp =  false;
 
    showcellnumbersp =  false;
 
    showcellsaxesp = false;
 
    showcellstrainp =  false;
 
    movieframesp = true;
 
    showboundaryonlyp =  false;
 
    showwallsp =  false;
 
    showfluxesp = false;
 
    dynamicscellsp = true;
 
    showtooltipsp = false;
 
    hidecellsp = false;
 
  }
 
  virtual ~MainBase() {};
 

	
 
  virtual double TimeStep();
 
  virtual void Init(char *leaffile=0);
 
  virtual void Init(const char *leaffile=0);
 

	
 
  virtual bool ShowCentersP(void) {return showcentersp;}
 
  virtual bool ShowMeshP(void) {return showmeshp; }
 
  virtual bool ShowBorderCellsP(void) {return showbordercellp; }
 
  virtual bool PausedP(void) {return false; }
 
  virtual bool ShowNodeNumbersP(void) {return shownodenumbersp; }
 
  virtual bool ShowCellNumbersP(void) {return showcellnumbersp;}
 
  virtual bool ShowCellAxesP(void) {return showcellsaxesp;}
 
  virtual bool ShowCellStrainP(void) {return showcellstrainp;}
 
  virtual bool MovieFramesP(void) {return movieframesp;}
 
  virtual bool ShowBoundaryOnlyP(void) {return showboundaryonlyp;}
 
  virtual bool ShowToolTipsP(void) {return showtooltipsp;}
 
  virtual bool ShowWallsP(void) {return showwallsp;}
 
  virtual bool ShowApoplastsP(void) { return showapoplastsp;}
 
  virtual bool ShowFluxesP(void) { return showfluxesp; }
 
  virtual bool DynamicCellsP(void) { return dynamicscellsp; }
 
  virtual void FitCanvasToWindow() {};
 
  virtual void FitLeafToCanvas() {};
 
  virtual bool HideCellsP(void) { return hidecellsp; }
 
  virtual void clear(void) {
 
    QList<QGraphicsItem *> list = canvas.items();
 
    QList<QGraphicsItem *>::Iterator it = list.begin();
 
    for (; it != list.end(); ++it) {
 
      if ( *it )
 
	delete *it;
 
    }
 
  };
 
  virtual void XMLReadSettings(xmlNode *settings);
 
  virtual void XMLReadViewport(xmlNode *viewport);
 

	
 
  virtual double getFluxArrowsize(void) { return 10.;}
 

	
 
  void Save(const char *fname, const char *format, int sizex=640, int sizey=480);
 
  void CutSAM(void);
 

	
 
  void Plot(int resize_stride=10);
 

	
 
  virtual void UserMessage(QString message, int timeout = 0) {
 
    cerr << message.toAscii().constData() << endl;
 
  }
 

	
 
  Mesh &mesh;
 
  QTransform viewport;
 

	
 
 protected:
 
  QGraphicsScene &canvas;
 
  virtual xmlNode *XMLSettingsTree(void) const;
 
  virtual xmlNode *XMLViewportTree(QTransform &transform) const;
 
  
 
 protected:
 
  bool showcentersp;
 
  bool showmeshp;
 
  bool showbordercellp;
 
  bool shownodenumbersp;
 
  bool showcellnumbersp;
 
  bool showcellsaxesp;
 
  bool showcellstrainp;
 
  bool movieframesp;
 
  bool showboundaryonlyp;
 
  bool showwallsp;
 
  bool showapoplastsp;
 
  bool showfluxesp;
 
  bool dynamicscellsp;
 
  bool showtooltipsp;
 
  bool hidecellsp;
 
};
 

	
 
//#include <qapplication.h>
 
#define TIMESTEP double MainBase::TimeStep(void)
 
#define INIT void MainBase::Init(char *leaffile)
 
#define INIT void MainBase::Init(const char *leaffile)
 

	
 
#endif
 

	
 
/* finis */
src/mesh.cpp
Show inline comments
 
/*
 
 *
 
 *  This file is part of the Virtual Leaf.
 
 *
 
 *  The Virtual Leaf 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.
 
 *
 
 *  The Virtual Leaf is distributed in the hope that it will be useful,
 
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 *  GNU General Public License for more details.
 
 *
 
 *  You should have received a copy of the GNU General Public License
 
 *  along with the Virtual Leaf.  If not, see <http://www.gnu.org/licenses/>.
 
 *
 
 *  Copyright 2010 Roeland Merks.
 
 *
 
 */
 

	
 
#include <string>
 
#include <algorithm>
 
#include <vector>
 
#include <sstream>
 
#include <cstdlib>
 
//#include <cerrno>
 
#include <cstring>
 
#include <numeric>
 
#include <functional>
 
#include <fstream>
 
#include <QPair>
 
#include "mesh.h"
 
#include "tiny.h"
 
#include "parameter.h"
 
#include "random.h"
 
#include "pi.h"
 
#include "parse.h"
 
#include "matrix.h"
 
#include "sqr.h"
 
#include "nodeset.h"
 
#include "nodeitem.h"
 
#include "simplugin.h"
 

	
 
#include <QDebug>
 
#include <set>
 
#include <iostream>
 
#include <iterator>
 
#include <QTextStream>
 

	
 
static const std::string _module_id("$Id$");
 

	
 
extern Parameter par;
 

	
 
void Mesh::AddNodeToCellAtIndex(Cell *c, Node *n, Node *nb1, Node *nb2, list<Node *>::iterator ins_pos) {
 
  c->nodes.insert(ins_pos, n);        
 
  n->owners.push_back( Neighbor(c, nb1, nb2 ) );
 
}
 

	
 

	
 
void Mesh::AddNodeToCell(Cell *c, Node *n, Node *nb1, Node *nb2) {
 

	
 
  c->nodes.push_back( n );
 
  n->owners.push_back( Neighbor(c, nb1, nb2 ) );
 
}
 

	
 
void Mesh::PerturbChem(int chemnum, double range) {
 

	
 
  for (vector<Cell *>::iterator i=cells.begin(); i!=cells.end(); i++) {
 
    (*i)->chem[chemnum] += range*(RANDOM()-0.5);
 
    if ((*i)->chem[chemnum]<0.) (*i)->chem[chemnum]=0.;
 
  }
 
}
 

	
 
void Mesh::CellFiles(const Vector ll, const Vector ur) {
 

	
 
  Cell *cell = RectangularCell(ll,ur,0.001); 
 

	
 
  for (int c=0;c<Cell::NChem();c++) {
 
    cell->SetChemical(c,par.initval[c]);
 
  }
 

	
 
  cell->SetTargetArea(cell->CalcArea());
 

	
 
  Vector axis(1,0,0);
 

	
 
  // divide rectangle a number of times
 
  for (int i=0;i<6;i++) {
 
    IncreaseCellCapacityIfNecessary();
 

	
 
    vector <Cell *> current_cells = cells;
 
    for (vector<Cell *>::iterator j=current_cells.begin(); j!=current_cells.end();j++) {
 
      (*j)->DivideOverAxis(axis);
 
    }
 
    axis=axis.Perp2D();
 

	
 
  }
 

	
 
  IncreaseCellCapacityIfNecessary();
 

	
 
  axis=axis.Perp2D();
 

	
 
  vector <Cell *> current_cells = cells;
 
  for (vector<Cell *>::iterator j=current_cells.begin(); j!=current_cells.end();j++) {
 
    (*j)->DivideOverAxis(axis);
 
  }
 

	
 

	
 
  double sum_l=0; int n_l=0;
 
  for (list<Node *>::const_iterator i=cell->nodes.begin(); i!=cell->nodes.end(); i++) {
 
    list<Node *>::const_iterator nb=i; nb++; 
 
    if (nb==cell->nodes.end()) 
 
      nb=cell->nodes.begin();
 

	
 
    double l = (**nb-**i).Norm();
 

	
 
    sum_l += l;
 
    n_l++;
 

	
 
  }
 

	
 

	
 
  Node::target_length = sum_l/(double)n_l;
 
  // a bit more tension
 
  Node::target_length/=4.;
 

	
 
  SetBaseArea();
 
}
 

	
 
Cell *Mesh::RectangularCell(const Vector ll, const Vector ur, double rotation) {
 

	
 
  Cell *cell=AddCell(new Cell());
 
  cell->m=this;
 

	
 
  Matrix rotmat;
 
  rotmat.Rot2D(rotation); // rotation over 0,0
 

	
 
  Node *n1=AddNode(new Node(rotmat * ll));
 
  Node *n2=AddNode(new Node(rotmat * Vector(ll.x, ur.y,0)));
 
  Node *n3=AddNode(new Node(rotmat * ur));
 
  Node *n4=AddNode(new Node(rotmat * Vector(ur.x, ll.y,0)));
 

	
 
  n1->boundary=true;
 
  n2->boundary=true;
 
  n3->boundary=true;
 
  n4->boundary=true;
 

	
 
  //n1.fixed=n2.fixed=n3.fixed=n4.fixed=true;
 

	
 
  AddNodeToCell(cell, n4, 
 
		n1,
 
		n3);
 

	
 
  AddNodeToCell(cell, n3, 
 
		n4,
 
		n2);
 

	
 
  AddNodeToCell(cell, n2, 
 
		n3,
 
		n1);
 

	
 
  AddNodeToCell(cell, n1, 
 
		n2,
 
		n4);
 

	
 

	
 
  AddNodeToCell(boundary_polygon, n4, 
 
		n1,
 
		n3);
 
  AddNodeToCell(boundary_polygon, n3, 
 
		n4,
 
		n2);
 
  AddNodeToCell(boundary_polygon, n2, 
 
		n3,
 
		n1);
 
  AddNodeToCell(boundary_polygon, n1, 
 
		n2,
 
		n4);
 

	
 
  cell->setCellVec(Vector(0,1,0));
 

	
 
  boundary_polygon->m = this;
 
  boundary_polygon->area = 0;
 

	
 
  cell->area = cell->CalcArea();
 
  // target length is the length of the elements
 

	
 
  Node::target_length = ur.y-ll.y;
 
  // a bit more tension
 
  Node::target_length/=2;
 

	
 
  cell->SetIntegrals(); 
 
  cell->ConstructNeighborList();
 

	
 
  return cell;
 
}
 

	
 
Cell &Mesh::EllipticCell(double xc, double yc, double ra, double rb,  int nnodes, double rotation) {
 

	
 
  int first_node=Node::nnodes;
 
  //  nodes.reserve(nodes.size()+nnodes);
 

	
 

	
 
  //cells.push_back(Cell(xc,yc));
 
  Cell *c=AddCell(new Cell(xc,yc));
 
  c->m=this;
 

	
 
  for (int i=0;i<nnodes;i++) {
 

	
 
    double angle=2*Pi*(i/(double)nnodes);
 
    double x=xc+ra*cos(angle)*cos(rotation) - rb*sin(angle)*sin(rotation);
 
    double y=yc+ra*cos(angle)*sin(rotation) + rb*sin(angle)*cos(rotation);
 

	
 

	
 
    Node *n=AddNode(new Node(x,y,0));
 
    n->boundary = true;
 

	
 
  } 
 

	
 
  for (int i=0;i<nnodes;i++) {
 

	
 
    AddNodeToCell(c,
 
		  nodes[first_node + i],
 
		  nodes[first_node+ (nnodes+i-1)%nnodes],
 
		  nodes[first_node+ (i + 1)%nnodes]);
 
    AddNodeToCell(boundary_polygon,
 
		  nodes[first_node + i],
 
		  nodes[first_node+ (nnodes+i-1)%nnodes],
 
		  nodes[first_node+ (i + 1)%nnodes]);
 
  }
 

	
 
  boundary_polygon->m = this;
 
  boundary_polygon->area = 0;
 

	
 
  c->area = c->CalcArea();
 
  // target length is the length of the elements
 

	
 
  Node::target_length = (2 * ((ra +rb)/2.) * sin (Pi/nnodes));
 
  // a bit more tension
 
  Node::target_length/=2;
 

	
 
  c->SetIntegrals(); 
 
  c->at_boundary=true;
 

	
 
  return *c;
 
}
 

	
 
Cell &Mesh::LeafPrimordium(int nnodes, double pet_length) {
 

	
 
  // first leaf cell
 

	
 
  int first_node=Node::nnodes;
 

	
 
  Cell *circle=AddCell(new Cell(0,0));
 
  circle->m=this;
 
  const double ra=10, rb=10;
 
  const double xc=0,yc=0;
 
  const double rotation=0;
 
  for (int i=0;i<nnodes;i++) {
 

	
 
    double angle=2*Pi*(i/(double)nnodes);
 
    double x=xc+ra*cos(angle)*cos(rotation) - rb*sin(angle)*sin(rotation);
 
    double y=yc+ra*cos(angle)*sin(rotation) + rb*sin(angle)*cos(rotation);
 

	
 

	
 
    Node *n=AddNode(new Node(x,y,0));
 

	
 
    /* if (angle > 1.25*Pi && angle < 1.75*Pi ) {
 
       n.sam = true;
 
       }*/
 

	
 
    AddNodeToCell(circle,
 
		  n,
 
		  nodes[first_node+ (nnodes+i-1)%nnodes],
 
		  nodes[first_node+ (i + 1)%nnodes]);
 

	
 
  }
 

	
 
  boundary_polygon->m = this;
 
  boundary_polygon->area = 0;
 

	
 
  circle->area = circle->CalcArea();
 
  // target length is the length of the elements
 

	
 
  Node::target_length = (2 * ((ra +rb)/2.) * sin (Pi/nnodes));
 
  // a bit more tension
 
  Node::target_length/=2;
 

	
 
  circle->SetIntegrals(); 
 

	
 
  //return c;
 

	
 
  circle->SetTargetArea(2*circle->Area());
 

	
 
  // Petiole: starts at both sides of the circular cell
 
  // get position of the (n/4)'th and (3*(n/4))'th node.
 

	
 
  list<Node *>::reverse_iterator it_n1=circle->nodes.rbegin();
 
  for (int i=0; i<nnodes/2; i++) 
 
    it_n1++;
 
  it_n1--;
 

	
 
  list<Node *>::reverse_iterator it_n2=--circle->nodes.rend();
 

	
 
  Cell *petiole=AddCell(new Cell());
 

	
 
  Node *n1 = *it_n1;
 
  Node *n2 = *it_n2;
 

	
 
  Node *n3=AddNode( new Node ( *n2 + Vector( 0, pet_length, 0) ) );
 
  Node *n4=AddNode( new Node ( *n1 + Vector( 0, pet_length, 0) ) );
 

	
 
  n3->boundary=true;
 
  n4->boundary=true;
 

	
 
  AddNodeToCell(petiole, *it_n1, 
 
		n4,
 
		nodes[(*it_n2)->Index() 
 
		      + (( (*it_n1)->Index() - (*it_n2)->Index() )-1+nnodes)%nnodes]);
 

	
 

	
 

	
 
  list<Node *>::reverse_iterator i=it_n1; i++;
 
  for (; i!=it_n2; ++i) {
 
    AddNodeToCell(petiole, *i,
 
		  nodes[(*it_n2)->Index() + (((*i)->Index()-(*it_n2)->Index()) + 1)%nnodes],
 
		  nodes[(*it_n2)->Index() + (((*i)->Index()-(*it_n2)->Index())-1+nnodes)%nnodes]);
 
  }
 

	
 
  AddNodeToCell(petiole, *it_n2, *it_n2 + 1, n3);
 

	
 
  (*it_n2)->boundary=true;
 

	
 
  AddNodeToCell(petiole, n3, n2, n4);
 
  AddNodeToCell(petiole, n4, n3, n1);
 

	
 

	
 
#ifdef QDEBUG  
 
  qDebug() << circle << endl;
 
  qDebug() << petiole << endl;
 
#endif
 

	
 
  AddNodeToCell(boundary_polygon, *it_n1, n4, *it_n2 + ((*it_n1-*it_n2)+1)%nnodes); // is this gonna work?
 

	
 
  (*it_n1)->boundary=true;
 

	
 
  for (int i=0;i<nnodes;i++) {
 

	
 
    if (nodes[(first_node + i)]->owners.size()==1) {
 
      AddNodeToCell(boundary_polygon,
 
		    nodes[first_node +i],
 
		    nodes[first_node+ (nnodes+i-1)%nnodes],
 
		    nodes[first_node+ (i + 1)%nnodes]);
 

	
 
      nodes[first_node+i]->boundary=true;
 
    }
 
  }
 

	
 
  AddNodeToCell(boundary_polygon, *it_n2, nodes[(nnodes+(*it_n2)->Index() - 1)%nnodes], n3);
 
  AddNodeToCell(boundary_polygon, n3, n2, n4);
 
  AddNodeToCell(boundary_polygon, n4, n3, n1);
 

	
 
  // make petiole solid
 
  for (list<Node *>::iterator i=petiole->nodes.begin(); i!=petiole->nodes.end(); i++) {
 
    (*i)->Fix();
 
  }
 
  petiole->Fix();
 

	
 
  petiole->area=petiole->CalcArea();
 
  petiole->target_area=petiole->area;  
 
  petiole->ConstructNeighborList();
 
  circle->ConstructNeighborList();
 
  boundary_polygon->ConstructConnections();
 
  boundary_polygon->ConstructNeighborList();
 

	
 
  circle->setCellVec(Vector(0,1,0));
 

	
 
  return *circle;
 
}
 

	
 
/*Cell &Mesh::Box() {
 
  }*/
 

	
 

	
 
// return bounding box of mesh
 
void Mesh::BoundingBox(Vector &LowerLeft, Vector &UpperRight) {
 

	
 
  LowerLeft = **nodes.begin();
 
  UpperRight = **nodes.begin();
 
  for (vector<Node *>::iterator c=nodes.begin(); c!=nodes.end(); c++) {
 
    if ((*c)->x < LowerLeft.x)
 
      LowerLeft.x = (*c)->x;
 
    if ((*c)->y < LowerLeft.y)
 
      LowerLeft.y = (*c)->y;
 
    if ((*c)->z < LowerLeft.z)
 
      LowerLeft.z = (*c)->z;
 
    if ((*c)->x > UpperRight.x) 
 
      UpperRight.x = (*c)->x;
 
    if ((*c)->y > UpperRight.y) 
 
      UpperRight.y = (*c)->y;
 
    if ((*c)->z > UpperRight.z)
 
      UpperRight.z = (*c)->z;
 
  }
 
}
 

	
 

	
 
double Mesh::Area(void) {
 

	
 
  double area=0;
 
  vector<Cell *>::iterator i=cells.begin();
 
  while (i != cells.end()) {
 
    area += (*(i++))->Area();
 
  }
 
  return area;
 
}
 

	
 
void Mesh::SetBaseArea(void) {
 

	
 
  // Set base area to mean area. 
 
  // This method is typically called during initiation, after
 
  // defining the first cell
 
  Cell::BaseArea() = Area()/cells.size();
 
}
 

	
 
// for optimization, we moved Displace to Mesh
 

	
 
class DeltaIntgrl {
 

	
 
public:
 
  double area;
 
  double ix, iy;
 
  double ixx,ixy,iyy;
 
  DeltaIntgrl(double sarea,double six,double siy,double sixx,double sixy,double siyy) {
 
    area=sarea;
 
    ix=six;
 
    iy=siy;
 
    ixx=sixx;
 
    ixy=sixy;
 
    iyy=siyy;
 
  }
 
};
 

	
 
void Mesh::Clear(void) {
 

	
 
  // clear nodes
 
  for (vector<Node *>::iterator i=nodes.begin(); i!=nodes.end(); i++) {
 
    delete *i;
 
  }
 

	
 
  nodes.clear();
 
  Node::nnodes=0;
 

	
 
  node_insertion_queue.clear();
 
  // Clear NodeSets
 
  for (vector<NodeSet *>::iterator i=node_sets.begin(); i!=node_sets.end(); i++) {
 
    delete *i;
 
  }
 

	
 
  node_sets.clear();
 
  time = 0;
 

	
 
  // clear cells
 

	
 
  for (vector<Cell *>::iterator i=cells.begin(); i!=cells.end(); i++) {
 
    delete *i;
 
  }
 

	
 
  cells.clear();
 
  Cell::NCells() = 0;
 

	
 
  if (boundary_polygon) {
 
  delete boundary_polygon;
 
    boundary_polygon=0;
 
  }
 

	
 
  // Clear walls
 
  for (list<Wall *>::iterator i=walls.begin(); i!=walls.end(); i++) {
 
    delete *i;
 
  }
 

	
 
  walls.clear();
 
  WallBase::nwalls = 0;
 
  //tmp_walls->clear();
 

	
 
  shuffled_cells.clear();
 
  shuffled_nodes.clear();
 
  //Cell::ncells=0;
 
  /*    Cell::ClearNCells();
 
	Node::nnodes=0;
 

	
 
	cells.clear();
 
	nodes.clear();
 
	shuffled_cells.clear();
 
	shuffled_nodes.clear();
 
	node_insertion_queue.empty();
 

	
 
	cerr << "Meshed cleared: cells: " << cells.size() << ", nodes: " << nodes.size() << endl;
 
  */
 

	
 
#ifdef QDEBUG
 
  qDebug() << "cells.size() = " << cells.size() << endl;
 
  qDebug() << "walls.size() = " << walls.size() << endl;
 
  qDebug() << "nodes.size() = " << nodes.size() << endl;
 
#endif
 
}
 

	
 
double Mesh::DisplaceNodes(void) {
 

	
 
  MyUrand r(shuffled_nodes.size());
 
  random_shuffle(shuffled_nodes.begin(),shuffled_nodes.end(),r);
 

	
 
  double sum_dh=0;
 

	
 
  list<DeltaIntgrl> delta_intgrl_list;
 

	
 
  for_each( node_sets.begin(), node_sets.end(), mem_fun( &NodeSet::ResetDone ) );
 

	
 
  for (vector<Node *>::const_iterator i=shuffled_nodes.begin(); i!=shuffled_nodes.end(); i++) {
 

	
 
    //int n=shuffled_nodes[*i];
 
    Node &node(**i);
 

	
 
    // Do not allow displacement if fixed
 
    //if (node.fixed) continue;
 

	
 
    if (node.DeadP()) continue;
 

	
 
    // Attempt to move this cell in a random direction
 
    double rx=par.mc_stepsize*(RANDOM()-0.5); // was 100.
 
    double ry=par.mc_stepsize*(RANDOM()-0.5);
 

	
 
    // Uniform with a circle of radius par.mc_stepsize
 
    /* double r = RANDOM() * par.mc_stepsize;
 
       double th = RANDOM()*2*Pi;
 

	
 
       double rx = r * cos(th);
 
       double ry = r * sin(th);
 
    */
 
    Vector new_p(node.x+rx,node.y+ry,0);
 
    Vector old_p(node.x,node.y,0);
 

	
 
    /* if (node.boundary  && boundary_polygon->MoveSelfIntersectsP(n,  new_p )) {
 
    // reject if move of boundary results in self intersection
 
    continue;
 
    }*/
 

	
 

	
 
    if (node.node_set) {
 
      // move each node set only once
 
      if (!node.node_set->DoneP()) 
 
	node.node_set->AttemptMove(rx,ry);
 

	
 
    } else {
 

	
 
      // for all cells to which this node belongs:
 
      //   calculate energy difference
 

	
 
      double area_dh=0.;
 
      double length_dh=0.;
 
      double bending_dh=0.;
 
      double cell_length_dh=0.;
 
      double alignment_dh=0.;
 

	
 
      double old_l1=0.,old_l2=0.,new_l1=0.,new_l2=0.;
 

	
 
      double sum_stiff=0.;
 
      double dh=0.;
 

	
 
      for (list<Neighbor>::const_iterator cit=node.owners.begin(); cit!=node.owners.end(); cit++) {
 

	
 
	//Cell &c=m->getCell(cit->cell);
 
	//Cell &c=cit->cell->BoundaryPolP()?*boundary_polygon:*(cit->cell);
 
	Cell &c=*((Cell *)(cit->cell));
 
	//Cell &c=cells[cit->cell];
 
	if (c.MoveSelfIntersectsP(&node,  new_p )) {
 
	  // reject if move results in self intersection
 
	  //
 
	  // I know: using goto's is bad practice... except when jumping out
 
	  // of deeply nested loops :-)
 
	  //cerr << "Rejecting due to self-intersection\n";
 
	  goto next_node;
 
	}
 

	
 
	// summing stiffnesses of cells. Move has to overcome this minimum required energy.
 
	sum_stiff += c.stiffness;
 
	// area - (area after displacement): see notes for derivation
 
	//Vector i_min_1 = m->getNode(cit->nb1);
 

	
 
	Vector i_min_1 = *(cit->nb1);
 
	//Vector i_plus_1 = m->getNode(cit->nb2);
 
	Vector i_plus_1 = *(cit->nb2);
 

	
 

	
 
	// We must double the weights for the perimeter (otherwise they start bulging...)
 
	double w1, w2;
 
	if (node.boundary && cit->nb1->boundary) 
 
#ifdef FLEMING
 
	  w1 = par.rel_perimeter_stiffness;
 
#else
 
	w1=2;
 
#endif
 
	else
 
	  w1 = 1;
 

	
 
	if (node.boundary && cit->nb2->boundary) 
 
#ifdef FLEMING
 
	  w2 = par.rel_perimeter_stiffness;
 
#else
 
	w2 = 2;
 
#endif
 
	else 
 
	  w2 = 1;
 

	
 
	//if (cit->cell>=0) {
 
	if (!cit->cell->BoundaryPolP()) {
 
	  double delta_A = 0.5 * ( ( new_p.x - old_p.x ) * (i_min_1.y - i_plus_1.y) +
 
				   ( new_p.y - old_p.y ) * ( i_plus_1.x - i_min_1.x ) );
 

	
 
	  area_dh +=  delta_A * (2 * c.target_area - 2 * c.area + delta_A);
 

	
 

	
 
	  // cell length constraint
 
	  // expensive and not always needed
 
	  // so we check the value of lambda_celllength
 

	
 
	  if (/* par.lambda_celllength */  cit->cell->lambda_celllength) {
 

	
 
	    double delta_ix = 
 
	      (i_min_1.x + new_p.x)
 
	      * (new_p.x * i_min_1.y- i_min_1.x * new_p.y) +
 
	      (new_p.x + i_plus_1.x)
 
	      * (i_plus_1.x * new_p.y- new_p.x * i_plus_1.y) -
 

	
 
	      (i_min_1.x + old_p.x)
 
	      * (old_p.x * i_min_1.y- i_min_1.x * old_p.y) -
 
	      (old_p.x + i_plus_1.x)
 
	      * (i_plus_1.x * old_p.y - old_p.x * i_plus_1.y);
 

	
 

	
 
	    double delta_iy =
 
	      (i_min_1.y + new_p.y)
 
	      * (new_p.x * i_min_1.y- i_min_1.x * new_p.y) +
 
	      (new_p.y + i_plus_1.y)
 
	      * (i_plus_1.x * new_p.y- new_p.x * i_plus_1.y) -
 

	
 
	      (i_min_1.y + old_p.y)
 
	      * (old_p.x * i_min_1.y- i_min_1.x * old_p.y) -
 
	      (old_p.y + i_plus_1.y)
 
	      * (i_plus_1.x * old_p.y - old_p.x * i_plus_1.y);
 

	
 

	
 
	    double delta_ixx = 
 
	      (new_p.x*new_p.x+
 
	       i_min_1.x*new_p.x+
 
	       i_min_1.x*i_min_1.x ) *
 
	      (new_p.x*i_min_1.y - i_min_1.x*new_p.y) +
 

	
 
	      (i_plus_1.x*i_plus_1.x+
 
	       new_p.x*i_plus_1.x+
 
	       new_p.x*new_p.x ) *
 
	      (i_plus_1.x*new_p.y - new_p.x*i_plus_1.y) -
 

	
 
	      (old_p.x*old_p.x+
 
	       i_min_1.x*old_p.x+
 
	       i_min_1.x*i_min_1.x ) *
 
	      (old_p.x*i_min_1.y - i_min_1.x*old_p.y) -
 

	
 
	      (i_plus_1.x*i_plus_1.x+
 
	       old_p.x*i_plus_1.x+
 
	       old_p.x*old_p.x ) *
 
	      (i_plus_1.x*old_p.y - old_p.x*i_plus_1.y);
 

	
 

	
 
	    double delta_ixy =
 
	      (i_min_1.x*new_p.y-
 
	       new_p.x*i_min_1.y)*
 
	      (new_p.x*(2*new_p.y+i_min_1.y)+
 
	       i_min_1.x*(new_p.y+2*i_min_1.y)) +
 

	
 
	      (new_p.x*i_plus_1.y-
 
	       i_plus_1.x*new_p.y)*
 
	      (i_plus_1.x*(2*i_plus_1.y+new_p.y)+
 
	       new_p.x*(i_plus_1.y+2*new_p.y)) -
 

	
 
	      (i_min_1.x*old_p.y-
 
	       old_p.x*i_min_1.y)*
 
	      (old_p.x*(2*old_p.y+i_min_1.y)+
 
	       i_min_1.x*(old_p.y+2*i_min_1.y)) -
 

	
 
	      (old_p.x*i_plus_1.y-
 
	       i_plus_1.x*old_p.y)*
 
	      (i_plus_1.x*(2*i_plus_1.y+old_p.y)+
 
	       old_p.x*(i_plus_1.y+2*old_p.y));
 

	
 

	
 
	    double delta_iyy = 
 
	      (new_p.x*i_min_1.y-
 
	       i_min_1.x*new_p.y)*
 
	      (new_p.y*new_p.y+
 
	       i_min_1.y*new_p.y+
 
	       i_min_1.y*i_min_1.y ) + 
 

	
 
	      (i_plus_1.x*new_p.y-
 
	       new_p.x*i_plus_1.y)*
 
	      (i_plus_1.y*i_plus_1.y+
 
	       new_p.y*i_plus_1.y+
 
	       new_p.y*new_p.y ) -
 

	
 
	      (old_p.x*i_min_1.y-
 
	       i_min_1.x*old_p.y)*
 
	      (old_p.y*old_p.y+
 
	       i_min_1.y*old_p.y+
 
	       i_min_1.y*i_min_1.y ) -
 

	
 
	      (i_plus_1.x*old_p.y-
 
	       old_p.x*i_plus_1.y)*
 
	      (i_plus_1.y*i_plus_1.y+
 
	       old_p.y*i_plus_1.y+
 
	       old_p.y*old_p.y );
 

	
 
	    delta_intgrl_list.push_back(DeltaIntgrl(delta_A,delta_ix,delta_iy,delta_ixx,delta_ixy,delta_iyy));
 

	
 
	    Vector old_axis;
 
	    double old_celllength = c.Length(&old_axis);
 
	    old_axis=old_axis.Normalised().Perp2D();
 

	
 
	    // calculate length after proposed update
 
	    double intrx=(c.intgrl_x-delta_ix)/6.;
 
	    double intry=(c.intgrl_y-delta_iy)/6.;
 
	    double ixx=((c.intgrl_xx-delta_ixx)/12.)-(intrx*intrx)/(c.area-delta_A);
 
	    double ixy=((c.intgrl_xy-delta_ixy)/24.)+(intrx*intry)/(c.area-delta_A);
 
	    double iyy=((c.intgrl_yy-delta_iyy)/12.)-(intry*intry)/(c.area-delta_A);
 

	
 
	    double rhs1=(ixx+iyy)/2., rhs2=sqrt( (ixx-iyy)*(ixx-iyy)+4*ixy*ixy )/2.;
 

	
 
	    double lambda_b=rhs1+rhs2;
 

	
 

	
 
	    double new_celllength=4*sqrt(lambda_b/(c.area-delta_A));
 
	    //cerr << "new_celllength = "  << new_celllength << endl;
 
	    //cerr << "target_length = "  << c.target_length << endl;
 

	
 
	    cell_length_dh += c.lambda_celllength * ( DSQR(c.target_length - new_celllength) - DSQR(c.target_length-old_celllength) );
 

	
 
	    Vector norm_long_axis(lambda_b - ixx, ixy, 0);
 
	    norm_long_axis.Normalise();
 

	
 
	    double alignment_before = InnerProduct(old_axis, c.cellvec);
 
	    double alignment_after = InnerProduct(norm_long_axis, c.cellvec);
 

	
 
	    /* cerr << "Delta alignment = " << alignment_before - alignment_after << endl;
 
	       cerr << "Old alignment is " << alignment_before << ", new alignment is " << alignment_after << endl;
 
	       cerr << "Old axis is " << old_axis << ", new axis is " << norm_long_axis << endl; 
 
	    */
 
	    alignment_dh += alignment_before - alignment_after;
 

	
 
	    /* cerr << "alignment_dh  = " << alignment_dh << endl;
 
	       cerr << "cellvec = " << c.cellvec << endl;*/
 

	
 
	  } else {
 
	    // if we have no length constraint, still need to update area
 
	    delta_intgrl_list.push_back(DeltaIntgrl(delta_A,0,0,0,0,0));
 

	
 
	  }
 

	
 
	  old_l1=(old_p-i_min_1).Norm();
 
	  old_l2=(old_p-i_plus_1).Norm();
 
	  new_l1=(new_p-i_min_1).Norm();
 
	  new_l2=(new_p-i_plus_1).Norm();
 

	
 

	
 

	
 

	
 
	  static int count=0;
 
	  // Insertion of nodes (cell wall yielding)
 
	  if (!node.fixed) {
 
	    if (old_l1 > 4*Node::target_length && !cit->nb1->fixed) {
 
	      node_insertion_queue.push( Edge(cit->nb1, &node) );
 
	    }
 
	    if (old_l2 > 4*Node::target_length && !cit->nb2->fixed) {
 
	      node_insertion_queue.push( Edge(&node, cit->nb2 ) );
 
	    }
 
	    count++;
 
	    /*length_dh += 2*Node::target_length * (old_l1 + old_l2 - new_l1 - new_l2) 
 
	      + DSQR(new_l1) - DSQR(old_l1) + DSQR(new_l2) - DSQR(old_l2);*/
 
	  }
 
	  /* length_dh += 2*Node::target_length * ( w1*(old_l1 - new_l1) + 
 
	     w2*(old_l2 - new_l2) ) +
 
	     w1*(DSQR(new_l1) 
 
	     - DSQR(old_l1)) 
 
	     + w2*(DSQR(new_l2) 
 
	     - DSQR(old_l2)); */
 

	
 

	
 
	  /*	if (c.cellvec.ManhattanNorm()!=0) { 
 

	
 
	  // wall element length constraint
 
	  double old_aniso1, old_aniso2;
 
	  double new_aniso1, new_aniso2;
 

	
 
	  // anisotropic expansion?
 
	  old_aniso1 = 1 + par.lambda_aniso*(1 - fabs(InnerProduct((old_p-i_min_1), c.cellvec))/old_l1);
 
	  old_aniso2 = 1 + par.lambda_aniso*(1 - fabs(InnerProduct((old_p-i_plus_1), c.cellvec))/old_l2);
 
	  new_aniso1 = 1 + par.lambda_aniso*(1 - fabs(InnerProduct((new_p-i_min_1),  c.cellvec))/new_l1);
 
	  new_aniso2 = 1 + par.lambda_aniso*(1 - fabs(InnerProduct((new_p-i_plus_1), c.cellvec))/new_l2);
 

	
 

	
 
	  length_dh += w1 * ( new_aniso1 * DSQR(new_l1 - Node::target_length) -
 
	  old_aniso1 * DSQR(old_l1 - Node::target_length) )
 
	  +  w2 * ( new_aniso2 * DSQR(new_l2 - Node::target_length) -
 
	  old_aniso2 * DSQR(old_l2 - Node::target_length) );
 

	
 
	  } else {
 
	  */	  
 
	  length_dh += 2*Node::target_length * ( w1*(old_l1 - new_l1) + 
 
						 w2*(old_l2 - new_l2) ) +
 
	    w1*(DSQR(new_l1) 
 
		- DSQR(old_l1)) 
 
	    + w2*(DSQR(new_l2) 
 
		  - DSQR(old_l2));
 

	
 

	
 
	  //}
 

	
 

	
 

	
 
	}
 

	
 
	// bending energy also holds for outer boundary
 
	// first implementation. Can probably be done more efficiently
 
	// calculate circumcenter radius (gives local curvature)
 
	// the ideal bending state is flat... (K=0)
 
	// if (cit->cell==-1 && node.cells.size()>2 /* boundary_pol, cell and a second cell */)  {
 
	{
 
	  // strong bending energy to resist "cleaving" by division planes
 
	  double r1, r2, xc, yc;
 
	  CircumCircle(i_min_1.x, i_min_1.y, old_p.x, old_p.y, i_plus_1.x, i_plus_1.y,
 
		       &xc,&yc,&r1);
 
	  CircumCircle(i_min_1.x, i_min_1.y, new_p.x, new_p.y, i_plus_1.x, i_plus_1.y,
 
		       &xc,&yc, &r2);
 

	
 
	  if (r1<0 || r2<0) {
 
	    MyWarning::warning("r1 = %f, r2 = %f",r1,r2);
 
	  }
 
	  bending_dh += DSQR(1/r2 - 1/r1);
 
	  //bending_dh += ( DSQR(1/r2) - DSQR(1/r1) );
 

	
 
	  //cerr << "bending_dh = " << par.bend_lambda*bending_dh << endl;
 

	
 
	}
 
	/*cerr << "Bending = " <<  ( DSQR(1/r2) - DSQR(1/r1))  << endl;
 
	  cerr << node.index << ": " << bending_dh << " (" << r1 << ", " << r2 << ") " << cit->nb1 << ", " << cit->nb2 << ")" << endl;
 
	  }*/
 
	/*else
 
	  bending_dh += ( DSQR(1/r2) - DSQR(1/r1) );*/
 

	
 

	
 
	/*if (cit->cell==-1) {
 
@@ -1614,417 +1618,512 @@ private:
 

	
 

	
 
void Mesh::ReactDiffuse(double delta_t) {
 

	
 
  // Set Lengths of Walls
 
  for_each ( walls.begin(), walls.end(), 
 
	     mem_fun( &Wall::SetLength ) );
 

	
 
  static SolveMesh *solver = new SolveMesh(this);
 

	
 
  int nok, nbad, nvar;
 
  double *ystart = getValues(&nvar);
 

	
 
  solver->odeint(ystart, nvar, getTime(), getTime() + delta_t, 
 
		 par.ode_accuracy, par.dt, 1e-10, &nok, &nbad);
 

	
 
  setTime(getTime()+delta_t);
 
  setValues(getTime(),ystart);
 
}
 

	
 

	
 
Vector Mesh::FirstConcMoment(int chem) {
 

	
 
  Vector moment;
 
  for (vector<Cell *>::const_iterator c=cells.begin(); c!=cells.end(); c++) {
 
    moment += (*c)->Chemical(chem) * (*c)->Centroid();
 
  }
 
  return moment / (double)cells.size();
 
}
 

	
 
/*! This member function deletes all walls connected to two dead cells from the mesh.
 
  It should be called before the Cells are actually removed.
 
  If the cell is connect to one dead cell only, that reference is substituted for a reference 
 
  to the boundary polygon.
 
*/
 
void Mesh::DeleteLooseWalls(void) {
 

	
 
  list<Wall *>::iterator w=walls.begin();
 

	
 
  while (w!=walls.end()) {
 

	
 
    // if both cells of the wall are dead, remove the wall
 
    if ((*w)->C1()->DeadP() || (*w)->C2()->DeadP()) {
 
      if ((*w)->C1()->DeadP() && (*w)->C2()->DeadP()) {
 
	delete *w;
 
	w=walls.erase(w);
 
      } else {
 
	if ((*w)->C1()->DeadP())
 
	  (*w)->c1 = boundary_polygon;
 
	else
 
	  (*w)->c2 = boundary_polygon;
 
	w++;
 
      }
 
    } else {
 
      w++;
 
    }
 

	
 
  }
 
}
 

	
 
/*void Mesh::FitLeafToCanvas(double width, double height) {
 

	
 
  Vector bbll,bbur;
 
  BoundingBox(bbll,bbur);
 

	
 
  double scale_x = width/(bbur.x-bbll.x);
 
  double scale_y = height/(bbur.y-bbll.y);
 

	
 
  double factor = scale_x<scale_y ? scale_x:scale_y;
 

	
 
  Cell::SetMagnification(factor); // smallest of scale_x and scale_y
 

	
 
  double offset_x = (width/Cell::Magnification()-(bbur.x-bbll.x))/2.;  
 
  double offset_y = (height/Cell::Magnification()-(bbur.y-bbll.y))/2.;
 

	
 
  Cell::setOffset(offset_x, offset_y);
 

	
 
  }*/
 

	
 

	
 

	
 
void Mesh::CleanChemicals(const vector<double> &clean_chem) {
 

	
 
  if (clean_chem.size()!=(unsigned)Cell::NChem()) {
 
    throw "Run time error in Mesh::CleanChemicals: size of clean_chem should be equal to Cell::NChem()";
 
  }
 
  for (vector<Cell *>::iterator c=cells.begin(); c!=cells.end(); c++) {
 
    for (int i=0;i<Cell::NChem();i++) {
 
      (*c)->SetChemical(i,clean_chem[i]);
 
    }
 
    (*c)->SetNewChemToChem();
 
  }
 
}
 

	
 

	
 
void Mesh::CleanTransporters(const vector<double> &clean_transporters) {
 

	
 
  if (clean_transporters.size()!=(unsigned)Cell::NChem()) {
 
    throw "Run time error in Mesh::CleanTransporters: size ofclean_transporters should be equal to Cell::NChem()";
 
  }
 

	
 

	
 
  // clean transporters
 
  for (list<Wall *>::iterator w=walls.begin(); w!=walls.end(); w++) {
 
    for (int i=0;i<Cell::NChem();i++) {
 
      (*w)->setTransporters1(i,clean_transporters[i]); (*w)->setNewTransporters1(i,clean_transporters[i]);
 
      (*w)->setTransporters2(i,clean_transporters[i]); (*w)->setNewTransporters2(i,clean_transporters[i]);
 
    }
 
  }
 
}
 

	
 

	
 
void Mesh::RandomizeChemicals(const vector<double> &max_chem, const vector<double> &max_transporters) {
 

	
 
  if (max_chem.size()!=(unsigned)Cell::NChem() || max_transporters.size()!=(unsigned)Cell::NChem()) {
 
    throw "Run time error in Mesh::CleanChemicals: size of max_chem and max_transporters should be equal to Cell::NChem()";
 
  }
 

	
 
  for (vector<Cell *>::iterator c=cells.begin(); c!=cells.end(); c++) {
 
    for (int i=0;i<Cell::NChem();i++) {
 
      (*c)->SetChemical(i,max_chem[i]*RANDOM());
 
    }
 
    (*c)->SetNewChemToChem();
 
  }
 

	
 
  // randomize transporters
 
  for (list<Wall *>::iterator w=walls.begin(); w!=walls.end(); w++) {
 
    for (int i=0;i<Cell::NChem();i++) {
 
      (*w)->setTransporters1(i,max_transporters[i] * RANDOM()); (*w)->setNewTransporters1(i, (*w)->Transporters1(i) );
 
      (*w)->setTransporters2(i,max_transporters[i] * RANDOM()); (*w)->setNewTransporters2(i, (*w)->Transporters1(i) );
 
    }
 
  }
 
}
 

	
 
//!\brief Calculates a vector with derivatives of all variables, which
 
// we can pass to an ODESolver. 
 
void Mesh::Derivatives(double *derivs) {
 

	
 
  int nwalls = walls.size();
 
  int ncells = cells.size();
 
  int nchems = Cell::NChem();
 

	
 
  // two eqs per chemical for each walls, and one eq per chemical for each cell
 
  // This is for generality. For a specific model you may optimize
 
  // this by removing superfluous (empty) equations.
 
  int neqs = 2 * nwalls * nchems + ncells * nchems;
 

	
 
  //static double *derivs = 0; 
 
  // derivs is allocated by RungeKutta class.
 

	
 
  for (int i=0;i<neqs;i++) {
 
    derivs[i]=0.;
 
  }
 

	
 
  // Layout of derivatives: cells [ chem1 ... chem n]  walls [ [ w1(chem 1) ... w1(chem n) ] [ w2(chem 1) ... w2(chem n) ] ]
 

	
 
  int i=0;
 

	
 
  for (vector<Cell *>::iterator c=cells.begin(); c!=cells.end(); c++) {
 
    plugin->CellDynamics(*c, &(derivs[i]));
 
    i+=nchems;
 
  }
 

	
 
  for (list<Wall *>::iterator w=walls.begin(); w!=walls.end(); w++) {
 
    // (*wr)(*w, &(derivs[i]), &(derivs[i+nchems]));
 
    plugin->WallDynamics(*w,  &(derivs[i]), &(derivs[i+nchems]));
 
    // Transport function adds to derivatives of cell chemicals
 
    double *dchem_c1 = &(derivs[(*w)->c1->Index() * nchems]);
 
    double *dchem_c2 = &(derivs[(*w)->c2->Index() * nchems]);
 
    //plugin->CelltoCellTransport(*w, &(derivs[(*w)->c1->Index() * nchems]),
 
    //	  &(derivs[(*w)->c2->Index() * nchems]));
 
    // quick fix: dummy values to prevent end user from writing into outer space and causing a crash :-)
 
    // start here if you want to implement chemical input/output into environment over boundaries
 
    double dummy1, dummy2;
 
    if ((*w)->c1->Index()<0) { // tests if c1 is the boundary pol
 
      dchem_c1 = &dummy1;
 
    }
 
    if ((*w)->c2->Index()<0) {
 
      dchem_c2 = &dummy2;
 
    }
 
    plugin->CelltoCellTransport(*w, dchem_c1, dchem_c2); 
 

	
 
    //(*tf)(*w, &(derivs[(*w)->c1->Index() * nchems]),
 
    //&(derivs[(*w)->c2->Index() * nchems] ) );
 
    i+=2*nchems;
 
  }
 
}
 

	
 
void Mesh::setValues(double x, double *y) {
 

	
 
  //int nwalls = walls.size();
 
  //int ncells = cells.size();
 
  int nchems = Cell::NChem();
 

	
 
  // two eqs per chemical for each walls, and one eq per chemical for each cell
 
  // This is for generality. For a specific model you may optimize
 
  // this by removing superfluous (empty) equations.
 
  //int neqs = 2 * nwalls * nchems + ncells * nchems;
 

	
 
  // Layout of derivatives: cells [ chem1 ... chem n]  walls [ [ w1(chem 1) ... w1(chem n) ] [ w2(chem 1) ... w2(chem n) ] ]
 

	
 
  int i=0;
 
  static int emit_count=0;
 
  const int stride = 100;
 
  for (vector<Cell *>::iterator c=cells.begin(); c!=cells.end(); c++) {
 
    for (int ch=0;ch<nchems;ch++) {
 
      (*c)->SetChemical(ch, y[i+ch]);
 
    }
 
    if ( !(emit_count%stride)) {
 
      (*c)->EmitValues(x);
 
    }
 
    i+=nchems;
 
  }
 

	
 
  for (list<Wall *>::iterator w=walls.begin(); w!=walls.end(); w++) {
 
    for (int ch=0;ch<nchems;ch++) {
 
      (*w)->setTransporters1(ch,y[i+ch]);
 
    }
 
    i+=nchems;
 

	
 
    for (int ch=0;ch<nchems;ch++) {
 
      (*w)->setTransporters2(ch,y[i+ch]);
 
    }
 
    i+=nchems;
 
  }
 
  emit_count++;
 
}
 

	
 
double *Mesh::getValues(int *neqs) {
 

	
 
  int nwalls = walls.size();
 
  int ncells = cells.size();
 
  int nchems = Cell::NChem();
 

	
 
  // two eqs per chemical for each wall, and one eq per chemical for each cell
 
  // This is for generality. For a specific model you may optimize
 
  // this by removing superfluous (empty) equations.
 
  (*neqs) = 2 * nwalls * nchems + ncells * nchems;
 

	
 
  // Layout of derivatives: cells [ chem1 ... chem n]  walls [ [ w1(chem 1) ... w1(chem n) ] [ w2(chem 1) ... w2(chem n) ] ]
 

	
 
  static double *values = 0;
 
  if (values!=0) { delete[] values; }
 

	
 
  values = new double[*neqs];
 

	
 
  int i=0;
 
  for (vector<Cell *>::iterator c=cells.begin(); c!=cells.end(); c++) {
 
    for (int ch=0;ch<nchems;ch++) {
 
      values[i+ch]=(*c)->Chemical(ch);
 
    }
 
    i+=nchems;
 
  }
 

	
 
  for (list<Wall *>::iterator w=walls.begin(); w!=walls.end(); w++) {
 
    for (int ch=0;ch<nchems;ch++) {
 
      values[i+ch]=(*w)->Transporters1(ch);
 
    }
 
    i+=nchems;
 

	
 
    for (int ch=0;ch<nchems;ch++) {
 
      values[i+ch]=(*w)->Transporters2(ch);
 
    }
 
    i+=nchems;
 
  }
 
  return values;
 
}
 

	
 
void Mesh::DrawNodes(QGraphicsScene *c) const {
 

	
 
  for (vector<Node *>::const_iterator n=nodes.begin(); n!=nodes.end(); n++) {
 
    Node *i=*n;
 

	
 
    NodeItem *item = new NodeItem ( &(*i), c );
 
    item->setColor();
 

	
 
    item->setZValue(5);
 
    item->show();
 
    item ->setPos(((Cell::offset[0]+i->x)*Cell::factor),
 
		  ((Cell::offset[1]+i->y)*Cell::factor) );
 
  }
 
}
 

	
 
/*! Returns the sum of protein "ch" of a cycling protein in cells and walls */
 
double Mesh::CalcProtCellsWalls(int ch) const {
 

	
 

	
 
  double sum_prot=0.;
 

	
 
  // At membranes
 
  for (list<Wall *>::const_iterator w=walls.begin(); w!=walls.end(); w++) {
 
    sum_prot += (*w)->Transporters1(ch);
 
    sum_prot += (*w)->Transporters2(ch);
 
  }
 

	
 
  // At cells
 
  for (vector<Cell *>::const_iterator c=cells.begin(); c!=cells.end(); c++) {
 
    sum_prot += (*c)->Chemical(ch);
 
  }
 
  return sum_prot;
 
}
 

	
 
void Mesh::SettoInitVals(void) {
 

	
 
  vector<double> clean_chem(Cell::NChem());
 
  vector<double> clean_transporters(Cell::NChem());
 

	
 
  for (int i=0;i<Cell::NChem();i++) {
 
    clean_transporters[i]=0.;
 
    clean_chem[i]=par.initval[i];
 
  }
 

	
 
  // Amount of PIN1
 
  //clean_chem[1] = 0.;
 

	
 
  CleanChemicals(clean_chem);
 
  CleanTransporters(clean_transporters);
 
}
 

	
 
string Mesh::getTimeHours(void) const {
 
  int hours = (int)(time / 3600);
 
  int mins = (int)((time - hours * 3600)/60);
 
  int secs = (int)((time - hours * 3600 - mins * 60));
 
  ostringstream tstr;
 
  tstr << hours << " h " << mins << " m " << secs << " s";
 
  return tstr.str();
 
}
 

	
 
QVector<qreal> Mesh::VertexAngles(void) {
 
  QVector<qreal> angles;
 
  for (vector<Node *>::const_iterator n=nodes.begin(); n!=nodes.end(); n++) {
 
    if ((*n)->Value()>2 && !(*n)->BoundaryP() ) {
 
      angles+=(*n)->NeighbourAngles();
 
    }
 
  }
 
  return angles;
 
}
 

	
 
QVector< QPair<qreal,int> > Mesh::VertexAnglesValues(void) {
 

	
 
  QVector< QPair<qreal,int> > anglesvalues;
 
  for (vector<Node *>::const_iterator n=nodes.begin(); n!=nodes.end(); n++) {
 
    if ((*n)->Value()>2 && !(*n)->BoundaryP() ) {
 
      QVector<qreal> angles = (*n)->NeighbourAngles();
 
      int value_vertex = angles.size();
 
      for (QVector<qreal>::ConstIterator i=angles.begin(); i!=angles.end(); i++) {
 
	anglesvalues += QPair< qreal, int > (*i, value_vertex);
 
      }
 
    }
 
  }
 
  return anglesvalues;
 
}
 

	
 
void Mesh::Clean(void) {
 
#ifdef QDEBUG
 
  qDebug() << "Freeing nodes" << endl;
 
#endif
 
  for (vector<Node *>::iterator i=nodes.begin(); i!=nodes.end(); i++) {
 
    delete *i;
 
  }
 
  nodes.clear();
 
  Node::nnodes=0;
 

	
 
#ifdef QDEBUG
 
  qDebug() << "Freeing node sets" << endl;
 
#endif
 
  for (vector<NodeSet *>::iterator i=node_sets.begin(); i!=node_sets.end(); i++) {
 
    delete *i;
 
  }
 
  node_sets.clear();
 

	
 

	
 
#ifdef QDEBUG
 
  qDebug() << "Freeing cells" << endl;
 
#endif	
 
  //CellsStaticDatamembers *old_static_data_mem = Cell::GetStaticDataMemberPointer();
 
  for (vector<Cell *>::iterator i=cells.begin(); i!=cells.end(); i++) {
 
    delete *i;
 
  }
 
  //Cell::static_data_members = old_static_data_mem;
 

	
 
  cells.clear();
 
  Cell::NCells()=0;
 

	
 
  if (boundary_polygon) {
 
  delete boundary_polygon; // (already deleted during cleaning of cells?)
 

	
 
    boundary_polygon=0;
 
  }
 
#ifdef QDEBUG
 
  qDebug() << "Freeing walls" << endl;
 
#endif
 
  for (list<Wall *>::iterator i=walls.begin(); i!=walls.end(); i++) {
 
    delete *i;
 
  }
 
  walls.clear();
 
  Wall::nwalls=0;
 

	
 
  node_insertion_queue.clear();
 
  shuffled_nodes.clear();
 
  shuffled_cells.clear();
 
  time = 0.0;
 
}
 

	
 
void Mesh::StandardInit(void) {
 

	
 
  boundary_polygon = new BoundaryPolygon();
 
  Cell &circle=CircularCell(0,0,10,10);
 

	
 
  circle.SetTargetArea(circle.CalcArea());
 
  circle.SetTargetLength(par.target_length);
 
  circle.SetLambdaLength(par.lambda_celllength);
 
  SetBaseArea();
 
  // clean up chemicals 
 
  for (int c=0; c<Cell::NChem(); c++) {
 
    circle.SetChemical(c, 0.);
 
  }
 
}
 

	
 
#include "hull.h"
 

	
 
double Mesh::Compactness(double *res_compactness, double *res_area, double *res_cell_area) {
 
  
 
  // Calculate compactness using the convex hull of the cells
 
  // We use Andrew's Monotone Chain Algorithm (see hull.cpp)
 

	
 
  // Step 1. Prepare data for 2D hull code - get boundary polygon
 
  int pc=0;
 
  Point *p=new Point[boundary_polygon->nodes.size()];
 
  for (list<Node *>::const_iterator i = boundary_polygon->nodes.begin(); 
 
       i!=boundary_polygon->nodes.end(); i++) {
 
    p[pc++]=Point((*i)->x,(*i)->y);
 
  }
 
  
 
  // Step 2: call 2D Hull code
 
  int np=boundary_polygon->nodes.size();
 
  Point *hull=new Point[np];
 
  int nph=chainHull_2D(p,np,hull);
 
  
 
  
 
  // Step 3: calculate area of convex hull
 
  double hull_area=0.;
 
  for (int i=0;i<nph-1;i++) {
 
    hull_area+=hull[i].x * hull[i+1].y - hull[i+1].x * hull[i].y;
 
  }
 
  hull_area/=2.;
 

	
 
  // Step 4: get area of bounary polygon
 
  double boundary_pol_area = boundary_polygon->CalcArea();
 
  
 

	
 
  /*  ofstream datastr("hull.dat");
 
  for (int i=0;i<nph<i++) {
 
    datastr << hull.x << " " << hull.y << endl;
 
  }
 
  ofstream polstr("pol.dat");
 
  for (int i=0;i<np;h*/
 
  delete[] p;
 
  delete[] hull;
 

	
 

	
 
  // put intermediate results into optional pointers
 
  if (res_compactness) {
 
    *res_compactness = boundary_pol_area/hull_area;
 
  }
 
  if (res_area) {
 
    *res_area = hull_area;
 
  }
 
  if (res_cell_area) {
 
    *res_cell_area = boundary_pol_area;
 
  }
 

	
 
  // return compactness
 
  return boundary_pol_area/hull_area;
 

	
 
}
 

	
 
// DataExport
 
void Mesh::CSVExportCellData(QTextStream &csv_stream) const {
 

	
 
  csv_stream << "\"Cell Index\",\"Center of mass (x)\",\"Center of mass (y)\",\"Cell area\",\"Cell length\"";
 
  
 
  for (int c=0;c<Cell::NChem(); c++) {
 
    csv_stream << ",\"Chemical " << c << "\"";
 
  }
 
  csv_stream << endl;
 
  for (vector<Cell *>::const_iterator i=cells.begin();
 
       i!=cells.end();
 
       i++) {
 
    Vector centroid = (*i)->Centroid();
 
    csv_stream << (*i)->Index() << ", "
 
	       << centroid.x << ", "
 
	       << centroid.y << ", " 
 
	       <<  (*i)->Area() << ", "
 
	       <<(*i)->Length();
 
    for (int c=0;c<Cell::NChem(); c++) {
 
      csv_stream << ", " << (*i)->Chemical(c);
 
    }
 
    csv_stream << endl;
 
  }
 
}
 

	
 

	
 
void Mesh::CSVExportMeshData(QTextStream &csv_stream) { 
 
  
 
  csv_stream << "\"Mesh area\",\"Number of cells\",\"Number of nodes\",\"Compactness\",\"Hull area\",\"Cell area\"" << endl;
 
  
 
  double res_compactness, res_area, res_cell_area;
 
  Compactness(&res_compactness, &res_area, &res_cell_area);
 
  csv_stream << Area() << ", " << NCells() << ", " << NNodes() << ", " << res_compactness << ", " << res_area << ", " << res_cell_area  << endl;
 
  
 
}
 
/* finis */
src/mesh.h
Show inline comments
 
/*
 
 *
 
 *  $Id$
 
 *
 
 *  This file is part of the Virtual Leaf.
 
 *
 
 *  The Virtual Leaf 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.
 
 *
 
 *  The Virtual Leaf is distributed in the hope that it will be useful,
 
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 *  GNU General Public License for more details.
 
 *
 
 *  You should have received a copy of the GNU General Public License
 
 *  along with the Virtual Leaf.  If not, see <http://www.gnu.org/licenses/>.
 
 *
 
 *  Copyright 2010 Roeland Merks.
 
 *
 
 */
 

	
 

	
 
// Cell derives from Vector, where Vector is simply used as a Vertex
 

	
 
#ifndef _MESH_H_
 
#define _MESH_H_
 

	
 
#include <vector>
 
#include <algorithm>
 
#include <queue>
 
#include <iterator>
 
#include <functional>
 
#ifdef QTGRAPHICS
 
#include <QGraphicsScene>
 
#endif
 
#include "cell.h"
 
#include "node.h"
 
#include "simplugin.h"
 
#include <QVector>
 
#include <QPair>
 
#include <QDebug>
 
#include <QTextStream>
 

	
 
using namespace std;
 
// new queue which rejects duplicate elements
 
template<class T, class C = deque<T> > class unique_queue : public queue<T,C> {
 

	
 
 public:
 
 typedef typename C::value_type value_type;
 
 // reimplements push: reject element if it exists already
 
 void push(const value_type &x) {
 
   if (find (queue<T,C>::c.begin(),queue<T,C>::c.end(),x)==queue<T,C>::c.end()) {
 
     queue<T,C>::c.push_back(x);
 
   }
 
 }
 
 void clear(void) {
 
   queue<T,C>::c.clear();
 
 }
 
};
 

	
 
template<class P> P& deref_ptr ( P *obj) { return *obj; }
 

	
 

	
 
class Mesh {
 

	
 
  friend class Cell;
 
  friend class Node;
 
  friend class FigureEditor;
 

	
 
 public: 
 
  Mesh(void) {
 
    // Make sure the reserved value is large enough if a cell is added
 
    // in "Divide" when the capacity is insufficient, "cells" might be
 
    // relocated including the current Cell (i.e. the value of *this)
 
    // calling "Mesh::IncreaseCapacityIfNecessary" (from another
 
    // object than Cell, e.g. Mesh) before entering Divide will solve
 
    // this issue (solved now).
 
    cells.reserve(2);
 
    nodes.reserve(500);
 

	
 
    time = 0.;
 
    plugin = 0;
 
    boundary_polygon=0;
 
  };
 
  ~Mesh(void) {
 
    if (boundary_polygon) {
 
    delete boundary_polygon;
 
      boundary_polygon=0;
 
    }
 
  };
 

	
 
  void Clean(void);
 
  Cell &EllipticCell(double xc, double yc, double ra, double rb, int nnodes=10, double rotation=0);
 
  Cell &CircularCell(double xc, double yc, double r, int nnodes=10) {
 
    return EllipticCell(xc, yc, r, r, nnodes, 0);
 
  }
 
  Cell &LeafPrimordium(int n, double pet_length);
 
  Cell &LeafPrimordium2(int n);
 
  Cell *RectangularCell(const Vector ll, const Vector ur, double rotation = 0);
 
  void CellFiles(const Vector ll, const Vector ur);
 

	
 
  inline Cell &getCell(int i) {
 
    if ((unsigned)i<cells.size())
 
      return *cells[i];
 
    else {
 
#ifdef QDEBUG
 
      qDebug() << i << endl;
 
      qDebug() << "size is " << cells.size() << endl;
 
#endif
 
      abort();
 
    }
 
  }
 

	
 
  inline Node &getNode(int i) {
 
    return *nodes[i];    
 
  }
 

	
 
  //double Diffusion(void);
 
  inline int size(void) {
 
    return cells.size();
 
  }
 
  inline int nnodes(void) {
 
    return nodes.size();
 
  }
 

	
 
  template<class Op> void LoopCells(Op f) {
 
    for (vector <Cell *>::iterator i=cells.begin();
 
	 i!=cells.end();
 
	 i++) {
 
      f(**i);
 
    }
 
  }
 

	
 
  template<class Op> void LoopWalls(Op f) {
 
    for (list <Wall *>::iterator i=walls.begin();
 
	 i!=walls.end();
 
	 i++) {
 
      f(**i);
 
    }
 
  }
 

	
 
  // if the amount of cells might increase, during looping, use this template
 
  template<class Op> void LoopCurrentCells(Op f) {
 
    vector<Cell *> current_cells = cells;
 
    for (vector <Cell *>::iterator i=current_cells.begin();
 
	 i!=current_cells.end();
 
	 i++) {
 
      f(**i);
 

	
 
    }
 
  }
 

	
 
  template<class Op> void LoopNodes(Op f) {
 
    for (vector<Node *>::iterator i=nodes.begin();
 
	 i!=nodes.end();
 
	 i++) {
 
      f(**i); 
 
    }
 
  }
 

	
 
  template<class Op> void RandomlyLoopNodes(Op f) {
 

	
 
    MyUrand r(shuffled_nodes.size());
 
    random_shuffle(shuffled_nodes.begin(),shuffled_nodes.end(),r);
 

	
 
    for (vector<Node *>::const_iterator i=shuffled_nodes.begin();
 
	 i!=shuffled_nodes.end();
 
	 i++) {
 
      f(*shuffled_nodes[*i]);
 
    }
 
  }
 

	
 
  template<class Op> void RandomlyLoopCells(Op f) {
 

	
 
    MyUrand r(shuffled_cells.size());
 
    random_shuffle(shuffled_cells.begin(),shuffled_cells.end(),r);
 

	
 
    for (vector<Cell *>::const_iterator i=shuffled_cells.begin();
 
	 i!=shuffled_cells.end();
 
	 i++) {
 
      f(*shuffled_cells[*i]);
 
    }
 
  }
 

	
 
  template<class Op1, class Op2> void LoopCells(Op1 f, Op2 &g) {
 
    for (vector<Cell *>::iterator i=cells.begin();
 
	 i!=cells.end();
 
	 i++) {
 
      f(**i,g); 
 
    }
 
  }
 

	
 
  template<class Op1, class Op2, class Op3> void LoopCells(Op1 f, Op2 &g, Op3 &h) {
 
    for (vector<Cell *>::iterator i=cells.begin();
 
	 i!=cells.end();
 
	 i++) {
 
      f(**i,g,h); 
 
    }
 
  }
 

	
 
  void DoCellHouseKeeping(void) {
 
    vector<Cell *> current_cells = cells;
 
    for (vector<Cell *>::iterator i = current_cells.begin();
 
	 i != current_cells.end();
 
	 i ++) {
 
      plugin->CellHouseKeeping(*i);
 

	
 
      // Call functions of Cell that cannot be called from CellBase, including Division
 
      if ((*i)->flag_for_divide) {
 
	if ((*i)->division_axis) {
 
	  (*i)->DivideOverAxis(*(*i)->division_axis);
 
	  delete (*i)->division_axis;
 
	  (*i)->division_axis = 0;
 
	} else {
 
	  (*i)->Divide();
 
	}
 
	(*i)->flag_for_divide=false;
 
      }
 
    }
 
  }
 

	
 
  // Apply "f" to cell i
 
  // i.e. this is an adapter which allows you to call a function
 
  // operating on Cell on its numeric index index
 
  template<class Op> void cell_index_adapter(Op f,int i) {
 
    f(cells[i]);
 
  }
 

	
 
  double DisplaceNodes(void);
 

	
 
  void BoundingBox(Vector &LowerLeft, Vector &UpperRight);
 
  int NEqs(void) {     int nwalls = walls.size();
 
    int ncells =cells.size();
 
    int nchems = Cell::NChem();
 

	
 
    // two eqs per chemical for each walls, and one eq per chemical for each cell
 
    // This is for generality. For a specific model you may optimize
 
    // this by removing superfluous (empty) equations.
 
    int neqs = 2 * nwalls * nchems + ncells * nchems;
 

	
 
    return neqs;
 
  }
 
  void IncreaseCellCapacityIfNecessary(void) {
 

	
 
    return;
 
    // cerr << "Entering Mesh::IncreaseCellCapacityIfNecessary \n";
 
    // make sure we always have enough space 
 
    // to have each cell divide at least once
 
    //
 
    // Note that we must do this, because Cell::Divide pushes a new Cell
 
    // onto Mesh::cells. As a result, Mesh::cells might be relocated 
 
    // if we are _within_ a Cell object: i.e. pointer "this" will be changed!!
 
    // 
 
    // An alternative solution could be to make "Mesh::cells" a list,
 
    // but this won't work because we need random access for 
 
    // the Monte Carlo algorithm.
 

	
 
    if (2*cells.size()>cells.capacity()) {
 
      cerr << "Increasing capacity to "  << 2*cells.capacity() << endl;
 
      cerr << "Current capacity is " << cells.capacity() << endl;
 
      cells.reserve(cells.capacity()*2);
 
    }
 
  }
 

	
 
  void ReserveMoreCells(int n) {
 
    if (nodes.size()+n>nodes.capacity()) {
 
      nodes.reserve(size()+n);
 
    }
 
  }
 
  double Area(void);
 
  double MeanArea(void) {
 
    double sum=0.;
 
    for (vector<Cell *>::const_iterator i=cells.begin();
 
	 i!=cells.end();
 
	 i++) {
 
      sum+=(*i)->Area();
 
    }
 
    return sum/(double)NCells();
 
  }
 

	
 
  void SetBaseArea(void);
 
  int NCells(void) const {
 
    return cells.size();
 
  }
 
  inline int NNodes(void) const {
 
    return nodes.size();
 
  }
 
  void PrintQueue(ostream &os) {
 
    while (!node_insertion_queue.empty()) {
 
      os << node_insertion_queue.front() << endl;
 
      node_insertion_queue.pop();
 
    }
 
  }
 

	
 
  void InsertNodes(void) {
 
    // insert the nodes in the insertion queue
 
    while (!node_insertion_queue.empty()) {
 

	
 
      //cerr << node_insertion_queue.front() << endl;
 
      InsertNode(node_insertion_queue.front());
 
      node_insertion_queue.pop();
 
    }
 

	
 
  }
 

	
 
  void Clear(); 
 

	
 
  void ReactDiffuse( double delta_t = 1 );
 
  double SumChemical(int ch);
 
  void SetChemical(int ch, double value) {
 
    for (vector<Cell *>::iterator c=cells.begin();
 
	 c!=cells.end();
 
	 c++) {
 
      (*c)->chem[ch]=value;
 
    }
 
  }
 

	
 
  // used for interacing with ODE-solvers (e.g. NRCRungeKutta)
 
  void setValues(double x, double *y);
 
  double *getValues(int *neqs);
 
  void Derivatives(double *derivs);
 
#ifdef QTGRAPHICS
 
  inline void DrawBoundary(QGraphicsScene *c) {
 
    boundary_polygon->Draw(c);
 
  }
 
  void DrawNodes(QGraphicsScene *c) const;
 

	
 
#endif
 
  double max_chem;
 

	
 
  void XMLSave(const char *docname, xmlNode *settings=0) const;
 
  void XMLRead(const char *docname, xmlNode **settings=0, bool geometry = true, bool pars = true, bool simtime = true);
 
  void XMLReadPars(const xmlNode * root_node);
 
  void XMLReadGeometry(const xmlNode *root_node);
 
  void XMLReadSimtime(const xmlNode *root_node);
 
  void XMLReadNodes(xmlNode *cur);
 
  void XMLReadCells(xmlNode *cur);
 
  void XMLParseTree(const xmlNode * root_node);
 
  void XMLReadWalls(xmlNode *cur, vector<Wall *> *tmp_cells);
 
  void XMLReadWallsToCells(xmlNode *root, vector<Wall *> *tmp_walls);
 
  void XMLReadNodeSets(xmlNode *root);
 
  void XMLReadNodeSetsToNodes(xmlNode *root);
 
  void PerturbChem(int chemnum, double range);
 
  void CleanUpCellNodeLists(void);
 
  void CleanUpWalls(void);
 
  void CutAwayBelowLine( Vector startpoint, Vector endpoint );
 
  void CutAwaySAM(void);
 
  void RepairBoundaryPolygon(void);
 
  void Rotate(double angle, Vector center);
 
  void PrintWallList( void );
 
  void TestIllegalWalls(void);
 
  Vector FirstConcMoment(int chem);
 
  inline Vector Centroid(void) {
 
    return boundary_polygon->Centroid();
 
  }
 

	
 
  inline Vector Offset(void) {
 
    return boundary_polygon->Offset();
 
  }
 

	
 
  inline double Factor(void) {
 
    return boundary_polygon->Factor();
 
  }
 

	
 
  void DeleteLooseWalls(void);
 
  void FitLeafToCanvas(double width, double height);
 
  void AddNodeSet(NodeSet *node_set) {
 
    node_sets.push_back(node_set);
 
  }
 

	
 
  void CleanChemicals(const vector<double> &clean_chem);
 
  void CleanTransporters(const vector<double> &clean_transporters);
 
  void RandomizeChemicals(const vector<double> &max_chem, const vector<double> &max_transporters);
 
  inline double getTime(void) const { return time; }
 
  string getTimeHours(void) const; 
 
  inline void setTime(double t) { time = t; }
 
  double CalcProtCellsWalls(int ch) const;  
 
  void SettoInitVals(void);
 
  QVector<qreal> VertexAngles(void);
 
  QVector< QPair<qreal,int> > VertexAnglesValues(void);
 
  void SetSimPlugin(SimPluginInterface *new_plugin) {
 
    plugin=new_plugin;
 
  }
 
  QString ModelID(void) { return plugin?plugin->ModelID():QString("undefined"); }
 
  void StandardInit(void);	
 
  double Compactness(double *res_compactness=0, double *res_area=0, double *res_cell_area=0);
 
  void CSVExportCellData(QTextStream &csv_stream) const;
 
  void CSVExportMeshData(QTextStream &csv_stream);
 

	
 
  Node* findNextBoundaryNode(Node*);
 

	
 
 private:
 

	
 
  // Data members
 
  vector<Cell *> cells;
 
  vector<Node *> nodes;
 
  list<Wall *> walls; // we need to erase elements from this container frequently, hence a list.
 
 public:
 
  vector<NodeSet *> node_sets;
 
 private:
 
  vector<Node *> shuffled_nodes;
 
  vector<Cell *> shuffled_cells;
 
  unique_queue<Edge> node_insertion_queue;
 
  BoundaryPolygon *boundary_polygon;
 
  double time;
 
  SimPluginInterface *plugin;
 

	
 
  // Private member functions
 
  void AddNodeToCell(Cell *c, Node *n, Node *nb1 , Node *nb2);
 
  void AddNodeToCellAtIndex(Cell *c, Node *n, Node *nb1 , Node *nb2, list<Node *>::iterator ins_pos);
 
  void InsertNode(Edge &e);
 
  inline Node *AddNode(Node *n) {
 
    nodes.push_back(n);
 
    shuffled_nodes.push_back(n);
 
    n->m=this;
 
    return n;
 
  }
 

	
 
  inline Cell *AddCell(Cell *c) {
 
    cells.push_back(c);
 
    shuffled_cells.push_back(c);
 
    //cerr << "Shuffled cell indices:  ";
 
    /*copy(shuffled_cells.begin(),shuffled_cells.end(),ostream_iterator<int>(cerr," "));
 
      cerr << endl;*/
 
    c->m=this;
 
    return c;
 
  }
 

	
 
  void CircumCircle(double x1,double y1,double x2,double y2,double x3,double y3,
 
		    double *xc,double *yc,double *r);
 
};
 
#endif
 

	
 
/* finis */
src/modelcatalogue.cpp
Show inline comments
 
/*
 
 *
 
 *  This file is part of the Virtual Leaf.
 
 *
 
 *  The Virtual Leaf 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.
 
 *
 
 *  The Virtual Leaf is distributed in the hope that it will be useful,
 
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 *  GNU General Public License for more details.
 
 *
 
 *  You should have received a copy of the GNU General Public License
 
 *  along with the Virtual Leaf.  If not, see <http://www.gnu.org/licenses/>.
 
 *
 
 *  Copyright 2010 Roeland Merks.
 
 *
 
 */
 

	
 
#include <string>
 
#include "modelcatalogue.h"
 
#include <QVariant>
 

	
 
static const std::string _module_id("$Id$");
 

	
 
ModelCatalogue::ModelCatalogue(Mesh *_mesh, Main *_mainwin, const char *model=0) {
 
  mesh = _mesh;
 
  mainwin = _mainwin;
 
  if (model) {
 
    cerr << "Loading model: " << model << endl;
 
    LoadPlugin(model);
 
  } else {
 
    cerr << "Loading all models." << endl;
 
    LoadPlugins();
 
  }
 
}
 

	
 
void ModelCatalogue::LoadPlugins() {
 

	
 
  QDir pluginDir(QApplication::applicationDirPath()); 
 
  QStringList plugin_filters; // filter for plugins, i.e "*.dll", "*.dylib"
 
#if defined(Q_OS_WIN) 
 
  if (pluginDir.dirName().toLower() =="debug" 
 
      ||pluginDir.dirName().toLower() =="release") 
 
    pluginDir.cdUp(); 
 
  plugin_filters << "*.dll";
 
#elif defined(Q_OS_MAC) 
 
  if (pluginDir.dirName() =="MacOS"){ 
 
    pluginDir.cdUp(); 
 
    pluginDir.cdUp(); 
 
    pluginDir.cdUp(); 
 
  } 
 
  plugin_filters << "*.dylib";
 
#endif 
 
  pluginDir.setNameFilters(plugin_filters);
 

	
 
  if (!pluginDir.cd("models")) {
 
    MyWarning::error("Directory 'models' not found!");
 
  }
 

	
 

	
 
  //QVector<SimPluginInterface *> plugins;
 
  foreach (QString fileName, pluginDir.entryList(QDir::Files)){ 
 
    QPluginLoader loader(pluginDir.absoluteFilePath(fileName)); 
 
    if (SimPluginInterface *plugin = 
 
	qobject_cast<SimPluginInterface *>(loader.instance())) {
 
      models.append(plugin); 
 
    } else {
 
      cerr << loader.errorString().toStdString().c_str() << endl;
 
      MyWarning::warning("Could not load model %s: %s",fileName.toStdString().c_str(), loader.errorString().toStdString().c_str());
 
    }
 
  }
 
  if (models.size()==0) {
 
    MyWarning::error("No models could be loaded.");
 
  }
 
}
 

	
 
void ModelCatalogue::LoadPlugin(const char *model) {
 

	
 

	
 
  QDir pluginDir(QApplication::applicationDirPath()); 
 
  QStringList plugin_filters; // filter for plugins, i.e "*.dll", "*.dylib"
 

	
 

	
 
#if defined(Q_OS_WIN) 
 
  if (pluginDir.dirName().toLower() =="debug" 
 
      ||pluginDir.dirName().toLower() =="release") 
 
    pluginDir.cdUp(); 
 
  //plugin_filters << "*.dll";
 
#elif defined(Q_OS_MAC) 
 
  if (pluginDir.dirName() =="MacOS"){ 
 
    pluginDir.cdUp(); 
 
    pluginDir.cdUp(); 
 
    pluginDir.cdUp(); 
 
  } 
 
  //plugin_filters << "*.dylib";
 
#endif
 
  plugin_filters << model;
 
  pluginDir.setNameFilters(plugin_filters);
 

	
 
  if (!pluginDir.cd("models")) {
 
    MyWarning::error("Directory 'models' not found!");
 
  }
 

	
 
  QStringList modelnames=pluginDir.entryList(QDir::Files);
 
  if (modelnames.empty()) {
 
    MyWarning::error("Model %s not found - hint: do not include path in filename.",model);
 
  }
 
  foreach (QString fileName, modelnames){ 
 
    QPluginLoader loader(pluginDir.absoluteFilePath(fileName)); 
 

	
 
    if (SimPluginInterface *plugin = 
 
	qobject_cast<SimPluginInterface *>(loader.instance())) {
 
      models.append(plugin); 
 
      //MyWarning::warning("Successfully loaded model %s",fileName.toStdString().c_str());
 
    } else {
 
      MyWarning::warning("Could not load plugin %s",fileName.toStdString().c_str());
 
    }
 
  }
 
}
 

	
 
void ModelCatalogue::InstallFirstModel() {
 
  InstallModel(models[0]);
 
}
 
void ModelCatalogue::PopulateModelMenu() {
 
  foreach (SimPluginInterface *model, models) {
 
    QAction *modelaction = new QAction(model->ModelID(), mainwin); 
 
    QVariant data;
 
    data.setValue(model);
 
    modelaction->setData(data);
 
    mainwin->modelmenu->addAction(modelaction);
 

	
 
  }
 
  connect(mainwin->modelmenu, SIGNAL(triggered(QAction *)), this, SLOT(InstallModel(QAction *)) );
 
}	
 

	
 
void ModelCatalogue::InstallModel(QAction *modelaction) {
 
  QVariant data = modelaction->data();
 
  SimPluginInterface *model = data.value<SimPluginInterface *>();
 
  cerr << "You chose model " << model->ModelID().toStdString() << "!\n";
 
  mesh->Clean();
 
  InstallModel(model);
 
}
 

	
 
void ModelCatalogue::InstallModel(SimPluginInterface *plugin) {
 

	
 
  // make sure both main and plugin use the same static datamembers (ncells, nchems...)
 
  mesh->Clean();
 
  plugin->SetCellsStaticDatamembers(CellBase::GetStaticDataMemberPointer());
 

	
 
  mesh->SetSimPlugin(plugin);
 
 
 

	
 
  Cell::SetNChem(plugin->NChem());
 
  plugin->SetParameters(&par);
 

	
 
  if (mainwin) {
 
    mainwin->RefreshInfoBar();
 
    if (plugin->DefaultLeafML().isEmpty()) {
 
    mainwin->Init(0);
 
    } else {
 
      // locate LeafML file
 
      
 
      QDir pluginDir(QApplication::applicationDirPath()); 
 
      QStringList plugin_filters; // filter for plugins, i.e "*.dll", "*.dylib"
 
      
 
      
 
#if defined(Q_OS_WIN) 
 
      if (pluginDir.dirName().toLower() =="debug" 
 
	  ||pluginDir.dirName().toLower() =="release") 
 
	pluginDir.cdUp(); 
 
      //plugin_filters << "*.dll";
 
#elif defined(Q_OS_MAC) 
 
      if (pluginDir.dirName() =="MacOS"){ 
 
	pluginDir.cdUp(); 
 
	pluginDir.cdUp(); 
 
	pluginDir.cdUp(); 
 
      }
 
     
 
#endif
 
       // for all OS-es. Move from "bin" directory to root application folder.
 
      pluginDir.cdUp();
 
      cerr << "pluginDir: " << pluginDir.dirName().toStdString().c_str() << endl;
 
      if (!pluginDir.cd("data/leaves")) {
 
	MyWarning::warning("Directory 'data/leaves' not found! Cannot load LeafML file '%s'. Reverting to standard initial condition now...",plugin->DefaultLeafML().toStdString().c_str());
 
	mainwin->Init(0);
 
      } else {
 
      
 
	if (!pluginDir.exists(plugin->DefaultLeafML())) {
 
	  MyWarning::error("LeafML file '%s' not found - hint: is file in data/leaves folder? Reverting to standard initial condition now...",plugin->DefaultLeafML().toStdString().c_str());
 
	  mainwin->Init(0); 
 
	} else {
 
	  // Initialize simulation using default LeafML file referenced in plugin.
 
	  //mainwin->Init(0);
 
	  cerr << "Default LeafML: " << plugin->DefaultLeafML().toStdString().c_str() << endl;
 
	  mainwin->Init(pluginDir.absFilePath(plugin->DefaultLeafML()).toStdString().c_str());
 
  }
 
}
 
    }
 
  }
 

	
 
}
src/simplugin.cpp
Show inline comments
 
/*
 
 *
 
 *  This file is part of the Virtual Leaf.
 
 *
 
 *  The Virtual Leaf 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.
 
 *
 
 *  The Virtual Leaf is distributed in the hope that it will be useful,
 
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 *  GNU General Public License for more details.
 
 *
 
 *  You should have received a copy of the GNU General Public License
 
 *  along with the Virtual Leaf.  If not, see <http://www.gnu.org/licenses/>.
 
 *
 
 *  Copyright 2010 Roeland Merks.
 
 *
 
 */
 

	
 
#include <string>
 
#include <QString>
 
#include "simplugin.h"
 

	
 
static const std::string _module_id("$Id$");
 

	
 
void SimPluginInterface::SetParameters(Parameter *pass_pars)
 
{
 
  par = pass_pars; 
 
}
 

	
 
void SimPluginInterface::SetCellsStaticDatamembers( CellsStaticDatamembers *cells_static_data_members_of_main)
 
{
 
  CellBase::static_data_members = cells_static_data_members_of_main;
 
}
 

	
 
QString SimPluginInterface::DefaultLeafML(void) { return QString(); }
 

	
 
/* finis */
src/simplugin.h
Show inline comments
 
/*
 
 *
 
 *  $Id$
 
 *
 
 *  This file is part of the Virtual Leaf.
 
 *
 
 *  The Virtual Leaf 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.
 
 *
 
 *  The Virtual Leaf is distributed in the hope that it will be useful,
 
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 *  GNU General Public License for more details.
 
 *
 
 *  You should have received a copy of the GNU General Public License
 
 *  along with the Virtual Leaf.  If not, see <http://www.gnu.org/licenses/>.
 
 *
 
 *  Copyright 2010 Roeland Merks.
 
 *
 
 */
 

	
 

	
 
#ifndef _SIMPLUGIN_H_
 
#define _SIMPLUGIN_H_
 

	
 
#include <QtPlugin>
 
#include <QMetaType>
 
#include "cellbase.h"
 
#include "wallbase.h"
 

	
 
class Parameter;
 

	
 
#include <QColor>
 
#include <QString>
 

	
 

	
 
class SimPluginInterface {
 

	
 
 public:
 
  virtual QString ModelID(void) = 0; 
 

	
 
  virtual ~SimPluginInterface() { }
 

	
 
  // Executed after the cellular mechanics steps have equillibrized
 
  virtual void CellHouseKeeping(CellBase *c) = 0;
 

	
 
  // Differential equations describing transport of chemicals from cell to cell
 
  virtual void CelltoCellTransport(Wall *, double *dchem_c1, double *dchem_c2) = 0;
 

	
 
  // Differential equations describing chemical reactions taking place at or near the cell walls
 
  // (e.g. PIN accumulation)
 
  virtual void WallDynamics(Wall *w, double *dw1, double *dw)  = 0;
 

	
 
  // Differential equations describing chemical reactions inside the cells
 
  virtual void CellDynamics(CellBase *c, double *dchem) = 0;
 

	
 
  // to be executed after a cell division
 
  virtual void OnDivide(ParentInfo *parent_info, CellBase *daughter1, CellBase *daughter2) = 0;
 

	
 
  // to be executed for coloring a cell
 
  virtual void SetCellColor(CellBase *c, QColor *color) = 0;
 

	
 
  // Number of chemicals
 
  virtual int NChem(void) = 0;
 

	
 
  // Default LeafML-file to be read after model startup
 
  virtual QString DefaultLeafML(void);
 
  
 
  // For internal use; not to be redefined by end users
 
  virtual void SetParameters(Parameter *pass_pars);// { par = pass_pars; }
 
  virtual void SetCellsStaticDatamembers (CellsStaticDatamembers *cells_static_data_members_of_main);
 

	
 
 protected:
 
  class Parameter *par;
 
};
 

	
 
Q_DECLARE_INTERFACE(SimPluginInterface, "nl.cwi.VirtualLeaf.SimPluginInterface/1.2") 
 
Q_DECLARE_INTERFACE(SimPluginInterface, "nl.cwi.VirtualLeaf.SimPluginInterface/1.3") 
 
Q_DECLARE_METATYPE(SimPluginInterface *)
 

	
 
#endif
 

	
 
/* finis */
src/xmlwrite.cpp
Show inline comments
 
@@ -1065,505 +1065,508 @@ void Mesh::XMLReadNodes(xmlNode *root)
 
      double y = standardlocale.toDouble((const char *)yc, &ok);
 
      if (!ok) MyWarning::error("Could not convert \"%s\" to double in XMLRead.",(const char *)yc);
 

	
 
      Node *new_node = new Node(x,y);
 
      nodes.push_back(new_node);
 

	
 
      new_node->m = this;
 
      new_node->fixed = strtobool( (char *)fixedc);
 
      new_node->boundary = strtobool( (char *)boundaryc );
 
      new_node->sam = strtobool ( (char *)samc);
 
      new_node->node_set = 0;
 

	
 
      xmlFree(xc);
 
      xmlFree(yc);
 
      xmlFree(boundaryc);
 
      xmlFree(fixedc);
 
      xmlFree(samc);
 
    }
 
    cur=cur->next;
 
  }
 

	
 
  shuffled_nodes.clear();
 
  shuffled_nodes = nodes;
 

	
 
  MyUrand r(shuffled_nodes.size());
 
  random_shuffle(shuffled_nodes.begin(),shuffled_nodes.end(),r);
 
}
 

	
 
void Mesh::XMLReadWalls(xmlNode *root, vector<Wall *> *tmp_walls)
 
{
 

	
 
  xmlNode *cur = root;
 
  cur = cur->xmlChildrenNode;
 

	
 
  for (list<Wall *>::iterator i=walls.begin(); i!=walls.end(); i++) {
 
    delete *i;
 
  }
 

	
 
  walls.clear();
 
  Wall::nwalls = 0;
 
  tmp_walls->clear();
 

	
 
  QLocale standardlocale(QLocale::C);
 
  bool ok;
 

	
 
  while (cur!=NULL) {
 

	
 
    vector<int> tmp_nodes;
 
    while(cur!=NULL) {
 
      if ((!xmlStrcmp(cur->name, (const xmlChar *)"wall"))) {
 

	
 
	xmlChar *nc = xmlGetProp(cur, BAD_CAST "c1");
 

	
 
	if (nc==0) {
 
	  unique_warning("Token \"c1\" not found in xmlwrite.cpp at or around line no. 777");
 
	}
 
	int c1 = atoi( (char *)nc);
 
	xmlFree(nc);
 

	
 
	nc = xmlGetProp(cur, BAD_CAST "c2");
 

	
 
	if (	nc==0) {
 
	  unique_warning("Token \"c2\" not found in xmlwrite.cpp at or around line no. 785");
 
	}
 
	int c2 = atoi( (char *)nc);
 
	xmlFree(nc);
 

	
 
	nc = xmlGetProp(cur, BAD_CAST "n1");
 

	
 
	if (	nc==0) {
 
	  unique_warning("Token \"n1\" not found in xmlwrite.cpp at or around line no. 793");
 
	}
 
	int n1 = atoi( (char *)nc);
 
	xmlFree(nc);
 

	
 
	nc = xmlGetProp(cur, BAD_CAST "n2");
 

	
 
	if (	nc==0) {
 
	  unique_warning("Token \"n2\" not found in xmlwrite.cpp at or around line no. 801");
 
	}
 
	int n2 = atoi( (char *)nc);
 
	xmlFree(nc);
 

	
 
	nc = xmlGetProp(cur, BAD_CAST "length");
 

	
 
	if (	nc==0) {
 
	  unique_warning("Token \"length\" not found in xmlwrite.cpp at or around line no. 809");
 
	}
 

	
 
	double length = standardlocale.toDouble((const char *)nc, &ok);
 
	if (!ok) MyWarning::error("Could not convert \"%s\" to double in XMLRead.",(const char *)nc);
 

	
 
	xmlFree(nc);
 

	
 

	
 
	nc = xmlGetProp(cur, BAD_CAST "viz_flux");
 

	
 
	double viz_flux = 0.0;
 
	if (nc!=0) {
 

	
 
	  viz_flux = standardlocale.toDouble((const char *)nc, &ok);
 
	  if (!ok) MyWarning::error("Could not convert \"%s\" to double in XMLRead.",(const char *)nc);
 
	}
 
	xmlFree(nc);
 

	
 
	Wall::WallType wall_type;
 
	{
 
	  xmlChar *v_str = xmlGetProp(cur, BAD_CAST "wall_type");
 

	
 
	  if (v_str != 0) {
 

	
 
	    if (!xmlStrcmp(v_str, (const xmlChar *)"aux_source")) {
 
	      wall_type = Wall::AuxSource;
 
	    } else {
 
	      if (!xmlStrcmp(v_str, (const xmlChar *)"aux_sink")) {
 
		wall_type = Wall::AuxSink;
 
	      } else {
 
		wall_type = Wall::Normal;
 
	      }
 
	    }
 
	    xmlFree(v_str);
 

	
 
	  } else {
 
	    wall_type = Wall::Normal;
 
	  }
 
	}
 

	
 
	bool dead = false;
 
	{
 
	  // Note: property "delete" is used to manually clean up wall lists in XML files
 
	  // Simply add property 'delete="true"' to the wall and it will be removed from
 
	  // the mesh. (This saves us from manually reindexing the file). Otherwise do not use it.
 
	  xmlChar *v_str = xmlGetProp(cur, BAD_CAST "delete");
 

	
 
	  if (v_str != 0) {
 
	    dead = strtobool( (char *)v_str);
 
	    xmlFree(v_str);
 
	  }
 
	}
 

	
 
	Cell *cc1 = c1 != -1 ? cells[c1] : boundary_polygon;
 
	Cell *cc2 = c2 != -1 ? cells[c2] : boundary_polygon;
 

	
 
	Wall *w = new Wall( nodes[n1], nodes[n2], cc1, cc2);
 
	w->length = length;
 
	w->viz_flux = viz_flux;
 
	w->wall_type = wall_type;
 
	w->dead = dead;
 
	tmp_walls->push_back(w);
 
	walls.push_back(w);
 

	
 
	xmlNode *w_node = cur->xmlChildrenNode;
 
	while (w_node!=NULL) {
 
	  if ((!xmlStrcmp(w_node->name, (const xmlChar *)"transporters1"))) {
 

	
 
	    xmlNode *v_node = w_node->xmlChildrenNode;
 
	    int nv=0;
 
	    while (v_node!=NULL) {
 

	
 
	      if ((!xmlStrcmp(v_node->name, (const xmlChar *)"val"))) {
 
		if (nv>=Cell::NChem()) {
 
		  {
 
		    stringstream text;
 
		    text << "Exception in Mesh::XMLRead: Too many transporter values given for wall(s). Ignoring remaining values.";
 

	
 
		    unique_warning(text.str().c_str());
 
		    break;
 
		  }
 
		}
 
		xmlChar *nc = xmlGetProp(v_node, (const xmlChar *) "v");
 

	
 
		if (nc==0) {
 
		  unique_warning("Token \"v\" not found in xmlwrite.cpp at or around line no. 835");
 
		}
 

	
 
		double v = standardlocale.toDouble((const char *)nc, &ok);
 
		if (!ok) MyWarning::error("Could not convert \"%s\" to double in XMLRead.",(const char *)nc);
 

	
 
		w->transporters1[nv++]=v;
 
		xmlFree(nc);
 

	
 
	      }
 
	      v_node = v_node->next; 
 
	    }
 
	  }
 

	
 
	  if ((!xmlStrcmp(w_node->name, (const xmlChar *)"transporters2"))) {
 

	
 
	    xmlNode *v_node = w_node->xmlChildrenNode;
 
	    int nv=0;
 
	    while (v_node!=NULL) {
 
	      if ((!xmlStrcmp(v_node->name, (const xmlChar *)"val"))) {
 
		if (nv>=Cell::NChem()) {
 
		  {
 
		    stringstream text;
 
		    text << "Exception in Mesh::XMLRead: Too many transporter values given for wall(s). Ignoring remaining values.";
 
		    unique_warning(text.str().c_str());
 
		    break;
 
		  }
 
		}
 

	
 
		xmlChar *nc = xmlGetProp(v_node, (const xmlChar *) "v");
 

	
 
		if (nc==0) {
 
		  unique_warning("Token \"v\" not found in xmlwrite.cpp at or around line no. 861");
 
		}
 

	
 
		double v = standardlocale.toDouble((const char *)nc, &ok);
 
		if (!ok) MyWarning::error("Could not convert \"%s\" to double in XMLRead.",(const char *)nc);
 

	
 
		w->transporters2[nv++]=v;
 
		xmlFree(nc);
 
	      }
 
	      v_node = v_node->next; 
 
	    }
 
	  } 
 

	
 
	  if ((!xmlStrcmp(w_node->name, (const xmlChar *)"apoplast"))) {
 

	
 
	    xmlNode *v_node = w_node->xmlChildrenNode;
 
	    int nv=0;
 
	    while (v_node!=NULL) {
 

	
 
	      if ((!xmlStrcmp(v_node->name, (const xmlChar *)"val"))) {
 
		if (nv>=Cell::NChem()) {
 
		  {
 
		    stringstream text;
 
		    text << "Exception in Mesh::XMLRead: Too many transporter values given for wall(s). Ignoring remaining values.";
 

	
 
		    unique_warning(text.str().c_str());
 
		    break;
 
		  }
 
		}
 
		xmlChar *nc = xmlGetProp(v_node, (const xmlChar *) "v");
 

	
 
		if (nc==0) {
 
		  unique_warning("Token \"v\" not found in xmlwrite.cpp at or around line no. 887");
 
		}
 

	
 
		double v = standardlocale.toDouble((const char *)nc, &ok);
 
		if (!ok) MyWarning::error("Could not convert \"%s\" to double in XMLRead.",(const char *)nc);
 

	
 
		w->apoplast[nv++]=v;
 
		xmlFree(nc);
 
	      }
 
	      v_node = v_node->next; 
 
	    }
 
	  }
 
	  w_node=w_node->next;
 
	}
 
      }
 
      cur = cur->next;
 
    }
 
  }
 
}
 

	
 

	
 
void Mesh::XMLReadWallsToCells(xmlNode *root, vector<Wall *> *tmp_walls)
 
{
 

	
 
  // Add the walls to the cells (do this after reading the walls; read walls after reading cells...)
 
  // 1. Read Nodes
 
  // 2. Read Cells
 
  // 3. Read Walls
 
  // 4. Read Walls into Cells
 

	
 
  xmlNode *cur = root->xmlChildrenNode;
 
  int ci=0; // cell index
 

	
 
  while (cur!=NULL) {
 

	
 
    if ((!xmlStrcmp(cur->name, (const xmlChar *)"cell")) ||
 
	(!xmlStrcmp(cur->name, (const xmlChar *)"boundary_polygon" ))) {
 

	
 
      vector<int> tmp_walls_ind;
 
      xmlNode *n = cur->xmlChildrenNode;
 

	
 
      while(n!=NULL) {
 

	
 
	if ((!xmlStrcmp(n->name, (const xmlChar *)"wall"))) {
 
	  xmlChar *nc = xmlGetProp(n, BAD_CAST "w");
 

	
 
	  if (nc==0) {
 
	    unique_warning("Token \"w\" not found in xmlwrite.cpp at or around line no. 931");
 
	  }
 
	  tmp_walls_ind.push_back(atoi( (char *)nc));
 
	  xmlFree(nc);
 
	}
 
	n = n->next;
 
      }
 

	
 
      if (!xmlStrcmp(cur->name, (const xmlChar *)"boundary_polygon")) {
 

	
 
	int nwalls = tmp_walls_ind.size();
 
	for (int i=0;i<nwalls;i++) {
 
	  boundary_polygon->walls.push_back((*tmp_walls)[tmp_walls_ind[i]]);
 
	}
 
      } else {
 

	
 
	int nwalls = tmp_walls_ind.size();
 
	for (int i=0;i<nwalls;i++) {
 
	  cells[ci]->walls.push_back((*tmp_walls)[tmp_walls_ind[i]]);	
 
	}
 
	ci++;
 
      }
 
    } 
 
    cur=cur->next;
 
  }
 
}
 

	
 

	
 
void Mesh::XMLReadNodeSetsToNodes(xmlNode *root)
 
{
 

	
 
  xmlNode *cur = root->xmlChildrenNode;
 
  int ci=0; // cell index
 

	
 
  while (cur!=NULL) {
 

	
 

	
 

	
 
    if ((!xmlStrcmp(cur->name, (const xmlChar *)"node"))) {
 

	
 
      xmlNode *n = cur->xmlChildrenNode;
 

	
 
      while(n!=NULL) {
 

	
 
	xmlChar *nc = xmlGetProp(n, BAD_CAST "nodeset");
 

	
 
	if (nc!=0) {
 
	  int nodeset_n = atoi( (char *)nc);
 
	  nodes[ci]->node_set = node_sets[nodeset_n];
 
	  xmlFree(nc);
 
	} else {
 
	  nodes[ci]->node_set = 0;
 
	}
 

	
 
	n = n->next;
 
      }
 
      ci++;
 
    } 
 
    cur=cur->next;
 
  }
 
}
 

	
 

	
 
void Mesh::XMLReadNodeSets(xmlNode *root)
 
{
 

	
 
  for (vector<NodeSet *>::iterator i=node_sets.begin(); i!=node_sets.end(); i++) {
 
    delete *i;
 
  }
 

	
 
  node_sets.clear();
 

	
 
  xmlNode *cur = root->xmlChildrenNode;
 

	
 
  while (cur!=NULL) {
 

	
 
    NodeSet *new_nodeset=0;
 

	
 
    if ((!xmlStrcmp(cur->name, (const xmlChar *)"nodeset"))){
 
      new_nodeset = new NodeSet();
 
      node_sets.push_back(new_nodeset);
 
    } 
 

	
 
    if (new_nodeset == 0) { 
 
      cur = cur->next;
 
      continue;
 
    }
 
    new_nodeset->XMLRead(cur, this);
 
    cur=cur->next;
 
  }
 
}
 

	
 
void Mesh::XMLReadCells(xmlNode *root)
 
{
 
  for (vector<Cell *>::iterator i=cells.begin(); i!=cells.end(); i++) {
 
    delete *i;
 
  }
 

	
 
  cells.clear();
 
  Cell::NCells() = 0;
 

	
 
  if (boundary_polygon) {
 
  delete boundary_polygon;
 
    boundary_polygon=0;
 
  }
 

	
 
  xmlNode *cur = root->xmlChildrenNode;
 

	
 
  while (cur!=NULL) {
 

	
 
    Cell *new_cell=0;
 

	
 
    if ((!xmlStrcmp(cur->name, (const xmlChar *)"cell"))){
 

	
 
      new_cell = new Cell(0,0);
 
      new_cell->m = this;
 
      cells.push_back(new_cell);
 
    } else {
 
      if ((!xmlStrcmp(cur->name, (const xmlChar *)"boundary_polygon"))) {
 
	new_cell = boundary_polygon = new BoundaryPolygon(0,0);
 
	boundary_polygon->m = this;
 
      }
 
    }
 

	
 
    if (new_cell == 0) { 
 
      cur = cur->next;
 
      continue;
 
    }
 

	
 
    new_cell->XMLRead(cur);
 
    cur=cur->next;
 
  }
 
}
 

	
 
void Mesh::XMLRead(const char *docname, xmlNode **settings, bool geometry, bool pars, bool simtime)
 
{
 

	
 
  xmlDocPtr doc = xmlParseFile(docname);
 
  if (doc == NULL ) {
 
    throw("Document not parsed successfully.");
 
    return;
 
  }
 

	
 
  xmlNodePtr cur = xmlDocGetRootElement(doc);
 

	
 
  if (cur == NULL) {
 
    throw("Document is empty");
 
    xmlFreeDoc(doc);
 
    return;
 
  }
 

	
 
  if (xmlStrcmp(cur->name, (const xmlChar *) "leaf")) {
 
    throw("XML file of the wrong type, it is not a leaf.");
 
    xmlFreeDoc(doc);
 
    return;
 
  }
 

	
 
  /*Get the root element node */
 
  xmlNode *root_element = xmlDocGetRootElement(doc);
 

	
 
  if (geometry) XMLReadGeometry(root_element);
 
  if (pars) XMLReadPars(root_element);
 
  if (simtime) XMLReadSimtime(root_element);
 

	
 
  // If pointer settings defined, return a copy of the settings tree
 
  if (settings) {
 
    xmlNode *cur = root_element->xmlChildrenNode;
 
    // if settings field is not found, *settings will be returned 0.
 
    *settings = 0;
 
    while (cur!=NULL) {
 
      if ((!xmlStrcmp(cur->name, (const xmlChar *)"settings"))){
 
	*settings = xmlCopyNode(cur,1);
 
      }
 
      cur=cur->next;
 
    }
 
  }
 
  xmlFreeDoc(doc);
 

	
 
  /*
 
   *Free the global variables that may
 
   *have been allocated by the parser.
 
   */
 
  xmlCleanupParser();
 

	
 
  // We're doing this so we can manually delete walls with by adding the 'delete="true"' property
 
  CleanUpCellNodeLists();
 
}
 

	
 

	
 
void Parameter::XMLRead(xmlNode *root)
 
{
 

	
 
  xmlNode *cur = root->xmlChildrenNode;
 
  while (cur!=NULL) {
 
    if ((!xmlStrcmp(cur->name, (const xmlChar *)"parameter"))){
 
      xmlNode *par_node = cur->xmlChildrenNode;
 
      while (par_node!=NULL) {
 
	{
 
	  if (!xmlStrcmp(par_node->name, (const xmlChar *)"par")) {
 
	    xmlChar *namec = xmlGetProp(par_node, BAD_CAST "name");
 
	    xmlChar *valc = xmlGetProp(par_node, BAD_CAST "val");
 
	    if (valc) {
 
	      AssignValToPar((const char*)namec,(const char*)valc);
 
	    } else {
 
	      /* Probably a valarray */
 
	      xmlNode *sub_par_node = par_node->xmlChildrenNode;
 
	      vector<double> valarray; 
 
	      while (sub_par_node != NULL) {
 
		if (!xmlStrcmp(sub_par_node->name, (const xmlChar *)"valarray")) {
 
		  valarray = XMLIO::XMLReadValArray(sub_par_node);
 
		}
 
		sub_par_node = sub_par_node->next;
 
	      }
 
	      AssignValArrayToPar((const char*)namec, valarray);
 
	    }
 
	  }
 
	}	  
 
	par_node = par_node->next;
 
      }
 
    }
 
    cur=cur->next;
 
  }
 
}
 

	
 
/* finis */
0 comments (0 inline, 0 general)