# HG changeset patch # User Michael Guravage # Date 2010-04-14 07:51:02 # Node ID 79f94eaa3b9ef0ddd6c87c31ff602588c97ab86f Initial Commit diff --git a/.hgignore b/.hgignore new file mode 100644 --- /dev/null +++ b/.hgignore @@ -0,0 +1,21 @@ +# use glob syntax. +syntax: glob + +*~ +*.a +*.o +*.so +*moc_*.cpp +*make.out +*Makefile +src/parameter.h +src/parameter.cpp +src/default.par +src/pardialog.h +src/pardialog.cpp +bin* + +# switch to regexp syntax. +syntax: regexp + +# finis diff --git a/AUTHORS b/AUTHORS new file mode 100644 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,2 @@ +Roeland Merks +Michael Guravage \ No newline at end of file diff --git a/COPYING b/COPYING new file mode 100644 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 diff --git a/INSTALL b/INSTALL new file mode 100644 --- /dev/null +++ b/INSTALL @@ -0,0 +1,23 @@ +Basic Installation +================== + +Linux:: + + qmake -makefile libplugin.pro + rm -f make.out + make clean all 2>&1 | tee make.out + cd build_models + rm -f make.out + for i in *.pro; do echo $i; qmake -makefile $i; make clean all 2>&1 | tee -a make.out; done + cd - + qmake -makefile VirtualLeaf.pro + make clean all 2>&1 | tee -a make.out + +MacOSX:: + + tbw + +Windows:: + + tbw + diff --git a/NEWS b/NEWS new file mode 100644 diff --git a/README b/README new file mode 100644 diff --git a/data/leaves/auxin.xml b/data/leaves/auxin.xml new file mode 100644 --- /dev/null +++ b/data/leaves/auxin.xml @@ -0,0 +1,27056 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #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" +#ifdef QTGRAPHICS +#include "canvas.h" +#include "cell.h" +#include <qwidget.h> +#include <q3process.h> +#include <qapplication.h> +#include <QDesktopWidget> +#include <QGraphicsScene> +#include <QMessageBox> +//Added by qt3to4: +#include <QMouseEvent> +#endif +#include <unistd.h> +#include <q3textstream.h> +#ifdef USE_LICENSE +#include "license.h" +#endif + +#ifdef HAVE_QWT +#include "data_plot.h" +#endif +#include <QPalette> +#include <QBrush> +#include <QToolTip> +//#include "reactions.h" + +//#include "reactions_auxacc.h" +//#include "reactions_pinaux3.h" +//#include "reactions_aux1.h" +//#define REACTIONS_HEADER "reactions_growth.h" +//#include "reactions_tips_nor.h" +#define _xstr_(s) _str_(s) +#define _str_(s) #s +#include _xstr_(REACTIONS_HEADER) +extern Parameter par; + +MainBase *main_window = 0; +double auxin_account = 0.; + +#ifdef XFIGGRAPHICS +#define TIMESTEP double Graphics::TimeStep(void) +#endif + +//ofstream debug_stream("debug.log"); + +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 SetArea { + public: + void operator() (Cell &c) const { + c.SetTargetArea(4*c.CalcArea()); + c.SetTargetLength(4*c.Length()); + } + + };*/ + +#ifdef XFIGGRAPHICS +class XFigCell { +public: + void operator() (Cell &c,std::ostream &os) const { + c.XFigPrint(os); + } +}; +#endif + +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 ); + +} + +#ifdef QTGRAPHICS +class DrawCell { +public: + /*void operator() (Cell &c,Graphics &g) const { + c.Draw(g); + }*/ + 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.ShowMeshP()) + // c.DrawNodes(&canvas); + if (m.ShowFluxesP()) + c.DrawFluxes(&canvas, par.arrowsize); + + //c.DrawTriangles(canvas); + } + //cerr << "Area of cell " << c.Index() << " is " << c.Area() << endl; + } + +}; +#endif + + +const char *xfig_header = "#FIG 3.2\nLandscape\nCenter\nInches\nLetter\n100.00\nSingle\n-2\n1200 2\n"; + +Mesh mesh; +bool batch=false; + + + +#ifdef QTGRAPHICS +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.LoopCells( bind2nd (mem_fun_ref( &Cell::DrawWalls), &canvas ) ); + 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); + + + // check to see if chemicals emitted by leaf edge diffuse evenly. Yes, they do. + + /* + Vector mesh_centroid = mesh.Centroid(); + Vector first_moment = mesh.FirstConcMoment(0); + + QCanvasEllipse *disk1 = 0; + if (disk1==0) { + disk1 = new QCanvasEllipse ( 50, 50, &canvas ); + disk1->setBrush( QColor("red") ); + disk1->setZ(5); + disk1->show(); + } + + QCanvasEllipse *disk2 = 0; + if (disk2==0) { + disk2=new QCanvasEllipse ( 50, 50, &canvas ); + disk2->setBrush( QColor("blue") ); + disk2->setZ(7); + disk2->show(); + + } + + Vector offset = mesh.Offset(); + double factor = mesh.Factor(); + + disk1 -> move((offset.x+mesh_centroid.x)*factor,(offset.y+mesh_centroid.y)*factor); + disk2 -> move((offset.x+first_moment.x)*factor,(offset.y+first_moment.y)*factor); + + */ + + + 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 << mesh.getTime() << ".xml"; + 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()); + } + + } +} +#endif + +//double areaunit=0; + +void Cell::Flux(double *flux, double *D) { + + // Algorithm according to Rudge & Haseloff 2005 + // (will we need to take cell area into account?) + // For the time being, we don't: assume cell area is + // mainly determined by vacuole. + + // loop over cell edges + + for (int c=0;c<Cell::nchem;c++) flux[c]=0.; + + for (list<Wall *>::iterator i=walls.begin(); + i!=walls.end(); + i++) { + + + // leaf cannot take up chemicals from environment ("no flux boundary") + if ((*i)->c2->BoundaryPolP()) continue; + + // calculate edge length + // (will later be updated during node displacement for efficiency) + //double edge_length = (m->nodes[(*i)->n1]-m->nodes[(*i)->n2]).Norm(); + + // flux depends on edge length and concentration difference + for (int c=0;c<Cell::nchem;c++) { + double phi = (*i)->length * ( D[c] ) * ( (*i)->c2->chem[c] - chem[c] ); + + if ((*i)->c1!=this) { + cerr << "Warning, bad cells boundary: " << (*i)->c1->index << ", " << index << endl; + } + + flux[c] += phi; + } + } + +} + +INIT { + + //Cell &circle=mesh.CircularCell(0,0,10,10); + 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 { + + //Cell &circle=mesh.LeafPrimordium(10,50); + Cell &circle=mesh.CircularCell(0,0,10,10); + //circle.SetChemical(1,0); + + // petiole is auxin sink + //mesh.getCell(1).SetSource(0,0); + + // petiole becomes provascular cell + //mesh.getCell(1).SetChemical(1,0.6); + + circle.SetTargetArea(circle.CalcArea()); + //circle.SetChemical(2,1e-60); + mesh.SetBaseArea(); + + circle.SetChemical(1, par.Pi_tot ); + circle.SetChemical(0, 0.); + //mesh.LoopCells(EdgeSource()); + } +} + +TIMESTEP { + + static int i=0; + static int t=0; + static int ncells; + + if (!batch) { + UserMessage(QString("Time: %1").arg(mesh.getTimeHours().c_str()),0); + } + + + + //if (!(i%1000)) + //Save(QString("leaf%1.eps").arg(i).ascii(), "PDF"); + + /* static ofstream *kinematic=0; + if (kinematic == 0) { + stringstream kinematic_fname; + kinematic_fname << par.datadir << "/kinematic.dat"; + cerr << "Writing kinematic data to " << kinematic_fname.str() << endl; + + kinematic = new ofstream(kinematic_fname.str().c_str()); + } + + *kinematic << t << " " << i << " " << mesh.Area() << " " << mesh.NCells() << endl; + */ + + ncells=mesh.NCells(); + + + double dh; + //const double en_threshold = 1; + + //mesh.RandomlyLoopCells( mem_fun_ref(&Cell::Displace )); + + //static bool dumpflag=false; + + if(DynamicCellsP()) { + dh = mesh.DisplaceNodes(); + + + //static ofstream enfile("energy.dat"); + //enfile << i << " " << dh << "\n"; + + // 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.LoopCurrentCells(CellHouseKeeping()); // this includes cell division + + // Reaction diffusion + TransportFunction *transport_f = new AuxinTransport(); + CellReaction *cellreaction_f = new AuxinAndDifferentiation(); + WallReaction *wall_f = new PIN1Localization(); + + mesh.ReactDiffuse_New(transport_f, cellreaction_f, wall_f, par.rd_dt); + + + t++; + + Plot(); + } + } else { + + TransportFunction *transport_f = new AuxinTransport(); + CellReaction *cellreaction_f = new AuxinAndDifferentiation(); + WallReaction *wall_f = new PIN1Localization(); + + mesh.ReactDiffuse_New(transport_f, cellreaction_f, wall_f, par.rd_dt); + + Plot(); + + } + + + i++; + return mesh.getTime(); + +} + +void Main::Divide(void) { + + static Vector axis(1,0,0); + mesh.IncreaseCellCapacityIfNecessary(); + + mesh.LoopCurrentCells( bind2nd( mem_fun_ref(&Cell::DivideOverAxis),axis) ); + axis=axis.Perp2D(); + //mesh.LoopCells( mem_fun_ref(&Cell::Divide) ); + Plot(); + +} + + +/* Called if a cell is clicked */ +void Cell::OnClick(QMouseEvent *e) { + + cerr << "Calling OnClick()\n"; + /* cerr << *m->boundary_polygon; + //m->RepairBoundaryPolygon(); + //SetChemical(0,par.cellsource); + cerr << "Mesh's centroid: " << m->Centroid() << endl; + cerr << "First moment of chem[0]: " << m->FirstConcMoment(0) << endl; + + */ + /* + cerr << "Cell: " << Index() << ", has walls: "; + + for (list<Wall *>::const_iterator w=walls.begin(); + w!=walls.end(); + w++) { + cerr << **w << " "; + } + + cerr << endl; + */ + /* cerr << "Cell: " << Index() << ", has neighbors: "; + + for (list<Cell *>::const_iterator c=neighbors.begin(); + c!=neighbors.end(); + c++) { + cerr << (*c)->Index() << " "; + } + + cerr << endl;*/ + + /* + mesh.PrintWallList(); + */ + + double circ=0.; + for (list<Wall *>::const_iterator w=walls.begin(); + w!=walls.end(); + w++) { + cerr << (*w)->N1()->Index() << "->" << (*w)->N2()->Index() << " = " << (*w)->Length() << endl; + circ += (*w)->Length(); + } + cerr << "Circ is " << circ << endl; + + + if (e->button() == Qt::MidButton) { + double sum_pin_bef = getMesh().CalcProtCellsWalls(1); + + QString message(QString("Dividing Cell %1").arg(index)); + ((Main *)main_window)->UserMessage(message); + cerr << message.toStdString(); + Divide(); + ((Main *)main_window)->Plot(); + + double sum_pin_aft = getMesh().CalcProtCellsWalls(1); + + cerr << "Sum PIN1, before: " << sum_pin_bef << ", after: " << sum_pin_aft << endl; + return; + } + if (e->button() == Qt::LeftButton) { + //double sum_walls=0.; + cerr << "Wall lengths: "; + for (list<Wall *>::const_iterator w=walls.begin(); + w!=walls.end(); + w++) { + //cerr << (*w)->getTransporter(this, 1) << " "; + cerr << (*w)->Length() << " "; + //sum_walls+=(*w)->getTransporter(this, 1); //* (*w)->Length(); + } + + cerr << ", Chemical(1) = " << Chemical(1) << ", sum = " << SumTransporters(1) + Chemical(1) << endl; + + QString message; + message=QString("Cell %1 has chemicals ( %2, %3, %4, %5), and it has %6 of PIN1 at its walls. Area is %7").arg(Index()).arg(chem[0]).arg(chem[1]).arg(chem[2]).arg(chem[3]).arg(SumTransporters(1)).arg(Area()); + + ((Main *)main_window)->UserMessage(message); + + /* cerr << "Cell " << index << " has chemicals ( " << chem[0] << ", " << chem[1] << " ) " << endl; + + cerr << "Cell " << index << "'s amount of PIN1 at the walls: " << SumTransporters(1) << endl;*/ +#ifdef QTGRAPHICS + /* double sum_PIN1 = Chemical(4); + + for (list<Wall *>::const_iterator i = walls.begin(); + i!=walls.end(); + i++) { + sum_PIN1 += (*i)->getTransporter(this, 0); + } + */ + + //main_window->UserMessage(QString("Concentration chemical 0 of cell %1 = %2").arg(Index()).arg(chem[0])); + //main_window->UserMessage(QString("Target area of cell %1 = %2").arg(Index()).arg(TargetArea())); + //main_window->UserMessage(QString("Sum PIN1 of cell %1 = %2 (intracellular %3)").arg(Index()).arg(sum_PIN1).arg(Chemical(4))); + + /* QString message; + message=QString("Cell %1's nodes are:").arg(Index()); + + for (list<int>::iterator it=neighbors.begin(); + it!=neighbors.end(); + it++) { + message += QString(" %2").arg(*it); + } + + main_window->UserMessage(message);*/ + + //SetWallLengths(); + //main_window->UserMessage(QString("Cell %1 apoptoses.").arg(Index())); + //Apoptose(); + //m->CleanUpCellNodeLists(); +#ifdef HAVE_QWT + QStringList curvenames; + curvenames += QString("Auxin"); + curvenames += QString("PIN1"); + curvenames += QString("auxill. prot"); + curvenames += QString("Wall PIN1"); + PlotDialog *plot = new PlotDialog((Main *)main_window, QString("Monitor for Cell %1").arg(Index()), curvenames); + QObject::connect(this, SIGNAL(ChemMonValue(double, double *)), + plot, SLOT(AddValue(double,double *))); +#endif + } else { + //Divide(); + } +#endif +} + + + +void Wall::OnWallInsert(void) { + + // NOTE: THIS FUNCTION IS CALLED AFTER Cell::OnDivide(); + // After division, we put some PIN1 on the walls, to prevent quick + // redistribution of PIN1s and consequent auxin accumulation after + // division. + + // make sure dPij/dt = 0 at both sides of the new wall. + + // True for: + // Pij = k1/k2 * A_j * L_ij * P_i + // Pji = k1/k2 * A_i * L_ij * P_j + + //transporters1[1] = (par.k1/par.k2) * c2->Chemical(0) * length * c1->Chemical(1); + //transporters2[1] = (par.k1/par.k2) * c1->Chemical(0) * length * c2->Chemical(1); + + //transporters1[1]=transporters2[1]=0.; + // cerr << "Length of new wall is " << length << endl; + //cerr << "PIN1 is [ " << transporters1[1] << " | " << transporters2[1] << "] "; +} + + + + +int main(int argc,char **argv) { + + try { + + //if (argc<2 || strstr(argv[1],".par")==0) { + // throw "Usage: Leaf [parfile].par"; + //} + //par.Read(argv[1]); + + // parse command-line options + + int c; + //int digit_optind = 0; + + char *parfile=0; + char *leaffile=0; + /* ofstream args("args.txt"); + for (int i=0;i<argc;i++) { + args << argv[i] << endl; + }*/ + + while (1) { + + //int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = { + {"batch", 0, 0, 0}, + {"par", 1, 0, 0}, + {"leaffile", 2, 0, 0} + }; + + // short option 'p' creates trouble for non-commandline usage on MacOSX. Option -p changed to -P (capital) + static char *short_options = "bP:l"; + c = getopt_long (argc, argv, "bP:l:", + 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 'p': + parfile=strdup(optarg); + if (!parfile) { + throw("Out of memory"); + } + printf ("parameter file is '%s'\n", parfile); + + break; + + case 'l': + leaffile=strdup(optarg); + if (!leaffile) { + throw("Out of memory"); + } + printf("Reading leaf state file '%s'\n", leaffile); + 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"); + } + + +#ifdef X11GRAPHICS + X11Graphics g(par.sizex,par.sizey); + g.ChangeTitle("Virtual Leaf"); +#endif +#ifdef QTGRAPHICS + //QApplication app(argc, argv); + + //argc=1; + QCoreApplication *app; + + QGraphicsScene canvas(0,0,8000,6000); + + QPalette tooltippalette = QToolTip::palette(); + QColor transparentcolor = QColor(tooltippalette.brush(QPalette::Window).color()); + //transparentcolor.setAlphaF(0.9); + + tooltippalette.setBrush (QPalette::Window, QBrush (transparentcolor) ); + QToolTip::setPalette( tooltippalette ); + + if (batch) { + // Note: QCoreApplication allows for command line applications, independent of X-Server on Unix. + // Allows for running on cluster + app = new QCoreApplication(argc,argv); + main_window=new MainBase(canvas, mesh); + } else { + app = new QApplication(argc,argv); + main_window=new Main(canvas, mesh); + ((Main *)main_window)->resize( ((Main *)main_window)->sizeHint()); + } + + + if (!batch) { + if ( QApplication::desktop()->width() > ((Main *)main_window)->width() + 10 + && QApplication::desktop()->height() > ((Main *)main_window)->height() +30 ) + ((Main *)main_window)->show(); + else + ((Main *)main_window)->showMaximized(); + } +#ifdef USE_LICENSE + CheckLicenseFile(); +#endif + + canvas.setSceneRect(QRectF()); + if (!batch) { + QObject::connect( qApp, SIGNAL(lastWindowClosed()), qApp, SLOT(quit()) ); + } + // return app.exec(); +#endif + + if (parfile) + par.Read(parfile); + + //else + // par.Read("default.par"); + Seed(par.rseed); + +#ifdef XFIGGRAPHICS + Graphics g; +#endif + + + main_window->Init(leaffile); + + // if we have supplied a leaffile AND a parfile, reread the parameter file, + // because the leaffile also contains parameter information + if (leaffile && parfile) { + par.Read(parfile); + } + + Cell::SetMagnification(10); + Cell::setOffset(0,0); + + main_window->FitLeafToCanvas(); + +#ifdef QTGRAPHICS + + // check if leaf crosses boundary + /* Vector bbll,bbur; + mesh.BoundingBox(bbll,bbur); + + double scale_x = canvas.width()/(bbur.x-bbll.x); + double scale_y = canvas.height()/(bbur.y-bbll.y); + + Cell::Scale(scale_x<scale_y ? scale_x:scale_y); // smallest of scale_x and scale_y + + double offset_x = (canvas.width()/Cell::Magnification()-(bbur.x-bbll.x))/2.; + double offset_y = (canvas.height()/Cell::Magnification()-(bbur.y-bbll.y))/2.; + Cell::setOffset(offset_x, offset_y); + */ + /* Vector bbll,bbur; + mesh.BoundingBox(bbll,bbur); + cerr << "bbll = " << bbll << endl; + cerr << "bbur = " << bbur << endl; + double cw = (bbur.x - bbll.x); + double ch = (bbur.y - bbll.y); + double factor = canvas.width()/(2*cw); + cerr << "factor = " << factor << ", width = " << canvas.width() << endl; + cerr << "Size is " << cw << " x " << ch << endl; + canvas.resize((int)(2*cw*factor),(int)(2*ch*factor)); + Cell::SetMagnification(factor); + //main_window->scale(factor); + //Cell::Translate(-bbll.x+cw/2,-bbll.y+ch/2); + cerr << -bbll.x+cw/2 << ", " << -bbll.y+ch/2 << ", " << canvas.width() << ", " << canvas.height() << endl; + Cell::Translate((-bbll.x+cw/2),(-bbll.y+ch/2)); + */ + + + main_window->Plot(); + + +#endif + +#ifdef QTGRAPHICS + if (batch) { + //main_window->toggleMovieFrames(); + //main_window->toggleShowNodes(); + //for (int i=0;i<par.nit;i++) { + double t=0.; + do { + t = main_window->TimeStep(); + /* cerr << endl << "***********************************************" << endl; + cerr << "Time is " << t << endl; + cerr << "par.maxt = " << par.maxt << endl; + cerr << endl << "***********************************************" << endl;*/ + } while (t < par.maxt); + + } else + return app->exec(); +#else + //for (int i=0;i<par.nit;i++) { + do { + t= g.TimeStep(); + } while (t < par.maxt); +#endif + + } 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(); + } + } + +} + + +extern double auxin_account; +const int Cell::nchem = 4; + +class AuxinTransport : public TransportFunction { + + public: + virtual void operator()(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; + // dchem_c2 is undefined..! + return; + } else { + if (w->AuxinSink()) { + + // efflux into Shoot Apical meristem + // we assume all PINs are directed towards shoot apical meristem + dchem_c1[0] -= par.sam_efflux * w->C1()->Chemical(0) / (par.ka + w->C1()->Chemical(0)); + + return; + } else + return; + + + } + } + + + if (w->C1()->BoundaryPolP()) { + + if (w->AuxinSource()) { + double aux_flux = par.leaf_tip_source * w->Length(); + dchem_c2[0] += aux_flux; + // dchem_c1 is undefined...! + 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; + + } +}; + +class PIN1Localization : public WallReaction { + + public: + virtual void operator()(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.; + dw1[2] = 0.; dw2[2] = 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]=dw1[2]=dw2[2]; + return; + } + } + + if (w->C1()->BoundaryPolP()) { + if (w->AuxinSink()) { + + dw1[0] = 0.; dw2[0] = 0.; + dw1[2] = 0.; dw2[2] = 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]=dw1[2]=dw2[2]; + 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[2] = 0.; dw2[2] = 0.; + + dw1[1] = dPijdt1; + dw2[1] = dPijdt2; + + } +}; + + +inline double YlPilPli(Cell &here, Cell &nb, Wall &w) { + return nb.Chemical(2) * w.Transporters1(1) * w.Transporters2(1); +} + +inline double AlPil(Cell &here, Cell &nb, Wall &w) { + return nb.Chemical(0) * w.getTransporter( &here, 1 ); +} + +inline double AlplusYlLil(Cell &here, Cell &nb, Wall &w) { + return ( nb.Chemical(0) + nb.Chemical(2) ) * w.Length(); +} + +inline double AlLil(Cell &here, Cell &nb, Wall &w) { + return ( nb.Chemical(0) ) * w.Length(); +} + +inline double Lil(Cell &here, Cell &nb, Wall &w) { + return w.Length(); +} + +inline double complex_PijAj(Cell &here, Cell &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)); + +} + +class AuxinAndDifferentiation : public CellReaction { + + // Note: Pi and Pij measured in numbers of molecules, not concentrations + public: + virtual void operator()(Cell *c, double *dchem) { + + + double dPidt = 0.; + + double sum_Pij = c->SumTransporters( 1 ); + + // exocytosis regulated: + dPidt = -par.k1 * c->ReduceCellAndWalls<double>( 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; + + /*if (c->AtBoundaryP()) { + dchem[2] = 0.01; + //cerr << "Making cell blue.\n"; + } else { + dchem[2] = -0.1 * c->Chemical(2); + }*/ + // no PIN production in SAM + if (c->Boundary() == Cell::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); + + } + } + +}; + + + + +class CellHouseKeeping { +public: + void operator() (Cell &c) const { + + if (c.Boundary()==Cell::None) { + c.CheckForDivision(); + + // expand if this is not a provascular cell + // if (c.Chemical(2) < 0.7 ) { + c.EnlargeTargetArea((c.Chemical(0)/(1.+c.Chemical(0)))*par.cell_expansion_rate); + // } + } + } +}; + + +void Cell::SetColor(QColor &color) { + + // Red: PIN1 + // Green: Auxin + // Blue: AUX1 + color.setRgb(chem[1]/(1+chem[1]) * 255.,(chem[0]/(1+chem[0]) * 255.),(chem[2]/(1+chem[2]) *255.) ); + +} + +void Cell::CheckForDivision(void) { + // if (/* Chemical(0)<0.4 && */ /* differentiated cells do not divide */ area > 2*base_area /* || Length()>50 */) { + +// if (Chemical(0) > par.morphogen_div_threshold ) + if (area > par.rel_cell_div_threshold * base_area ) { + /* remark no longer valid? //m->IncreaseCellCapacityIfNecessary(); + // Note that calling Divide as follows prevents trouble if cell + // vector is relocated */ + Divide(); + } + +} + +// Set sum of PIN1 back to Pi_tot. Hence we assume the cell maintain a constant level of Pi. + +void Cell::OnDivide(ParentInfo &parent_info, Cell &daughter) { + + //cerr << "Calling Cell::OnDivide()" << endl; + + // Auxin distributes between parent and daughter according to area + double area = Area(), daughter_area = daughter.Area(); + double tot_area = area + daughter_area; + + chem[0]*=(area/tot_area); + daughter.chem[0]*=(daughter_area/tot_area); + + // For lack of detailed data, or a better rule, we assume that cells remain polarized + // after division + + // So the PIN1 is redistributed according to the original polarization over the walls + + // parent_info contains info about the parent + // redistribute the PIN in the endosome according to area + + //chem[1] = parent_info.PINendosome*(area/tot_area); + //daughter.chem[1] = parent_info.PINendosome*(daughter_area/tot_area); + chem[1] = par.initval[1]; + daughter.chem[1] = par.initval[1]; + + + // Now redistribute the membrane PINs according to the original polarization in the parent + // mmm... I'd like to have a better, biologically motivated rule for this, + // but for lack of something better... I hope I'm excused :-). Let's say the overall + // organization of the actin fibres is not completely destroyed after division... + + // distribute wallPINs according to the circumference of the parent and daughter + /* double circ = Circumference( ); + double daughter_circ = daughter.Circumference(); + double tot_circ = circ + daughter_circ; + + double wallPINs = (circ / tot_circ) * parent_info.PINmembrane; + double daughter_wallPINs = (daughter_circ / tot_circ) * parent_info.PINmembrane; + */ + //cerr << "wallPINs = " << wallPINs << ", daughter_wallPINs = " << daughter_wallPINs << "sum = " << wallPINs + daughter_wallPINs << ", PINmembrane = " << parent_info.PINmembrane << endl; + // distrubute it according to the overall polarity + //Vector polarization = parent_info.polarization.Normalised().Perp2D(); + + // double sum=0.; + for (list<Wall *>::const_iterator w=walls.begin(); + w!=walls.end(); + w++) { + + // distribute according to angle (0 degrees: maximum, 180 degrees minimum) + //double tmp=InnerProduct((*w)->getWallVector(this),polarization); // move domain from [-1,1] to [0,1] + + //cerr << "[" << tmp << "]"; + //sum+=tmp; + // reset transporter value + (*w)->setTransporter(this, 1, 0.); + //(*w)->setTransporter(this, 1, + } + + for (list<Wall *>::const_iterator w=daughter.walls.begin(); + w!=daughter.walls.end(); + w++) { + // reset transporter value + (*w)->setTransporter(&daughter, 1, 0.); + } + //cerr << "Sum is " << sum << endl; + //double sum_wall_Pi = SumTransporters(1); + + // After division, cells produce PIN1 (in intracellular storage) until total amount becomes Pi_tot + //SetChemical(1, par.Pi_tot - sum_wall_Pi ); + //SetNewChem(1, Chemical(1)); + + //cerr << "[ " << sum_wall_Pi + Chemical(1) << "]"; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/leaves/auxin_growth.xml b/data/leaves/auxin_growth.xml new file mode 100644 --- /dev/null +++ b/data/leaves/auxin_growth.xml @@ -0,0 +1,959 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #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 "testplugin.h" + +/* #define _xstr_(s) _str_(s) +#define _str_(s) #s +#include _xstr_(REACTIONS_HEADER) +*/ +extern Parameter par; + +MainBase *main_window = 0; +double auxin_account = 0.; + + + +TestPlugin *plugin = new TestPlugin(); + +#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 << ".pdf"; + if (par.storage_stride <= par.xml_storage_stride) { + frame++; + } + + // Write high-res JPG snapshot every plot step + Save(fname.str().c_str(), "PDF"); + */ + + 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()); + } + + } +} + + +void Cell::Flux(double *flux, double *D) { + + + // loop over cell edges + + for (int c=0;c<Cell::nchem;c++) flux[c]=0.; + + for (list<Wall *>::iterator i=walls.begin(); + i!=walls.end(); + i++) { + + + // leaf cannot take up chemicals from environment ("no flux boundary") + if ((*i)->c2->BoundaryPolP()) continue; + + + // flux depends on edge length and concentration difference + for (int c=0;c<Cell::nchem;c++) { + double phi = (*i)->length * ( D[c] ) * ( (*i)->c2->chem[c] - chem[c] ); + + if ((*i)->c1!=this) { + cerr << "Warning, bad cells boundary: " << (*i)->c1->index << ", " << index << endl; + } + + flux[c] += phi; + } + } + +} + +INIT { + + 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 { + + Cell &circle=mesh.CircularCell(0,0,10,10); + + circle.SetTargetArea(circle.CalcArea()); + mesh.SetBaseArea(); + // clean up chemicals + for (int c=0; c<Cell::NChem(); c++) { + circle.SetChemical(c, 0.); + } + } +} + +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.LoopCurrentCells(&TestPlugin::CellHouseKeeping); // this includes cell division + + // Reaction diffusion + /*CelltoCellTransport *transport_f = &TestPlugin::CelltoCellTransport; + CellReaction *cellreaction_f = new plugin->CellDynamics(); + WallReaction *wall_f = new WallDynamics();*/ + + mesh.ReactDiffuse(plugin, par.rd_dt); + + + t++; + + Plot(par.resize_stride); + + /*QVector< QPair<double, int> > angles=mesh.VertexAnglesValues(); + QString afname=QString("Angles/anglesvalues%1.dat").arg(t,6,10,QChar('0')); + ofstream af(afname.toStdString().c_str()); + */ + + /*for (QVector< QPair<qreal, int> >::const_iterator v=angles.begin(); + v!=angles.end(); + v++) { + af << v->first << " " << v->second << endl; + } + */ + } + + } else { + + /* TransportFunction *transport_f = new CelltoCellTransport(); + CellReaction *cellreaction_f = new CellDynamics(); + WallReaction *wall_f = new WallDynamics(); + + mesh.ReactDiffuse_New(transport_f, cellreaction_f, wall_f, par.rd_dt);*/ + mesh.ReactDiffuse(plugin, par.rd_dt); + + Plot(par.resize_stride); + + } + + + + + + i++; + return mesh.getTime(); + +} + + + +/* Called if a cell is clicked */ +void Cell::OnClick(QMouseEvent *e) { + +} + + + +void Wall::OnWallInsert(void) { + + +} + + + + +int main(int argc,char **argv) { + + try { + + + int c; + + + char *leaffile=0; + + + while (1) { + + //int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = { + {"batch", 0, 0, 0}, + {"leaffile", 2, 0, 0} + }; + + // short option 'p' creates trouble for non-commandline usage on MacOSX. Option -p changed to -P (capital) + static char *short_options = "bl"; + c = getopt_long (argc, argv, "bl:", + 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 '?': + 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"); + } + + MakeDir("Angles"); + bool useGUI = !batch; + 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(); + } + } else { + main_window=new MainBase(canvas, mesh); + + } + + + + canvas.setSceneRect(QRectF()); + if (!batch) { + QObject::connect( qApp, SIGNAL(lastWindowClosed()), qApp, SLOT(quit()) ); + } + + + + main_window->Init(leaffile); + + Cell::SetMagnification(10); + 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(); + } + } + +} + + + +// Executed after the cellular mechanics steps have equillibrized +class CellHouseKeeping { +public: + void operator() (Cell &c) const { + + c.EnlargeTargetArea(par.cell_expansion_rate); + + if (c.Area() > par.rel_cell_div_threshold * c.BaseArea() ) { + c.Divide(); + } + } +}; + +// The number of chemical species in the cels +const int Cell::nchem = 0; + +// Differential equations describing transport of chemicals from cell to cell +class CelltoCellTransport : public TransportFunction { + + public: + virtual void operator()(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) +class WallDynamics : public WallReaction { + public: + virtual void operator()(Wall *w, double *dw1, double *dw2) {}; + +}; + + +// Differential equations describing chemical reactions inside the cells +class CellDynamics : public CellReaction { + public: + virtual void operator()(Cell *c, double *dchem) { + + }; + +}; + +// Rules for cell coloring +void Cell::SetColor(QColor &color) { } + +// To be executed after cell division +void Cell::OnDivide(ParentInfo &parent_info, Cell &daughter) {} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/leaves/bend.xml b/data/leaves/bend.xml new file mode 100644 --- /dev/null +++ b/data/leaves/bend.xml @@ -0,0 +1,1061 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #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 "testplugin.h" + +/* #define _xstr_(s) _str_(s) +#define _str_(s) #s +#include _xstr_(REACTIONS_HEADER) +*/ +extern Parameter par; + +MainBase *main_window = 0; +double auxin_account = 0.; + + + +TestPlugin *plugin = new TestPlugin(); + +#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 << ".pdf"; + if (par.storage_stride <= par.xml_storage_stride) { + frame++; + } + + // Write high-res JPG snapshot every plot step + Save(fname.str().c_str(), "PDF"); + */ + + 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()); + } + + } +} + + +void Cell::Flux(double *flux, double *D) { + + + // loop over cell edges + + for (int c=0;c<Cell::nchem;c++) flux[c]=0.; + + for (list<Wall *>::iterator i=walls.begin(); + i!=walls.end(); + i++) { + + + // leaf cannot take up chemicals from environment ("no flux boundary") + if ((*i)->c2->BoundaryPolP()) continue; + + + // flux depends on edge length and concentration difference + for (int c=0;c<Cell::nchem;c++) { + double phi = (*i)->length * ( D[c] ) * ( (*i)->c2->chem[c] - chem[c] ); + + if ((*i)->c1!=this) { + cerr << "Warning, bad cells boundary: " << (*i)->c1->index << ", " << index << endl; + } + + flux[c] += phi; + } + } + +} + +INIT { + + 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 { + + Cell &circle=mesh.CircularCell(0,0,10,10); + + circle.SetTargetArea(circle.CalcArea()); + mesh.SetBaseArea(); + // clean up chemicals + for (int c=0; c<Cell::NChem(); c++) { + circle.SetChemical(c, 0.); + } + } +} + +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.LoopCurrentCells(&TestPlugin::CellHouseKeeping); // this includes cell division + + // Reaction diffusion + /*CelltoCellTransport *transport_f = &TestPlugin::CelltoCellTransport; + CellReaction *cellreaction_f = new plugin->CellDynamics(); + WallReaction *wall_f = new WallDynamics();*/ + + mesh.ReactDiffuse(plugin, par.rd_dt); + + + t++; + + Plot(par.resize_stride); + + /*QVector< QPair<double, int> > angles=mesh.VertexAnglesValues(); + QString afname=QString("Angles/anglesvalues%1.dat").arg(t,6,10,QChar('0')); + ofstream af(afname.toStdString().c_str()); + */ + + /*for (QVector< QPair<qreal, int> >::const_iterator v=angles.begin(); + v!=angles.end(); + v++) { + af << v->first << " " << v->second << endl; + } + */ + } + + } else { + + /* TransportFunction *transport_f = new CelltoCellTransport(); + CellReaction *cellreaction_f = new CellDynamics(); + WallReaction *wall_f = new WallDynamics(); + + mesh.ReactDiffuse_New(transport_f, cellreaction_f, wall_f, par.rd_dt);*/ + mesh.ReactDiffuse(plugin, par.rd_dt); + + Plot(par.resize_stride); + + } + + + + + + i++; + return mesh.getTime(); + +} + + + +/* Called if a cell is clicked */ +void Cell::OnClick(QMouseEvent *e) { + +} + + + +void Wall::OnWallInsert(void) { + + +} + + + + +int main(int argc,char **argv) { + + try { + + + int c; + + + char *leaffile=0; + + + while (1) { + + //int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = { + {"batch", 0, 0, 0}, + {"leaffile", 2, 0, 0} + }; + + // short option 'p' creates trouble for non-commandline usage on MacOSX. Option -p changed to -P (capital) + static char *short_options = "bl"; + c = getopt_long (argc, argv, "bl:", + 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 '?': + 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"); + } + + MakeDir("Angles"); + bool useGUI = !batch; + 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(); + } + } else { + main_window=new MainBase(canvas, mesh); + + } + + + + canvas.setSceneRect(QRectF()); + if (!batch) { + QObject::connect( qApp, SIGNAL(lastWindowClosed()), qApp, SLOT(quit()) ); + } + + + + main_window->Init(leaffile); + + Cell::SetMagnification(10); + 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(); + } + } + +} + + + +// Executed after the cellular mechanics steps have equillibrized +class CellHouseKeeping { +public: + void operator() (Cell &c) const { + + c.EnlargeTargetArea(par.cell_expansion_rate); + + if (c.Area() > par.rel_cell_div_threshold * c.BaseArea() ) { + c.Divide(); + } + } +}; + +// The number of chemical species in the cels +const int Cell::nchem = 0; + +// Differential equations describing transport of chemicals from cell to cell +class CelltoCellTransport : public TransportFunction { + + public: + virtual void operator()(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) +class WallDynamics : public WallReaction { + public: + virtual void operator()(Wall *w, double *dw1, double *dw2) {}; + +}; + + +// Differential equations describing chemical reactions inside the cells +class CellDynamics : public CellReaction { + public: + virtual void operator()(Cell *c, double *dchem) { + + }; + +}; + +// Rules for cell coloring +void Cell::SetColor(QColor &color) { } + +// To be executed after cell division +void Cell::OnDivide(ParentInfo &parent_info, Cell &daughter) {} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/leaves/circ.xml b/data/leaves/circ.xml new file mode 100644 --- /dev/null +++ b/data/leaves/circ.xml @@ -0,0 +1,794 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #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 "testplugin.h" + +/* #define _xstr_(s) _str_(s) +#define _str_(s) #s +#include _xstr_(REACTIONS_HEADER) +*/ +extern Parameter par; + +MainBase *main_window = 0; +double auxin_account = 0.; + + + +TestPlugin *plugin = new TestPlugin(); + +#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 << ".pdf"; + if (par.storage_stride <= par.xml_storage_stride) { + frame++; + } + + // Write high-res JPG snapshot every plot step + Save(fname.str().c_str(), "PDF"); + */ + + 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()); + } + + } +} + + +void Cell::Flux(double *flux, double *D) { + + + // loop over cell edges + + for (int c=0;c<Cell::nchem;c++) flux[c]=0.; + + for (list<Wall *>::iterator i=walls.begin(); + i!=walls.end(); + i++) { + + + // leaf cannot take up chemicals from environment ("no flux boundary") + if ((*i)->c2->BoundaryPolP()) continue; + + + // flux depends on edge length and concentration difference + for (int c=0;c<Cell::nchem;c++) { + double phi = (*i)->length * ( D[c] ) * ( (*i)->c2->chem[c] - chem[c] ); + + if ((*i)->c1!=this) { + cerr << "Warning, bad cells boundary: " << (*i)->c1->index << ", " << index << endl; + } + + flux[c] += phi; + } + } + +} + +INIT { + + 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 { + + Cell &circle=mesh.CircularCell(0,0,10,10); + + circle.SetTargetArea(circle.CalcArea()); + mesh.SetBaseArea(); + // clean up chemicals + for (int c=0; c<Cell::NChem(); c++) { + circle.SetChemical(c, 0.); + } + } +} + +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.LoopCurrentCells(&TestPlugin::CellHouseKeeping); // this includes cell division + + // Reaction diffusion + /*CelltoCellTransport *transport_f = &TestPlugin::CelltoCellTransport; + CellReaction *cellreaction_f = new plugin->CellDynamics(); + WallReaction *wall_f = new WallDynamics();*/ + + mesh.ReactDiffuse(plugin, par.rd_dt); + + + t++; + + Plot(par.resize_stride); + + /*QVector< QPair<double, int> > angles=mesh.VertexAnglesValues(); + QString afname=QString("Angles/anglesvalues%1.dat").arg(t,6,10,QChar('0')); + ofstream af(afname.toStdString().c_str()); + */ + + /*for (QVector< QPair<qreal, int> >::const_iterator v=angles.begin(); + v!=angles.end(); + v++) { + af << v->first << " " << v->second << endl; + } + */ + } + + } else { + + /* TransportFunction *transport_f = new CelltoCellTransport(); + CellReaction *cellreaction_f = new CellDynamics(); + WallReaction *wall_f = new WallDynamics(); + + mesh.ReactDiffuse_New(transport_f, cellreaction_f, wall_f, par.rd_dt);*/ + mesh.ReactDiffuse(plugin, par.rd_dt); + + Plot(par.resize_stride); + + } + + + + + + i++; + return mesh.getTime(); + +} + + + +/* Called if a cell is clicked */ +void Cell::OnClick(QMouseEvent *e) { + +} + + + +void Wall::OnWallInsert(void) { + + +} + + + + +int main(int argc,char **argv) { + + try { + + + int c; + + + char *leaffile=0; + + + while (1) { + + //int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = { + {"batch", 0, 0, 0}, + {"leaffile", 2, 0, 0} + }; + + // short option 'p' creates trouble for non-commandline usage on MacOSX. Option -p changed to -P (capital) + static char *short_options = "bl"; + c = getopt_long (argc, argv, "bl:", + 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 '?': + 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"); + } + + MakeDir("Angles"); + bool useGUI = !batch; + 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(); + } + } else { + main_window=new MainBase(canvas, mesh); + + } + + + + canvas.setSceneRect(QRectF()); + if (!batch) { + QObject::connect( qApp, SIGNAL(lastWindowClosed()), qApp, SLOT(quit()) ); + } + + + + main_window->Init(leaffile); + + Cell::SetMagnification(10); + 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(); + } + } + +} + + + +// Executed after the cellular mechanics steps have equillibrized +class CellHouseKeeping { +public: + void operator() (Cell &c) const { + + c.EnlargeTargetArea(par.cell_expansion_rate); + + if (c.Area() > par.rel_cell_div_threshold * c.BaseArea() ) { + c.Divide(); + } + } +}; + +// The number of chemical species in the cels +const int Cell::nchem = 0; + +// Differential equations describing transport of chemicals from cell to cell +class CelltoCellTransport : public TransportFunction { + + public: + virtual void operator()(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) +class WallDynamics : public WallReaction { + public: + virtual void operator()(Wall *w, double *dw1, double *dw2) {}; + +}; + + +// Differential equations describing chemical reactions inside the cells +class CellDynamics : public CellReaction { + public: + virtual void operator()(Cell *c, double *dchem) { + + }; + +}; + +// Rules for cell coloring +void Cell::SetColor(QColor &color) { } + +// To be executed after cell division +void Cell::OnDivide(ParentInfo &parent_info, Cell &daughter) {} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/leaves/explant.xml b/data/leaves/explant.xml new file mode 100644 --- /dev/null +++ b/data/leaves/explant.xml @@ -0,0 +1,40235 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #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 "testplugin.h" + +/* #define _xstr_(s) _str_(s) +#define _str_(s) #s +#include _xstr_(REACTIONS_HEADER) +*/ +extern Parameter par; + +MainBase *main_window = 0; +double auxin_account = 0.; + + + +TestPlugin *plugin = new TestPlugin(); + +#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 << ".pdf"; + if (par.storage_stride <= par.xml_storage_stride) { + frame++; + } + + // Write high-res JPG snapshot every plot step + Save(fname.str().c_str(), "PDF"); + */ + + 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()); + } + + } +} + + +void Cell::Flux(double *flux, double *D) { + + + // loop over cell edges + + for (int c=0;c<Cell::nchem;c++) flux[c]=0.; + + for (list<Wall *>::iterator i=walls.begin(); + i!=walls.end(); + i++) { + + + // leaf cannot take up chemicals from environment ("no flux boundary") + if ((*i)->c2->BoundaryPolP()) continue; + + + // flux depends on edge length and concentration difference + for (int c=0;c<Cell::nchem;c++) { + double phi = (*i)->length * ( D[c] ) * ( (*i)->c2->chem[c] - chem[c] ); + + if ((*i)->c1!=this) { + cerr << "Warning, bad cells boundary: " << (*i)->c1->index << ", " << index << endl; + } + + flux[c] += phi; + } + } + +} + +INIT { + + 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 { + + Cell &circle=mesh.CircularCell(0,0,10,10); + + circle.SetTargetArea(circle.CalcArea()); + mesh.SetBaseArea(); + // clean up chemicals + for (int c=0; c<Cell::NChem(); c++) { + circle.SetChemical(c, 0.); + } + } +} + +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.LoopCurrentCells(&TestPlugin::CellHouseKeeping); // this includes cell division + + // Reaction diffusion + /*CelltoCellTransport *transport_f = &TestPlugin::CelltoCellTransport; + CellReaction *cellreaction_f = new plugin->CellDynamics(); + WallReaction *wall_f = new WallDynamics();*/ + + mesh.ReactDiffuse(plugin, par.rd_dt); + + + t++; + + Plot(par.resize_stride); + + /*QVector< QPair<double, int> > angles=mesh.VertexAnglesValues(); + QString afname=QString("Angles/anglesvalues%1.dat").arg(t,6,10,QChar('0')); + ofstream af(afname.toStdString().c_str()); + */ + + /*for (QVector< QPair<qreal, int> >::const_iterator v=angles.begin(); + v!=angles.end(); + v++) { + af << v->first << " " << v->second << endl; + } + */ + } + + } else { + + /* TransportFunction *transport_f = new CelltoCellTransport(); + CellReaction *cellreaction_f = new CellDynamics(); + WallReaction *wall_f = new WallDynamics(); + + mesh.ReactDiffuse_New(transport_f, cellreaction_f, wall_f, par.rd_dt);*/ + mesh.ReactDiffuse(plugin, par.rd_dt); + + Plot(par.resize_stride); + + } + + + + + + i++; + return mesh.getTime(); + +} + + + +/* Called if a cell is clicked */ +void Cell::OnClick(QMouseEvent *e) { + +} + + + +void Wall::OnWallInsert(void) { + + +} + + + + +int main(int argc,char **argv) { + + try { + + + int c; + + + char *leaffile=0; + + + while (1) { + + //int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = { + {"batch", 0, 0, 0}, + {"leaffile", 2, 0, 0} + }; + + // short option 'p' creates trouble for non-commandline usage on MacOSX. Option -p changed to -P (capital) + static char *short_options = "bl"; + c = getopt_long (argc, argv, "bl:", + 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 '?': + 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"); + } + + MakeDir("Angles"); + bool useGUI = !batch; + 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(); + } + } else { + main_window=new MainBase(canvas, mesh); + + } + + + + canvas.setSceneRect(QRectF()); + if (!batch) { + QObject::connect( qApp, SIGNAL(lastWindowClosed()), qApp, SLOT(quit()) ); + } + + + + main_window->Init(leaffile); + + Cell::SetMagnification(10); + 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(); + } + } + +} + + + +// Executed after the cellular mechanics steps have equillibrized +class CellHouseKeeping { +public: + void operator() (Cell &c) const { + + c.EnlargeTargetArea(par.cell_expansion_rate); + + if (c.Area() > par.rel_cell_div_threshold * c.BaseArea() ) { + c.Divide(); + } + } +}; + +// The number of chemical species in the cels +const int Cell::nchem = 0; + +// Differential equations describing transport of chemicals from cell to cell +class CelltoCellTransport : public TransportFunction { + + public: + virtual void operator()(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) +class WallDynamics : public WallReaction { + public: + virtual void operator()(Wall *w, double *dw1, double *dw2) {}; + +}; + + +// Differential equations describing chemical reactions inside the cells +class CellDynamics : public CellReaction { + public: + virtual void operator()(Cell *c, double *dchem) { + + }; + +}; + +// Rules for cell coloring +void Cell::SetColor(QColor &color) { } + +// To be executed after cell division +void Cell::OnDivide(ParentInfo &parent_info, Cell &daughter) {} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/leaves/init.xml b/data/leaves/init.xml new file mode 100644 --- /dev/null +++ b/data/leaves/init.xml @@ -0,0 +1,1050 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #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 "testplugin.h" + +/* #define _xstr_(s) _str_(s) +#define _str_(s) #s +#include _xstr_(REACTIONS_HEADER) +*/ +extern Parameter par; + +MainBase *main_window = 0; +double auxin_account = 0.; + + + +TestPlugin *plugin = new TestPlugin(); + +#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 << ".pdf"; + if (par.storage_stride <= par.xml_storage_stride) { + frame++; + } + + // Write high-res JPG snapshot every plot step + Save(fname.str().c_str(), "PDF"); + */ + + 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()); + } + + } +} + + +void Cell::Flux(double *flux, double *D) { + + + // loop over cell edges + + for (int c=0;c<Cell::nchem;c++) flux[c]=0.; + + for (list<Wall *>::iterator i=walls.begin(); + i!=walls.end(); + i++) { + + + // leaf cannot take up chemicals from environment ("no flux boundary") + if ((*i)->c2->BoundaryPolP()) continue; + + + // flux depends on edge length and concentration difference + for (int c=0;c<Cell::nchem;c++) { + double phi = (*i)->length * ( D[c] ) * ( (*i)->c2->chem[c] - chem[c] ); + + if ((*i)->c1!=this) { + cerr << "Warning, bad cells boundary: " << (*i)->c1->index << ", " << index << endl; + } + + flux[c] += phi; + } + } + +} + +INIT { + + 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 { + + Cell &circle=mesh.CircularCell(0,0,10,10); + + circle.SetTargetArea(circle.CalcArea()); + mesh.SetBaseArea(); + // clean up chemicals + for (int c=0; c<Cell::NChem(); c++) { + circle.SetChemical(c, 0.); + } + } +} + +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.LoopCurrentCells(&TestPlugin::CellHouseKeeping); // this includes cell division + + // Reaction diffusion + /*CelltoCellTransport *transport_f = &TestPlugin::CelltoCellTransport; + CellReaction *cellreaction_f = new plugin->CellDynamics(); + WallReaction *wall_f = new WallDynamics();*/ + + mesh.ReactDiffuse(plugin, par.rd_dt); + + + t++; + + Plot(par.resize_stride); + + /*QVector< QPair<double, int> > angles=mesh.VertexAnglesValues(); + QString afname=QString("Angles/anglesvalues%1.dat").arg(t,6,10,QChar('0')); + ofstream af(afname.toStdString().c_str()); + */ + + /*for (QVector< QPair<qreal, int> >::const_iterator v=angles.begin(); + v!=angles.end(); + v++) { + af << v->first << " " << v->second << endl; + } + */ + } + + } else { + + /* TransportFunction *transport_f = new CelltoCellTransport(); + CellReaction *cellreaction_f = new CellDynamics(); + WallReaction *wall_f = new WallDynamics(); + + mesh.ReactDiffuse_New(transport_f, cellreaction_f, wall_f, par.rd_dt);*/ + mesh.ReactDiffuse(plugin, par.rd_dt); + + Plot(par.resize_stride); + + } + + + + + + i++; + return mesh.getTime(); + +} + + + +/* Called if a cell is clicked */ +void Cell::OnClick(QMouseEvent *e) { + +} + + + +void Wall::OnWallInsert(void) { + + +} + + + + +int main(int argc,char **argv) { + + try { + + + int c; + + + char *leaffile=0; + + + while (1) { + + //int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = { + {"batch", 0, 0, 0}, + {"leaffile", 2, 0, 0} + }; + + // short option 'p' creates trouble for non-commandline usage on MacOSX. Option -p changed to -P (capital) + static char *short_options = "bl"; + c = getopt_long (argc, argv, "bl:", + 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 '?': + 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"); + } + + MakeDir("Angles"); + bool useGUI = !batch; + 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(); + } + } else { + main_window=new MainBase(canvas, mesh); + + } + + + + canvas.setSceneRect(QRectF()); + if (!batch) { + QObject::connect( qApp, SIGNAL(lastWindowClosed()), qApp, SLOT(quit()) ); + } + + + + main_window->Init(leaffile); + + Cell::SetMagnification(10); + 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(); + } + } + +} + + + +// Executed after the cellular mechanics steps have equillibrized +class CellHouseKeeping { +public: + void operator() (Cell &c) const { + + c.EnlargeTargetArea(par.cell_expansion_rate); + + if (c.Area() > par.rel_cell_div_threshold * c.BaseArea() ) { + c.Divide(); + } + } +}; + +// The number of chemical species in the cels +const int Cell::nchem = 0; + +// Differential equations describing transport of chemicals from cell to cell +class CelltoCellTransport : public TransportFunction { + + public: + virtual void operator()(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) +class WallDynamics : public WallReaction { + public: + virtual void operator()(Wall *w, double *dw1, double *dw2) {}; + +}; + + +// Differential equations describing chemical reactions inside the cells +class CellDynamics : public CellReaction { + public: + virtual void operator()(Cell *c, double *dchem) { + + }; + +}; + +// Rules for cell coloring +void Cell::SetColor(QColor &color) { } + +// To be executed after cell division +void Cell::OnDivide(ParentInfo &parent_info, Cell &daughter) {} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/leaves/meinhardt_init.xml b/data/leaves/meinhardt_init.xml new file mode 100644 --- /dev/null +++ b/data/leaves/meinhardt_init.xml @@ -0,0 +1,2066 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #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 "testplugin.h" + +/* #define _xstr_(s) _str_(s) +#define _str_(s) #s +#include _xstr_(REACTIONS_HEADER) +*/ +extern Parameter par; + +MainBase *main_window = 0; +double auxin_account = 0.; + + + +TestPlugin *plugin = new TestPlugin(); + +#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 << ".pdf"; + if (par.storage_stride <= par.xml_storage_stride) { + frame++; + } + + // Write high-res JPG snapshot every plot step + Save(fname.str().c_str(), "PDF"); + */ + + 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()); + } + + } +} + + +void Cell::Flux(double *flux, double *D) { + + + // loop over cell edges + + for (int c=0;c<Cell::nchem;c++) flux[c]=0.; + + for (list<Wall *>::iterator i=walls.begin(); + i!=walls.end(); + i++) { + + + // leaf cannot take up chemicals from environment ("no flux boundary") + if ((*i)->c2->BoundaryPolP()) continue; + + + // flux depends on edge length and concentration difference + for (int c=0;c<Cell::nchem;c++) { + double phi = (*i)->length * ( D[c] ) * ( (*i)->c2->chem[c] - chem[c] ); + + if ((*i)->c1!=this) { + cerr << "Warning, bad cells boundary: " << (*i)->c1->index << ", " << index << endl; + } + + flux[c] += phi; + } + } + +} + +INIT { + + 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 { + + Cell &circle=mesh.CircularCell(0,0,10,10); + + circle.SetTargetArea(circle.CalcArea()); + mesh.SetBaseArea(); + // clean up chemicals + for (int c=0; c<Cell::NChem(); c++) { + circle.SetChemical(c, 0.); + } + } +} + +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.LoopCurrentCells(&TestPlugin::CellHouseKeeping); // this includes cell division + + // Reaction diffusion + /*CelltoCellTransport *transport_f = &TestPlugin::CelltoCellTransport; + CellReaction *cellreaction_f = new plugin->CellDynamics(); + WallReaction *wall_f = new WallDynamics();*/ + + mesh.ReactDiffuse(plugin, par.rd_dt); + + + t++; + + Plot(par.resize_stride); + + /*QVector< QPair<double, int> > angles=mesh.VertexAnglesValues(); + QString afname=QString("Angles/anglesvalues%1.dat").arg(t,6,10,QChar('0')); + ofstream af(afname.toStdString().c_str()); + */ + + /*for (QVector< QPair<qreal, int> >::const_iterator v=angles.begin(); + v!=angles.end(); + v++) { + af << v->first << " " << v->second << endl; + } + */ + } + + } else { + + /* TransportFunction *transport_f = new CelltoCellTransport(); + CellReaction *cellreaction_f = new CellDynamics(); + WallReaction *wall_f = new WallDynamics(); + + mesh.ReactDiffuse_New(transport_f, cellreaction_f, wall_f, par.rd_dt);*/ + mesh.ReactDiffuse(plugin, par.rd_dt); + + Plot(par.resize_stride); + + } + + + + + + i++; + return mesh.getTime(); + +} + + + +/* Called if a cell is clicked */ +void Cell::OnClick(QMouseEvent *e) { + +} + + + +void Wall::OnWallInsert(void) { + + +} + + + + +int main(int argc,char **argv) { + + try { + + + int c; + + + char *leaffile=0; + + + while (1) { + + //int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = { + {"batch", 0, 0, 0}, + {"leaffile", 2, 0, 0} + }; + + // short option 'p' creates trouble for non-commandline usage on MacOSX. Option -p changed to -P (capital) + static char *short_options = "bl"; + c = getopt_long (argc, argv, "bl:", + 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 '?': + 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"); + } + + MakeDir("Angles"); + bool useGUI = !batch; + 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(); + } + } else { + main_window=new MainBase(canvas, mesh); + + } + + + + canvas.setSceneRect(QRectF()); + if (!batch) { + QObject::connect( qApp, SIGNAL(lastWindowClosed()), qApp, SLOT(quit()) ); + } + + + + main_window->Init(leaffile); + + 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(); + } + } + +} + + + +// Executed after the cellular mechanics steps have equillibrized +class CellHouseKeeping { +public: + void operator() (Cell &c) const { + + c.EnlargeTargetArea(par.cell_expansion_rate); + + if (c.Area() > par.rel_cell_div_threshold * c.BaseArea() ) { + c.Divide(); + } + } +}; + +// The number of chemical species in the cels +const int Cell::nchem = 0; + +// Differential equations describing transport of chemicals from cell to cell +class CelltoCellTransport : public TransportFunction { + + public: + virtual void operator()(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) +class WallDynamics : public WallReaction { + public: + virtual void operator()(Wall *w, double *dw1, double *dw2) {}; + +}; + + +// Differential equations describing chemical reactions inside the cells +class CellDynamics : public CellReaction { + public: + virtual void operator()(Cell *c, double *dchem) { + + }; + +}; + +// Rules for cell coloring +void Cell::SetColor(QColor &color) { } + +// To be executed after cell division +void Cell::OnDivide(ParentInfo &parent_info, Cell &daughter) {} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/leaves/one_acc_point_van3.xml b/data/leaves/one_acc_point_van3.xml new file mode 100644 --- /dev/null +++ b/data/leaves/one_acc_point_van3.xml @@ -0,0 +1,5181 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #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" +#ifdef QTGRAPHICS +#include "canvas.h" +#include "cell.h" +#include <qwidget.h> +#include <q3process.h> +#include <qapplication.h> +#include <QDesktopWidget> +#include <QGraphicsScene> +#include <QMessageBox> +//Added by qt3to4: +#include <QMouseEvent> +#endif +#include <unistd.h> +#include <q3textstream.h> +#ifdef HAVE_QWT +#include "data_plot.h" +#endif +#include <QPalette> +#include <QBrush> +#include <QToolTip> +//#include "reactions.h" + +//#include "reactions_auxacc.h" +//#include "reactions_pinaux3.h" +//#include "reactions_aux1.h" +//#define REACTIONS_HEADER "reactions_growth.h" +//#include "reactions_tips_nor.h" +#define _xstr_(s) _str_(s) +#define _str_(s) #s +#include _xstr_(REACTIONS_HEADER) +extern Parameter par; + +MainBase *main_window = 0; +double auxin_account = 0.; + +#ifdef XFIGGRAPHICS +#define TIMESTEP double Graphics::TimeStep(void) +#endif + +//ofstream debug_stream("debug.log"); + +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 SetArea { + public: + void operator() (Cell &c) const { + c.SetTargetArea(4*c.CalcArea()); + c.SetTargetLength(4*c.Length()); + } + + };*/ + +#ifdef XFIGGRAPHICS +class XFigCell { +public: + void operator() (Cell &c,std::ostream &os) const { + c.XFigPrint(os); + } +}; +#endif + +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 ); + +} + +#ifdef QTGRAPHICS +class DrawCell { +public: + /*void operator() (Cell &c,Graphics &g) const { + c.Draw(g); + }*/ + 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)\n %6 of PIN1 at walls.\n Area is %7\n PIN sum is %8\n Circumference is %9\n Boundary type is %10").arg(c.Index()).arg(c.Chemical(0)).arg(c.Chemical(1)).arg(c.Chemical(2)).arg(c.Chemical(3)).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.ShowMeshP()) + // c.DrawNodes(&canvas); + if (m.ShowFluxesP()) + c.DrawFluxes(&canvas, par.arrowsize); + + //c.DrawTriangles(canvas); + } + //cerr << "Area of cell " << c.Index() << " is " << c.Area() << endl; + } + +}; +#endif + + +const char *xfig_header = "#FIG 3.2\nLandscape\nCenter\nInches\nLetter\n100.00\nSingle\n-2\n1200 2\n"; + +Mesh mesh; +bool batch=false; + + + +#ifdef QTGRAPHICS +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.LoopCells( bind2nd (mem_fun_ref( &Cell::DrawWalls), &canvas ) ); + mesh.LoopWalls( bind2nd( mem_fun_ref( &Wall::Draw ), &canvas ) ); + + if (ShowMeshP()) + mesh.DrawNodes(&canvas); + + if (ShowBoundaryOnlyP()) + mesh.DrawBoundary(&canvas); + + + // check to see if chemicals emitted by leaf edge diffuse evenly. Yes, they do. + + /* + Vector mesh_centroid = mesh.Centroid(); + Vector first_moment = mesh.FirstConcMoment(0); + + QCanvasEllipse *disk1 = 0; + if (disk1==0) { + disk1 = new QCanvasEllipse ( 50, 50, &canvas ); + disk1->setBrush( QColor("red") ); + disk1->setZ(5); + disk1->show(); + } + + QCanvasEllipse *disk2 = 0; + if (disk2==0) { + disk2=new QCanvasEllipse ( 50, 50, &canvas ); + disk2->setBrush( QColor("blue") ); + disk2->setZ(7); + disk2->show(); + + } + + Vector offset = mesh.Offset(); + double factor = mesh.Factor(); + + disk1 -> move((offset.x+mesh_centroid.x)*factor,(offset.y+mesh_centroid.y)*factor); + disk2 -> move((offset.x+first_moment.x)*factor,(offset.y+first_moment.y)*factor); + + */ + + + 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 << mesh.getTime() << ".xml"; + 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()); + } + + } +} +#endif + +//double areaunit=0; + +void Cell::Flux(double *flux, double *D) { + + // Algorithm according to Rudge & Haseloff 2005 + // (will we need to take cell area into account?) + // For the time being, we don't: assume cell area is + // mainly determined by vacuole. + + // loop over cell edges + + for (int c=0;c<Cell::nchem;c++) flux[c]=0.; + + for (list<Wall *>::iterator i=walls.begin(); + i!=walls.end(); + i++) { + + + // leaf cannot take up chemicals from environment ("no flux boundary") + if ((*i)->c2->BoundaryPolP()) continue; + + // calculate edge length + // (will later be updated during node displacement for efficiency) + //double edge_length = (m->nodes[(*i)->n1]-m->nodes[(*i)->n2]).Norm(); + + // flux depends on edge length and concentration difference + for (int c=0;c<Cell::nchem;c++) { + double phi = (*i)->length * ( D[c] ) * ( (*i)->c2->chem[c] - chem[c] ); + + if ((*i)->c1!=this) { + cerr << "Warning, bad cells boundary: " << (*i)->c1->index << ", " << index << endl; + } + + flux[c] += phi; + } + } + +} + +INIT { + + //Cell &circle=mesh.CircularCell(0,0,10,10); + 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 { + + //Cell &circle=mesh.LeafPrimordium(10,50); + Cell &circle=mesh.CircularCell(0,0,10,10); + //circle.SetChemical(1,0); + + // petiole is auxin sink + //mesh.getCell(1).SetSource(0,0); + + // petiole becomes provascular cell + //mesh.getCell(1).SetChemical(1,0.6); + + circle.SetTargetArea(circle.CalcArea()); + //circle.SetChemical(2,1e-60); + mesh.SetBaseArea(); + + circle.SetChemical(1, par.Pi_tot ); + circle.SetChemical(0, 0.); + //mesh.LoopCells(EdgeSource()); + } +} + +TIMESTEP { + + static int i=0; + static int t=0; + static int ncells; + + if (!batch) { + UserMessage(QString("Time: %1").arg(mesh.getTimeHours().c_str()),0); + } + + + + //if (!(i%1000)) + //Save(QString("leaf%1.eps").arg(i).ascii(), "PDF"); + + /* static ofstream *kinematic=0; + if (kinematic == 0) { + stringstream kinematic_fname; + kinematic_fname << par.datadir << "/kinematic.dat"; + cerr << "Writing kinematic data to " << kinematic_fname.str() << endl; + + kinematic = new ofstream(kinematic_fname.str().c_str()); + } + + *kinematic << t << " " << i << " " << mesh.Area() << " " << mesh.NCells() << endl; + */ + + ncells=mesh.NCells(); + + + double dh; + //const double en_threshold = 1; + + //mesh.RandomlyLoopCells( mem_fun_ref(&Cell::Displace )); + + //static bool dumpflag=false; + + if(DynamicCellsP()) { + dh = mesh.DisplaceNodes(); + + + //static ofstream enfile("energy.dat"); + //enfile << i << " " << dh << "\n"; + + // 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.LoopCurrentCells(CellHouseKeeping()); // this includes cell division + + // Reaction diffusion + TransportFunction *transport_f = new AuxinTransport(); + CellReaction *cellreaction_f = new AuxinAndDifferentiation(); + WallReaction *wall_f = new PIN1Localization(); + + mesh.ReactDiffuse_New(transport_f, cellreaction_f, wall_f, par.rd_dt); + + + t++; + + Plot(); + } + } else { + + TransportFunction *transport_f = new AuxinTransport(); + CellReaction *cellreaction_f = new AuxinAndDifferentiation(); + WallReaction *wall_f = new PIN1Localization(); + + mesh.ReactDiffuse_New(transport_f, cellreaction_f, wall_f, par.rd_dt); + + Plot(); + + } + + + i++; + return mesh.getTime(); + +} + +void Main::Divide(void) { + + static Vector axis(1,0,0); + mesh.IncreaseCellCapacityIfNecessary(); + + mesh.LoopCurrentCells( bind2nd( mem_fun_ref(&Cell::DivideOverAxis),axis) ); + axis=axis.Perp2D(); + //mesh.LoopCells( mem_fun_ref(&Cell::Divide) ); + Plot(); + +} + + +/* Called if a cell is clicked */ +void Cell::OnClick(QMouseEvent *e) { + + cerr << "Calling OnClick()\n"; + /* cerr << *m->boundary_polygon; + //m->RepairBoundaryPolygon(); + //SetChemical(0,par.cellsource); + cerr << "Mesh's centroid: " << m->Centroid() << endl; + cerr << "First moment of chem[0]: " << m->FirstConcMoment(0) << endl; + + */ + /* + cerr << "Cell: " << Index() << ", has walls: "; + + for (list<Wall *>::const_iterator w=walls.begin(); + w!=walls.end(); + w++) { + cerr << **w << " "; + } + + cerr << endl; + */ + /* cerr << "Cell: " << Index() << ", has neighbors: "; + + for (list<Cell *>::const_iterator c=neighbors.begin(); + c!=neighbors.end(); + c++) { + cerr << (*c)->Index() << " "; + } + + cerr << endl;*/ + + /* + mesh.PrintWallList(); + */ + + double circ=0.; + for (list<Wall *>::const_iterator w=walls.begin(); + w!=walls.end(); + w++) { + cerr << (*w)->N1()->Index() << "->" << (*w)->N2()->Index() << " = " << (*w)->Length() << endl; + circ += (*w)->Length(); + } + cerr << "Circ is " << circ << endl; + + + if (e->button() == Qt::MidButton) { + double sum_pin_bef = getMesh().CalcProtCellsWalls(1); + + QString message(QString("Dividing Cell %1").arg(index)); + ((Main *)main_window)->UserMessage(message); + cerr << message.toStdString(); + Divide(); + ((Main *)main_window)->Plot(); + + double sum_pin_aft = getMesh().CalcProtCellsWalls(1); + + cerr << "Sum PIN1, before: " << sum_pin_bef << ", after: " << sum_pin_aft << endl; + return; + } + if (e->button() == Qt::LeftButton) { + //double sum_walls=0.; + cerr << "Wall lengths: "; + for (list<Wall *>::const_iterator w=walls.begin(); + w!=walls.end(); + w++) { + //cerr << (*w)->getTransporter(this, 1) << " "; + cerr << (*w)->Length() << " "; + //sum_walls+=(*w)->getTransporter(this, 1); //* (*w)->Length(); + } + + cerr << ", Chemical(1) = " << Chemical(1) << ", sum = " << SumTransporters(1) + Chemical(1) << endl; + + QString message; + message=QString("Cell %1 has chemicals ( %2, %3, %4, %5), and it has %6 of PIN1 at its walls. Area is %7").arg(Index()).arg(chem[0]).arg(chem[1]).arg(chem[2]).arg(chem[3]).arg(SumTransporters(1)).arg(Area()); + + ((Main *)main_window)->UserMessage(message); + + /* cerr << "Cell " << index << " has chemicals ( " << chem[0] << ", " << chem[1] << " ) " << endl; + + cerr << "Cell " << index << "'s amount of PIN1 at the walls: " << SumTransporters(1) << endl;*/ +#ifdef QTGRAPHICS + /* double sum_PIN1 = Chemical(4); + + for (list<Wall *>::const_iterator i = walls.begin(); + i!=walls.end(); + i++) { + sum_PIN1 += (*i)->getTransporter(this, 0); + } + */ + + //main_window->UserMessage(QString("Concentration chemical 0 of cell %1 = %2").arg(Index()).arg(chem[0])); + //main_window->UserMessage(QString("Target area of cell %1 = %2").arg(Index()).arg(TargetArea())); + //main_window->UserMessage(QString("Sum PIN1 of cell %1 = %2 (intracellular %3)").arg(Index()).arg(sum_PIN1).arg(Chemical(4))); + + /* QString message; + message=QString("Cell %1's nodes are:").arg(Index()); + + for (list<int>::iterator it=neighbors.begin(); + it!=neighbors.end(); + it++) { + message += QString(" %2").arg(*it); + } + + main_window->UserMessage(message);*/ + + //SetWallLengths(); + //main_window->UserMessage(QString("Cell %1 apoptoses.").arg(Index())); + //Apoptose(); + //m->CleanUpCellNodeLists(); +#ifdef HAVE_QWT + QStringList curvenames; + curvenames += QString("Auxin"); + curvenames += QString("PIN1"); + curvenames += QString("AUX1"); + curvenames += QString("Wall PIN1"); + PlotDialog *plot = new PlotDialog((Main *)main_window, QString("Monitor for Cell %1").arg(Index()), curvenames); + QObject::connect(this, SIGNAL(ChemMonValue(double, double *)), + plot, SLOT(AddValue(double,double *))); +#endif + } else { + //Divide(); + } +#endif +} + + + // Set sum of PIN1 back to Pi_tot. Hence we assume the cell maintain a constant level of Pi. + +void Cell::OnDivide(ParentInfo &parent_info, Cell &daughter) { + + //cerr << "Calling Cell::OnDivide()" << endl; + + // PIN1 distributes between parent and daughter according to area + double area = Area(), daughter_area = daughter.Area(); + double tot_area = area + daughter_area; + + //chem[1]*=(area/tot_area); + //daughter.chem[1]*=(daughter_area/tot_area); + + // For lack of detailed data, or a better rule, we assume that cells remain polarized + // after division + + // So the PIN1 is redistributed according to the original polarization over the walls + + // parent_info contains info about the parent + // redistribute the PIN in the endosome according to area + + chem[1] = parent_info.PINendosome*(area/tot_area); + daughter.chem[1] = parent_info.PINendosome*(daughter_area/tot_area); + + // Now redistribute the membrane PINs according to the original polarization in the parent + // mmm... I'd like to have a better, biologically motivated rule for this, + // but for lack of something better... I hope I'm excused :-). Let's say the overall + // organization of the actin fibres is not completely destroyed after division... + + // distribute wallPINs according to the circumference of the parent and daughter + double circ = Circumference( ); + double daughter_circ = daughter.Circumference(); + double tot_circ = circ + daughter_circ; + + double wallPINs = (circ / tot_circ) * parent_info.PINmembrane; + double daughter_wallPINs = (daughter_circ / tot_circ) * parent_info.PINmembrane; + + //cerr << "wallPINs = " << wallPINs << ", daughter_wallPINs = " << daughter_wallPINs << "sum = " << wallPINs + daughter_wallPINs << ", PINmembrane = " << parent_info.PINmembrane << endl; + // distrubute it according to the overall polarity + Vector polarization = parent_info.polarization.Normalised().Perp2D(); + + double sum=0.; + for (list<Wall *>::const_iterator w=walls.begin(); + w!=walls.end(); + w++) { + + // distribute according to angle (0 degrees: maximum, 180 degrees minimum) + double tmp=InnerProduct((*w)->getWallVector(this),polarization); // move domain from [-1,1] to [0,1] + + //cerr << "[" << tmp << "]"; + sum+=tmp; + //(*w)->setTransporter(this, 1, + } + + //cerr << "Sum is " << sum << endl; + //double sum_wall_Pi = SumTransporters(1); + + // After division, cells produce PIN1 (in intracellular storage) until total amount becomes Pi_tot + //SetChemical(1, par.Pi_tot - sum_wall_Pi ); + //SetNewChem(1, Chemical(1)); + + //cerr << "[ " << sum_wall_Pi + Chemical(1) << "]"; +} + +void Wall::OnWallInsert(void) { + + // NOTE: THIS FUNCTION IS CALLED AFTER Cell::OnDivide(); + // After division, we put some PIN1 on the walls, to prevent quick + // redistribution of PIN1s and consequent auxin accumulation after + // division. + + // make sure dPij/dt = 0 at both sides of the new wall. + + // True for: + // Pij = k1/k2 * A_j * L_ij * P_i + // Pji = k1/k2 * A_i * L_ij * P_j + + //transporters1[1] = (par.k1/par.k2) * c2->Chemical(0) * length * c1->Chemical(1); + //transporters2[1] = (par.k1/par.k2) * c1->Chemical(0) * length * c2->Chemical(1); + + //transporters1[1]=transporters2[1]=0.; + // cerr << "Length of new wall is " << length << endl; + //cerr << "PIN1 is [ " << transporters1[1] << " | " << transporters2[1] << "] "; +} + + + + +int main(int argc,char **argv) { + + try { + + //if (argc<2 || strstr(argv[1],".par")==0) { + // throw "Usage: Leaf [parfile].par"; + //} + //par.Read(argv[1]); + + // parse command-line options + + int c; + //int digit_optind = 0; + + char *parfile=0; + char *leaffile=0; + /* ofstream args("args.txt"); + for (int i=0;i<argc;i++) { + args << argv[i] << endl; + }*/ + + while (1) { + + //int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = { + {"batch", 0, 0, 0}, + {"par", 1, 0, 0}, + {"leaffile", 2, 0, 0} + }; + + // short option 'p' creates trouble for non-commandline usage on MacOSX. Option -p changed to -P (capital) + static char *short_options = "bP:l"; + c = getopt_long (argc, argv, "bP:l:", + 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 'p': + parfile=strdup(optarg); + if (!parfile) { + throw("Out of memory"); + } + printf ("parameter file is '%s'\n", parfile); + + break; + + case 'l': + leaffile=strdup(optarg); + if (!leaffile) { + throw("Out of memory"); + } + printf("Reading leaf state file '%s'\n", leaffile); + 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"); + } + + +#ifdef X11GRAPHICS + X11Graphics g(par.sizex,par.sizey); + g.ChangeTitle("Virtual Leaf"); +#endif +#ifdef QTGRAPHICS + //QApplication app(argc, argv); + + //argc=1; + QCoreApplication *app; + + QGraphicsScene canvas(0,0,8000,6000); + + QPalette tooltippalette = QToolTip::palette(); + QColor transparentcolor = QColor(tooltippalette.brush(QPalette::Window).color()); + //transparentcolor.setAlphaF(0.9); + + tooltippalette.setBrush (QPalette::Window, QBrush (transparentcolor) ); + QToolTip::setPalette( tooltippalette ); + + if (batch) { + // Note: QCoreApplication allows for command line applications, independent of X-Server on Unix. + // Allows for running on cluster + app = new QCoreApplication(argc,argv); + main_window=new MainBase(canvas, mesh); + } else { + app = new QApplication(argc,argv); + main_window=new Main(canvas, mesh); + ((Main *)main_window)->resize( ((Main *)main_window)->sizeHint()); + } + + + if (!batch) { + if ( QApplication::desktop()->width() > ((Main *)main_window)->width() + 10 + && QApplication::desktop()->height() > ((Main *)main_window)->height() +30 ) + ((Main *)main_window)->show(); + else + ((Main *)main_window)->showMaximized(); + } + + canvas.setSceneRect(QRectF()); + if (!batch) { + QObject::connect( qApp, SIGNAL(lastWindowClosed()), qApp, SLOT(quit()) ); + } + // return app.exec(); +#endif + + if (parfile) + par.Read(parfile); + + //else + // par.Read("default.par"); + Seed(par.rseed); + +#ifdef XFIGGRAPHICS + Graphics g; +#endif + + + main_window->Init(leaffile); + + // if we have supplied a leaffile AND a parfile, reread the parameter file, + // because the leaffile also contains parameter information + if (leaffile && parfile) { + par.Read(parfile); + } + + Cell::SetMagnification(10); + Cell::setOffset(0,0); + + main_window->FitLeafToCanvas(); + +#ifdef QTGRAPHICS + + // check if leaf crosses boundary + /* Vector bbll,bbur; + mesh.BoundingBox(bbll,bbur); + + double scale_x = canvas.width()/(bbur.x-bbll.x); + double scale_y = canvas.height()/(bbur.y-bbll.y); + + Cell::Scale(scale_x<scale_y ? scale_x:scale_y); // smallest of scale_x and scale_y + + double offset_x = (canvas.width()/Cell::Magnification()-(bbur.x-bbll.x))/2.; + double offset_y = (canvas.height()/Cell::Magnification()-(bbur.y-bbll.y))/2.; + Cell::setOffset(offset_x, offset_y); + */ + /* Vector bbll,bbur; + mesh.BoundingBox(bbll,bbur); + cerr << "bbll = " << bbll << endl; + cerr << "bbur = " << bbur << endl; + double cw = (bbur.x - bbll.x); + double ch = (bbur.y - bbll.y); + double factor = canvas.width()/(2*cw); + cerr << "factor = " << factor << ", width = " << canvas.width() << endl; + cerr << "Size is " << cw << " x " << ch << endl; + canvas.resize((int)(2*cw*factor),(int)(2*ch*factor)); + Cell::SetMagnification(factor); + //main_window->scale(factor); + //Cell::Translate(-bbll.x+cw/2,-bbll.y+ch/2); + cerr << -bbll.x+cw/2 << ", " << -bbll.y+ch/2 << ", " << canvas.width() << ", " << canvas.height() << endl; + Cell::Translate((-bbll.x+cw/2),(-bbll.y+ch/2)); + */ + + + main_window->Plot(); + + +#endif + +#ifdef QTGRAPHICS + if (batch) { + //main_window->toggleMovieFrames(); + //main_window->toggleShowNodes(); + //for (int i=0;i<par.nit;i++) { + double t=0.; + do { + t = main_window->TimeStep(); + /* cerr << endl << "***********************************************" << endl; + cerr << "Time is " << t << endl; + cerr << "par.maxt = " << par.maxt << endl; + cerr << endl << "***********************************************" << endl;*/ + } while (t < par.maxt); + + } else + return app->exec(); +#else + //for (int i=0;i<par.nit;i++) { + do { + t= g.TimeStep(); + } while (t < par.maxt); +#endif + + } 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(); + } + } + +} + + +extern double auxin_account; +const int Cell::nchem = 4; + +class AuxinTransport : public TransportFunction { + + public: + virtual void operator()(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; + + // dchem_c2 is undefined..! + return; + } else { + if (w->AuxinSink()) { + + // efflux into Shoot Apical meristem + // we assume all PINs are directed towards shoot apical meristem + dchem_c1[0] -= par.sam_efflux * w->C1()->Chemical(0) / (par.ka + w->C1()->Chemical(0)); + + return; + } else { + + // Active fluxes (PIN1 and AUX1 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)) + + par.aux1transport * w->C2()->Chemical(2) * w->C1()->Chemical(0) / (par.kaux1 + 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)) + + par.aux1transport * w->C1()->Chemical(2) * w->C2()->Chemical(0) / (par.kaux1 + w->C2()->Chemical(0)) ); + + dchem_c1[0] += trans21 - trans12; + dchem_c2[0] += trans12 - trans21; + return; + } + + } + } + + + if (w->C1()->BoundaryPolP()) { + + if (w->AuxinSource()) { + double aux_flux = par.leaf_tip_source * w->Length(); + dchem_c2[0] += aux_flux; + // dchem_c1 is undefined...! + return; + } else { + + if (w->AuxinSink()) { + + + // efflux into Shoot Apical meristem + // we assume all PINs are directed towards shoot apical meristem + + // no passive fluxes: outside is impermeable + + // Active fluxes (PIN1 and AUX1 mediated transport) + + // (Transporters measured in moles, here) + // efflux from cell 1 to cell 2 + // assumption: no AUX1 in shoot apical meristem + double trans12 = ( par.transport * w->Transporters1(1) * w->C1()->Chemical(0) / (par.ka + w->C1()->Chemical(0))); + dchem_c1[0] += - trans12; + + return; + + //dchem_c2[0] -= par.sam_efflux * w->C2()->Chemical(0) / (par.ka + w->C2()->Chemical(0)); + + // return; + } else { + + } + } + } + + + // Passive fluxes (Fick's law) + // only auxin flux now + // flux depends on edge length and concentration difference + for (int c=0;c<Cell::NChem();c++) { + 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 and AUX1 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)) + + par.aux1transport * w->C2()->Chemical(2) * w->C1()->Chemical(0) / (par.kaux1 + 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)) + + par.aux1transport * w->C1()->Chemical(2) * w->C2()->Chemical(0) / (par.kaux1 + w->C2()->Chemical(0)) ); + + dchem_c1[0] += trans21 - trans12; + dchem_c2[0] += trans12 - trans21; + + } +}; + +class PIN1Localization : public WallReaction { + + public: + virtual void operator()(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.; + dw1[2] = 0.; dw2[2] = 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]=dw1[2]=dw2[2]; + return; + } + } + + if (w->C1()->BoundaryPolP()) { + if (w->AuxinSink()) { + + dw1[0] = 0.; dw2[0] = 0.; + dw1[2] = 0.; dw2[2] = 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]=dw1[2]=dw2[2]; + 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[2] = 0.; dw2[2] = 0.; + + dw1[1] = dPijdt1; + dw2[1] = dPijdt2; + + } +}; + + +inline double YlPilPli(Cell &here, Cell &nb, Wall &w) { + return nb.Chemical(2) * w.Transporters1(1) * w.Transporters2(1); +} + +inline double AlPil(Cell &here, Cell &nb, Wall &w) { + return nb.Chemical(0) * w.getTransporter( &here, 1 ); +} + +inline double AlplusYlLil(Cell &here, Cell &nb, Wall &w) { + return ( nb.Chemical(0) + nb.Chemical(2) ) * w.Length(); +} + +inline double AlLil(Cell &here, Cell &nb, Wall &w) { + return ( nb.Chemical(0) ) * w.Length(); +} + +inline double Lil(Cell &here, Cell &nb, Wall &w) { + return w.Length(); +} + +inline double complex_PijAj(Cell &here, Cell &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)); + +} + +class AuxinAndDifferentiation : public CellReaction { + + // Note: Pi and Pij measured in numbers of molecules, not concentrations + public: + virtual void operator()(Cell *c, double *dchem) { + + double dPidt = 0.; + + double sum_Pij = c->SumTransporters( 1 ); + + // exocytosis regulated: + // van3 expression reduces rate of PIN1 endocytosis + dPidt = -par.k1 * c->ReduceCellAndWalls<double>( complex_PijAj ) + + (c->Chemical(3) < 0.5 ? par.k2 : par.k2van3) * 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; + + /*if (c->AtBoundaryP()) { + dchem[2] = 0.01; + //cerr << "Making cell blue.\n"; + } else { + dchem[2] = -0.1 * c->Chemical(2); + }*/ + + // no PIN production in SAM + if (c->Boundary() == Cell::SAM) { + dchem[1]=0.; + dchem[0]= - par.sam_auxin_breakdown * c->Chemical(0); + dchem[2]=0.; + } else { + + dchem[1] = dPidt; + + + // source of auxin + dchem[0] = par.aux_cons; + + // auxin-induced AUX1 production, in the epidermis + dchem[2] = ( c->AtBoundaryP() ? par.aux1prod : par.aux1prodmeso ) * ( c->Chemical(0) / ( 1. + par.kap * c->Chemical(0) ) ) - par.aux1decay * c->Chemical(2) ;//: 0.; + + // auxin-induced production of VAN-3? Autokatalysis? + //dchem[3] = par.van3prod * (c->Chemical(0) / (1. + par.kvp * c-> Chemical(0) ) ) + double A = c->Chemical(0); + double van3 = c->Chemical(3); + dchem[3] = par.van3prod * A - par.van3autokat * van3 + van3*van3/(1 + par.van3sat * van3*van3 ); + } + } + +}; + + + + +class CellHouseKeeping { +public: + void operator() (Cell &c) const { + + if (c.Boundary()==Cell::None) { + c.CheckForDivision(); + + // expand if this is not a provascular cell + if (c.Chemical(3) < 0.7 ) { + c.EnlargeTargetArea(par.cell_expansion_rate); + } + } + } +}; + +void Cell::SetColor(QColor &color) { + + // Red: AUX1 + // Green: Auxin + // Blue: van-3 + color.setRgb(chem[2]/(1+chem[2]) * 255.,(chem[0]/(1+chem[0]) * 255.),(chem[3]/(1+chem[3]) *255.) ); + +} + +void Cell::CheckForDivision(void) { + // if (/* Chemical(0)<0.4 && */ /* differentiated cells do not divide */ area > 2*base_area /* || Length()>50 */) { + + if (Chemical(0) > par.morphogen_div_threshold ) + if (area > par.rel_cell_div_threshold * base_area ) { + /* remark no longer valid? //m->IncreaseCellCapacityIfNecessary(); + // Note that calling Divide as follows prevents trouble if cell + // vector is relocated */ + Divide(); + } + +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/leaves/one_acc_point_van3_growth.xml b/data/leaves/one_acc_point_van3_growth.xml new file mode 100644 --- /dev/null +++ b/data/leaves/one_acc_point_van3_growth.xml @@ -0,0 +1,5181 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #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" +#ifdef QTGRAPHICS +#include "canvas.h" +#include "cell.h" +#include <qwidget.h> +#include <q3process.h> +#include <qapplication.h> +#include <QDesktopWidget> +#include <QGraphicsScene> +#include <QMessageBox> +//Added by qt3to4: +#include <QMouseEvent> +#endif +#include <unistd.h> +#include <q3textstream.h> +#ifdef HAVE_QWT +#include "data_plot.h" +#endif +#include <QPalette> +#include <QBrush> +#include <QToolTip> +//#include "reactions.h" + +//#include "reactions_auxacc.h" +//#include "reactions_pinaux3.h" +//#include "reactions_aux1.h" +//#define REACTIONS_HEADER "reactions_growth.h" +//#include "reactions_tips_nor.h" +#define _xstr_(s) _str_(s) +#define _str_(s) #s +#include _xstr_(REACTIONS_HEADER) +extern Parameter par; + +MainBase *main_window = 0; +double auxin_account = 0.; + +#ifdef XFIGGRAPHICS +#define TIMESTEP double Graphics::TimeStep(void) +#endif + +//ofstream debug_stream("debug.log"); + +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 SetArea { + public: + void operator() (Cell &c) const { + c.SetTargetArea(4*c.CalcArea()); + c.SetTargetLength(4*c.Length()); + } + + };*/ + +#ifdef XFIGGRAPHICS +class XFigCell { +public: + void operator() (Cell &c,std::ostream &os) const { + c.XFigPrint(os); + } +}; +#endif + +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 ); + +} + +#ifdef QTGRAPHICS +class DrawCell { +public: + /*void operator() (Cell &c,Graphics &g) const { + c.Draw(g); + }*/ + 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)\n %6 of PIN1 at walls.\n Area is %7\n PIN sum is %8\n Circumference is %9\n Boundary type is %10").arg(c.Index()).arg(c.Chemical(0)).arg(c.Chemical(1)).arg(c.Chemical(2)).arg(c.Chemical(3)).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.ShowMeshP()) + // c.DrawNodes(&canvas); + if (m.ShowFluxesP()) + c.DrawFluxes(&canvas, par.arrowsize); + + //c.DrawTriangles(canvas); + } + //cerr << "Area of cell " << c.Index() << " is " << c.Area() << endl; + } + +}; +#endif + + +const char *xfig_header = "#FIG 3.2\nLandscape\nCenter\nInches\nLetter\n100.00\nSingle\n-2\n1200 2\n"; + +Mesh mesh; +bool batch=false; + + + +#ifdef QTGRAPHICS +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.LoopCells( bind2nd (mem_fun_ref( &Cell::DrawWalls), &canvas ) ); + mesh.LoopWalls( bind2nd( mem_fun_ref( &Wall::Draw ), &canvas ) ); + + if (ShowMeshP()) + mesh.DrawNodes(&canvas); + + if (ShowBoundaryOnlyP()) + mesh.DrawBoundary(&canvas); + + + // check to see if chemicals emitted by leaf edge diffuse evenly. Yes, they do. + + /* + Vector mesh_centroid = mesh.Centroid(); + Vector first_moment = mesh.FirstConcMoment(0); + + QCanvasEllipse *disk1 = 0; + if (disk1==0) { + disk1 = new QCanvasEllipse ( 50, 50, &canvas ); + disk1->setBrush( QColor("red") ); + disk1->setZ(5); + disk1->show(); + } + + QCanvasEllipse *disk2 = 0; + if (disk2==0) { + disk2=new QCanvasEllipse ( 50, 50, &canvas ); + disk2->setBrush( QColor("blue") ); + disk2->setZ(7); + disk2->show(); + + } + + Vector offset = mesh.Offset(); + double factor = mesh.Factor(); + + disk1 -> move((offset.x+mesh_centroid.x)*factor,(offset.y+mesh_centroid.y)*factor); + disk2 -> move((offset.x+first_moment.x)*factor,(offset.y+first_moment.y)*factor); + + */ + + + 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 << mesh.getTime() << ".xml"; + 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()); + } + + } +} +#endif + +//double areaunit=0; + +void Cell::Flux(double *flux, double *D) { + + // Algorithm according to Rudge & Haseloff 2005 + // (will we need to take cell area into account?) + // For the time being, we don't: assume cell area is + // mainly determined by vacuole. + + // loop over cell edges + + for (int c=0;c<Cell::nchem;c++) flux[c]=0.; + + for (list<Wall *>::iterator i=walls.begin(); + i!=walls.end(); + i++) { + + + // leaf cannot take up chemicals from environment ("no flux boundary") + if ((*i)->c2->BoundaryPolP()) continue; + + // calculate edge length + // (will later be updated during node displacement for efficiency) + //double edge_length = (m->nodes[(*i)->n1]-m->nodes[(*i)->n2]).Norm(); + + // flux depends on edge length and concentration difference + for (int c=0;c<Cell::nchem;c++) { + double phi = (*i)->length * ( D[c] ) * ( (*i)->c2->chem[c] - chem[c] ); + + if ((*i)->c1!=this) { + cerr << "Warning, bad cells boundary: " << (*i)->c1->index << ", " << index << endl; + } + + flux[c] += phi; + } + } + +} + +INIT { + + //Cell &circle=mesh.CircularCell(0,0,10,10); + 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 { + + //Cell &circle=mesh.LeafPrimordium(10,50); + Cell &circle=mesh.CircularCell(0,0,10,10); + //circle.SetChemical(1,0); + + // petiole is auxin sink + //mesh.getCell(1).SetSource(0,0); + + // petiole becomes provascular cell + //mesh.getCell(1).SetChemical(1,0.6); + + circle.SetTargetArea(circle.CalcArea()); + //circle.SetChemical(2,1e-60); + mesh.SetBaseArea(); + + circle.SetChemical(1, par.Pi_tot ); + circle.SetChemical(0, 0.); + //mesh.LoopCells(EdgeSource()); + } +} + +TIMESTEP { + + static int i=0; + static int t=0; + static int ncells; + + if (!batch) { + UserMessage(QString("Time: %1").arg(mesh.getTimeHours().c_str()),0); + } + + + + //if (!(i%1000)) + //Save(QString("leaf%1.eps").arg(i).ascii(), "PDF"); + + /* static ofstream *kinematic=0; + if (kinematic == 0) { + stringstream kinematic_fname; + kinematic_fname << par.datadir << "/kinematic.dat"; + cerr << "Writing kinematic data to " << kinematic_fname.str() << endl; + + kinematic = new ofstream(kinematic_fname.str().c_str()); + } + + *kinematic << t << " " << i << " " << mesh.Area() << " " << mesh.NCells() << endl; + */ + + ncells=mesh.NCells(); + + + double dh; + //const double en_threshold = 1; + + //mesh.RandomlyLoopCells( mem_fun_ref(&Cell::Displace )); + + //static bool dumpflag=false; + + if(DynamicCellsP()) { + dh = mesh.DisplaceNodes(); + + + //static ofstream enfile("energy.dat"); + //enfile << i << " " << dh << "\n"; + + // 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.LoopCurrentCells(CellHouseKeeping()); // this includes cell division + + // Reaction diffusion + TransportFunction *transport_f = new AuxinTransport(); + CellReaction *cellreaction_f = new AuxinAndDifferentiation(); + WallReaction *wall_f = new PIN1Localization(); + + mesh.ReactDiffuse_New(transport_f, cellreaction_f, wall_f, par.rd_dt); + + + t++; + + Plot(); + } + } else { + + TransportFunction *transport_f = new AuxinTransport(); + CellReaction *cellreaction_f = new AuxinAndDifferentiation(); + WallReaction *wall_f = new PIN1Localization(); + + mesh.ReactDiffuse_New(transport_f, cellreaction_f, wall_f, par.rd_dt); + + Plot(); + + } + + + i++; + return mesh.getTime(); + +} + +void Main::Divide(void) { + + static Vector axis(1,0,0); + mesh.IncreaseCellCapacityIfNecessary(); + + mesh.LoopCurrentCells( bind2nd( mem_fun_ref(&Cell::DivideOverAxis),axis) ); + axis=axis.Perp2D(); + //mesh.LoopCells( mem_fun_ref(&Cell::Divide) ); + Plot(); + +} + + +/* Called if a cell is clicked */ +void Cell::OnClick(QMouseEvent *e) { + + cerr << "Calling OnClick()\n"; + /* cerr << *m->boundary_polygon; + //m->RepairBoundaryPolygon(); + //SetChemical(0,par.cellsource); + cerr << "Mesh's centroid: " << m->Centroid() << endl; + cerr << "First moment of chem[0]: " << m->FirstConcMoment(0) << endl; + + */ + /* + cerr << "Cell: " << Index() << ", has walls: "; + + for (list<Wall *>::const_iterator w=walls.begin(); + w!=walls.end(); + w++) { + cerr << **w << " "; + } + + cerr << endl; + */ + /* cerr << "Cell: " << Index() << ", has neighbors: "; + + for (list<Cell *>::const_iterator c=neighbors.begin(); + c!=neighbors.end(); + c++) { + cerr << (*c)->Index() << " "; + } + + cerr << endl;*/ + + /* + mesh.PrintWallList(); + */ + + double circ=0.; + for (list<Wall *>::const_iterator w=walls.begin(); + w!=walls.end(); + w++) { + cerr << (*w)->N1()->Index() << "->" << (*w)->N2()->Index() << " = " << (*w)->Length() << endl; + circ += (*w)->Length(); + } + cerr << "Circ is " << circ << endl; + + + if (e->button() == Qt::MidButton) { + double sum_pin_bef = getMesh().CalcProtCellsWalls(1); + + QString message(QString("Dividing Cell %1").arg(index)); + ((Main *)main_window)->UserMessage(message); + cerr << message.toStdString(); + Divide(); + ((Main *)main_window)->Plot(); + + double sum_pin_aft = getMesh().CalcProtCellsWalls(1); + + cerr << "Sum PIN1, before: " << sum_pin_bef << ", after: " << sum_pin_aft << endl; + return; + } + if (e->button() == Qt::LeftButton) { + //double sum_walls=0.; + cerr << "Wall lengths: "; + for (list<Wall *>::const_iterator w=walls.begin(); + w!=walls.end(); + w++) { + //cerr << (*w)->getTransporter(this, 1) << " "; + cerr << (*w)->Length() << " "; + //sum_walls+=(*w)->getTransporter(this, 1); //* (*w)->Length(); + } + + cerr << ", Chemical(1) = " << Chemical(1) << ", sum = " << SumTransporters(1) + Chemical(1) << endl; + + QString message; + message=QString("Cell %1 has chemicals ( %2, %3, %4, %5), and it has %6 of PIN1 at its walls. Area is %7").arg(Index()).arg(chem[0]).arg(chem[1]).arg(chem[2]).arg(chem[3]).arg(SumTransporters(1)).arg(Area()); + + ((Main *)main_window)->UserMessage(message); + + /* cerr << "Cell " << index << " has chemicals ( " << chem[0] << ", " << chem[1] << " ) " << endl; + + cerr << "Cell " << index << "'s amount of PIN1 at the walls: " << SumTransporters(1) << endl;*/ +#ifdef QTGRAPHICS + /* double sum_PIN1 = Chemical(4); + + for (list<Wall *>::const_iterator i = walls.begin(); + i!=walls.end(); + i++) { + sum_PIN1 += (*i)->getTransporter(this, 0); + } + */ + + //main_window->UserMessage(QString("Concentration chemical 0 of cell %1 = %2").arg(Index()).arg(chem[0])); + //main_window->UserMessage(QString("Target area of cell %1 = %2").arg(Index()).arg(TargetArea())); + //main_window->UserMessage(QString("Sum PIN1 of cell %1 = %2 (intracellular %3)").arg(Index()).arg(sum_PIN1).arg(Chemical(4))); + + /* QString message; + message=QString("Cell %1's nodes are:").arg(Index()); + + for (list<int>::iterator it=neighbors.begin(); + it!=neighbors.end(); + it++) { + message += QString(" %2").arg(*it); + } + + main_window->UserMessage(message);*/ + + //SetWallLengths(); + //main_window->UserMessage(QString("Cell %1 apoptoses.").arg(Index())); + //Apoptose(); + //m->CleanUpCellNodeLists(); +#ifdef HAVE_QWT + QStringList curvenames; + curvenames += QString("Auxin"); + curvenames += QString("PIN1"); + curvenames += QString("AUX1"); + curvenames += QString("Wall PIN1"); + PlotDialog *plot = new PlotDialog((Main *)main_window, QString("Monitor for Cell %1").arg(Index()), curvenames); + QObject::connect(this, SIGNAL(ChemMonValue(double, double *)), + plot, SLOT(AddValue(double,double *))); +#endif + } else { + //Divide(); + } +#endif +} + + + // Set sum of PIN1 back to Pi_tot. Hence we assume the cell maintain a constant level of Pi. + +void Cell::OnDivide(ParentInfo &parent_info, Cell &daughter) { + + //cerr << "Calling Cell::OnDivide()" << endl; + + // PIN1 distributes between parent and daughter according to area + double area = Area(), daughter_area = daughter.Area(); + double tot_area = area + daughter_area; + + //chem[1]*=(area/tot_area); + //daughter.chem[1]*=(daughter_area/tot_area); + + // For lack of detailed data, or a better rule, we assume that cells remain polarized + // after division + + // So the PIN1 is redistributed according to the original polarization over the walls + + // parent_info contains info about the parent + // redistribute the PIN in the endosome according to area + + chem[1] = parent_info.PINendosome*(area/tot_area); + daughter.chem[1] = parent_info.PINendosome*(daughter_area/tot_area); + + // Now redistribute the membrane PINs according to the original polarization in the parent + // mmm... I'd like to have a better, biologically motivated rule for this, + // but for lack of something better... I hope I'm excused :-). Let's say the overall + // organization of the actin fibres is not completely destroyed after division... + + // distribute wallPINs according to the circumference of the parent and daughter + double circ = Circumference( ); + double daughter_circ = daughter.Circumference(); + double tot_circ = circ + daughter_circ; + + double wallPINs = (circ / tot_circ) * parent_info.PINmembrane; + double daughter_wallPINs = (daughter_circ / tot_circ) * parent_info.PINmembrane; + + //cerr << "wallPINs = " << wallPINs << ", daughter_wallPINs = " << daughter_wallPINs << "sum = " << wallPINs + daughter_wallPINs << ", PINmembrane = " << parent_info.PINmembrane << endl; + // distrubute it according to the overall polarity + Vector polarization = parent_info.polarization.Normalised().Perp2D(); + + double sum=0.; + for (list<Wall *>::const_iterator w=walls.begin(); + w!=walls.end(); + w++) { + + // distribute according to angle (0 degrees: maximum, 180 degrees minimum) + double tmp=InnerProduct((*w)->getWallVector(this),polarization); // move domain from [-1,1] to [0,1] + + //cerr << "[" << tmp << "]"; + sum+=tmp; + //(*w)->setTransporter(this, 1, + } + + //cerr << "Sum is " << sum << endl; + //double sum_wall_Pi = SumTransporters(1); + + // After division, cells produce PIN1 (in intracellular storage) until total amount becomes Pi_tot + //SetChemical(1, par.Pi_tot - sum_wall_Pi ); + //SetNewChem(1, Chemical(1)); + + //cerr << "[ " << sum_wall_Pi + Chemical(1) << "]"; +} + +void Wall::OnWallInsert(void) { + + // NOTE: THIS FUNCTION IS CALLED AFTER Cell::OnDivide(); + // After division, we put some PIN1 on the walls, to prevent quick + // redistribution of PIN1s and consequent auxin accumulation after + // division. + + // make sure dPij/dt = 0 at both sides of the new wall. + + // True for: + // Pij = k1/k2 * A_j * L_ij * P_i + // Pji = k1/k2 * A_i * L_ij * P_j + + //transporters1[1] = (par.k1/par.k2) * c2->Chemical(0) * length * c1->Chemical(1); + //transporters2[1] = (par.k1/par.k2) * c1->Chemical(0) * length * c2->Chemical(1); + + //transporters1[1]=transporters2[1]=0.; + // cerr << "Length of new wall is " << length << endl; + //cerr << "PIN1 is [ " << transporters1[1] << " | " << transporters2[1] << "] "; +} + + + + +int main(int argc,char **argv) { + + try { + + //if (argc<2 || strstr(argv[1],".par")==0) { + // throw "Usage: Leaf [parfile].par"; + //} + //par.Read(argv[1]); + + // parse command-line options + + int c; + //int digit_optind = 0; + + char *parfile=0; + char *leaffile=0; + /* ofstream args("args.txt"); + for (int i=0;i<argc;i++) { + args << argv[i] << endl; + }*/ + + while (1) { + + //int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = { + {"batch", 0, 0, 0}, + {"par", 1, 0, 0}, + {"leaffile", 2, 0, 0} + }; + + // short option 'p' creates trouble for non-commandline usage on MacOSX. Option -p changed to -P (capital) + static char *short_options = "bP:l"; + c = getopt_long (argc, argv, "bP:l:", + 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 'p': + parfile=strdup(optarg); + if (!parfile) { + throw("Out of memory"); + } + printf ("parameter file is '%s'\n", parfile); + + break; + + case 'l': + leaffile=strdup(optarg); + if (!leaffile) { + throw("Out of memory"); + } + printf("Reading leaf state file '%s'\n", leaffile); + 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"); + } + + +#ifdef X11GRAPHICS + X11Graphics g(par.sizex,par.sizey); + g.ChangeTitle("Virtual Leaf"); +#endif +#ifdef QTGRAPHICS + //QApplication app(argc, argv); + + //argc=1; + QCoreApplication *app; + + QGraphicsScene canvas(0,0,8000,6000); + + QPalette tooltippalette = QToolTip::palette(); + QColor transparentcolor = QColor(tooltippalette.brush(QPalette::Window).color()); + //transparentcolor.setAlphaF(0.9); + + tooltippalette.setBrush (QPalette::Window, QBrush (transparentcolor) ); + QToolTip::setPalette( tooltippalette ); + + if (batch) { + // Note: QCoreApplication allows for command line applications, independent of X-Server on Unix. + // Allows for running on cluster + app = new QCoreApplication(argc,argv); + main_window=new MainBase(canvas, mesh); + } else { + app = new QApplication(argc,argv); + main_window=new Main(canvas, mesh); + ((Main *)main_window)->resize( ((Main *)main_window)->sizeHint()); + } + + + if (!batch) { + if ( QApplication::desktop()->width() > ((Main *)main_window)->width() + 10 + && QApplication::desktop()->height() > ((Main *)main_window)->height() +30 ) + ((Main *)main_window)->show(); + else + ((Main *)main_window)->showMaximized(); + } + + canvas.setSceneRect(QRectF()); + if (!batch) { + QObject::connect( qApp, SIGNAL(lastWindowClosed()), qApp, SLOT(quit()) ); + } + // return app.exec(); +#endif + + if (parfile) + par.Read(parfile); + + //else + // par.Read("default.par"); + Seed(par.rseed); + +#ifdef XFIGGRAPHICS + Graphics g; +#endif + + + main_window->Init(leaffile); + + // if we have supplied a leaffile AND a parfile, reread the parameter file, + // because the leaffile also contains parameter information + if (leaffile && parfile) { + par.Read(parfile); + } + + Cell::SetMagnification(10); + Cell::setOffset(0,0); + + main_window->FitLeafToCanvas(); + +#ifdef QTGRAPHICS + + // check if leaf crosses boundary + /* Vector bbll,bbur; + mesh.BoundingBox(bbll,bbur); + + double scale_x = canvas.width()/(bbur.x-bbll.x); + double scale_y = canvas.height()/(bbur.y-bbll.y); + + Cell::Scale(scale_x<scale_y ? scale_x:scale_y); // smallest of scale_x and scale_y + + double offset_x = (canvas.width()/Cell::Magnification()-(bbur.x-bbll.x))/2.; + double offset_y = (canvas.height()/Cell::Magnification()-(bbur.y-bbll.y))/2.; + Cell::setOffset(offset_x, offset_y); + */ + /* Vector bbll,bbur; + mesh.BoundingBox(bbll,bbur); + cerr << "bbll = " << bbll << endl; + cerr << "bbur = " << bbur << endl; + double cw = (bbur.x - bbll.x); + double ch = (bbur.y - bbll.y); + double factor = canvas.width()/(2*cw); + cerr << "factor = " << factor << ", width = " << canvas.width() << endl; + cerr << "Size is " << cw << " x " << ch << endl; + canvas.resize((int)(2*cw*factor),(int)(2*ch*factor)); + Cell::SetMagnification(factor); + //main_window->scale(factor); + //Cell::Translate(-bbll.x+cw/2,-bbll.y+ch/2); + cerr << -bbll.x+cw/2 << ", " << -bbll.y+ch/2 << ", " << canvas.width() << ", " << canvas.height() << endl; + Cell::Translate((-bbll.x+cw/2),(-bbll.y+ch/2)); + */ + + + main_window->Plot(); + + +#endif + +#ifdef QTGRAPHICS + if (batch) { + //main_window->toggleMovieFrames(); + //main_window->toggleShowNodes(); + //for (int i=0;i<par.nit;i++) { + double t=0.; + do { + t = main_window->TimeStep(); + /* cerr << endl << "***********************************************" << endl; + cerr << "Time is " << t << endl; + cerr << "par.maxt = " << par.maxt << endl; + cerr << endl << "***********************************************" << endl;*/ + } while (t < par.maxt); + + } else + return app->exec(); +#else + //for (int i=0;i<par.nit;i++) { + do { + t= g.TimeStep(); + } while (t < par.maxt); +#endif + + } 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(); + } + } + +} + + +extern double auxin_account; +const int Cell::nchem = 4; + +class AuxinTransport : public TransportFunction { + + public: + virtual void operator()(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; + + // dchem_c2 is undefined..! + return; + } else { + if (w->AuxinSink()) { + + // efflux into Shoot Apical meristem + // we assume all PINs are directed towards shoot apical meristem + dchem_c1[0] -= par.sam_efflux * w->C1()->Chemical(0) / (par.ka + w->C1()->Chemical(0)); + + return; + } else { + + // Active fluxes (PIN1 and AUX1 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)) + + par.aux1transport * w->C2()->Chemical(2) * w->C1()->Chemical(0) / (par.kaux1 + 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)) + + par.aux1transport * w->C1()->Chemical(2) * w->C2()->Chemical(0) / (par.kaux1 + w->C2()->Chemical(0)) ); + + dchem_c1[0] += trans21 - trans12; + dchem_c2[0] += trans12 - trans21; + return; + } + + } + } + + + if (w->C1()->BoundaryPolP()) { + + if (w->AuxinSource()) { + double aux_flux = par.leaf_tip_source * w->Length(); + dchem_c2[0] += aux_flux; + // dchem_c1 is undefined...! + return; + } else { + + if (w->AuxinSink()) { + + + // efflux into Shoot Apical meristem + // we assume all PINs are directed towards shoot apical meristem + + // no passive fluxes: outside is impermeable + + // Active fluxes (PIN1 and AUX1 mediated transport) + + // (Transporters measured in moles, here) + // efflux from cell 1 to cell 2 + // assumption: no AUX1 in shoot apical meristem + double trans12 = ( par.transport * w->Transporters1(1) * w->C1()->Chemical(0) / (par.ka + w->C1()->Chemical(0))); + dchem_c1[0] += - trans12; + + return; + + //dchem_c2[0] -= par.sam_efflux * w->C2()->Chemical(0) / (par.ka + w->C2()->Chemical(0)); + + // return; + } else { + + } + } + } + + + // Passive fluxes (Fick's law) + // only auxin flux now + // flux depends on edge length and concentration difference + for (int c=0;c<Cell::NChem();c++) { + 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 and AUX1 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)) + + par.aux1transport * w->C2()->Chemical(2) * w->C1()->Chemical(0) / (par.kaux1 + 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)) + + par.aux1transport * w->C1()->Chemical(2) * w->C2()->Chemical(0) / (par.kaux1 + w->C2()->Chemical(0)) ); + + dchem_c1[0] += trans21 - trans12; + dchem_c2[0] += trans12 - trans21; + + } +}; + +class PIN1Localization : public WallReaction { + + public: + virtual void operator()(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.; + dw1[2] = 0.; dw2[2] = 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]=dw1[2]=dw2[2]; + return; + } + } + + if (w->C1()->BoundaryPolP()) { + if (w->AuxinSink()) { + + dw1[0] = 0.; dw2[0] = 0.; + dw1[2] = 0.; dw2[2] = 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]=dw1[2]=dw2[2]; + 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[2] = 0.; dw2[2] = 0.; + + dw1[1] = dPijdt1; + dw2[1] = dPijdt2; + + } +}; + + +inline double YlPilPli(Cell &here, Cell &nb, Wall &w) { + return nb.Chemical(2) * w.Transporters1(1) * w.Transporters2(1); +} + +inline double AlPil(Cell &here, Cell &nb, Wall &w) { + return nb.Chemical(0) * w.getTransporter( &here, 1 ); +} + +inline double AlplusYlLil(Cell &here, Cell &nb, Wall &w) { + return ( nb.Chemical(0) + nb.Chemical(2) ) * w.Length(); +} + +inline double AlLil(Cell &here, Cell &nb, Wall &w) { + return ( nb.Chemical(0) ) * w.Length(); +} + +inline double Lil(Cell &here, Cell &nb, Wall &w) { + return w.Length(); +} + +inline double complex_PijAj(Cell &here, Cell &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)); + +} + +class AuxinAndDifferentiation : public CellReaction { + + // Note: Pi and Pij measured in numbers of molecules, not concentrations + public: + virtual void operator()(Cell *c, double *dchem) { + + double dPidt = 0.; + + double sum_Pij = c->SumTransporters( 1 ); + + // exocytosis regulated: + // van3 expression reduces rate of PIN1 endocytosis + dPidt = -par.k1 * c->ReduceCellAndWalls<double>( complex_PijAj ) + + (c->Chemical(3) < 0.5 ? par.k2 : par.k2van3) * 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; + + /*if (c->AtBoundaryP()) { + dchem[2] = 0.01; + //cerr << "Making cell blue.\n"; + } else { + dchem[2] = -0.1 * c->Chemical(2); + }*/ + + // no PIN production in SAM + if (c->Boundary() == Cell::SAM) { + dchem[1]=0.; + dchem[0]= - par.sam_auxin_breakdown * c->Chemical(0); + dchem[2]=0.; + } else { + + dchem[1] = dPidt; + + + // source of auxin + dchem[0] = par.aux_cons; + + // auxin-induced AUX1 production, in the epidermis + dchem[2] = ( c->AtBoundaryP() ? par.aux1prod : par.aux1prodmeso ) * ( c->Chemical(0) / ( 1. + par.kap * c->Chemical(0) ) ) - par.aux1decay * c->Chemical(2) ;//: 0.; + + // auxin-induced production of VAN-3? Autokatalysis? + //dchem[3] = par.van3prod * (c->Chemical(0) / (1. + par.kvp * c-> Chemical(0) ) ) + double A = c->Chemical(0); + double van3 = c->Chemical(3); + dchem[3] = par.van3prod * A - par.van3autokat * van3 + van3*van3/(1 + par.van3sat * van3*van3 ); + } + } + +}; + + + + +class CellHouseKeeping { +public: + void operator() (Cell &c) const { + + if (c.Boundary()==Cell::None) { + c.CheckForDivision(); + + // expand if this is not a provascular cell + if (c.Chemical(3) < 0.7 ) { + c.EnlargeTargetArea(par.cell_expansion_rate); + } + } + } +}; + +void Cell::SetColor(QColor &color) { + + // Red: AUX1 + // Green: Auxin + // Blue: van-3 + color.setRgb(chem[2]/(1+chem[2]) * 255.,(chem[0]/(1+chem[0]) * 255.),(chem[3]/(1+chem[3]) *255.) ); + +} + +void Cell::CheckForDivision(void) { + // if (/* Chemical(0)<0.4 && */ /* differentiated cells do not divide */ area > 2*base_area /* || Length()>50 */) { + + //if (Chemical(0) > par.morphogen_div_threshold ) + if (area > par.rel_cell_div_threshold * base_area ) { + /* remark no longer valid? //m->IncreaseCellCapacityIfNecessary(); + // Note that calling Divide as follows prevents trouble if cell + // vector is relocated */ + Divide(); + } + +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/leaves/one_acc_point_van3_growth2.xml b/data/leaves/one_acc_point_van3_growth2.xml new file mode 100644 --- /dev/null +++ b/data/leaves/one_acc_point_van3_growth2.xml @@ -0,0 +1,11348 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #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" +#ifdef QTGRAPHICS +#include "canvas.h" +#include "cell.h" +#include <qwidget.h> +#include <q3process.h> +#include <qapplication.h> +#include <QDesktopWidget> +#include <QGraphicsScene> +#include <QMessageBox> +//Added by qt3to4: +#include <QMouseEvent> +#endif +#include <unistd.h> +#include <q3textstream.h> +#ifdef HAVE_QWT +#include "data_plot.h" +#endif +#include <QPalette> +#include <QBrush> +#include <QToolTip> +//#include "reactions.h" + +//#include "reactions_auxacc.h" +//#include "reactions_pinaux3.h" +//#include "reactions_aux1.h" +//#define REACTIONS_HEADER "reactions_growth.h" +//#include "reactions_tips_nor.h" +#define _xstr_(s) _str_(s) +#define _str_(s) #s +#include _xstr_(REACTIONS_HEADER) +extern Parameter par; + +MainBase *main_window = 0; +double auxin_account = 0.; + +#ifdef XFIGGRAPHICS +#define TIMESTEP double Graphics::TimeStep(void) +#endif + +//ofstream debug_stream("debug.log"); + +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 SetArea { + public: + void operator() (Cell &c) const { + c.SetTargetArea(4*c.CalcArea()); + c.SetTargetLength(4*c.Length()); + } + + };*/ + +#ifdef XFIGGRAPHICS +class XFigCell { +public: + void operator() (Cell &c,std::ostream &os) const { + c.XFigPrint(os); + } +}; +#endif + +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 ); + +} + +#ifdef QTGRAPHICS +class DrawCell { +public: + /*void operator() (Cell &c,Graphics &g) const { + c.Draw(g); + }*/ + 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)\n %6 of PIN1 at walls.\n Area is %7\n PIN sum is %8\n Circumference is %9\n Boundary type is %10").arg(c.Index()).arg(c.Chemical(0)).arg(c.Chemical(1)).arg(c.Chemical(2)).arg(c.Chemical(3)).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.ShowMeshP()) + // c.DrawNodes(&canvas); + if (m.ShowFluxesP()) + c.DrawFluxes(&canvas, par.arrowsize); + + //c.DrawTriangles(canvas); + } + //cerr << "Area of cell " << c.Index() << " is " << c.Area() << endl; + } + +}; +#endif + + +const char *xfig_header = "#FIG 3.2\nLandscape\nCenter\nInches\nLetter\n100.00\nSingle\n-2\n1200 2\n"; + +Mesh mesh; +bool batch=false; + + + +#ifdef QTGRAPHICS +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.LoopCells( bind2nd (mem_fun_ref( &Cell::DrawWalls), &canvas ) ); + mesh.LoopWalls( bind2nd( mem_fun_ref( &Wall::Draw ), &canvas ) ); + + if (ShowMeshP()) + mesh.DrawNodes(&canvas); + + if (ShowBoundaryOnlyP()) + mesh.DrawBoundary(&canvas); + + + // check to see if chemicals emitted by leaf edge diffuse evenly. Yes, they do. + + /* + Vector mesh_centroid = mesh.Centroid(); + Vector first_moment = mesh.FirstConcMoment(0); + + QCanvasEllipse *disk1 = 0; + if (disk1==0) { + disk1 = new QCanvasEllipse ( 50, 50, &canvas ); + disk1->setBrush( QColor("red") ); + disk1->setZ(5); + disk1->show(); + } + + QCanvasEllipse *disk2 = 0; + if (disk2==0) { + disk2=new QCanvasEllipse ( 50, 50, &canvas ); + disk2->setBrush( QColor("blue") ); + disk2->setZ(7); + disk2->show(); + + } + + Vector offset = mesh.Offset(); + double factor = mesh.Factor(); + + disk1 -> move((offset.x+mesh_centroid.x)*factor,(offset.y+mesh_centroid.y)*factor); + disk2 -> move((offset.x+first_moment.x)*factor,(offset.y+first_moment.y)*factor); + + */ + + + 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 << mesh.getTime() << ".xml"; + 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()); + } + + } +} +#endif + +//double areaunit=0; + +void Cell::Flux(double *flux, double *D) { + + // Algorithm according to Rudge & Haseloff 2005 + // (will we need to take cell area into account?) + // For the time being, we don't: assume cell area is + // mainly determined by vacuole. + + // loop over cell edges + + for (int c=0;c<Cell::nchem;c++) flux[c]=0.; + + for (list<Wall *>::iterator i=walls.begin(); + i!=walls.end(); + i++) { + + + // leaf cannot take up chemicals from environment ("no flux boundary") + if ((*i)->c2->BoundaryPolP()) continue; + + // calculate edge length + // (will later be updated during node displacement for efficiency) + //double edge_length = (m->nodes[(*i)->n1]-m->nodes[(*i)->n2]).Norm(); + + // flux depends on edge length and concentration difference + for (int c=0;c<Cell::nchem;c++) { + double phi = (*i)->length * ( D[c] ) * ( (*i)->c2->chem[c] - chem[c] ); + + if ((*i)->c1!=this) { + cerr << "Warning, bad cells boundary: " << (*i)->c1->index << ", " << index << endl; + } + + flux[c] += phi; + } + } + +} + +INIT { + + //Cell &circle=mesh.CircularCell(0,0,10,10); + 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 { + + //Cell &circle=mesh.LeafPrimordium(10,50); + Cell &circle=mesh.CircularCell(0,0,10,10); + //circle.SetChemical(1,0); + + // petiole is auxin sink + //mesh.getCell(1).SetSource(0,0); + + // petiole becomes provascular cell + //mesh.getCell(1).SetChemical(1,0.6); + + circle.SetTargetArea(circle.CalcArea()); + //circle.SetChemical(2,1e-60); + mesh.SetBaseArea(); + + circle.SetChemical(1, par.Pi_tot ); + circle.SetChemical(0, 0.); + //mesh.LoopCells(EdgeSource()); + } +} + +TIMESTEP { + + static int i=0; + static int t=0; + static int ncells; + + if (!batch) { + UserMessage(QString("Time: %1").arg(mesh.getTimeHours().c_str()),0); + } + + + + //if (!(i%1000)) + //Save(QString("leaf%1.eps").arg(i).ascii(), "PDF"); + + /* static ofstream *kinematic=0; + if (kinematic == 0) { + stringstream kinematic_fname; + kinematic_fname << par.datadir << "/kinematic.dat"; + cerr << "Writing kinematic data to " << kinematic_fname.str() << endl; + + kinematic = new ofstream(kinematic_fname.str().c_str()); + } + + *kinematic << t << " " << i << " " << mesh.Area() << " " << mesh.NCells() << endl; + */ + + ncells=mesh.NCells(); + + + double dh; + //const double en_threshold = 1; + + //mesh.RandomlyLoopCells( mem_fun_ref(&Cell::Displace )); + + //static bool dumpflag=false; + + if(DynamicCellsP()) { + dh = mesh.DisplaceNodes(); + + + //static ofstream enfile("energy.dat"); + //enfile << i << " " << dh << "\n"; + + // 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.LoopCurrentCells(CellHouseKeeping()); // this includes cell division + + // Reaction diffusion + TransportFunction *transport_f = new AuxinTransport(); + CellReaction *cellreaction_f = new AuxinAndDifferentiation(); + WallReaction *wall_f = new PIN1Localization(); + + mesh.ReactDiffuse_New(transport_f, cellreaction_f, wall_f, par.rd_dt); + + + t++; + + Plot(); + } + } else { + + TransportFunction *transport_f = new AuxinTransport(); + CellReaction *cellreaction_f = new AuxinAndDifferentiation(); + WallReaction *wall_f = new PIN1Localization(); + + mesh.ReactDiffuse_New(transport_f, cellreaction_f, wall_f, par.rd_dt); + + Plot(); + + } + + + i++; + return mesh.getTime(); + +} + +void Main::Divide(void) { + + static Vector axis(1,0,0); + mesh.IncreaseCellCapacityIfNecessary(); + + mesh.LoopCurrentCells( bind2nd( mem_fun_ref(&Cell::DivideOverAxis),axis) ); + axis=axis.Perp2D(); + //mesh.LoopCells( mem_fun_ref(&Cell::Divide) ); + Plot(); + +} + + +/* Called if a cell is clicked */ +void Cell::OnClick(QMouseEvent *e) { + + cerr << "Calling OnClick()\n"; + /* cerr << *m->boundary_polygon; + //m->RepairBoundaryPolygon(); + //SetChemical(0,par.cellsource); + cerr << "Mesh's centroid: " << m->Centroid() << endl; + cerr << "First moment of chem[0]: " << m->FirstConcMoment(0) << endl; + + */ + /* + cerr << "Cell: " << Index() << ", has walls: "; + + for (list<Wall *>::const_iterator w=walls.begin(); + w!=walls.end(); + w++) { + cerr << **w << " "; + } + + cerr << endl; + */ + /* cerr << "Cell: " << Index() << ", has neighbors: "; + + for (list<Cell *>::const_iterator c=neighbors.begin(); + c!=neighbors.end(); + c++) { + cerr << (*c)->Index() << " "; + } + + cerr << endl;*/ + + /* + mesh.PrintWallList(); + */ + + double circ=0.; + for (list<Wall *>::const_iterator w=walls.begin(); + w!=walls.end(); + w++) { + cerr << (*w)->N1()->Index() << "->" << (*w)->N2()->Index() << " = " << (*w)->Length() << endl; + circ += (*w)->Length(); + } + cerr << "Circ is " << circ << endl; + + + if (e->button() == Qt::MidButton) { + double sum_pin_bef = getMesh().CalcProtCellsWalls(1); + + QString message(QString("Dividing Cell %1").arg(index)); + ((Main *)main_window)->UserMessage(message); + cerr << message.toStdString(); + Divide(); + ((Main *)main_window)->Plot(); + + double sum_pin_aft = getMesh().CalcProtCellsWalls(1); + + cerr << "Sum PIN1, before: " << sum_pin_bef << ", after: " << sum_pin_aft << endl; + return; + } + if (e->button() == Qt::LeftButton) { + //double sum_walls=0.; + cerr << "Wall lengths: "; + for (list<Wall *>::const_iterator w=walls.begin(); + w!=walls.end(); + w++) { + //cerr << (*w)->getTransporter(this, 1) << " "; + cerr << (*w)->Length() << " "; + //sum_walls+=(*w)->getTransporter(this, 1); //* (*w)->Length(); + } + + cerr << ", Chemical(1) = " << Chemical(1) << ", sum = " << SumTransporters(1) + Chemical(1) << endl; + + QString message; + message=QString("Cell %1 has chemicals ( %2, %3, %4, %5), and it has %6 of PIN1 at its walls. Area is %7").arg(Index()).arg(chem[0]).arg(chem[1]).arg(chem[2]).arg(chem[3]).arg(SumTransporters(1)).arg(Area()); + + ((Main *)main_window)->UserMessage(message); + + /* cerr << "Cell " << index << " has chemicals ( " << chem[0] << ", " << chem[1] << " ) " << endl; + + cerr << "Cell " << index << "'s amount of PIN1 at the walls: " << SumTransporters(1) << endl;*/ +#ifdef QTGRAPHICS + /* double sum_PIN1 = Chemical(4); + + for (list<Wall *>::const_iterator i = walls.begin(); + i!=walls.end(); + i++) { + sum_PIN1 += (*i)->getTransporter(this, 0); + } + */ + + //main_window->UserMessage(QString("Concentration chemical 0 of cell %1 = %2").arg(Index()).arg(chem[0])); + //main_window->UserMessage(QString("Target area of cell %1 = %2").arg(Index()).arg(TargetArea())); + //main_window->UserMessage(QString("Sum PIN1 of cell %1 = %2 (intracellular %3)").arg(Index()).arg(sum_PIN1).arg(Chemical(4))); + + /* QString message; + message=QString("Cell %1's nodes are:").arg(Index()); + + for (list<int>::iterator it=neighbors.begin(); + it!=neighbors.end(); + it++) { + message += QString(" %2").arg(*it); + } + + main_window->UserMessage(message);*/ + + //SetWallLengths(); + //main_window->UserMessage(QString("Cell %1 apoptoses.").arg(Index())); + //Apoptose(); + //m->CleanUpCellNodeLists(); +#ifdef HAVE_QWT + QStringList curvenames; + curvenames += QString("Auxin"); + curvenames += QString("PIN1"); + curvenames += QString("AUX1"); + curvenames += QString("Wall PIN1"); + PlotDialog *plot = new PlotDialog((Main *)main_window, QString("Monitor for Cell %1").arg(Index()), curvenames); + QObject::connect(this, SIGNAL(ChemMonValue(double, double *)), + plot, SLOT(AddValue(double,double *))); +#endif + } else { + //Divide(); + } +#endif +} + + + // Set sum of PIN1 back to Pi_tot. Hence we assume the cell maintain a constant level of Pi. + +void Cell::OnDivide(ParentInfo &parent_info, Cell &daughter) { + + //cerr << "Calling Cell::OnDivide()" << endl; + + // PIN1 distributes between parent and daughter according to area + double area = Area(), daughter_area = daughter.Area(); + double tot_area = area + daughter_area; + + //chem[1]*=(area/tot_area); + //daughter.chem[1]*=(daughter_area/tot_area); + + // For lack of detailed data, or a better rule, we assume that cells remain polarized + // after division + + // So the PIN1 is redistributed according to the original polarization over the walls + + // parent_info contains info about the parent + // redistribute the PIN in the endosome according to area + + chem[1] = parent_info.PINendosome*(area/tot_area); + daughter.chem[1] = parent_info.PINendosome*(daughter_area/tot_area); + + // Now redistribute the membrane PINs according to the original polarization in the parent + // mmm... I'd like to have a better, biologically motivated rule for this, + // but for lack of something better... I hope I'm excused :-). Let's say the overall + // organization of the actin fibres is not completely destroyed after division... + + // distribute wallPINs according to the circumference of the parent and daughter + double circ = Circumference( ); + double daughter_circ = daughter.Circumference(); + double tot_circ = circ + daughter_circ; + + double wallPINs = (circ / tot_circ) * parent_info.PINmembrane; + double daughter_wallPINs = (daughter_circ / tot_circ) * parent_info.PINmembrane; + + //cerr << "wallPINs = " << wallPINs << ", daughter_wallPINs = " << daughter_wallPINs << "sum = " << wallPINs + daughter_wallPINs << ", PINmembrane = " << parent_info.PINmembrane << endl; + // distrubute it according to the overall polarity + Vector polarization = parent_info.polarization.Normalised().Perp2D(); + + double sum=0.; + for (list<Wall *>::const_iterator w=walls.begin(); + w!=walls.end(); + w++) { + + // distribute according to angle (0 degrees: maximum, 180 degrees minimum) + double tmp=InnerProduct((*w)->getWallVector(this),polarization); // move domain from [-1,1] to [0,1] + + //cerr << "[" << tmp << "]"; + sum+=tmp; + //(*w)->setTransporter(this, 1, + } + + //cerr << "Sum is " << sum << endl; + //double sum_wall_Pi = SumTransporters(1); + + // After division, cells produce PIN1 (in intracellular storage) until total amount becomes Pi_tot + //SetChemical(1, par.Pi_tot - sum_wall_Pi ); + //SetNewChem(1, Chemical(1)); + + //cerr << "[ " << sum_wall_Pi + Chemical(1) << "]"; +} + +void Wall::OnWallInsert(void) { + + // NOTE: THIS FUNCTION IS CALLED AFTER Cell::OnDivide(); + // After division, we put some PIN1 on the walls, to prevent quick + // redistribution of PIN1s and consequent auxin accumulation after + // division. + + // make sure dPij/dt = 0 at both sides of the new wall. + + // True for: + // Pij = k1/k2 * A_j * L_ij * P_i + // Pji = k1/k2 * A_i * L_ij * P_j + + //transporters1[1] = (par.k1/par.k2) * c2->Chemical(0) * length * c1->Chemical(1); + //transporters2[1] = (par.k1/par.k2) * c1->Chemical(0) * length * c2->Chemical(1); + + //transporters1[1]=transporters2[1]=0.; + // cerr << "Length of new wall is " << length << endl; + //cerr << "PIN1 is [ " << transporters1[1] << " | " << transporters2[1] << "] "; +} + + + + +int main(int argc,char **argv) { + + try { + + //if (argc<2 || strstr(argv[1],".par")==0) { + // throw "Usage: Leaf [parfile].par"; + //} + //par.Read(argv[1]); + + // parse command-line options + + int c; + //int digit_optind = 0; + + char *parfile=0; + char *leaffile=0; + /* ofstream args("args.txt"); + for (int i=0;i<argc;i++) { + args << argv[i] << endl; + }*/ + + while (1) { + + //int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = { + {"batch", 0, 0, 0}, + {"par", 1, 0, 0}, + {"leaffile", 2, 0, 0} + }; + + // short option 'p' creates trouble for non-commandline usage on MacOSX. Option -p changed to -P (capital) + static char *short_options = "bP:l"; + c = getopt_long (argc, argv, "bP:l:", + 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 'p': + parfile=strdup(optarg); + if (!parfile) { + throw("Out of memory"); + } + printf ("parameter file is '%s'\n", parfile); + + break; + + case 'l': + leaffile=strdup(optarg); + if (!leaffile) { + throw("Out of memory"); + } + printf("Reading leaf state file '%s'\n", leaffile); + 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"); + } + + +#ifdef X11GRAPHICS + X11Graphics g(par.sizex,par.sizey); + g.ChangeTitle("Virtual Leaf"); +#endif +#ifdef QTGRAPHICS + //QApplication app(argc, argv); + + //argc=1; + QCoreApplication *app; + + QGraphicsScene canvas(0,0,8000,6000); + + QPalette tooltippalette = QToolTip::palette(); + QColor transparentcolor = QColor(tooltippalette.brush(QPalette::Window).color()); + //transparentcolor.setAlphaF(0.9); + + tooltippalette.setBrush (QPalette::Window, QBrush (transparentcolor) ); + QToolTip::setPalette( tooltippalette ); + + if (batch) { + // Note: QCoreApplication allows for command line applications, independent of X-Server on Unix. + // Allows for running on cluster + app = new QCoreApplication(argc,argv); + main_window=new MainBase(canvas, mesh); + } else { + app = new QApplication(argc,argv); + main_window=new Main(canvas, mesh); + ((Main *)main_window)->resize( ((Main *)main_window)->sizeHint()); + } + + + if (!batch) { + if ( QApplication::desktop()->width() > ((Main *)main_window)->width() + 10 + && QApplication::desktop()->height() > ((Main *)main_window)->height() +30 ) + ((Main *)main_window)->show(); + else + ((Main *)main_window)->showMaximized(); + } + + canvas.setSceneRect(QRectF()); + if (!batch) { + QObject::connect( qApp, SIGNAL(lastWindowClosed()), qApp, SLOT(quit()) ); + } + // return app.exec(); +#endif + + if (parfile) + par.Read(parfile); + + //else + // par.Read("default.par"); + Seed(par.rseed); + +#ifdef XFIGGRAPHICS + Graphics g; +#endif + + + main_window->Init(leaffile); + + // if we have supplied a leaffile AND a parfile, reread the parameter file, + // because the leaffile also contains parameter information + if (leaffile && parfile) { + par.Read(parfile); + } + + Cell::SetMagnification(10); + Cell::setOffset(0,0); + + main_window->FitLeafToCanvas(); + +#ifdef QTGRAPHICS + + // check if leaf crosses boundary + /* Vector bbll,bbur; + mesh.BoundingBox(bbll,bbur); + + double scale_x = canvas.width()/(bbur.x-bbll.x); + double scale_y = canvas.height()/(bbur.y-bbll.y); + + Cell::Scale(scale_x<scale_y ? scale_x:scale_y); // smallest of scale_x and scale_y + + double offset_x = (canvas.width()/Cell::Magnification()-(bbur.x-bbll.x))/2.; + double offset_y = (canvas.height()/Cell::Magnification()-(bbur.y-bbll.y))/2.; + Cell::setOffset(offset_x, offset_y); + */ + /* Vector bbll,bbur; + mesh.BoundingBox(bbll,bbur); + cerr << "bbll = " << bbll << endl; + cerr << "bbur = " << bbur << endl; + double cw = (bbur.x - bbll.x); + double ch = (bbur.y - bbll.y); + double factor = canvas.width()/(2*cw); + cerr << "factor = " << factor << ", width = " << canvas.width() << endl; + cerr << "Size is " << cw << " x " << ch << endl; + canvas.resize((int)(2*cw*factor),(int)(2*ch*factor)); + Cell::SetMagnification(factor); + //main_window->scale(factor); + //Cell::Translate(-bbll.x+cw/2,-bbll.y+ch/2); + cerr << -bbll.x+cw/2 << ", " << -bbll.y+ch/2 << ", " << canvas.width() << ", " << canvas.height() << endl; + Cell::Translate((-bbll.x+cw/2),(-bbll.y+ch/2)); + */ + + + main_window->Plot(); + + +#endif + +#ifdef QTGRAPHICS + if (batch) { + //main_window->toggleMovieFrames(); + //main_window->toggleShowNodes(); + //for (int i=0;i<par.nit;i++) { + double t=0.; + do { + t = main_window->TimeStep(); + /* cerr << endl << "***********************************************" << endl; + cerr << "Time is " << t << endl; + cerr << "par.maxt = " << par.maxt << endl; + cerr << endl << "***********************************************" << endl;*/ + } while (t < par.maxt); + + } else + return app->exec(); +#else + //for (int i=0;i<par.nit;i++) { + do { + t= g.TimeStep(); + } while (t < par.maxt); +#endif + + } 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(); + } + } + +} + + +extern double auxin_account; +const int Cell::nchem = 4; + +class AuxinTransport : public TransportFunction { + + public: + virtual void operator()(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; + + // dchem_c2 is undefined..! + return; + } else { + if (w->AuxinSink()) { + + // efflux into Shoot Apical meristem + // we assume all PINs are directed towards shoot apical meristem + dchem_c1[0] -= par.sam_efflux * w->C1()->Chemical(0) / (par.ka + w->C1()->Chemical(0)); + + return; + } else { + + // Active fluxes (PIN1 and AUX1 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)) + + par.aux1transport * w->C2()->Chemical(2) * w->C1()->Chemical(0) / (par.kaux1 + 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)) + + par.aux1transport * w->C1()->Chemical(2) * w->C2()->Chemical(0) / (par.kaux1 + w->C2()->Chemical(0)) ); + + dchem_c1[0] += trans21 - trans12; + dchem_c2[0] += trans12 - trans21; + return; + } + + } + } + + + if (w->C1()->BoundaryPolP()) { + + if (w->AuxinSource()) { + double aux_flux = par.leaf_tip_source * w->Length(); + dchem_c2[0] += aux_flux; + // dchem_c1 is undefined...! + return; + } else { + + if (w->AuxinSink()) { + + + // efflux into Shoot Apical meristem + // we assume all PINs are directed towards shoot apical meristem + + // no passive fluxes: outside is impermeable + + // Active fluxes (PIN1 and AUX1 mediated transport) + + // (Transporters measured in moles, here) + // efflux from cell 1 to cell 2 + // assumption: no AUX1 in shoot apical meristem + double trans12 = ( par.transport * w->Transporters1(1) * w->C1()->Chemical(0) / (par.ka + w->C1()->Chemical(0))); + dchem_c1[0] += - trans12; + + return; + + //dchem_c2[0] -= par.sam_efflux * w->C2()->Chemical(0) / (par.ka + w->C2()->Chemical(0)); + + // return; + } else { + + } + } + } + + + // Passive fluxes (Fick's law) + // only auxin flux now + // flux depends on edge length and concentration difference + for (int c=0;c<Cell::NChem();c++) { + 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 and AUX1 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)) + + par.aux1transport * w->C2()->Chemical(2) * w->C1()->Chemical(0) / (par.kaux1 + 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)) + + par.aux1transport * w->C1()->Chemical(2) * w->C2()->Chemical(0) / (par.kaux1 + w->C2()->Chemical(0)) ); + + dchem_c1[0] += trans21 - trans12; + dchem_c2[0] += trans12 - trans21; + + } +}; + +class PIN1Localization : public WallReaction { + + public: + virtual void operator()(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.; + dw1[2] = 0.; dw2[2] = 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]=dw1[2]=dw2[2]; + return; + } + } + + if (w->C1()->BoundaryPolP()) { + if (w->AuxinSink()) { + + dw1[0] = 0.; dw2[0] = 0.; + dw1[2] = 0.; dw2[2] = 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]=dw1[2]=dw2[2]; + 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[2] = 0.; dw2[2] = 0.; + + dw1[1] = dPijdt1; + dw2[1] = dPijdt2; + + } +}; + + +inline double YlPilPli(Cell &here, Cell &nb, Wall &w) { + return nb.Chemical(2) * w.Transporters1(1) * w.Transporters2(1); +} + +inline double AlPil(Cell &here, Cell &nb, Wall &w) { + return nb.Chemical(0) * w.getTransporter( &here, 1 ); +} + +inline double AlplusYlLil(Cell &here, Cell &nb, Wall &w) { + return ( nb.Chemical(0) + nb.Chemical(2) ) * w.Length(); +} + +inline double AlLil(Cell &here, Cell &nb, Wall &w) { + return ( nb.Chemical(0) ) * w.Length(); +} + +inline double Lil(Cell &here, Cell &nb, Wall &w) { + return w.Length(); +} + +inline double complex_PijAj(Cell &here, Cell &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)); + +} + +class AuxinAndDifferentiation : public CellReaction { + + // Note: Pi and Pij measured in numbers of molecules, not concentrations + public: + virtual void operator()(Cell *c, double *dchem) { + + double dPidt = 0.; + + double sum_Pij = c->SumTransporters( 1 ); + + // exocytosis regulated: + // van3 expression reduces rate of PIN1 endocytosis + dPidt = -par.k1 * c->ReduceCellAndWalls<double>( complex_PijAj ) + + (c->Chemical(3) < 0.5 ? par.k2 : par.k2van3) * 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; + + /*if (c->AtBoundaryP()) { + dchem[2] = 0.01; + //cerr << "Making cell blue.\n"; + } else { + dchem[2] = -0.1 * c->Chemical(2); + }*/ + + // no PIN production in SAM + if (c->Boundary() == Cell::SAM) { + dchem[1]=0.; + dchem[0]= - par.sam_auxin_breakdown * c->Chemical(0); + dchem[2]=0.; + } else { + + dchem[1] = dPidt; + + + // source of auxin + dchem[0] = par.aux_cons; + + // auxin-induced AUX1 production, in the epidermis + dchem[2] = ( c->AtBoundaryP() ? par.aux1prod : par.aux1prodmeso ) * ( c->Chemical(0) / ( 1. + par.kap * c->Chemical(0) ) ) - par.aux1decay * c->Chemical(2) ;//: 0.; + + // auxin-induced production of VAN-3? Autokatalysis? + //dchem[3] = par.van3prod * (c->Chemical(0) / (1. + par.kvp * c-> Chemical(0) ) ) + double A = c->Chemical(0); + double van3 = c->Chemical(3); + dchem[3] = par.van3prod * A - par.van3autokat * van3 + van3*van3/(1 + par.van3sat * van3*van3 ); + } + } + +}; + + + + +class CellHouseKeeping { +public: + void operator() (Cell &c) const { + + if (c.Boundary()==Cell::None) { + c.CheckForDivision(); + + // expand if this is not a provascular cell + if (c.Chemical(3) < 0.7 ) { + c.EnlargeTargetArea(par.cell_expansion_rate); + } + } + } +}; + +void Cell::SetColor(QColor &color) { + + // Red: AUX1 + // Green: Auxin + // Blue: van-3 + color.setRgb(chem[2]/(1+chem[2]) * 255.,(chem[0]/(1+chem[0]) * 255.),(chem[3]/(1+chem[3]) *255.) ); + +} + +void Cell::CheckForDivision(void) { + // if (/* Chemical(0)<0.4 && */ /* differentiated cells do not divide */ area > 2*base_area /* || Length()>50 */) { + + //if (Chemical(0) > par.morphogen_div_threshold ) + if (area > par.rel_cell_div_threshold * base_area ) { + /* remark no longer valid? //m->IncreaseCellCapacityIfNecessary(); + // Note that calling Divide as follows prevents trouble if cell + // vector is relocated */ + Divide(); + } + +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/leaves/schemas.xml b/data/leaves/schemas.xml new file mode 100644 --- /dev/null +++ b/data/leaves/schemas.xml @@ -0,0 +1,30 @@ + + + + + + + + diff --git a/data/leaves/tissue.xml b/data/leaves/tissue.xml new file mode 100644 --- /dev/null +++ b/data/leaves/tissue.xml @@ -0,0 +1,20231 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #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 "testplugin.h" + +/* #define _xstr_(s) _str_(s) +#define _str_(s) #s +#include _xstr_(REACTIONS_HEADER) +*/ +extern Parameter par; + +MainBase *main_window = 0; +double auxin_account = 0.; + + + +TestPlugin *plugin = new TestPlugin(); + +#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 << ".pdf"; + if (par.storage_stride <= par.xml_storage_stride) { + frame++; + } + + // Write high-res JPG snapshot every plot step + Save(fname.str().c_str(), "PDF"); + */ + + 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()); + } + + } +} + + +void Cell::Flux(double *flux, double *D) { + + + // loop over cell edges + + for (int c=0;c<Cell::nchem;c++) flux[c]=0.; + + for (list<Wall *>::iterator i=walls.begin(); + i!=walls.end(); + i++) { + + + // leaf cannot take up chemicals from environment ("no flux boundary") + if ((*i)->c2->BoundaryPolP()) continue; + + + // flux depends on edge length and concentration difference + for (int c=0;c<Cell::nchem;c++) { + double phi = (*i)->length * ( D[c] ) * ( (*i)->c2->chem[c] - chem[c] ); + + if ((*i)->c1!=this) { + cerr << "Warning, bad cells boundary: " << (*i)->c1->index << ", " << index << endl; + } + + flux[c] += phi; + } + } + +} + +INIT { + + 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 { + + Cell &circle=mesh.CircularCell(0,0,10,10); + + circle.SetTargetArea(circle.CalcArea()); + mesh.SetBaseArea(); + // clean up chemicals + for (int c=0; c<Cell::NChem(); c++) { + circle.SetChemical(c, 0.); + } + } +} + +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.LoopCurrentCells(&TestPlugin::CellHouseKeeping); // this includes cell division + + // Reaction diffusion + /*CelltoCellTransport *transport_f = &TestPlugin::CelltoCellTransport; + CellReaction *cellreaction_f = new plugin->CellDynamics(); + WallReaction *wall_f = new WallDynamics();*/ + + mesh.ReactDiffuse(plugin, par.rd_dt); + + + t++; + + Plot(par.resize_stride); + + /*QVector< QPair<double, int> > angles=mesh.VertexAnglesValues(); + QString afname=QString("Angles/anglesvalues%1.dat").arg(t,6,10,QChar('0')); + ofstream af(afname.toStdString().c_str()); + */ + + /*for (QVector< QPair<qreal, int> >::const_iterator v=angles.begin(); + v!=angles.end(); + v++) { + af << v->first << " " << v->second << endl; + } + */ + } + + } else { + + /* TransportFunction *transport_f = new CelltoCellTransport(); + CellReaction *cellreaction_f = new CellDynamics(); + WallReaction *wall_f = new WallDynamics(); + + mesh.ReactDiffuse_New(transport_f, cellreaction_f, wall_f, par.rd_dt);*/ + mesh.ReactDiffuse(plugin, par.rd_dt); + + Plot(par.resize_stride); + + } + + + + + + i++; + return mesh.getTime(); + +} + + + +/* Called if a cell is clicked */ +void Cell::OnClick(QMouseEvent *e) { + +} + + + +void Wall::OnWallInsert(void) { + + +} + + + + +int main(int argc,char **argv) { + + try { + + + int c; + + + char *leaffile=0; + + + while (1) { + + //int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = { + {"batch", 0, 0, 0}, + {"leaffile", 2, 0, 0} + }; + + // short option 'p' creates trouble for non-commandline usage on MacOSX. Option -p changed to -P (capital) + static char *short_options = "bl"; + c = getopt_long (argc, argv, "bl:", + 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 '?': + 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"); + } + + MakeDir("Angles"); + bool useGUI = !batch; + 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(); + } + } else { + main_window=new MainBase(canvas, mesh); + + } + + + + canvas.setSceneRect(QRectF()); + if (!batch) { + QObject::connect( qApp, SIGNAL(lastWindowClosed()), qApp, SLOT(quit()) ); + } + + + + main_window->Init(leaffile); + + Cell::SetMagnification(10); + 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(); + } + } + +} + + + +// Executed after the cellular mechanics steps have equillibrized +class CellHouseKeeping { +public: + void operator() (Cell &c) const { + + c.EnlargeTargetArea(par.cell_expansion_rate); + + if (c.Area() > par.rel_cell_div_threshold * c.BaseArea() ) { + c.Divide(); + } + } +}; + +// The number of chemical species in the cels +const int Cell::nchem = 0; + +// Differential equations describing transport of chemicals from cell to cell +class CelltoCellTransport : public TransportFunction { + + public: + virtual void operator()(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) +class WallDynamics : public WallReaction { + public: + virtual void operator()(Wall *w, double *dw1, double *dw2) {}; + +}; + + +// Differential equations describing chemical reactions inside the cells +class CellDynamics : public CellReaction { + public: + virtual void operator()(Cell *c, double *dchem) { + + }; + +}; + +// Rules for cell coloring +void Cell::SetColor(QColor &color) { } + +// To be executed after cell division +void Cell::OnDivide(ParentInfo &parent_info, Cell &daughter) {} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/leaves/vleaf.rnc b/data/leaves/vleaf.rnc new file mode 100644 --- /dev/null +++ b/data/leaves/vleaf.rnc @@ -0,0 +1,159 @@ +# $Rev$: Revision of last commit +# $Author$: Author of last commit +# $Source$: Source file +# $Date$: Date of last commit +# +# 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 . +# +# Copyright 2010 Michael Guravage. +# + +default namespace = "vl" +namespace a = "http://relaxng.org/ns/compatibility/annotations/1.0" +namespace rng = "http://relaxng.org/ns/structure/1.0" + +a:documentation [ " Virtual Leaf " ] +# A precooked comment element +comment = element comment { xsd:string } +# An element with a integer value named 'v.' +iValueType = attribute v { xsd:integer }? +# An element with a real value named 'v.' +rValueType = attribute v { xsd:float }? +# Leaf is the root element +start = leaf +leaf = + element leaf { + element comment { xsd:string }?, + element parameter { ParameterType }?, + element code { CodeType }*, + element nodes { NodesType }?, + element cells { CellsType }?, + element walls { WallsType }?, + element nodesets { NodesetsType }?, + element settings { SettingsType }?, + leafAttrubutes + } +# List of leaf attributes +leafAttrubutes = + attribute uri { xsd:anyURI }?, + attribute date { xsd:dateTime }?, + attribute simtime { xsd:integer }? +# Parameters +ParameterType = + element comment { xsd:string }?, + element par { + element valarray { + element val { rValueType }+ + }?, + attribute name { xsd:string }, + attribute val { xsd:string }? + }* +# Code +CodeType = + text, + attribute name { xsd:string } +# Nodes +NodesType = + element comment { xsd:string }?, + element node { nodeAttributes }*, + nodesAttributes +# A list of node attributes +nodeAttributes = + attribute x { xsd:float }, + attribute y { xsd:float }, + attribute fixed { xsd:boolean }?, + attribute boundary { xsd:boolean }?, + attribute sam { xsd:boolean }? +# A list of nodes attributes +nodesAttributes = + attribute n { xsd:integer }?, + attribute target_length { xsd:float }? +# Cells +CellsType = + element comment { xsd:string }?, + element cell { CellType }+, + element boundary_polygon { CellType }, + cellsAttributes +# A list of cells attributes and their types +cellsAttributes = + attribute n { xsd:integer }, + attribute offsetx { xsd:float }?, + attribute offsety { xsd:float }?, + attribute magnification { xsd:float }?, + attribute base_area { xsd:float }, + attribute nchem { xsd:integer } +# Cell +CellType = + element node { CellNodeType }*, + element wall { CellWallType }*, + element chem { CellChemType }?, + cellAttributes +# A list of cell attributes and their types. +cellAttributes = + attribute index { xsd:integer }, + attribute area { xsd:float }?, + attribute target_area { xsd:float }, + attribute target_length { xsd:float }?, + attribute lambda_celllength { xsd:float }?, + attribute stiffness { xsd:float }?, + attribute fixed { xsd:boolean }?, + attribute pin_fixed { xsd:boolean }?, + attribute at_boundary { xsd:boolean }?, + attribute dead { xsd:boolean }?, + attribute source { xsd:boolean }?, + attribute boundary { xsd:boolean }?, + attribute div_counter { xsd:integer }?, + attribute cell_type { xsd:integer }? +# Cell Node +CellNodeType = attribute n { xsd:integer } +# Cell Wall +CellWallType = attribute w { xsd:integer } +# Cell Chemical +CellChemType = + element val { rValueType }*, + attribute n { xsd:integer } +# Walls +WallsType = + element comment { xsd:string }?, + element wall { + element transporter1 { + element val { rValueType }* + }?, + element transporter2 { + element val { rValueType }* + }?, + element apoplast { + element val { rValueType }* + }?, + attribute index { xsd:integer }, + attribute c1 { xsd:integer }, + attribute c2 { xsd:integer }, + attribute n1 { xsd:integer }, + attribute n2 { xsd:integer }, + attribute length { xsd:float }?, + attribute viz_flux { xsd:float }?, + attribute wall_type { "normal" | "aux_source" }? + }*, + attribute n { xsd:integer } +# Nodesets +NodesetsType = attribute n { xsd:integer } +# Settings +SettingsType = + element comment { xsd:string }?, + element setting { + attribute name { xsd:string }, + attribute val { xsd:string } + }* diff --git a/data/leaves/vleaf.rng b/data/leaves/vleaf.rng new file mode 100644 --- /dev/null +++ b/data/leaves/vleaf.rng @@ -0,0 +1,485 @@ + + + + + + + Virtual Leaf + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + normal + aux_source + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/leaves/vleaf.xml b/data/leaves/vleaf.xml new file mode 100644 --- /dev/null +++ b/data/leaves/vleaf.xml @@ -0,0 +1,1254 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #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 "testplugin.h" + +/* #define _xstr_(s) _str_(s) +#define _str_(s) #s +#include _xstr_(REACTIONS_HEADER) +*/ +extern Parameter par; + +MainBase *main_window = 0; +double auxin_account = 0.; + + + +TestPlugin *plugin = new TestPlugin(); + +#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 << ".pdf"; + if (par.storage_stride <= par.xml_storage_stride) { + frame++; + } + + // Write high-res JPG snapshot every plot step + Save(fname.str().c_str(), "PDF"); + */ + + 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()); + } + + } +} + + +void Cell::Flux(double *flux, double *D) { + + + // loop over cell edges + + for (int c=0;c<Cell::nchem;c++) flux[c]=0.; + + for (list<Wall *>::iterator i=walls.begin(); + i!=walls.end(); + i++) { + + + // leaf cannot take up chemicals from environment ("no flux boundary") + if ((*i)->c2->BoundaryPolP()) continue; + + + // flux depends on edge length and concentration difference + for (int c=0;c<Cell::nchem;c++) { + double phi = (*i)->length * ( D[c] ) * ( (*i)->c2->chem[c] - chem[c] ); + + if ((*i)->c1!=this) { + cerr << "Warning, bad cells boundary: " << (*i)->c1->index << ", " << index << endl; + } + + flux[c] += phi; + } + } + +} + +INIT { + + 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 { + + Cell &circle=mesh.CircularCell(0,0,10,10); + + circle.SetTargetArea(circle.CalcArea()); + mesh.SetBaseArea(); + // clean up chemicals + for (int c=0; c<Cell::NChem(); c++) { + circle.SetChemical(c, 0.); + } + } +} + +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.LoopCurrentCells(&TestPlugin::CellHouseKeeping); // this includes cell division + + // Reaction diffusion + /*CelltoCellTransport *transport_f = &TestPlugin::CelltoCellTransport; + CellReaction *cellreaction_f = new plugin->CellDynamics(); + WallReaction *wall_f = new WallDynamics();*/ + + mesh.ReactDiffuse(plugin, par.rd_dt); + + + t++; + + Plot(par.resize_stride); + + /*QVector< QPair<double, int> > angles=mesh.VertexAnglesValues(); + QString afname=QString("Angles/anglesvalues%1.dat").arg(t,6,10,QChar('0')); + ofstream af(afname.toStdString().c_str()); + */ + + /*for (QVector< QPair<qreal, int> >::const_iterator v=angles.begin(); + v!=angles.end(); + v++) { + af << v->first << " " << v->second << endl; + } + */ + } + + } else { + + /* TransportFunction *transport_f = new CelltoCellTransport(); + CellReaction *cellreaction_f = new CellDynamics(); + WallReaction *wall_f = new WallDynamics(); + + mesh.ReactDiffuse_New(transport_f, cellreaction_f, wall_f, par.rd_dt);*/ + mesh.ReactDiffuse(plugin, par.rd_dt); + + Plot(par.resize_stride); + + } + + + + + + i++; + return mesh.getTime(); + +} + + + +/* Called if a cell is clicked */ +void Cell::OnClick(QMouseEvent *e) { + +} + + + +void Wall::OnWallInsert(void) { + + +} + + + + +int main(int argc,char **argv) { + + try { + + + int c; + + + char *leaffile=0; + + + while (1) { + + //int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = { + {"batch", 0, 0, 0}, + {"leaffile", 2, 0, 0} + }; + + // short option 'p' creates trouble for non-commandline usage on MacOSX. Option -p changed to -P (capital) + static char *short_options = "bl"; + c = getopt_long (argc, argv, "bl:", + 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 '?': + 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"); + } + + MakeDir("Angles"); + bool useGUI = !batch; + 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(); + } + } else { + main_window=new MainBase(canvas, mesh); + + } + + + + canvas.setSceneRect(QRectF()); + if (!batch) { + QObject::connect( qApp, SIGNAL(lastWindowClosed()), qApp, SLOT(quit()) ); + } + + + + main_window->Init(leaffile); + + 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(); + } + } + +} + + + +// Executed after the cellular mechanics steps have equillibrized +class CellHouseKeeping { +public: + void operator() (Cell &c) const { + + c.EnlargeTargetArea(par.cell_expansion_rate); + + if (c.Area() > par.rel_cell_div_threshold * c.BaseArea() ) { + c.Divide(); + } + } +}; + +// The number of chemical species in the cels +const int Cell::nchem = 0; + +// Differential equations describing transport of chemicals from cell to cell +class CelltoCellTransport : public TransportFunction { + + public: + virtual void operator()(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) +class WallDynamics : public WallReaction { + public: + virtual void operator()(Wall *w, double *dw1, double *dw2) {}; + +}; + + +// Differential equations describing chemical reactions inside the cells +class CellDynamics : public CellReaction { + public: + virtual void operator()(Cell *c, double *dchem) { + + }; + +}; + +// Rules for cell coloring +void Cell::SetColor(QColor &color) { } + +// To be executed after cell division +void Cell::OnDivide(ParentInfo &parent_info, Cell &daughter) {} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/leaves/vleaf.xsd b/data/leaves/vleaf.xsd new file mode 100644 --- /dev/null +++ b/data/leaves/vleaf.xsd @@ -0,0 +1,285 @@ + + + + + + + + Virtual Leaf + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/leaves/vleaf2.xml b/data/leaves/vleaf2.xml new file mode 100644 --- /dev/null +++ b/data/leaves/vleaf2.xml @@ -0,0 +1,1254 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#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 "testplugin.h" + +/* #define _xstr_(s) _str_(s) +#define _str_(s) #s +#include _xstr_(REACTIONS_HEADER) +*/ +extern Parameter par; + +MainBase *main_window = 0; +double auxin_account = 0.; + + + +TestPlugin *plugin = new TestPlugin(); + +#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 << ".pdf"; + if (par.storage_stride <= par.xml_storage_stride) { + frame++; + } + + // Write high-res JPG snapshot every plot step + Save(fname.str().c_str(), "PDF"); + */ + + 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()); + } + + } +} + + +void Cell::Flux(double *flux, double *D) { + + + // loop over cell edges + + for (int c=0;c<Cell::nchem;c++) flux[c]=0.; + + for (list<Wall *>::iterator i=walls.begin(); + i!=walls.end(); + i++) { + + + // leaf cannot take up chemicals from environment ("no flux boundary") + if ((*i)->c2->BoundaryPolP()) continue; + + + // flux depends on edge length and concentration difference + for (int c=0;c<Cell::nchem;c++) { + double phi = (*i)->length * ( D[c] ) * ( (*i)->c2->chem[c] - chem[c] ); + + if ((*i)->c1!=this) { + cerr << "Warning, bad cells boundary: " << (*i)->c1->index << ", " << index << endl; + } + + flux[c] += phi; + } + } + +} + +INIT { + + 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 { + + Cell &circle=mesh.CircularCell(0,0,10,10); + + circle.SetTargetArea(circle.CalcArea()); + mesh.SetBaseArea(); + // clean up chemicals + for (int c=0; c<Cell::NChem(); c++) { + circle.SetChemical(c, 0.); + } + } +} + +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.LoopCurrentCells(&TestPlugin::CellHouseKeeping); // this includes cell division + + // Reaction diffusion + /*CelltoCellTransport *transport_f = &TestPlugin::CelltoCellTransport; + CellReaction *cellreaction_f = new plugin->CellDynamics(); + WallReaction *wall_f = new WallDynamics();*/ + + mesh.ReactDiffuse(plugin, par.rd_dt); + + + t++; + + Plot(par.resize_stride); + + /*QVector< QPair<double, int> > angles=mesh.VertexAnglesValues(); + QString afname=QString("Angles/anglesvalues%1.dat").arg(t,6,10,QChar('0')); + ofstream af(afname.toStdString().c_str()); + */ + + /*for (QVector< QPair<qreal, int> >::const_iterator v=angles.begin(); + v!=angles.end(); + v++) { + af << v->first << " " << v->second << endl; + } + */ + } + + } else { + + /* TransportFunction *transport_f = new CelltoCellTransport(); + CellReaction *cellreaction_f = new CellDynamics(); + WallReaction *wall_f = new WallDynamics(); + + mesh.ReactDiffuse_New(transport_f, cellreaction_f, wall_f, par.rd_dt);*/ + mesh.ReactDiffuse(plugin, par.rd_dt); + + Plot(par.resize_stride); + + } + + + + + + i++; + return mesh.getTime(); + +} + + + +/* Called if a cell is clicked */ +void Cell::OnClick(QMouseEvent *e) { + +} + + + +void Wall::OnWallInsert(void) { + + +} + + + + +int main(int argc,char **argv) { + + try { + + + int c; + + + char *leaffile=0; + + + while (1) { + + //int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = { + {"batch", 0, 0, 0}, + {"leaffile", 2, 0, 0} + }; + + // short option 'p' creates trouble for non-commandline usage on MacOSX. Option -p changed to -P (capital) + static char *short_options = "bl"; + c = getopt_long (argc, argv, "bl:", + 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 '?': + 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"); + } + + MakeDir("Angles"); + bool useGUI = !batch; + 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(); + } + } else { + main_window=new MainBase(canvas, mesh); + + } + + + + canvas.setSceneRect(QRectF()); + if (!batch) { + QObject::connect( qApp, SIGNAL(lastWindowClosed()), qApp, SLOT(quit()) ); + } + + + + main_window->Init(leaffile); + + 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(); + } + } + +} + + + +// Executed after the cellular mechanics steps have equillibrized +class CellHouseKeeping { +public: + void operator() (Cell &c) const { + + c.EnlargeTargetArea(par.cell_expansion_rate); + + if (c.Area() > par.rel_cell_div_threshold * c.BaseArea() ) { + c.Divide(); + } + } +}; + +// The number of chemical species in the cels +const int Cell::nchem = 0; + +// Differential equations describing transport of chemicals from cell to cell +class CelltoCellTransport : public TransportFunction { + + public: + virtual void operator()(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) +class WallDynamics : public WallReaction { + public: + virtual void operator()(Wall *w, double *dw1, double *dw2) {}; + +}; + + +// Differential equations describing chemical reactions inside the cells +class CellDynamics : public CellReaction { + public: + virtual void operator()(Cell *c, double *dchem) { + + }; + +}; + +// Rules for cell coloring +void Cell::SetColor(QColor &color) { } + +// To be executed after cell division +void Cell::OnDivide(ParentInfo &parent_info, Cell &daughter) {} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/usage.rst b/doc/usage.rst new file mode 100644 diff --git a/src/Neighbor.cpp b/src/Neighbor.cpp new file mode 100644 --- /dev/null +++ b/src/Neighbor.cpp @@ -0,0 +1,46 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include "Neighbor.h" +#include "cell.h" +#include + +static const std::string _module_id("$Id$"); + +Neighbor::Neighbor(void): + cell(NULL), nb1(NULL), nb2(NULL){} + +Neighbor::Neighbor(Cell *c, Node *n1, Node *n2): + cell(c), nb1(n1), nb2(n2){} + +Neighbor::Neighbor(const Neighbor &src): + cell(src.cell), nb1(src.nb1), nb2(src.nb2){} // copy constructor + +bool Neighbor::CellEquals(int i) const { return cell->Index() == i; } +bool Neighbor::Cmp(Neighbor &c) const { return cell->Index() < c.cell->Index(); } // Compare cell indices not pointers. +bool Neighbor::Eq(Neighbor &c) const { return cell->Index() == c.cell->Index(); } +Cell* Neighbor::getCell(void) const { return cell; } + +bool neighbor_cell_eq(const Neighbor &n1, const Neighbor &n2) { + return (n1.getCell())->Index() == (n2.getCell())->Index(); // Compare cell indices not pointers. +} + +// finis diff --git a/src/Neighbor.h b/src/Neighbor.h new file mode 100644 --- /dev/null +++ b/src/Neighbor.h @@ -0,0 +1,71 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + + +#ifndef _NEIGHBOR_H_ +#define _NEIGHBOR_H_ + +#include +#include +#include + +using namespace std; + +class Cell; +class Node; + +class Neighbor { + + protected: + // Proper accessors and mutators should obviate these friend classes! + friend class Cell; + friend class Mesh; + friend class Node; + friend class FigureEditor; + + Cell *cell; + Node *nb1, *nb2; + + public: + Neighbor(void); + Neighbor(Cell*, Node*, Node*); + Neighbor(const Neighbor&); + + bool CellEquals(int) const; + bool Cmp(Neighbor&) const; + bool Eq(Neighbor&) const; + Cell* getCell(void) const; + void XMLAdd(xmlNodePtr) const; + + ostream &print(ostream &os) const; +}; + +// Overload the << operator +ostream &operator<<(ostream &os, const Neighbor &n); + +// Realize that we're equating like pointers with object equivalence! +bool neighbor_cell_eq(const Neighbor &n1, const Neighbor &n2); + +#endif + +// finis diff --git a/src/OptionFileDialog.cpp b/src/OptionFileDialog.cpp new file mode 100644 --- /dev/null +++ b/src/OptionFileDialog.cpp @@ -0,0 +1,53 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include "OptionFileDialog.h" +#include +#include +using namespace std; + +static const std::string _module_id("$Id$"); + +OptionFileDialog::OptionFileDialog(QWidget *parent, const char *name, bool modal) : Q3FileDialog(parent, name, modal) { + + //cerr << "This is an OptionFileDialog\n"; + geometrycheck = new QCheckBox("geometry",this); + geometrycheck -> setCheckState(Qt::Checked); + + parcheck = new QCheckBox("parameters", this); + parcheck -> setCheckState(Qt::Checked); + + //timecheck = new QCheckBox("time",this); + //timecheck -> setCheckState(Qt::Checked); + + addToolButton(geometrycheck); + //addToolButton(timecheck); + addToolButton(parcheck); + +}; + +OptionFileDialog::OptionFileDialog ( const QString & dirName, const QString & filter , QWidget * parent, const char * name , bool modal ) : + Q3FileDialog(dirName, filter, parent, name, modal) { + + cerr << "This is an OptionFileDialog\n"; + + }; diff --git a/src/OptionFileDialog.h b/src/OptionFileDialog.h new file mode 100644 --- /dev/null +++ b/src/OptionFileDialog.h @@ -0,0 +1,41 @@ +/* + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include + +class OptionFileDialog : public Q3FileDialog { + Q_OBJECT +public: + OptionFileDialog(QWidget *parent = 0, const char *name = 0, bool modal = false); + OptionFileDialog ( const QString & dirName, const QString & filter = QString(), QWidget * parent = 0, const char * name = 0, bool modal = false ); + + bool readGeometryP(void) const { return geometrycheck->checkState()==Qt::Checked; } + bool readParametersP(void) const { return parcheck->checkState()==Qt::Checked; } + +private: + QCheckBox *geometrycheck; + QCheckBox *parcheck; + //QCheckBox *timecheck; + + +}; diff --git a/src/UniqueMessage.cpp b/src/UniqueMessage.cpp new file mode 100644 --- /dev/null +++ b/src/UniqueMessage.cpp @@ -0,0 +1,89 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include +#include +#include +#include +#include "UniqueMessage.h" + +#include + +static const std::string _module_id("$Id$"); + +using namespace std; +UniqueMessageBox::UniqueMessageBox ( /* Icon icon,*/ + const QString & title, + const QString & text, + QWidget * parent , + Qt::WindowFlags f ) : +QDialog(parent, f) { + + label = new QLabel(text); + boxtext = text; + + show_again = new QCheckBox(tr("Do not show this message again")); + //addButton(show_again, QMessageBox::ActionRole); + //addButton(QMessageBox::Ok); + okButton = new QPushButton(tr("Ok")); + + if (issued_messages.contains(boxtext) ) { + + cerr << "Saw message before\n"; + display = false; + } else { + //cerr << "First time message\n"; + //issued_messages << text; + display=true; + } + + connect(okButton, SIGNAL(clicked()), this, SLOT(close()) ); + QHBoxLayout *hlayout = new QHBoxLayout; + hlayout->addWidget(label); + hlayout->addWidget(okButton); + QVBoxLayout *layout = new QVBoxLayout; + layout->addLayout(hlayout); + layout->addWidget(show_again); + setLayout(layout); + setWindowTitle(title); +}; + +UniqueMessageBox::~UniqueMessageBox(void) { + + if (show_again->checkState() == Qt::Checked ) { + cerr << "Message won't be shown again\n"; + issued_messages << boxtext; + + } +} +int UniqueMessageBox::exec(void) { + + if (display) + return QDialog::exec(); + else { + return 1; + } + +} + +QStringList UniqueMessageBox::issued_messages; + diff --git a/src/UniqueMessage.h b/src/UniqueMessage.h new file mode 100644 --- /dev/null +++ b/src/UniqueMessage.h @@ -0,0 +1,54 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + + +#ifndef _UNIQUEMESSAGE_H_ +#define _UNIQUEMESSAGE_H_ + +#include +#include +#include +#include + +class UniqueMessageBox : public QDialog { + Q_OBJECT + +public: + UniqueMessageBox ( /* Icon icon,*/ + const QString & title, + const QString & text, + /* StandardButtons buttons = Ok, */ + QWidget * parent = 0 , Qt::WindowFlags f = Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint ); + ~UniqueMessageBox(); + int exec(void); + +private: + static QStringList issued_messages; + bool display; + QCheckBox *show_again; + QPushButton *okButton; + QLabel *label; + QString boxtext; +}; + +#endif diff --git a/src/VirtualLeaf.cpp b/src/VirtualLeaf.cpp new file mode 100644 --- /dev/null +++ b/src/VirtualLeaf.cpp @@ -0,0 +1,548 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +//Added by qt3to4: +#include + +#include +#include + +#ifdef HAVE_QWT +#include "data_plot.h" +#endif +#include +#include +#include +#include "simplugin.h" +#include +#include +#include "modelcatalogue.h" + +/* #define _xstr_(s) _str_(s) +#define _str_(s) #s +#include _xstr_(REACTIONS_HEADER) +*/ + +static const std::string _module_id("$Id$"); + +extern Parameter par; + +MainBase *main_window = 0; +//double auxin_account = 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::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( 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 << ".pdf"; + if (par.storage_stride <= par.xml_storage_stride) { + frame++; + } + + // Write high-res JPG snapshot every plot step + Save(fname.str().c_str(), "PDF"); + */ + + 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(); + } +} + +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 + /*CelltoCellTransport *transport_f = &TestPlugin::CelltoCellTransport; + CellReaction *cellreaction_f = new plugin->CellDynamics(); + WallReaction *wall_f = new WallDynamics();*/ + + mesh.ReactDiffuse(par.rd_dt); + + + t++; + + Plot(par.resize_stride); + + /*QVector< QPair > angles=mesh.VertexAnglesValues(); + QString afname=QString("Angles/anglesvalues%1.dat").arg(t,6,10,QChar('0')); + ofstream af(afname.toStdString().c_str()); + */ + + /*for (QVector< QPair >::const_iterator v=angles.begin(); + v!=angles.end(); + v++) { + af << v->first << " " << v->second << endl; + } + */ + } + + } else { + + /* TransportFunction *transport_f = new CelltoCellTransport(); + CellReaction *cellreaction_f = new CellDynamics(); + WallReaction *wall_f = new WallDynamics(); + + mesh.ReactDiffuse_New(transport_f, cellreaction_f, wall_f, par.rd_dt);*/ + mesh.ReactDiffuse(par.rd_dt); + + Plot(par.resize_stride); + + } + + + + + + i++; + return mesh.getTime(); + +} + + + +/* Called if a cell is clicked */ +void Cell::OnClick(QMouseEvent *e) { + /* #ifdef HAVE_QWT + // Launch DataPlot window + QStringList curvenames; + for (int i=0;iOnClick(*this); +} + + +/* 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; + + + while (1) { + + //int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = { + {"batch", 0, 0, 0}, + {"leaffile", 2, 0, 0} + }; + + // short option 'p' creates trouble for non-commandline usage on MacOSX. Option -p changed to -P (capital) + static char *short_options = "bl"; + c = getopt_long (argc, argv, "bl:", + 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 '?': + 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(); + } + } else { + main_window=new MainBase(canvas, mesh); + + } + + + + canvas.setSceneRect(QRectF()); + if (!batch) { + QObject::connect( qApp, SIGNAL(lastWindowClosed()), qApp, SLOT(quit()) ); + } + + // Load plugins + /*QVector plugins = LoadPlugins(); + InstallPlugin(plugins[0], main_window); + + cerr << "List of models:" << endl; + foreach (SimPluginInterface *p, plugins) { + cerr << p->ModelID().toStdString() << endl; + } + */ + + + + ModelCatalogue model_catalogue(&mesh, (Main *)main_window); + model_catalogue.PopulateModelMenu(); + model_catalogue.InstallFirstModel(); + + //main_window->Init(leaffile); + + 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() <. +# +# Copyright 2010 Roeland Merks. +# + +#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 + +CONFIG += qt release +PERLDIR = ./perl +BINDIR = ../bin +DESTDIR = $$BINDIR +TARGET = VirtualLeaf +PARTMPL = $${TARGET}par.tmpl +MAINSRC = $${TARGET}.cpp +QT -= network sql xml +QT += qt3support +QWTDIR = /usr/local/qwt +QMAKE_LIBDIR += $$QWTDIR/lib + +win32:QWTDIR = "C:\Qwt-5.0.2" +unix:LIBS += -lxml2 -lz -lm +win32:LIBS += -L\"c:\Lib\" -L\"c:\Bin\" -lxml2 -lz -lm -lwsock32 + +!win32 { + GRAPHICS = qt #qwt +} + +win32 { + GRAPHICS = qt + RC_FILE = VirtualLeaf.rc + QMAKE_CXXFLAGS_RELEASE += -fexceptions -IC:\Include + QMAKE_CXXFLAGS_DEBUG += -fexceptions -IC:\Include + QMAKE_POST_LINK = "\ + C:\Bin\cp release\VirtualLeaf.exe \ + C:\Qt\4.5.3\bin\Qt3Support4.dll \ + C:\Qt\4.5.3\bin\QtGui4.dll \ + C:\Qt\4.5.3\bin\QtSql4.dll \ + C:\Qt\4.5.3\bin\QtXml4.dll \ + C:\Qt\4.5.3\bin\QtCore4.dll \ + C:\Qt\4.5.3\bin\QtNetwork4.dll \ + C:\Qt\4.5.3\bin\QtSvg4.dll \ + C:\bin\iconv.dll \ + C:\bin\libxml2.dll \ + C:\bin\zlib1.dll \ + C:\MinGW\bin\mingwm10.dll \ + $${DESTDIR}" +} + +# Application icons +macx { + ICON = leaficon.icns + # make sure that the executable can find the libqwt share library + QMAKE_POST_LINK = "mkdir $${DESTDIR}/$${TARGET}.app/Contents/Frameworks; \ + cp /usr/local/qwt/lib/libqwt.dylib $${DESTDIR}/$${TARGET}.app/Contents/Frameworks/.; \ + #install_name_tool -change libqwt.5.dylib $$QWTDIR/lib/libqwt.dylib $${DESTDIR}/$${TARGET}.app/Contents/MacOS/$${TARGET}; \ + install_name_tool -id @executable_path/../Frameworks/libqwt.dylib $${DESTDIR}/$${TARGET}.app/Contents/Frameworks/libqwt.dylib; \ + install_name_tool -change libqwt.5.dylib @executable_path/../Frameworks/libqwt.dylib $${DESTDIR}/$${TARGET}.app/Contents/MacOS/$${TARGET};\ + cp leaficon.icns $${DESTDIR}/$${TARGET}.app; " +} + +macx:release { + LIBS+= -dead_strip +} + +unix { + system(rm -f parameter.cpp parameter.h) + system(perl $$PERLDIR/make_parameter_source.pl $$PARTMPL) + system(perl $$PERLDIR/make_pardialog_source.pl $$PARTMPL) + #system(perl $$PERLDIR/make_xmlwritecode.pl -h $$REACTIONS) + CC = /usr/bin/gcc + QMAKE_CXXFLAGS_RELEASE += -fexceptions -I/usr/include/libxml2 + QMAKE_CXXFLAGS_DEBUG += -fexceptions -I/usr/include/libxml2 -DQDEBUG -ggdb3 -O0 + QMAKE_LFLAGS += -fPIC +} + +# Input +HEADERS += \ + apoplastitem.h \ + canvas.h \ + cellbase.h \ + cell.h \ + cellitem.h \ + forwardeuler.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 \ + 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 \ + xmlwritecode.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_RELEASE += -DQTGRAPHICS # -fpermissive + QMAKE_CXXFLAGS_DEBUG += -DQTGRAPHICS +} + +contains( GRAPHICS, xfig ) { + message("Building Xfig executable (background runnable).") + QMAKE_CXXFLAGS_RELEASE += -DXFIGGRAPHICS + QMAKE_CXXFLAGS_DEBUG += -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_RELEASE += -DX11GRAPHICS + QMAKE_CXXFLAGS_DEBUG += -DX11GRAPHICS + CONFIG -= qt + CONFIG += x11 + unix:LIBS += -lpng +} + +# finis diff --git a/src/VirtualLeaf.rc b/src/VirtualLeaf.rc new file mode 100644 --- /dev/null +++ b/src/VirtualLeaf.rc @@ -0,0 +1,2 @@ +IDI_ICON1 ICON DISCARDABLE "leaficon.ico" + diff --git a/src/VirtualLeaf_d.pro b/src/VirtualLeaf_d.pro new file mode 100644 --- /dev/null +++ b/src/VirtualLeaf_d.pro @@ -0,0 +1,215 @@ +# +# $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 . +# +# Copyright 2010 Roeland Merks. +# + +#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 + +CONFIG += qt debug +PERLDIR = ./perl +BINDIR = ../bin +DESTDIR = $$BINDIR +TARGET = VirtualLeaf +PARTMPL = $${TARGET}par.tmpl +MAINSRC = $${TARGET}.cpp +QT -= network sql xml +QT += qt3support +QWTDIR = /usr/local/qwt +QMAKE_LIBDIR += $$QWTDIR/lib + +win32:QWTDIR = "C:\Qwt-5.0.2" +unix:LIBS += -lxml2 -lz -lm +win32:LIBS += -L\"c:\Lib\" -L\"c:\Bin\" -lxml2 -lz -lm -lwsock32 + +!win32 { + GRAPHICS = qt #qwt +} + +win32 { + GRAPHICS = qt + RC_FILE = VirtualLeaf.rc + QMAKE_CXXFLAGS_RELEASE += -fexceptions -IC:\Include + QMAKE_CXXFLAGS_DEBUG += -fexceptions -IC:\Include + QMAKE_POST_LINK = "\ + C:\Bin\cp release\VirtualLeaf.exe \ + C:\Qt\4.5.3\bin\Qt3Support4.dll \ + C:\Qt\4.5.3\bin\QtGui4.dll \ + C:\Qt\4.5.3\bin\QtSql4.dll \ + C:\Qt\4.5.3\bin\QtXml4.dll \ + C:\Qt\4.5.3\bin\QtCore4.dll \ + C:\Qt\4.5.3\bin\QtNetwork4.dll \ + C:\Qt\4.5.3\bin\QtSvg4.dll \ + C:\bin\iconv.dll \ + C:\bin\libxml2.dll \ + C:\bin\zlib1.dll \ + C:\MinGW\bin\mingwm10.dll \ + $${DESTDIR}" +} + +# Application icons +macx { + ICON = leaficon.icns + # make sure that the executable can find the libqwt share library + QMAKE_POST_LINK = "mkdir $${DESTDIR}/$${TARGET}.app/Contents/Frameworks; \ + cp /usr/local/qwt/lib/libqwt.dylib $${DESTDIR}/$${TARGET}.app/Contents/Frameworks/.; \ + #install_name_tool -change libqwt.5.dylib $$QWTDIR/lib/libqwt.dylib $${DESTDIR}/$${TARGET}.app/Contents/MacOS/$${TARGET}; \ + install_name_tool -id @executable_path/../Frameworks/libqwt.dylib $${DESTDIR}/$${TARGET}.app/Contents/Frameworks/libqwt.dylib; \ + install_name_tool -change libqwt.5.dylib @executable_path/../Frameworks/libqwt.dylib $${DESTDIR}/$${TARGET}.app/Contents/MacOS/$${TARGET};\ + cp leaficon.icns $${DESTDIR}/$${TARGET}.app; " +} + +macx:release { + LIBS+= -dead_strip +} + +unix { + system(rm -f parameter.cpp parameter.h) + system(perl $$PERLDIR/make_parameter_source.pl $$PARTMPL) + system(perl $$PERLDIR/make_pardialog_source.pl $$PARTMPL) + #system(perl $$PERLDIR/make_xmlwritecode.pl -h $$REACTIONS) + CC = /usr/bin/gcc + QMAKE_CXXFLAGS_RELEASE += -fexceptions -I/usr/include/libxml2 + QMAKE_CXXFLAGS_DEBUG += -fexceptions -I/usr/include/libxml2 -DQDEBUG -ggdb3 -O0 + QMAKE_LFLAGS += -fPIC +} + +# Input +HEADERS += \ + apoplastitem.h \ + canvas.h \ + cellbase.h \ + cell.h \ + cellitem.h \ + forwardeuler.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 \ + 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 \ + xmlwritecode.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_RELEASE += -DQTGRAPHICS # -fpermissive + QMAKE_CXXFLAGS_DEBUG += -DQTGRAPHICS +} + +contains( GRAPHICS, xfig ) { + message("Building Xfig executable (background runnable).") + QMAKE_CXXFLAGS_RELEASE += -DXFIGGRAPHICS + QMAKE_CXXFLAGS_DEBUG += -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_RELEASE += -DX11GRAPHICS + QMAKE_CXXFLAGS_DEBUG += -DX11GRAPHICS + CONFIG -= qt + CONFIG += x11 + unix:LIBS += -lpng +} + +# finis diff --git a/src/VirtualLeafpar.tmpl b/src/VirtualLeafpar.tmpl new file mode 100644 --- /dev/null +++ b/src/VirtualLeafpar.tmpl @@ -0,0 +1,126 @@ +title = Parameter values for The Virtual Leaf / title +label = Visualization / label +arrowcolor = white / string +arrowsize = 100 / double +textcolor = red / string +cellnumsize = 1 / int +nodenumsize = 1 / int +node_mag = 1.0 / double +outlinewidth = 1.0 / double +cell_outline_color = forestgreen / string +resize_stride = 10 / int +label = / label +label = Cell mechanics / label +T = 1.0 / double +lambda_length = 100. / double +lambda_celllength = 0. / double +target_length = 60. / double +cell_expansion_rate = 1. / double +cell_div_expansion_rate = 0. / double +auxin_dependent_growth = true / bool +ode_accuracy = 1e-4 / double +mc_stepsize = 0.4 / double +mc_cell_stepsize = 0.2 / double +energy_threshold = 1000. / double +bend_lambda = 0. / double +alignment_lambda = 0. / double +rel_cell_div_threshold = 2. / double +rel_perimeter_stiffness = 2 / double +collapse_node_threshold = 0.05 / double +morphogen_div_threshold = 0.2 / double +morphogen_expansion_threshold = 0.01 / double +copy_wall = true / bool +label = / label +label = Auxin transport and PIN1 dynamics / label +source = 0. / double +D = 0., 0.0, 0.0, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0. / doublelist +initval = 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0. / doublelist +k1 = 1. / double +k2 = 0.3 / double +r = 1. / double +kr = 1. / double +#k3 = 1 / double +#k4 = 0.3 / double +km = 1. / double +Pi_tot = 1. / double +transport = 0.036 / double +ka = 1 / double +# d = 1e-06 / double +# e = 0.01 / double +# f = 10 / double +pin_prod = 0.001 / double +pin_prod_in_epidermis = 0.1 / double +pin_breakdown = 0.001 / double +pin_breakdown_internal = 0.001 / double +aux1prod = 0.001 / double +aux1prodmeso = 0. / double +aux1decay = 0.001 / double +aux1decaymeso = 0.1 / double +aux1transport = 0.036 / double +aux_cons = 0. / double +aux_breakdown = 0. / double +kaux1 = 1 / double +kap = 1 / double +leaf_tip_source = 0.001 / double +sam_efflux = 0.0001 / double +sam_auxin = 10. / double +# gf_prod = 1e-3 / double +# gf_decay = 1e-3 / double +sam_auxin_breakdown = 0 / double +#label = / label +#label = miscellaneous / label +van3prod = 0.002 / double +van3autokat = 0.1 / double +van3sat = 10 / double +k2van3 = 0.3 / double +#glvprod = 0.0 / double +#glvbreakdown = 0. / double +label = / label +label = Integration parameters / label +dt = 0.1 / double +rd_dt = 1.0 / double +datadir = . / directory +movie = false / bool +nit = 100000 / int +maxt = 1000. / double +storage_stride = 10 / int +xml_storage_stride = 500 / int +rseed = -1 / int +#label = / label +#label = Parameters for new chemical / label +#glvproduction = 0. / double +#glvdecay = 0. / double +#glvdiffthreshold = 0. / double +label = / label +label = Meinhardt leaf venation model / label +constituous_expansion_limit = 16 / int +vessel_inh_level = 1 / double +vessel_expansion_rate = 0.25 / double +d = 0. / double +e = 0. / double +f = 0. / double +c = 0. / double +mu = 0. / double +nu = 0. / double +rho0 = 0. / double +rho1 = 0. / double +c0 = 0. / double +gamma = 0. / double +eps = 0. / double +label = / label +label = User-defined parameters / label +k = 0., 0.0, 0.0, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0. / doublelist +i1 = 0 / int +i2 = 0 / int +i3 = 0 / int +i4 = 0 / int +i5 = 0 / int +s1 = / string +s2 = / string +s3 = / string +b1 = false / bool +b2 = false / bool +b3 = false / bool +b4 = false / bool +dir1 = . / directory +dir2 = . / directory diff --git a/src/apoplastitem.cpp b/src/apoplastitem.cpp new file mode 100644 --- /dev/null +++ b/src/apoplastitem.cpp @@ -0,0 +1,78 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + + +#include +#include +#include "canvas.h" +#include "parameter.h" +#include "node.h" +#include "apoplastitem.h" + +static const std::string _module_id("$Id$"); + +ApoplastItem::ApoplastItem( Wall *w, QGraphicsScene *canvas ) +: QGraphicsLineItem( 0, canvas ), SimItemBase( w, canvas){ + + extern Parameter par; + + setColor(); + + // line with "PIN1"is a bit inside the cell wall + Vector edgevec = (*(w->N2())) - (*(w->N1())); + Vector perp = edgevec.Normalised().Perp2D(); + + Vector offs = Cell::Offset(); + double factor = Cell::Factor(); + + Vector from = ( offs + *(w->N1()) ) * factor;// + (wn==1?-1:1) * par.outlinewidth;// * 0.2 * factor * perp; + Vector to = ( offs + *(w->N2()) ) *factor;// + (wn==1?-1:1) * par.outlinewidth;// * 0.2 * factor * perp; + + + setLine(( from.x ), + ( from.y ), + ( to.x ), + ( to.y ) ); + setZValue(12); +} + + +void ApoplastItem::setColor(void) { + + QColor diffcolor; + static const QColor purple("Purple"); + static const QColor blue("blue"); + + Wall *w=&getWall(); + double val = w->getApoplast(2); + + diffcolor.setRgb( 0,0,(int)( ( val / (1 + val) )*255.)); + setPen (QPen(diffcolor, 20) ); + +} + +void ApoplastItem::OnClick(QMouseEvent *e) { + + +} + diff --git a/src/apoplastitem.h b/src/apoplastitem.h new file mode 100644 --- /dev/null +++ b/src/apoplastitem.h @@ -0,0 +1,50 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#ifndef _APOPLASTITEM_H_ +#define _APOPLASTITEM_H_ +#include +#include +#include +#include +#include +#include "simitembase.h" +#include "wall.h" + +//! Shows transporter concentrations at one side of the wall + +class ApoplastItem : public QGraphicsLineItem, public SimItemBase +{ +public: + ApoplastItem( Wall *n, QGraphicsScene *canvas ); + virtual ~ApoplastItem() {} + Wall &getWall(void) const { return *class_cast(obj); } + void OnClick(QMouseEvent *e); + //virtual void userMove(double dx, double dy); + //virtual void paint( QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0); + void setColor(void); +private: + int wn; +}; + +#endif diff --git a/src/build_models/auxingrowthplugin.cpp b/src/build_models/auxingrowthplugin.cpp new file mode 100644 --- /dev/null +++ b/src/build_models/auxingrowthplugin.cpp @@ -0,0 +1,296 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include +#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$"); + +bool batch = false; + + +// 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 walls; + foreach(WallBase *w, walls) { + w->setTransporter(&daughter1, 1, 0.); + } + + //daughter1.LoopWalls(Wall::setTransporter(&daughter1, 1, 0.)); + + + /* for (list::const_iterator w=daughter2.walls.begin(); + w!=daughter2.walls.end(); + w++) { + // reset transporter value + (*w)->setTransporter(&daughter2, 1, 0.); + } + */ +} + +void AuxinGrowthPlugin::SetCellColor(CellBase &c, QColor &color) { + + // Red: PIN1 + // Green: Auxin + if (c.CellType()==1) color = QColor("Blue"); + else color.setRgb(c.Chemical(1)/(1+c.Chemical(1)) * 255.,(c.Chemical(0)/(1+c.Chemical(0)) * 255.),/* (chem[2]/(1+chem[2]) *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(); + } + if (c.Chemical(0)>0.6) { + c.SetCellType(1); + } + // 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; + // dchem_c2 is undefined..! + return; + } else { + return; + } + } + + + if (w->C1()->BoundaryPolP()) { + + if (w->AuxinSource()) { + double aux_flux = par->leaf_tip_source * w->Length(); + dchem_c2[0] += aux_flux; + // dchem_c1 is undefined...! + 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.; + //dw1[2] = 0.; dw2[2] = 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];//=dw1[2]=dw2[2]; + return; + } + } + + if (w->C1()->BoundaryPolP()) { + if (w->AuxinSink()) { + + dw1[0] = 0.; dw2[0] = 0.; + //dw1[2] = 0.; dw2[2] = 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];//=dw1[2]=dw2[2]; + 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[2] = 0.; dw2[2] = 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( far_3_arg_mem_fun( *this, &AuxinGrowthPlugin::complex_PijAj ) ) + par->k2 * sum_Pij; + /*for ( list::const_iterator w = c->walls.begin(); + w!=walls.end(); + w++) { + if ((*w)->C1() == c) + dPidt += complex_PijAj( (*w)->C1(), (*w)->C2(), *w ); + else + dPidt += complex_PijAj( (*w)->C2(), (*w)->C1(), *w ); + }*/ + + // 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) diff --git a/src/build_models/auxingrowthplugin.h b/src/build_models/auxingrowthplugin.h new file mode 100644 --- /dev/null +++ b/src/build_models/auxingrowthplugin.h @@ -0,0 +1,63 @@ +/* + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#ifndef _AUXINGROWTHPLUGIN_H_ +#define _AUXINGROWTHPLUGIN_H_ + +#include +#include +#include +#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; } + +private: + double complex_PijAj(CellBase &here, CellBase &nb, Wall &w); +}; + +#endif diff --git a/src/build_models/leafplugin.cpp b/src/build_models/leafplugin.cpp new file mode 100644 --- /dev/null +++ b/src/build_models/leafplugin.cpp @@ -0,0 +1,425 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include + +#include "simplugin.h" + +#include "parameter.h" + +#include "wallbase.h" +#include "cellbase.h" +#include "leafplugin.h" + +#include "far_mem_5.h" + +static const std::string _module_id("$Id$"); + +bool batch = false; + +// To be executed after cell division +void LeafPlugin::OnDivide(ParentInfo &parent_info, CellBase &daughter1, CellBase &daughter2) { + // PIN1 distributes between parent and daughter according to area + double area = daughter1.Area(), daughter_area = daughter2.Area(); + double tot_area = area + daughter_area; + + //chem[1]*=(area/tot_area); + //daughter.chem[1]*=(daughter_area/tot_area); + + // For lack of detailed data, or a better rule, we assume that cells remain polarized + // after division + + // So the PIN1 is redistributed according to the original polarization over the walls + + // parent_info contains info about the parent + // redistribute the PIN in the endosome according to area + + // "Fudge" rule: if one of the cells is at the boundary, remove all AUX1 in the other cell + if (daughter1.AtBoundaryP() && !daughter2.AtBoundaryP()) { + //daughter2.new_chem[2]=daughter2.chem[2]=0.; + daughter2.SetNewChem(2,0); + daughter2.SetChemical(2,0); + //daughter.new_chem[0]=daughter.chem[0]=0.; + //cerr << "Clearing daughter\n"; + //for (list::const_iterator w=daughter.walls.begin(); + // w!=daughter.walls.end(); + // w++) { + + // (*w)->setTransporter(&daughter, 1, 0.); + + //} + //new_chem[2]=chem[2]=parent_info.PINendosome; + daughter1.SetNewChem(2,parent_info.PINendosome); + daughter1.SetChemical(2,parent_info.PINendosome); + + } else { + if (daughter2.AtBoundaryP() && !daughter1.AtBoundaryP()) { + + //new_chem[2]=chem[2]=0.; + daughter1.SetNewChem(2,0); + daughter1.SetChemical(2,0); + + /*new_chem[0]=chem[0]=0.; + for (list::const_iterator w=walls.begin(); + w!=walls.end(); + w++) { + + (*w)->setTransporter(this, 1, 0.); + }*/ + //daughter2.chem[2]=parent_info.PINendosome; + daughter2.SetChemical(2,parent_info.PINendosome); + //cerr << "Clearing parent\n"; + + } else { + //daughter1.new_chem[2]=daughter1.chem[2] = parent_info.PINendosome*(area/tot_area); + daughter1.SetNewChem(2,parent_info.PINendosome*(area/tot_area)); + daughter1.SetChemical(2, parent_info.PINendosome*(area/tot_area)); + //daughter2.new_chem[2]=daughter2.chem[2] = parent_info.PINendosome*(daughter_area/tot_area); + daughter2.SetNewChem(2,parent_info.PINendosome*(daughter_area/tot_area)); + daughter2.SetChemical(2,parent_info.PINendosome*(daughter_area/tot_area)); + + } + } + + /* + // NB: Code commented out; not yet adapted to plugin format... RM 18/12/2009 + // Now redistribute the membrane PINs according to the original polarization in the parent + // mmm... I'd like to have a better, biologically motivated rule for this, + // but for lack of something better... I hope I'm excused :-). Let's say the overall + // organization of the actin fibres is not completely destroyed after division... + + // distribute wallPINs according to the circumference of the parent and daughter + double circ = Circumference( ); + double daughter_circ = daughter.Circumference(); + double tot_circ = circ + daughter_circ; + + double wallPINs = (circ / tot_circ) * parent_info.PINmembrane; + double daughter_wallPINs = (daughter_circ / tot_circ) * parent_info.PINmembrane; + + //cerr << "wallPINs = " << wallPINs << ", daughter_wallPINs = " << daughter_wallPINs << "sum = " << wallPINs + daughter_wallPINs << ", PINmembrane = " << parent_info.PINmembrane << endl; + // distrubute it according to the overall polarity + Vector polarization = parent_info.polarization.Normalised().Perp2D(); + + double sum=0.; + for (list::const_iterator w=walls.begin(); + w!=walls.end(); + w++) { + + // distribute according to angle (0 degrees: maximum, 180 degrees minimum) + double tmp=InnerProduct((*w)->getWallVector(this),polarization); // move domain from [-1,1] to [0,1] + + cerr << "[" << tmp << "]"; + sum+=tmp; + //(*w)->setTransporter(this, 1, + } + + //cerr << "Sum is " << sum << endl; + //double sum_wall_Pi = SumTransporters(1); + + // After division, cells produce PIN1 (in intracellular storage) until total amount becomes Pi_tot + //SetChemical(1, par.Pi_tot - sum_wall_Pi ); + //SetNewChem(1, Chemical(1)); + + //cerr << "[ " << sum_wall_Pi + Chemical(1) << "]"; + */ +} + +void LeafPlugin::SetCellColor(CellBase &c, QColor &color) { + + // Red: AUX1 + // Green: Auxin + // Blue: van-3 + // color.setRgb(chem[2]/(1+chem[2]) * 255.,(chem[0]/(1+chem[0]) * 255.),(chem[3]/(1+chem[3]) *255.) ); + color.setRgb(c.Chemical(2)/(1+c.Chemical(2)) * 255.,(c.Chemical(0)/(1+c.Chemical(0)) * 255.),(c.Chemical(3)/(1+c.Chemical(3)) *255.) ); + + +} + + + +void LeafPlugin::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 if this is not a provascular cell + if (c.Chemical(3) < 0.7 ) { + c.EnlargeTargetArea(par->cell_expansion_rate); + } + } + +} + +void LeafPlugin::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; + + // dchem_c2 is undefined..! + return; + } else { + if (w->AuxinSink()) { + + // efflux into Shoot Apical meristem + // we assume all PINs are directed towards shoot apical meristem + dchem_c1[0] -= par->sam_efflux * w->C1()->Chemical(0) / (par->ka + w->C1()->Chemical(0)); + + return; + } else { + + // Active fluxes (PIN1 and AUX1 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)) + + par->aux1transport * w->C2()->Chemical(2) * w->C1()->Chemical(0) / (par->kaux1 + 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)) + + par->aux1transport * w->C1()->Chemical(2) * w->C2()->Chemical(0) / (par->kaux1 + w->C2()->Chemical(0)) ); + + dchem_c1[0] += trans21 - trans12; + dchem_c2[0] += trans12 - trans21; + return; + } + + } + } + + + if (w->C1()->BoundaryPolP()) { + + if (w->AuxinSource()) { + double aux_flux = par->leaf_tip_source * w->Length(); + dchem_c2[0] += aux_flux; + // dchem_c1 is undefined...! + return; + } else { + + if (w->AuxinSink()) { + + + // efflux into Shoot Apical meristem + // we assume all PINs are directed towards shoot apical meristem + + // no passive fluxes: outside is impermeable + + // Active fluxes (PIN1 and AUX1 mediated transport) + + // (Transporters measured in moles, here) + // efflux from cell 1 to cell 2 + // assumption: no AUX1 in shoot apical meristem + double trans12 = ( par->transport * w->Transporters1(1) * w->C1()->Chemical(0) / (par->ka + w->C1()->Chemical(0))); + dchem_c1[0] += - trans12; + + return; + + //dchem_c2[0] -= par->sam_efflux * w->C2()->Chemical(0) / (par->ka + w->C2()->Chemical(0)); + + // return; + } else { + + } + } + } + + + // Passive fluxes (Fick's law) + // only auxin flux now + // flux depends on edge length and concentration difference + for (int c=0;cLength() * ( par->D[c] ) * ( w->C2()->Chemical(c) - w->C1()->Chemical(c) ); + dchem_c1[c] += phi; + dchem_c2[c] -= phi; + } + // Active fluxes (PIN1 and AUX1 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)) + + par->aux1transport * w->C2()->Chemical(2) * w->C1()->Chemical(0) / (par->kaux1 + 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)) + + par->aux1transport * w->C1()->Chemical(2) * w->C2()->Chemical(0) / (par->kaux1 + w->C2()->Chemical(0)) ); + + dchem_c1[0] += trans21 - trans12; + dchem_c2[0] += trans12 - trans21; + + + +} +void LeafPlugin::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.; + dw1[2] = 0.; dw2[2] = 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]=dw1[2]=dw2[2]; + return; + } + } + + if (w->C1()->BoundaryPolP()) { + if (w->AuxinSink()) { + + dw1[0] = 0.; dw2[0] = 0.; + dw1[2] = 0.; dw2[2] = 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]=dw1[2]=dw2[2]; + 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[2] = 0.; dw2[2] = 0.; + + dw1[1] = dPijdt1; + dw2[1] = dPijdt2; + +} + +double LeafPlugin::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 LeafPlugin::CellDynamics(CellBase *c, double *dchem) { + + double dPidt = 0.; + + double sum_Pij = c->SumTransporters( 1 ); + + // exocytosis regulated: + // van3 expression reduces rate of PIN1 endocytosis + dPidt = -par->k1 * c->ReduceCellAndWalls( far_3_arg_mem_fun( *this, &LeafPlugin::complex_PijAj ) ) + + (c->Chemical(3) < 0.5 ? par->k2 : par->k2van3) * 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; + + /*if (c->AtBoundaryP()) { + dchem[2] = 0.01; + //cerr << "Making cell blue.\n"; + } else { + dchem[2] = -0.1 * c->Chemical(2); + }*/ + + // no PIN production in SAM + if (c->Boundary() == CellBase::SAM) { + dchem[1]=0.; + dchem[0]= - par->sam_auxin_breakdown * c->Chemical(0); + dchem[2]=0.; + } else { + + dchem[1] = dPidt; + + + // source of auxin + dchem[0] = par->aux_cons; + + // auxin-induced AUX1 production, in the epidermis + dchem[2] = ( c->AtBoundaryP() ? par->aux1prod : par->aux1prodmeso ) * ( c->Chemical(0) / ( 1. + par->kap * c->Chemical(0) ) ) - ( par->aux1decay ) * c->Chemical(2) ;//: 0.; + + // auxin-induced production of VAN-3? Autokatalysis? + //dchem[3] = par->van3prod * (c->Chemical(0) / (1. + par->kvp * c-> Chemical(0) ) ) + double A = c->Chemical(0); + double van3 = c->Chemical(3); + dchem[3] = par->van3prod * A - par->van3autokat * van3 + van3*van3/(1 + par->van3sat * van3*van3 ); + } +} + + + + + + +Q_EXPORT_PLUGIN2(leafplugin, LeafPlugin) diff --git a/src/build_models/leafplugin.h b/src/build_models/leafplugin.h new file mode 100644 --- /dev/null +++ b/src/build_models/leafplugin.h @@ -0,0 +1,64 @@ +/* + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#ifndef _LEAFPLUGIN_H_ +#define _LEAFPLUGIN_H_ + +#include +#include +#include +#include "../simplugin.h" + + +class LeafPlugin : public QObject, SimPluginInterface { + Q_OBJECT + Q_INTERFACES(SimPluginInterface); + +public: + virtual QString ModelID(void) { return QString( "Traveling wave model with influx carriers - Merks and Beemster, 2006-2008" ); } + + // 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 4; } + + private: + double complex_PijAj(CellBase &here, CellBase &nb, Wall &w); + +}; + +#endif diff --git a/src/build_models/meinhardtplugin.cpp b/src/build_models/meinhardtplugin.cpp new file mode 100644 --- /dev/null +++ b/src/build_models/meinhardtplugin.cpp @@ -0,0 +1,134 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include +#include "simplugin.h" + +#include "parameter.h" +#include "warning.h" +#include "wallbase.h" +#include "cellbase.h" +#include "meinhardtplugin.h" + +static const std::string _module_id("$Id$"); + +bool batch = false; + +// To be executed after cell division +void MeinhardtPlugin::OnDivide(ParentInfo &parent_info, CellBase &daughter1, CellBase &daughter2) { + +} + +void MeinhardtPlugin::SetCellColor(CellBase &c, QColor &color) { + + if (fpclassify(c.Chemical(0))==FP_NAN) { + // somehow the function isnan doesn't work properly on my system... SuSE Linux + // 10.0 64-bits (isnan seems not be implemented using fpclassify). + MyWarning::warning("Whoops! Numerical instability!!"); + color.setNamedColor("red"); + } else { + double range_min = 0.;//, range_max = 1.; + if (c.Chemical(0) par->rel_cell_div_threshold * c.BaseArea() ) { + c.Divide(); + } + + // cell expansion is inhibited by substrate (chem 3) + if (!par->constituous_expansion_limit || c.NCells()constituous_expansion_limit) { + c.EnlargeTargetArea(par->cell_expansion_rate ); + } else { + if (c.Chemical(0)<0.5) { + double tmp; + c.EnlargeTargetArea((tmp=(1.-par->vessel_inh_level*c.Chemical(3))*par->cell_expansion_rate /* + c.Chemical(4)*/)<0?0:tmp); + } else { + c.EnlargeTargetArea(par->vessel_expansion_rate); + } + } + +} + +void MeinhardtPlugin::CelltoCellTransport(Wall *w, double *dchem_c1, double *dchem_c2) { + + // No flux boundaries for all chemicals, except activator: boundary is sink + if (w->C1()->BoundaryPolP() || w->C2()->BoundaryPolP()) { + + if (w->C1()->BoundaryPolP()) { + dchem_c2[1] -= w->Length() * ( par->D[1] ) * ( w->C2()->Chemical(1) ); + } else { + dchem_c1[1] -= w->Length() * ( par->D[1] ) * ( w->C1()->Chemical(1) ); + } + return; + } + + + // Passive fluxes (Fick's law) + for (int c=0;cLength() * ( par->D[c] ) * ( w->C2()->Chemical(c) - w->C1()->Chemical(c) ); + dchem_c1[c] += phi; + dchem_c2[c] -= phi; + } + + +} + +void MeinhardtPlugin::WallDynamics(Wall *w, double *dw1, double *dw2) { + for (int c = 0;cChemical(0); + double A = c->Chemical(1); + double H = c->Chemical(2); + double S = c->Chemical(3); + //double expansin = c->Chemical(4); + + + dchem[0] = ( par->d * A - par->e * Y + Y*Y/(1 + par->f * Y*Y ) ); + dchem[1] = ( par->c * A*A*S/H - par->mu * A + par->rho0*Y ); + dchem[2] = ( par->c * A*A*S - par->nu*H + par->rho1*Y ); + dchem[3] = ( par->c0 - par->gamma*S - par->eps * Y * S ); + //dchem[4] = ( -par->expansindecay * expansin ); + //for (int i=0;i<4;i++) { cerr << "[ " << dchem[i] << " ]"; } cerr << endl; + +// cerr << "Chemicals: "; for (int i=0;iChemical(i) << " "; } cerr << endl; + +// test: dchem[0] = 0.01 * c->Chemical(0) * ( 1. - c->Chemical(0)); +} + + +Q_EXPORT_PLUGIN2(meinhardtplugin, MeinhardtPlugin) diff --git a/src/build_models/meinhardtplugin.h b/src/build_models/meinhardtplugin.h new file mode 100644 --- /dev/null +++ b/src/build_models/meinhardtplugin.h @@ -0,0 +1,60 @@ +/* + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#ifndef _MEINHARDTPLUGIN_H_ +#define _MEINHARDTPLUGIN_H_ + +#include +#include +#include +#include "simplugin.h" + + +class MeinhardtPlugin : public QObject, SimPluginInterface { + Q_OBJECT + Q_INTERFACES(SimPluginInterface); + +public: + virtual QString ModelID(void) { return QString( "Meinhardt 1976, with 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 4; } +}; + +#endif diff --git a/src/build_models/plugin_auxingrowth.pro b/src/build_models/plugin_auxingrowth.pro new file mode 100644 --- /dev/null +++ b/src/build_models/plugin_auxingrowth.pro @@ -0,0 +1,45 @@ +# +# $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 . +# +# Copyright 2010 Roeland Merks. +# + +BINDIR = ../../bin +CONFIG += plugin release +DEFINES = QTGRAPHICS # VLEAFPLUGIN +DESTDIR = $${BINDIR}/models +TARGET = auxingrowth +HEADERS = ../simplugin.h $${TARGET}plugin.h +QMAKE_CXXFLAGS += -I.. +QT += qt3support +SOURCES = $${TARGET}plugin.cpp +TEMPLATE = lib + +unix:LIBS += -L"lib" -lvleaf +win32:LIBS += -L"lib" -lvleaf + +unix { + QMAKE_CXXFLAGS += -fexceptions -I/usr/include/libxml2 + QMAKE_LFLAGS += -fPIC +} + +win32 { + QMAKE_CXXFLAGS += -fexceptions -IC:\Include +} + +# finis diff --git a/src/build_models/plugin_leaf.pro b/src/build_models/plugin_leaf.pro new file mode 100644 --- /dev/null +++ b/src/build_models/plugin_leaf.pro @@ -0,0 +1,45 @@ +# +# $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 . +# +# Copyright 2010 Roeland Merks. +# + +BINDIR = ../../bin +CONFIG += plugin release +DEFINES = QTGRAPHICS # VLEAFPLUGIN +DESTDIR = $${BINDIR}/models +TARGET = leaf +HEADERS = ../simplugin.h $${TARGET}plugin.h +QMAKE_CXXFLAGS += -I.. +QT += qt3support +SOURCES = $${TARGET}plugin.cpp +TEMPLATE = lib + +unix:LIBS += -L"lib" -lvleaf +win32:LIBS += -L"lib" -lvleaf + +unix { + QMAKE_CXXFLAGS += -fexceptions -fPIC -I/usr/include/libxml2 + QMAKE_LFLAGS += -fPIC +} + +win32 { + QMAKE_CXXFLAGS += -fexceptions -IC:\Include +} + +# finis diff --git a/src/build_models/plugin_meinhardt.pro b/src/build_models/plugin_meinhardt.pro new file mode 100644 --- /dev/null +++ b/src/build_models/plugin_meinhardt.pro @@ -0,0 +1,46 @@ +# +# $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 . +# +# Copyright 2010 Roeland Merks. +# + +BINDIR = ../../bin +CONFIG += plugin release +DEFINES = QTGRAPHICS # VLEAFPLUGIN +DESTDIR = $${BINDIR}/models +TARGET = meinhardt +HEADERS = simplugin.h $${TARGET}plugin.h +QMAKE_CXXFLAGS += -I.. +QT += qt3support +SOURCES = $${TARGET}plugin.cpp +TEMPLATE = lib + +unix:LIBS += -L"lib" -lvleaf +win32:LIBS += -L"lib" -lvleaf + +unix { + QMAKE_CXXFLAGS += -fexceptions -I/usr/include/libxml2 + QMAKE_LFLAGS += -fPIC +} + +win32 { + QMAKE_CXXFLAGS += -fexceptions -IC:\Include +} + +# finis + diff --git a/src/build_models/plugin_test.pro b/src/build_models/plugin_test.pro new file mode 100644 --- /dev/null +++ b/src/build_models/plugin_test.pro @@ -0,0 +1,45 @@ +# +# $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 . +# +# Copyright 2010 Roeland Merks. +# + + +BINDIR = ../../bin +CONFIG += plugin release +DEFINES = QTGRAPHICS # VLEAFPLUGIN +DESTDIR = $${BINDIR}/models +TARGET = test +HEADERS = ../simplugin.h $${TARGET}plugin.h +QMAKE_CXXFLAGS += -I.. +QT += qt3support +SOURCES = $${TARGET}plugin.cpp +TEMPLATE = lib + +unix:LIBS += -L"lib" -lvleaf +win32:LIBS += -L"lib" -lvleaf + +unix { + QMAKE_LFLAGS += -fPIC + QMAKE_CXXFLAGS += -fexceptions -I/usr/include/libxml2 +} + +win32 { + QMAKE_CXXFLAGS += -fexceptions -IC:\Include +} + diff --git a/src/build_models/simplugin.h b/src/build_models/simplugin.h new file mode 100644 --- /dev/null +++ b/src/build_models/simplugin.h @@ -0,0 +1,80 @@ +/* + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#ifndef _SIMPLUGIN_H_ +#define _SIMPLUGIN_H_ + +#include +#include +#include "cellbase.h" +#include "wallbase.h" + +class Parameter; + +#include +#include + + +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; + + // 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.1") +Q_DECLARE_METATYPE(SimPluginInterface *) + + +#endif \ No newline at end of file diff --git a/src/build_models/testplugin.cpp b/src/build_models/testplugin.cpp new file mode 100644 --- /dev/null +++ b/src/build_models/testplugin.cpp @@ -0,0 +1,66 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include + +#include "simplugin.h" + +#include "parameter.h" + +#include "wallbase.h" +#include "cellbase.h" +#include "testplugin.h" + +static const std::string _module_id("$Id$"); + +bool batch = false; + +// To be executed after cell division +void TestPlugin::OnDivide(ParentInfo &parent_info, CellBase &daughter1, CellBase &daughter2) { + +} + +void TestPlugin::SetCellColor(CellBase &c, QColor &color) { + + static QStringList colors; + if (colors.size()==0) { + colors << "red" << "green" << "blue"; + } + color = colors[c.Index()%colors.size()]; +} + + + +void TestPlugin::CellHouseKeeping(CellBase &c) { + + c.EnlargeTargetArea(par->cell_expansion_rate); + if (c.Area() > par->rel_cell_div_threshold * c.BaseArea() ) { + c.Divide(); + } +} + +void TestPlugin::CelltoCellTransport(Wall *w, double *dchem_c1, double *dchem_c2) {} +void TestPlugin::WallDynamics(Wall *w, double *dw1, double *dw2) {} +void TestPlugin::CellDynamics(CellBase *c, double *dchem) { } + + +Q_EXPORT_PLUGIN2(testplugin, TestPlugin) diff --git a/src/build_models/testplugin.h b/src/build_models/testplugin.h new file mode 100644 --- /dev/null +++ b/src/build_models/testplugin.h @@ -0,0 +1,60 @@ +/* + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + + +#include +#include +#include +#include "../simplugin.h" + + +class TestPlugin : public QObject, SimPluginInterface { + Q_OBJECT + Q_INTERFACES(SimPluginInterface); + +public: + virtual QString ModelID(void) { return QString( "Test model" ); } + + // 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 0; } +}; + + + + diff --git a/src/canvas.cpp b/src/canvas.cpp new file mode 100644 --- /dev/null +++ b/src/canvas.cpp @@ -0,0 +1,1509 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +//Added by qt3to4: +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pardialog.h" +#include "parameter.h" +#include "canvas.h" +#include "node.h" +#include "nodeset.h" +#include "nodeitem.h" +#include "cellitem.h" +#include "wallitem.h" +#include "mesh.h" +#include "xmlwrite.h" +#include "miscq.h" +#include "OptionFileDialog.h" +#include +#include +#include "modelcatalogue.h" + +#include + +// Include VIB and PSB logos +#include "psb.xpm" +#include "cwi.xpm" + +static const std::string _module_id("$Id$"); + +// We use a global variable to save memory - all the brushes and pens in +// the mesh are shared. + +#define QUOTE_ME(s) QUOTE_ME_2NDLEV(s) +#define QUOTE_ME_2NDLEV(s) #s + +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 list = scene()->items(); + QList::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::contentsMousePressEvent(QMouseEvent* e) +void FigureEditor::mousePressEvent(QMouseEvent* e) +{ + static QList selected; + emit MousePressed(); + + //QPointF p = matrix().inverted().map(e->pos()); + QPointF p = mapToScene(e->pos()); + + NodeItem *item; + +#ifdef QDEBUG + qDebug() << endl << "MousePressEvent location: (" << p.x() << "," << p.y() << ")." << endl; + qDebug() << "Magnification: " << Cell::Magnification() << endl; + qDebug() << "Offsets: " << Cell::Offset() << endl; +#endif + + + /* if (dynamic_cast
(parent())->RotationModeP()) { + // if in rotation mode, exit it upon a mouse click + dynamic_cast
(parent())->ExitRotationMode(); + }*/ + + QList l=scene()->items(p); + +#ifdef QDEBUG + qDebug() << "MousePressEvents, items: " << l.size() << endl; + qDebug() << "Mouse button modifier: " << e->modifiers() << endl; +#endif + + + /* if (rotation_mid_point) { + + // calculate rotation angle alpha + QPointF q_v1 = intersection_line->line().p2(); + - intersection_line->line().p1(); + QPointF q_v2 = angle_line->line().p2(); + - angle_line->line().p1(); + + Vector v1(q_v1.x(),q_v1.y()); + Vector v2(q_v2.x(),q_v2.y()); + + cerr << "v1 = " << v1 << endl; + cerr << "v2 = " << v2 << endl; + + + rot_angle = v1.SignedAngle(v2); + cerr << "Angle is " << rot_angle << endl; + + const QPointF &q_c(angle_line->line().p1()); + Vector center = Vector(q_c.x(), q_c.y()) / Cell::Factor() - Cell::Offset(); + + cerr << "Center is " << center << endl; + + mesh.Rotate(rot_angle, center); + delete angle_line; + angle_line=0; + delete intersection_line; + intersection_line=0; + + viewport()->setMouseTracking( FALSE ); + dynamic_cast
(parent())->Plot(); + + + return; + + }*/ + + + 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::Iterator it=l.begin(); it!=l.end(); ++it) { + /*if ( (*it)->rtti() == imageRTTI ) { + ImageItem *item= (ImageItem*)(*it); + if ( !item->hit( p ) ) + continue; + }*/ + cerr << typeid(**it).name() << endl; + + if ( !strcmp(typeid(**it).name(),"8NodeItem")) { + //moving = dynamic_cast(*it); + //moving = *it; + //moving_start = p; + + stringstream data_strstream; + data_strstream << (dynamic_cast(*it))->getNode(); + dynamic_cast
(parent())->UserMessage(QString(data_strstream.str().c_str())); + + (dynamic_cast(*it))->OnClick(e->button()); + } + else + if ( !strcmp(typeid(**it).name(),"8CellItem") ) { + + Cell &c=((dynamic_cast(*it))->getCell()); + //static int old_stride=100; + //if (!c.Source()) { + /* if (!c.Source()) { + c.SetChemical(0,par.cellsource); + //flag=true; + //c.SetSource(0,par.cellsource); + //c.Fix(); + //cerr << "Setting source\n"; + + } else { + //c.UnsetSource(); + cerr << "Unsetting source\n"; + } */ + // OnClick to be defined in end-user code + c.OnClick(e); + } else { + if ( !strcmp(typeid(**it).name(),"8WallItem") ) { + (dynamic_cast(*it))->OnClick(e); + } + } + } + + FullRedraw(); + moving = 0; +} + +//void FigureEditor::contentsMouseMoveEvent(QMouseEvent* e) +void FigureEditor::mouseMoveEvent(QMouseEvent* e) +{ + + // User choose "rotation mode" and we can rotate the object around its center of mass + if (dynamic_cast
(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() ); + + //viewport()->setMouseTracking( FALSE ); + dynamic_cast
(parent())->Plot(0); + FullRedraw(); + return; + } + if ( moving ) { + //QPoint p = matrix().inverted().map(e->pos()); + 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 (angle_line) { + + QPointF sp = angle_line -> line().p1(); // startpoint + //QPointF ep = matrix().inverted().map(e->pos()); // endpoint + QPointF ep = mapToScene(e->pos()); // endpoint + angle_line -> setLine( QLineF(sp, ep) ); + scene()->update(); + + } else { */ + + if ( intersection_line ) { + + QPointF sp = intersection_line -> line().p1(); // startpoint + //QPointF ep = matrix().inverted().map(e->pos()); // endpoint + 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 ) { + + cerr << "Trying to cut leaf\n"; + QPointF sp = intersection_line -> line().p1(); // startpoint + //QPointF ep = matrix().inverted().map(e->pos()); // endpoint + QPointF ep = mapToScene(e->pos()); + + intersection_line -> setLine( QLineF(sp, ep) ); + intersection_line -> show(); + + vector intersected_cells = getIntersectedCells(); + + // no cells selected, do nothing + if (intersected_cells.size()==0) { + cerr << "No cells detected :-( \n"; + return; + } + + + Vector startpoint = Vector(sp.x(), sp.y()) / Cell::Factor() - Cell::Offset(); + Vector endpoint = Vector(ep.x(), ep.y()) / Cell::Factor() - Cell::Offset(); + + // Mesh &m(intersected_cells.front()->getCell().getMesh()); + NodeSet *node_set = new NodeSet; + + for (vector::iterator it = intersected_cells.begin(); + it != intersected_cells.end(); + it++) { + + //(*it)->Mark(); + (*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(); + + cerr << "Dividing Cell " << c.Index() << endl; + c.DivideOverGivenLine( startpoint, endpoint, true, node_set); + } + + node_set->CleanUp(); + mesh.AddNodeSet(node_set); + + cerr << "Done DivideOverGivenLine\n"; + mesh.TestIllegalWalls(); + // Do the actual cutting and removing + if (intersected_cells.size()) { + // Mesh &m(intersected_cells.front()->getCell().getMesh()); + 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 cells_attached_to_nodeset = node_set->getCells(); + for ( list::iterator c = cells_attached_to_nodeset.begin(); + c != cells_attached_to_nodeset.end(); + c++) { + (*c)->SetBoundary(Cell::SAM); + } + + + + cerr << "Done CutAwayBelowLine\n"; + mesh.TestIllegalWalls(); + mesh.RepairBoundaryPolygon(); + cerr << "Done RepairBoundaryPolygon\n"; + mesh.TestIllegalWalls(); + mesh.CleanUpWalls(); + cerr << "Done CleanUpWalls\n"; + mesh.TestIllegalWalls(); + } + + dynamic_cast
(parent())->Plot(); + + cerr << "NodeSet of cutting line: " << *node_set << endl; + + } + } else + if (e->button()==Qt::RightButton) { + + if (intersection_line /* && !angle_line*/) { + + //QPointF p = matrix().inverted().map(e->pos()); + QPointF p = mapToScene(e->pos()); + QPointF sp = intersection_line->line().p1(); + + viewport()->setMouseTracking( TRUE ); + /* angle_line = new QGraphicsLineItem( 0, scene() ); + angle_line->setPen( QPen( QColor("Blue"), 3, Qt::DashLine ) ); + angle_line->setLine( QLineF(sp, p) ); + angle_line->setZValue( 100 ); + angle_line->show(); + */ + } + + } +} + + +// returns a vector of pointer to cells colliding with intersection line +vector FigureEditor::getIntersectedCells(void) { + + vector colliding_cells; + + QList l = intersection_line->collidingItems( ); + + cerr << "l.size() = " << l.size() << endl; + for (QList::Iterator it=l.begin(); it!=l.end(); ++it) { + + cerr << typeid(**it).name() << endl; + + if ( !strcmp(typeid(**it).name(),"8CellItem") ) { + + colliding_cells.push_back(dynamic_cast(*it)); + + } + } + + delete intersection_line; + intersection_line = 0; + return colliding_cells; + +} + +void FigureEditor::FullRedraw(void) { + QList rl; + rl << sceneRect(); + updateScene(rl); +} + + +NodeItem *FigureEditor::selectedNodeItem(QList graphicItems) const +{ + NodeItem *nodeItem = 0; + // graphicItems is a list of the QgraphicItems under the mouse click event + QList::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(*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
(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) + + //canvas(c) +{ + editor = new FigureEditor(canvas,mesh, this); + cerr << "Interactive = " << editor->isEnabled(); + working_dir = 0; + QObject::connect( editor, SIGNAL(MousePressed()), this, SLOT(PauseIfRunning())); + QObject::connect( editor, SIGNAL(MouseReleased()), this, SLOT(ContIfRunning())); + QMenuBar* menu = menuBar(); + + Q3PopupMenu* file = new Q3PopupMenu( menu ); + // file->insertItem("&Fill canvas", this, SLOT(init()), Qt::CTRL+Qt::Key_F); + 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->insertItem("&New view", this, SLOT(newView()), Qt::CTRL+Qt::Key_N); + 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("Read ¶meters", this, SLOT(readPars())); + //file->insertItem("&Write parameters", this, SLOT(savePars())); + + 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("Randomize PIN1 Transporters", this, SLOT( RandomizeMesh()) ); + edit->insertItem("Cut away SAM", this, SLOT( CutSAM() )); + menu->insertItem("&Edit", edit); + + run = new Q3PopupMenu( menu ); + // edit->insertItem("Add &Polygon", this, SLOT(addPolygon()), ALT+Key_P); + //edit->insertItem("Add &Line", this, SLOT(addLine()), ALT+Key_L); + running = false; + paused_id = run->insertItem("&Simulation paused", this, SLOT(togglePaused()), Qt::Key_S); + run->setItemChecked(paused_id, FALSE); + + //run->insertItem("&Divide Cell", this, SLOT(Divide()), Qt::CTRL+Qt::Key_D); + // run->insertItem("&Restart Simulation", this, SLOT(RestartSim())); + menu->insertItem("&Run", run); + + view = new Q3PopupMenu( menu ); + //view->insertItem("&Enlarge", this, SLOT(enlarge()), SHIFT+CTRL+Key_Plus); + //view->insertItem("Shr&ink", this, SLOT(shrink()), SHIFT+CTRL+Key_Minus); + view->insertItem("&Zoom in", this, SLOT(zoomIn()), Qt::CTRL+Qt::Key_Equal); + view->insertItem("Zoom &out", this, SLOT(zoomOut()), Qt::CTRL+Qt::Key_Minus); + view->insertSeparator(); + com_id = view->insertItem("Show cell ¢ers", this, SLOT(toggleShowCellCenters())); + view->setItemChecked(com_id, FALSE); + //view->insertItem("Cell monitor", this, SLOT(cellmonitor())); + 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, TRUE); + cell_number_id = view->insertItem("Show cell numbers", this, SLOT(toggleCellNumbers())); + view->setItemChecked(cell_number_id, TRUE); + 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())); + //only_boundary_id = view->insertItem("Show only leaf &boundary", 0,0); + 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 ); + /* dbf_id = options->insertItem("Double buffer", this, SLOT(toggleDoubleBuffer())); + options->setItemChecked(dbf_id, TRUE);*/ + dyn_cells_id = options->insertItem("Cell growth", this, SLOT(toggleDynCells())); + options->setItemChecked(dyn_cells_id, true); + + options->insertItem("Edit ¶meters", this, SLOT(EditParameters()), Qt::CTRL+Qt::Key_E); + + rotation_mode_id = options->insertItem("Rotate leaf", this, SLOT(EnterRotationMode()), Qt::CTRL + Qt::SHIFT + Qt::Key_R); + options->setItemChecked(rotation_mode_id, false); + + menu->insertItem("&Options",options); + + // Menu of models + modelmenu = new QMenu( menu ); + menu->insertItem("&Models", modelmenu); + + + menu->insertSeparator(); + + helpmenu = new Q3PopupMenu( menu ); + tooltips_id = helpmenu->insertItem("Show Cell&Info", this, SLOT(Refresh())); + helpmenu->setItemChecked(tooltips_id, true); + helpmenu->insertSeparator(); + + helpmenu->insertItem("&About", this, SLOT(help()) ); //, Key_F1); + //help->insertItem( "What's &This", this , SLOT(whatsThis()), SHIFT+Key_F1); + menu->insertItem("&Help",helpmenu); + //QSlider *flux_arrow_slider = new QSlider( 0, 10000, 1, 100, Qt::Horizontal, this, "flux arrow size"); + //QSpinBox *stride = new QSpinBox( 1, 1000, 1, this, "stride"); + + + //QGridLayout *controlgrid = new QGridLayout( this, 1, 2, 10); + // 2x1, 10 pixel border + + //controlgrid->addWidget( stride, 0, 0); + //controlgrid->addWidget( flux_arrow_slider, 0, 1); + //controlgrid->setColStretch( 1, 10 ); + + + //flux_arrow_size = 1.; + //flux_arrow_slider -> setMinimumSize( 200,50); + //connect( flux_arrow_slider, SIGNAL( valueChanged( int ) ), this, SLOT( setFluxArrowSize(int) ) ); + + 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; + + // A little bit of PSB/CWI decoration ;-) + /*dockwindow = new Q3DockWindow(); + addDockWindow(dockwindow); + QLabel *viblab = new QLabel; viblab->setPixmap(QPixmap(cwi_xpm)); + QString virtleafstring("

The Virtual Leaf

\n
Model: %1
"); + QLabel *virtleaf = new QLabel(virtleafstring.arg(mesh.ModelID())); + QLabel *psblab = new QLabel; psblab->setPixmap(QPixmap(PSB_xpm)); + dockwindow->setHorizontalStretchable(true); + dockwindow->boxLayout()->addWidget(viblab);//,Qt::AlignLeft); + dockwindow->boxLayout()->addStretch(); + dockwindow->boxLayout()->addWidget(virtleaf);//, Qt::AlignHCenter); + dockwindow->boxLayout()->addStretch(); + dockwindow->boxLayout()->addWidget(psblab);//, Qt::AlignRight);*/ + //QString virtleafstring("

The Virtual Leaf

\n
Model: %1
"); + 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() { + + // bool timer_active; + /* if (timer_active=timer->isActive()) + imer->stop();*/ + 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(); + /* if (timer_active) + timer->start( 0 );*/ + +} + + +void Main::saveStateXML() { + + + // bool timer_active; + // if (timer_active=timer->isActive()) + // timer->stop(); + 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() { + + + // bool timer_active; + // if (timer_active=timer->isActive()) + // timer->stop(); + 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::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); + cerr << "Reading done.\n"; + XMLReadSettings(settings); + xmlFree(settings); + Cell::SetMagnification(1); + Cell::setOffset(0,0); + + FitLeafToCanvas(); + + currentFile = QString(filename); + + Plot(); + QString status_message = QString("Succesfully read leaf from file %1. Time is %2 h.\n").arg(currentFile).arg(mesh.getTimeHours().c_str()); + cerr << status_message.toStdString() << endl; + setCaption(caption_with_file.arg(filename)); + statusBar()->message(status_message); + emit ParsChanged(); + cerr << "Done. Returning 0.\n"; + 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::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 ); + + + next_file = xml_files.back(); + + next_file = currentFile_path+"/"+next_file; + + readStateXML((const char*)next_file); + } + + +} + + +void Main::readFirstStateXML() { + + // 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 ); + + + next_file = xml_files.front(); + + next_file = currentFile_path+"/"+next_file; + + readStateXML((const char*)next_file); + } + + +} + +void Main::readStateXML() { + + // extern Mesh mesh; + + stopSimulation(); + cerr << "Trying to open an OptionFileDialog\n"; + OptionFileDialog *fd = new OptionFileDialog( this, "read dialog", TRUE ); + fd->setMode( OptionFileDialog::ExistingFile ); + fd->setFilter( "XML files (*.xml)"); + if (working_dir) { + fd->setDir(*working_dir); + } + QString fileName; + if ( fd->exec() == QDialog::Accepted ) { + + fileName = fd->selectedFile(); + if (working_dir) { + delete working_dir; + } + working_dir = fd->dir(); + + if (readStateXML((const char *)fileName,fd->readGeometryP(), fd->readParametersP()) ) + return readStateXML(); // user can try again + +// try { +// mesh.XMLRead((const char *)fileName); +// currentFile = fileName; +// } catch (const char *error_message) { +// QMessageBox mb( "Read leaf from XML file", +// QString(error_message), +// QMessageBox::Critical, +// QMessageBox::Ok | QMessageBox::Default, +// QMessageBox::NoButton, +// QMessageBox::NoButton); +// mb.exec(); +// return readStateXML(); // user can try again +// } + +// Vector bbll,bbur; +// mesh.BoundingBox(bbll,bbur); + +// //FitLeafToCanvas(); +// Plot(); + } +} + + +void Main::clear() +{ + editor->clear(); +} + +void Main::help() +{ + static QMessageBox* about = new QMessageBox + ( "About", + "

Virtual Leaf

" + "

" + "Leaf growth computer model
" + "(c) 2005-2007, Roeland Merks
" + "VIB Department Plant Systems Biology
" + "Ghent, Belgium
" + "(c) 2008-2009, Roeland Merks
" + "CWI/NCSB, Amsterdam, Netherlands
" + "Pilot release for WUR/Biometris, 21-10-2009
", + QMessageBox::Information, 1, 0, 0, this, 0, FALSE ); + about->setButtonText( 1, "Dismiss" ); + about->show(); +} + + void Main::aboutQt() + { + QMessageBox::aboutQt( this, "Virtual Leaf" ); + } + +/* void Main::toggleDoubleBuffer() +{ + bool s = !options->isItemChecked(dbf_id); + options->setItemChecked(dbf_id,s); + canvas.setDoubleBuffering(s); +} +*/ + void Main::toggleShowCellCenters() + { + //bool s = !view->isItemChecked(com_id); + //view->setItemChecked(com_id,s); + Plot(); + } + + void Main::toggleShowWalls() + { + //bool s = !view->isItemChecked(cell_walls_id); + //view->setItemChecked(cell_walls_id,s); + Plot(); + } +void Main::toggleShowApoplasts() +{ + Plot(); +} + void Main::toggleShowNodes() + { + //bool s = !view->isItemChecked(mesh_id); + //view->setItemChecked(mesh_id,s); + Plot(); + } + + void Main::toggleNodeNumbers(void) { + + //bool s = !view->isItemChecked(node_number_id); + //view->setItemChecked(node_number_id,s); + Plot(); + } + + void Main::toggleCellNumbers(void) { + + //bool s = !view->isItemChecked(cell_number_id); + //view->setItemChecked(cell_number_id,s); + Plot(); + } + + void Main::toggleCellAxes(void) { + + //bool s = !view->isItemChecked(cell_axes_id); + //view->setItemChecked(cell_axes_id,s); + Plot(); + } + + void Main::toggleCellStrain(void) { + + //bool s = !view->isItemChecked(cell_strain_id); + //view->setItemChecked(cell_strain_id,s); + Plot(); + } + + void Main::toggleShowFluxes(void) { + + //bool s = !view->isItemChecked(fluxes_id); + //view->setItemChecked(fluxes_id,s); + Plot(); + } + + void Main::toggleShowBorderCells() + { + //bool s = !view->isItemChecked(border_id); + //view->setItemChecked(border_id,s); + Plot(); + } + +void Main::toggleHideCells(void) { + Plot(); + editor->FullRedraw(); +} + void Main::toggleMovieFrames() + { + //bool s = !view->isItemChecked(movie_frames_id); + //view->setItemChecked(movie_frames_id,s); + } + + void Main::toggleLeafBoundary() + { + //bool s = !view->isItemChecked(only_boundary_id); + //view->setItemChecked(only_boundary_id,s); + } + + void Main::toggleDynCells() + { + //bool s = !options->isItemChecked(dyn_cells_id); + //options->setItemChecked(dyn_cells_id,s); + } + + + void Main::startSimulation(void) { + //run->setItemChecked(paused_id, false); + timer->start( 0 ); + statusBar()->message("Simulation started"); + running = true; + } + + void Main::stopSimulation(void) { + //run->setItemChecked(paused_id, true); + timer->stop(); + cerr << "Stopping simulation\n"; + statusBar()->message("Simulation paused"); + running = false; + } + + void Main::togglePaused() + { + bool s = run->isItemChecked(paused_id); + if (s) { + cerr << "Calling start simulation\n"; + startSimulation(); + } else { + cerr << "Calling stop simulation\n"; + stopSimulation(); + } + } + + void Main::setFluxArrowSize(int size) { + + flux_arrow_size = size/100.; + } + + + void Main::enlarge() + { + //canvas.resize(canvas.width()*4/3, canvas.height()*4/3); + 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::scrollBy(int dx, int dy) { + editor->scrollBy(dx,dy); + }*/ + + 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); + cerr << "bbll = " << bbll << endl; + cerr << "bbur = " << bbur << endl; + double cw = (bbur.x - bbll.x); + double ch = (bbur.y - bbll.y); + QPainter pp(printer); + QRect vp=pp.viewport(); + cerr << "Paper width = " << vp.width() << " x " << vp.height() << endl; + + + // Note that Cell is also translated... + pp.translate(-bbur.x,-bbur.y); + if (cw>ch) { + pp.scale(vp.width()/(2*cw*Cell::Magnification()), vp.width()/(2*cw*Cell::Magnification())); + } 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\n"; + // extern Mesh mesh; + mesh.Clear(); + Init(); + Plot(); + editor->FullRedraw(); + } + //startSimulation(); + } + + /* + void Main::SaveToGifAnim(void) { + + if (gifanim) { + + QPixmap image(canvas.width(), canvas.height()); + QPainter im(&image); + + + canvas.drawArea(QRect(0,0,canvas.width(),canvas.height()),&im,FALSE); + QFile conversionpipe; + conversionpipe.open ( IO_WriteOnly, popen("pngtopnm | ppmtogif > tmp.gif", "w") ); + image.save( &conversionpipe, "PNG"); + conversionpipe.close(); + + QFile readconvertedimage("tmp.gif"); + readconvertedimage.open(IO_ReadOnly); + int c; + while ( (c=readconvertedimage.getch())!=-1) { + gifanim->putch(c); + } + } + }*/ + + /* void Main::StartGifAnim(QString fname) { + + if (gifanim) { + QMessageBox::information( this, "Animation", + "Already making another animation." + "Please end it and try again.", + QMessageBox::Ok ); + } else { + + QString cmdline("gifsicle --multifile - > "); + cmdline += fname; + + gifanim = new QFile; + gifanim->open( IO_WriteOnly, + popen((const char *)cmdline, "w") ); + + */ /* QStringList cmdline; + cmdline << "gifsicle" << "--multifile" << "-"; + gifanim = new QProcess( cmdline ); + + cmdline.clear(); + cmdline << "pngtopnm"; + pngtopnm = new QProcess( cmdline ); + pngtopnm->start(); + + cmdline.clear(); + cmdline << "ppmtogif"; + ppmtogif = new QProcess( cmdline ); + ppmtogif->start();*/ + /* } + + }*/ + + /*void Main::EndGifAnim(void) { + + if (gifanim) + gifanim->close(); + + }*/ + + +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(); + + cerr << "editor->width() = " << editor->width() << endl; + cerr << "editor->height() = " << editor->height() << endl; + + cerr << "scale_factor = " << scale_factor << endl; + cerr << "scale_factor_x = " << scale_factor_x << endl; + cerr << "scale_factor_y = " << scale_factor_y << endl; + 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); +} + +void Main::CleanMesh(void) { + + mesh.SettoInitVals(); + mesh.setTime(0); + Plot(); + + editor->FullRedraw(); + + // repaint(); +} + +void Main::RandomizeMesh(void) { + + vector max_chem(Cell::NChem()); + vector max_transporters(Cell::NChem()); + + for (int i=0;isetItemChecked(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); +} + +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); + + return MainBase::XMLSettingsTree(); +} + +/* finis */ + diff --git a/src/canvas.h b/src/canvas.h new file mode 100644 --- /dev/null +++ b/src/canvas.h @@ -0,0 +1,286 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#ifndef _CANVAS_H_ +#define _CANVAS_H_ + +#include +#include +#include +#include +#include +#include + +#include +#include + +//Added by qt3to4: +#include +#include +#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 +#include + +#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 contentsMousePressEvent(QMouseEvent*); + void contentsMouseMoveEvent(QMouseEvent*); + void contentsMouseReleaseEvent(QMouseEvent*);*/ + void mousePressEvent(QMouseEvent*); + void mouseMoveEvent(QMouseEvent*); + void mouseReleaseEvent(QMouseEvent*); + void wheelEvent(QWheelEvent *event); + void scaleView(qreal scaleFactor); + + vector getIntersectedCells(void); + void insertNode(QPointF p); + NodeItem *selectedNodeItem(QList 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 Save(const char *fname, const char *format); + /*void StartGifAnim(QString fname); + void SaveToGifAnim(void); + void EndGifAnim(void);*/ + + void FitCanvasToWindow(); + void FitLeafToCanvas(void); + + + public slots: + + void help(); + void TimeStepWrap(); + //void scrollBy(int dx, int dy); + void togglePaused(); + void setFluxArrowSize(int size); + //void Divide(void); + void RestartSim(void); + //void toggleDoubleBuffer(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())); + } + + } + void ExitRotationMode(void) { + UserMessage("Exited rotation mode.",2000); + + options->setItemChecked(rotation_mode_id, false); + if (editor) + disconnect(editor, SIGNAL(MousePressed()), this, SLOT(ExitRotationMode())); + } + + 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 saveStateXML(); + void snapshot(); + void savePars(); + void readPars(); + void clear(); + void init(); + virtual void CutSAM() { MainBase::CutSAM(); Refresh();} +/* void cellmonitor() { */ +/* PlotDialog *plot = new PlotDialog(this); */ +/* cerr << "Attempting to launch a cell monitor\n"; */ +/* } */ + + //void addPolygon(); + //void addLine(); + + void enlarge(); + void shrink(); + void zoomIn(); + void zoomOut(); + + void CleanMesh(); + void RandomizeMesh(); + + signals: + void SimulationDone(void); + void ParsChanged(void); + + protected: + Mesh &mesh; + + private: + NodeSet *node_set; + FigureEditor *editor; + //QCanvas& canvas; + 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 diff --git a/src/cell.cpp b/src/cell.cpp new file mode 100644 --- /dev/null +++ b/src/cell.cpp @@ -0,0 +1,1989 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include "cell.h" +#include "node.h" +#include "mesh.h" +#include "tiny.h" +#include "nodeset.h" +#include "cellitem.h" +#include "nodeitem.h" +#include "qcanvasarrow.h" +#include "parameter.h" + +#include + +static const std::string _module_id("$Id$"); + +extern Parameter par; + +double Cell::factor=1.; +double Cell::offset[3]={0,0,0}; + +Cell::Cell(void) : CellBase() { + + m=0; + +} + +Cell::Cell(double x, double y, double z) : CellBase(x,y,z) { + + m=0; + +} + +Cell::Cell(const Cell &src) : CellBase(src) { + + m=src.m; +} + +bool Cell::Cmp(Cell *c) const { return this->Index() < c->Index(); } +bool Cell::Eq(Cell *c) const { return this->Index() == c->Index(); } + +Cell Cell::operator=(const Cell &src) { + CellBase::operator=(src); + m=src.m; + return *this; +} +//Cell(void) : CellBase() {} + +void Cell::DivideOverAxis(Vector axis) { + + // Build a wall + // -> find the position of the wall + + // better look for intersection with a simple line intersection algorithm as below? + // this leads to some exceptions: e.g. dividing a horizontal rectangle. + // leaving it like this for the time being + + if (dead) return; + + Vector centroid=Centroid(); + double prev_cross_z=(axis * (centroid - *(nodes.back()) ) ).z ; + + ItList new_node_locations; + + for (list::iterator i=nodes.begin(); + i!=nodes.end(); + i++) { + + // cross product to detect position of division + Vector cross = axis * (centroid - *(*i)); + + if (cross.z * prev_cross_z < 0 ) { + + new_node_locations.push_back(i); + + } // else { + // //cerr << "cross.z * prev_cross_z = " << cross.z * prev_cross_z << endl; + // } + + prev_cross_z=cross.z; + } + + DivideWalls(new_node_locations, centroid, centroid+axis); + +} +double Cell::MeanArea(void) { + return m->MeanArea(); +} + + +void Cell::Apoptose(void) { + + // First kill walls + cerr << "This is cell " << Index() << "\n"; + cerr << "Number of walls: " << walls.size() << endl; + + for (list::iterator w=walls.begin(); + w!=walls.end(); + w++) { + cerr << "Before apoptosis, wall " << (*w)->Index() << " says: c1 = " << (*w)->c1->Index() << ", c2 = " << (*w)->c2->Index() << endl; + } + for (list::iterator w=walls.begin(); + w!=walls.end(); + w++) { + + bool illegal_flag = false; + if ((*w)->c1 == (*w)->c2 ) + illegal_flag=true; + if ((*w)->c1 == this) { + + // invert wall? + (*w)->c1 = (*w)->c2; + (*w)->c2 = m->boundary_polygon; + + Node *n1 = (*w)->n1; + (*w)->n1 = (*w)->n2; + (*w)->n2 = n1; + + } else { + (*w)->c2 = m->boundary_polygon; + } + + if (illegal_flag && (*w)->c1==(*w)->c2) { + cerr << "I created an illegal wall.\n"; + } + if ( ((*w)->N1()->DeadP() || (*w)->N2()->DeadP()) || + ((*w)->C1() == (*w)->C2() ) ){ + // kill wall + cerr << "Killing wall.\n"; + (*w)->Kill(); + if ((*w)) { + cerr << "Wall " << (*w)->Index() << " says: c1 = " << (*w)->c1->Index() << ", c2 = " << (*w)->c2->Index() << endl; + } + (*w)=0; + } else { + cerr << "Not killing wall.\n"; + cerr << "Wall " << (*w)->Index() << " says: c1 = " << (*w)->c1->Index() << ", c2 = " << (*w)->c2->Index() << endl; + } + + + + } + walls.remove(0); + + // Unregister me from my nodes, and delete the node if it no longer belongs to any cells + list superfluous_nodes; + for (list::iterator n=nodes.begin(); + n!=nodes.end(); + n++) { + + Node &no(*(*n)); + // locate myself in the node's owner list + list::iterator cellpos; + bool cell_found=false; + for (list::iterator nb=no.owners.begin(); + nb!=no.owners.end(); + nb++) { + if (nb->cell == this) { + cellpos = nb; + cell_found = true; + break; + } + } + + if (!cell_found) { + // I think this cannot happen; but if I am wrong, unpredictable things would happen. So throw an exception. + throw ("Cell not found in CellBase::Apoptose()\n\rPlease correct the code to handle this situation."); + } + + Neighbor tmp = *cellpos; + no.owners.erase(cellpos); + + // if node has no owners left, or only has a connection to special cell -1 (outside world), mark it as dead. + + if (no.owners.size()==0 || (no.owners.size()==1 && no.owners.front().cell->BoundaryPolP()) ) { + no.MarkDead(); + } else { + // register node with outside world + if (find_if( no.owners.begin(), no.owners.end(), + bind2nd ( mem_fun_ref(&Neighbor::CellEquals), m->boundary_polygon->Index() ) ) == no.owners.end() ) { + + tmp.cell = m->boundary_polygon; + no.owners.push_back(tmp); + } + } + } + + + + /* + // correct boundary polygon if this cell touches the boundary + + // find the first living boundary node after a dead node + bool node_found = false; + for (list::iterator n=nodes.begin(); + n!=nodes.end(); + n++) { + + Node &no(*(*n)); + + if (no.DeadP()) { + + list::iterator first_node = n; + if (++next_node == nodes.end()) first_node=nodes.begin(); + + if (!(*(*first_node)).DeadP() && ((*first_node)->boundary)) { + node_found=true; + break; + } + + } + } + + // locate it in the boundary_polygon + if (node_found) { + list::iterator insert_it = find(mesh->boundary_polygon->nodes.begin(), + mesh->boundary_polygon->nodes.end(), + ++first_node); + if (insert_it!=owners.end()) { + + if (insert_it==owners.end()) insert_it=owners.begin(); + + for (list::iterator n=insert_it; + n!=nodes.end(); + n++) { + + Node &no(*(*n)); + + mesh->boundary_polygon->nodes.insert( + + } + + + } + } */ + // mark cell as dead + MarkDead(); +} + +void Cell::ConstructConnections(void) { + + // Tie up the nodes of this cell, assuming they are correctly ordered + + //cerr << "Constructing connections of cell " << index << endl; + + for (list::iterator i=nodes.begin(); + i!=nodes.end(); + i++) { + + //cerr << "Connecting node " << *i << endl; + //cerr << "Node " << *i << endl << " = " << *(*i) << endl; + // 1. Tidy up existing connections (which are part of this cell) + if ((*i)->owners.size()>0) { + list::iterator neighb_with_this_cell= + // remove myself from the neighbor list of the node + find_if((*i)->owners.begin(), + (*i)->owners.end(), + bind2nd(mem_fun_ref( &Neighbor::CellEquals ),this->Index() ) ); + if (neighb_with_this_cell!=(*i)->owners.end()) + (*i)->owners.erase(neighb_with_this_cell); + } + + Node *previous; + if (i!=nodes.begin()) { + list::iterator previous_iterator=i; + previous_iterator--; + previous=*previous_iterator; + } else { + previous=nodes.back(); + } + + Node *next; + list::iterator next_iterator=i; + next_iterator++; + if (next_iterator==nodes.end()) { + next=nodes.front(); + } else { + next=*next_iterator; + } + + //cerr << "[" << *i << "]"; + //if (*i==10 || *i==11) { + //cerr << "previous = " << previous << ", next = " << next << endl; + //} + //if (*i!=10 && *i!=11) + //cerr << "Node " << *i << endl << " = " << *(*i) << endl; + (*i)->owners.push_back( Neighbor( this, previous, next ) ); + // if (*i==50 || *i==51) { + //cerr << "Node " << *i << ".size() = " << (*i)->owners.size() << endl; + // } + } +} + + +/*! \brief Divide the cell over the line v1-v2. + + \param v1: First vertex of line. + \param v2: Second vertex of line. + \param fixed_wall: If true: wall will be set to "fixed" (i.e. not motile) + \return: true if the cell divided, false if not (i.e. no intersection between v1 and v2, and the cell) + */ +bool Cell::DivideOverGivenLine(const Vector v1, const Vector v2, bool fix_cellwall, NodeSet *node_set ) { + + if (dead) return false; + + + + // check each edge for intersection with the line + ItList new_node_locations; + + cerr << "Cell " << Index() << " is doing DivideOverGivenLine \n"; + for (list::iterator i=nodes.begin(); + i!=nodes.end(); + i++) { + + Vector v3 = *(*i); + list::iterator nb=i; + nb++; + if (nb == nodes.end()) { + nb = nodes.begin(); + } + Vector v4 = *(*nb); + + double denominator = + (v4.y - v3.y)*(v2.x - v1.x) - (v4.x - v3.x)*(v2.y - v1.y); + + double ua = + ((v4.x - v3.x)*(v1.y - v3.y) - (v4.y - v3.y)*(v1.x -v3.x))/denominator; + double ub = + ((v2.x - v1.x)*(v1.y-v3.y) - (v2.y- v1.y)*(v1.x - v3.x))/denominator; + + /* double intersec_x = v1.x + ua*(v2.x-v1.x); + double intersec_y = v1.y + ua*(v2.y-v1.y);*/ + + //cerr << "Edge " << *i << " to " << *nb << ": ua = " << ua << ", ub = " << ub << ": "; + // this construction with "TINY" should simulate open/closed interval <0,1] + if ( ( TINY < ua && ua < 1.+TINY ) && ( TINY < ub && ub < 1.+TINY ) ) { + // yes, intersection detected. Push the location to the list of iterators + new_node_locations.push_back(nb); + + } + } + + if (new_node_locations.size()<2) { + + cerr << "Line does not intersect with two edges of Cell " << Index() << endl; + cerr << "new_node_locations.size() = " << new_node_locations.size() << endl; + return false; + } + + ItList::iterator i = new_node_locations.begin(); + list< Node *>::iterator j; + cerr << "-------------------------------\n"; + cerr << "Location of new nodes: " << (**i)->Index() << " and "; + ++i; + j = *i; + if (j==nodes.begin()) j=nodes.end(); j--; + + cerr << (*j)->Index() << endl; + cerr << "-------------------------------\n"; + + if ( **new_node_locations.begin() == *j ) { + cerr << "Rejecting proposed division (cutting off zero area).\n"; + return false; + } + + DivideWalls(new_node_locations, v1, v2, fix_cellwall, node_set); + + return true; + +} + +// Core division procedure +void Cell::DivideWalls(ItList new_node_locations, const Vector from, const Vector to, bool fix_cellwall, NodeSet *node_set) { + + if (dead) return; + + bool boundary_touched_flag=false; + + // Step 0: keep some data about the parent before dividing + + ParentInfo parent_info; + parent_info.polarization = ReduceCellAndWalls( PINdir ); + parent_info.polarization.Normalise(); + parent_info.PINmembrane = SumTransporters(1); + parent_info.PINendosome = Chemical(1); + + //cerr << "Parent polarization before division: " << parent_info.polarization << endl; + + // Step 1: create a daughter cell + Cell *daughter=m->AddCell(new Cell()); + + // Step 2: Copy the basics of parent cell to daughter + for (int i=0;ichem[i]=chem[i]; + } + + //extern double auxin_account; + //auxin_account += daughter->chem[0]; + + for (int i=0;inew_chem[i]=new_chem[i]; + } + + + daughter->boundary=boundary; + daughter->m=m; + + daughter->target_area=target_area/2.; + + target_area/=2; + daughter->cellvec=cellvec; +// daughter->BaseArea() = base_area; + + + // Division currently only works for convex cells: i.e. if the division line + // intersects the cells at two points only. + if (new_node_locations.size()!=2) { + + // Note: if you would add the possibility of dividing non-convex + // cells, remember to update the code below. There are some + // fixed-size arrays over there! + + cerr << "Warning in Cell::Division: division of non-convex cells not fully implemented" << endl; + + // Reject the daughter cell and decrement the amount of cells + // again. We can do this here because it is the last cell added. + // Never, ever try to fully erase a cell elsewhere, because we + // make heavy use of cell indices in this project; if you erase a + // Cell somewhere in the middle of Mesh::Cells the indices will + // get totally messed up...! (e.g. the indices used in Nodes::cells) + + cerr << "new_node_locations.size() = " << new_node_locations.size() <index = " << daughter->index << endl; + cerr << "cells.size() = " << m->cells.size() << endl; + m->cells.pop_back(); + Cell::NCells()--; + m->shuffled_cells.pop_back(); + return; + } + + + // We can be sure we only need two positions here because divisions + // of non-convex cells are rejected above. + Vector new_node[2]; + Node *new_node_ind[2]; + + int new_node_flag[2]; + Edge div_edges[2]; + + int nnc=0; + + Wall *div_wall[4]; + double orig_length[2]; + for (int i=0;i<4;i++) { div_wall[i]=0; orig_length[i/2] = 0.; } + + // construct new Nodes at the intersection points + // unless they coincide with existing points + for ( ItList::const_iterator i=new_node_locations.begin(); + i!=new_node_locations.end(); + i++) { + + // intersection between division axis + // and line from this node to its predecessor + + // method used: http://astronomy.swin.edu.au/~pbourke/geometry/lineline2d/ + Vector v1 = from; + Vector v2 = to; + Vector v3 = *(**i); + + // get previous node + list::iterator nb=*i; + if (nb == nodes.begin()) { + nb = nodes.end(); + } + nb--; + Vector v4=*( *nb ); + + double denominator = + (v4.y - v3.y)*(v2.x - v1.x) - (v4.x - v3.x)*(v2.y - v1.y); + + double ua = + ((v4.x - v3.x)*(v1.y - v3.y) - (v4.y - v3.y)*(v1.x -v3.x))/denominator; + + double intersec_x = v1.x + ua*(v2.x-v1.x); + double intersec_y = v1.y + ua*(v2.y-v1.y); + + // construct a new node at intersec + // we construct a vector temporarily, + // until we are sure we are going to keep the node... + // Node count is damaged if we construct superfluous nodes + Vector *n=new Vector(intersec_x,intersec_y,0); + + div_edges[nnc].first=*nb; + div_edges[nnc].second=**i; + + // Insert this new Node if it is far enough (5% of element length) + // from one of the two existing nodes, else use existing node + // + // old, fixed value was: par.collapse_node_threshold = 0.05 + double collapse_node_threshold = 0.05; +#ifdef FLEMING + collapse_node_threshold = par.collapse_node_threshold; +#endif + + double elem_length = ( (*(**i)) - (*(*nb)) ).Norm(); + if ( ( *(**i) - *n ).Norm() < collapse_node_threshold * elem_length ) { + new_node_flag[nnc]=1; + new_node[nnc] = *(**i); + new_node_ind[nnc] = **i; + //cerr << **i << "\n" ; + } else + if ( (*(*nb) - *n).Norm() < collapse_node_threshold * elem_length ) { + new_node_flag[nnc]=2; + new_node[nnc] = *(*nb); + new_node_ind[nnc] = *nb; + } else { + new_node_flag[nnc]=0; + new_node[nnc] = *n; + } + + nnc++; + delete n; + } + + + for (int i=0;i<2;i++) { + + Cell *neighbor_cell=0; // we need this to split up the "Wall" objects. + + // for both divided edges: + // insert its new node into all cells that own the divided edge + // but only if it really is a new node: + if (new_node_flag[i]!=0) { + if (fix_cellwall) { + (new_node_ind[i])->fixed = true; + + // all this we'll do later for the node set :-) + /* (new_node_ind[i])->boundary = true; + (new_node_ind[i])->sam = true; + boundary = SAM; + daughter->boundary = SAM; + boundary_touched_flag = true; + */ + } + + } else { + + // (Construct a list of all owners:) + // really construct the new node (if this is a new node) + new_node_ind[i] = + m->AddNode(new Node (new_node[i]) ); + + + + // if a new node is inserted into a fixed edge (i.e. in the petiole) + // make the new node fixed as well + (new_node_ind[i])->fixed = (div_edges[i].first)->fixed && + (div_edges[i].second)->fixed; + + // Insert Node into NodeSet if the div_edge is part of it. + if ( + (div_edges[i].first->node_set && div_edges[i].second->node_set) && + (div_edges[i].first->node_set == div_edges[i].second->node_set)) + { + //cerr << "Inserting node into node set\n"; + div_edges[i].first->node_set->AddNode( new_node_ind[i] ); + } + + // if the new wall should be fixed (i.e. immobile, or moving as + // solid body), make it so, and make it part of the boundary. Using + // this to make a nice initial condition by cutting off part of a + // growing leaf. + + if (fix_cellwall) { + (new_node_ind[i])->fixed = true; + + // All this we'll do later for the node set only + /* (new_node_ind[i])->boundary = true; + (new_node_ind[i])->sam = true; + boundary_touched_flag = true; + boundary = SAM; + daughter->boundary = SAM;*/ + } + + // if new node is inserted into the boundary + // it will be part of the boundary, too + + new_node_ind[i]->UnsetBoundary(); + if ((div_edges[i].first->BoundaryP() && div_edges[i].second->BoundaryP()) && // Both edge nodes are boundary nodes AND + ((m->findNextBoundaryNode(div_edges[i].first))->Index() == div_edges[i].second->Index())){ // The boundary proceeds from first to second. + + #ifdef QDEBUG + qDebug() << "Index of the first node: " << div_edges[i].first->Index() << endl; + qDebug() << "Index of the second node: " << div_edges[i].second->Index() << endl; + qDebug() << "Boundary proceeds from: " << div_edges[i].first->Index() + << "to: " << (m->findNextBoundaryNode(div_edges[i].first))->Index() << endl << endl; + #endif + new_node_ind[i]->SetBoundary(); + + // We will need to repair the boundary polygon later, since we will insert new nodes + //cerr << "Boundary touched for Node " << new_node_ind[i]->Index() << "\n"; + boundary_touched_flag=true; + + // and insert it into the boundary_polygon + // find the position of the first node in the boundary + list::iterator ins_pos = find + (m->boundary_polygon->nodes.begin(), + m->boundary_polygon->nodes.end(), + div_edges[i].first); + // ... second node comes before or after it ... + if (*(++ins_pos!=m->boundary_polygon->nodes.end()? + ins_pos:m->boundary_polygon->nodes.begin())!=div_edges[i].second) { + + m->boundary_polygon->nodes.insert(((ins_pos--)!=m->boundary_polygon->nodes.begin()?ins_pos:(--m->boundary_polygon->nodes.end())), new_node_ind[i]); + + // .. set the neighbors of the new node ... + // in this case e.second and e.first are inverted + } else { + // insert before second node, so leave ins_pos as it is, + // that is: incremented + m->boundary_polygon->nodes.insert(ins_pos, new_node_ind[i]); + // .. set the neighbors of the new node ... + } + } + + list owners; + + // push all cells owning the two nodes of the divides edges + // onto a list + + copy((div_edges[i].first)->owners.begin(), + (div_edges[i].first)->owners.end(), + back_inserter(owners)); + copy((div_edges[i].second)->owners.begin(), + (div_edges[i].second)->owners.end(), + back_inserter(owners)); + + + // find first non-self duplicate in the owners: + // cells owning the same two nodes + // share an edge with me + owners.sort( mem_fun_ref( &Neighbor::Cmp ) ); + + + #ifdef QDEBUG + list unique_owners; + copy(owners.begin(), owners.end(), back_inserter(unique_owners)); + unique_owners.unique( mem_fun_ref( &Neighbor::Eq ) ); + qDebug() << "The dividing edge nodes: " << div_edges[i].first->Index() << " and " << div_edges[i].second->Index() << " are owned by cells: "; + // spit out each owners' cell index + foreach(Neighbor neighbor, unique_owners){ + qDebug() << neighbor.cell->Index() << " "; + } + qDebug() << endl; + #endif + + // Search through the sorted list of edge node owners looking for duplicate pairs. Each pair represents an actual edge owner. + list edge_owners; + list::iterator it; + for (it=owners.begin(); it!=owners.end(); it++) { + it = adjacent_find(it, owners.end(), neighbor_cell_eq); + if (it == owners.end()) break; // bail if reach the end of the list + #ifdef QDEBUG + qDebug() << "Considering: " << it->cell->Index() << " as a possible edge owner." << endl; + #endif + if (it->cell->Index() != this->Index()) { + #ifdef QDEBUG + qDebug() << "Adding: " << it->cell->Index() << " to the list of edge owners." << endl; + #endif + edge_owners.push_back(*it); + } + } + + if (edge_owners.size() > 1){ + // Remove the boundary polygon - if its there + list::iterator it; + if ((it = find_if (edge_owners.begin(), edge_owners.end(), bind2nd(mem_fun_ref(&Neighbor::CellEquals), -1))) != edge_owners.end()) { + #ifdef QDEBUG + qDebug() << "deleating: " << it->cell->Index() << " from the list of edge owners." << endl; + #endif + edge_owners.erase(it); + } + } + + #ifdef QDEBUG + qDebug() << "The edge owners list has: " << edge_owners.size() << " elements" << endl; + #endif + + // Since the list should always contain exactly one element, pass it on as an iterator + list::iterator c = (edge_owners.size() != 0) ? edge_owners.begin() : edge_owners.end(); + + // (can we have more than one neighboring cell here??) + if (c!=owners.end()) { + neighbor_cell = c->cell; + if (!c->cell->BoundaryPolP()) { + + // find correct position in the cells node list + // to insert the new node + list::iterator ins_pos = find + (neighbor_cell->nodes.begin(), + neighbor_cell->nodes.end(), + div_edges[i].first); + + neighbor_cell->nodes.insert(ins_pos, new_node_ind[i]); + neighbor_cell->ConstructConnections(); + + // give walls to daughter later + } + } else { + neighbor_cell = 0; + } + } + + // Split the Wall with the neighboring cell + + // if the neighbor cell has not yet been identified above, do it now + if (neighbor_cell == 0) { + + list owners; + + // push all cells owning the two nodes of the divides edges + // onto a list + copy((div_edges[i].first)->owners.begin(), + (div_edges[i].first)->owners.end(), + back_inserter(owners)); + copy((div_edges[i].second)->owners.begin(), + (div_edges[i].second)->owners.end(), + back_inserter(owners)); + + + // find first non-self duplicate in the owners: + // cells owning the same two nodes + // share an edge with me + owners.sort( mem_fun_ref( &Neighbor::Cmp ) ); + + list::iterator c; + for (c=owners.begin(); + c!=owners.end(); + c++) { + c=adjacent_find(c,owners.end(),neighbor_cell_eq); + if (c->cell->Index() != this->Index() || c==owners.end()) break; + } + + if (c!=owners.end()) + neighbor_cell = c->cell; + else + neighbor_cell = 0; + } + + + if (neighbor_cell /* && !neighbor_cell->BoundaryPolP() */) { + + //cerr << "Cell " << index << " says: neighboring cell is " << neighbor_cell->index << endl; + + /*************** 1. Find the correct wall element ********************/ + + list::iterator w, start_search; + w = start_search = walls.begin(); + do { + // Find wall between this cell and neighbor cell + w = find_if( start_search, walls.end(), bind2nd (mem_fun( &Wall::is_wall_of_cell_p ), neighbor_cell ) ); + start_search = w; start_search++; // continue searching at next element + } while ( w!=walls.end() && !(*w)->IntersectsWithDivisionPlaneP( from, to ) ); // go on until we find the right one. + + if (w == walls.end()) { + cerr << "Whoops, wall element not found...!\n"; + cerr << "Cell ID: " << neighbor_cell->Index() << endl; + cerr << "My cell ID: " << Index() << endl; + + } else { + + // 2. Split it up, if we should (sometimes, the new node coincides with an existing node so + // we should not split up the Wall) + + if (new_node_ind[i]!=(*w)->n1 && new_node_ind[i]!=(*w)->n2) { + + Wall *new_wall; + + // keep the length of the original wall; we need it to equally divide the transporter concentrations + // over the two daughter walls + (*w)->SetLength(); // make sure we've got the current length + orig_length[i] = (*w)->Length(); + //cerr << "Original length is " << orig_length[i] << endl; + if ((*w)->c1 == this ) { + + // cerr << "Cell " << (*w)->c1->Index() << " splits up wall " << *(*w) << ", into: " << endl; + new_wall = new Wall( (*w)->n1, new_node_ind[i], this, neighbor_cell); + (*w)->n1 = new_node_ind[i]; + + // cerr << "wall " << *(*w) << ", and new wall " << *new_wall << endl; + + } else { + new_wall = new Wall( (*w)->n1, new_node_ind[i], neighbor_cell, this); + + (*w)->n1 = new_node_ind[i]; + } + + + //new_wall->ResetTransporterConcentrations(orig_length); + //(*w)->ResetTransporterConcentrations(orig_length); + + // reset the transporter concentrations + + + /* new_wall->SetLength(); + new_wall->CorrectLength(orig_length); + + (*w)->SetLength(); + (*w)->CorrectLength(orig_length);*/ + + // 3. Give wall elements to appropriate cells + if (new_wall->n1 != new_wall->n2) { + + if (par.copy_wall) + new_wall->CopyWallContents(**w); + else { + // If wall contents are not copied, decide randomly which wall will be the "parent" + // otherwise we will get biases (to the left), for example in the meristem growth model + if (RANDOM()<0.5) { + new_wall->SwapWallContents(*w); + } + } + AddWall(new_wall); + // cerr << "Building new wall: this=" << Index() << ", neighbor_cell = " << neighbor_cell->Index() << endl; + + neighbor_cell->AddWall( new_wall); + //cerr << "Existing wall: c1 = " << (*w)->c1->Index() << ", neighbor_cell = " << (*w)->c2->Index() << endl; + + // Remember the addresses of the new walls + div_wall[2*i+0] = *w; + div_wall[2*i+1] = new_wall; + + // we will correct the transporter concentrations later in this member function, after division + // First the new nodes should be inserted into the cells, before we can calculate wall lengths + // Remember that cell walls can be bent, so have a bigger length than the Euclidean distance n1->n2 + + } else { + delete new_wall; + } + } + } + } + } // closing loop over the two divided edges (for (int i=0;i<2;i++) ) + + // move half of the nodes to the daughter + { + //cerr << "Daughter: "; + list::iterator start, stop; + + start=new_node_locations.front(); + + //cerr << "*new_node_locations.front() = " << *new_node_locations.front() << endl; + if (new_node_flag[0]==1) { + start++; + if (start==nodes.end()) + start=nodes.begin(); + } + + stop=new_node_locations.back(); + if (new_node_flag[1]==2) { + if (stop==nodes.begin()) + stop=nodes.end(); + stop--; + } + list::iterator i=start; + while ( i!=stop) { + + // give the node to the daughter + // (find references to parent cell from this node, + // and remove them) + list::iterator neighb_with_this_cell= + find_if((*i)->owners.begin(), + (*i)->owners.end(), + bind2nd(mem_fun_ref( &Neighbor::CellEquals ),this->Index() ) ); + if (neighb_with_this_cell==(*i)->owners.end()) { + cerr << "not found\n"; + abort(); + } + + (*i)->owners.erase(neighb_with_this_cell); + + daughter->nodes.push_back( *i ); + + + i++; + if (i==nodes.end()) + i=nodes.begin(); + }; + } + + // new node list of parent + list new_nodes_parent; + + // half of the nodes stay with the parent + { + list::iterator start, stop; + start=new_node_locations.back(); + if (new_node_flag[1]==1) { + start++; + if (start==nodes.end()) + start=nodes.begin(); + } + stop=new_node_locations.front(); + if (new_node_flag[0]==2) { + if (stop==nodes.begin()) + stop=nodes.end(); + stop--; + } + + list::iterator i=start; + while (i!=stop) { + new_nodes_parent.push_back( *i ); + + i++; + if (i==nodes.end()) + i = nodes.begin(); + }; + } + + // insert shared wall + // insert shared nodes on surface of parent cell + new_nodes_parent.push_back( new_node_ind[0] ); + daughter->nodes.push_back ( new_node_ind[1] ); + + // optionally add the new node to the nodeset (passed by pointer) + // (in this way we can move the NodeSet as a whole; useful for a fixed cutting line) + if (node_set) { + node_set->AddNode( new_node_ind[0] ); + } + +#define MULTIPLE_NODES +#ifdef MULTIPLE_NODES + // intermediate, extra nodes + // Calculate distance between the two new nodes + double dist=( new_node[1] - new_node[0] ).Norm(); + //bool fixed_wall = (new_node_ind[0])->fixed && (new_node_ind[1])->fixed; + bool fixed_wall = false; + + // Estimate number of extra nodes in wall + // factor 4 is to keep tension on the walls; + // this is a hidden parameter and should be made explicit + // later on. + int n=(int)((dist/Node::target_length)/4+0.5); + + Vector nodevec = ( new_node[1]- new_node[0]).Normalised(); + + double element_length = dist/(double)(n+1); + + // note that wall nodes need to run in inverse order in parent + list::iterator ins_pos = daughter->nodes.end(); + for (int i=1;i<=n;i++) { + Node *node= + m->AddNode( new Node( new_node[0] + i*element_length*nodevec ) ); + + node->fixed=fixed_wall; + + if (!fix_cellwall) + node->boundary = false; + else { // if fix_cellwall is true, that is if we are cutting off + // part of a leaf to make a nice initial condition, we also want to make it part of the boundary + //node->boundary = true; + node->fixed = true; + //node->sam = true; + } + + ins_pos=daughter->nodes.insert(ins_pos, node ); + new_nodes_parent.push_back( node ); + + // optionally add the new node to the nodeset (passed by pointer) + // (in this way we can move the NodeSet as a whole; useful for a fixed cutting line) + if (node_set) { + node_set->AddNode( node ); + } + + } +#endif + daughter->nodes.push_back( new_node_ind[0] ); + new_nodes_parent.push_back( new_node_ind[1] ); + + // optionally add the new node to the nodeset (passed by pointer) + // (in this way we can move the NodeSet as a whole; useful for a fixed cutting line) + if (node_set) { + node_set->AddNode( new_node_ind[1] ); + } + + // move the new nodes to the parent + nodes.clear(); + copy( new_nodes_parent.begin(), + new_nodes_parent.end(), + back_inserter(nodes) ); + + + // Repair cell lists of Nodes, and node connectivities + ConstructConnections(); + daughter->ConstructConnections(); + + if (boundary_touched_flag) { + m->boundary_polygon->ConstructConnections(); + } + + // collecting neighbors of divided cell + list broken_neighbors; + + // this cell's old neighbors + copy(neighbors.begin(), neighbors.end(), back_inserter(broken_neighbors) ); + + // this cell + broken_neighbors.push_back(this); + + // its daughter + broken_neighbors.push_back(daughter); + + + + // Recalculate area of parent and daughter + area = CalcArea(); + daughter->area = daughter->CalcArea(); + + SetIntegrals(); + daughter->SetIntegrals(); + + + + // Add a "Cell Wall" for diffusion algorithms + Wall *wall = new Wall( new_node_ind[0], new_node_ind[1], this, daughter ); + + AddWall( wall ); + + daughter->AddWall( wall ); + + //cerr << "Correct walls of cell " << Index() << " and daughter " << daughter->Index() << endl; + + // Move Walls to daughter cell + list copy_walls = walls; + for (list::iterator w = copy_walls.begin(); + w!=copy_walls.end(); + w++) { + + //cerr << "Doing wall, before: " << **w << endl; + + // checks the nodes of the wall and gives it away if appropriate + (*w)->CorrectWall ( ); + + //cerr << "and after: " << **w << endl; + + } + + + + // Correct tranporterconcentrations of divided walls + for (int i=0;i<4;i++) { + if (div_wall[i]) { + div_wall[i]->SetLength(); + div_wall[i]->CorrectTransporters(orig_length[i/2]); + } + } + + //neighbors.push_back( daughter ); + //daughter->neighbors.push_back( this ); + + + //cerr << "Cell " << index << " has been dividing, and gave birth to Cell " << daughter->index << endl; + + // now reconstruct neighbor list for all "broken" neighbors + + for (list::iterator i=broken_neighbors.begin(); + i!=broken_neighbors.end();i++) { + ((Cell *)(*i))->ConstructNeighborList(); + } + + + ConstructNeighborList(); + daughter->ConstructNeighborList(); + + m->plugin->OnDivide(parent_info,*daughter, *this); + // wall->OnWallInsert(); + //daughter->OnDivide(); + + daughter->div_counter=(++div_counter); + + +} + +// Move the whole cell +void Cell::Move(const Vector T) { + + for (list::const_iterator i=nodes.begin(); + i!=nodes.end(); + i++) { + *(*i)+=T; + } +} + +double Cell::Displace(double dx, double dy, double dh) { + + // Displace whole cell, add resulting energy to dh, + // and accept displacement if energetically favorable + // + // Method is called if a "fixed" node is displaced + + // Warning: length constraint not yet CORRECTLY implemented for this function + + // Attempt to move this cell in a random direction + // Vector movement(par.mc_cell_stepsize*(RANDOM()-0.5),par.mc_cell_stepsize*(RANDOM()-0.5),0); + + + dh=0; + + Vector movement(dx,dy,0); + + vector< pair > length_edges; + vector cellareas; + cellareas.reserve(neighbors.size()); + + // for the length constraint, collect all edges to this cell's nodes, + // which are not part of the cell + // the length of these edges will change + + double old_length=0.; + for (list::const_iterator i=nodes.begin(); + i!=nodes.end(); + i++) { + + //if ((*i)->Fixed()) return; // commented out 01/12/05 + for (list::const_iterator n=(*i)->owners.begin(); + n!=(*i)->owners.end(); + n++) { + + if (n->getCell()!=this) { + //if (!(m->getNode(n->nb1).Fixed() && m->getNode(n->nb2).Fixed())) { + length_edges.push_back( pair (*i, n->nb1) ); + length_edges.push_back( pair (*i, n->nb2) ); + old_length += + DSQR(Node::target_length-(*(*i)-*(n->nb1)).Norm())+ + DSQR(Node::target_length-(*(*i)-*(n->nb2)).Norm()); + //} + } + } + } + + // calculate area energy difference of neighboring cells + // (this cells' shape remains unchanged) + double old_area_energy=0., old_length_energy=0.; + for (list::const_iterator i=neighbors.begin(); + i!=neighbors.end(); + i++) { + old_area_energy += DSQR((*i)->Area()-(*i)->TargetArea()); + old_length_energy += DSQR((*i)->Length()-(*i)->TargetLength()); + } + + Move(movement); + + double new_area_energy=0., new_length_energy=0.; + for (list::const_iterator i=neighbors.begin(); + i!=neighbors.end(); + i++) { + cellareas.push_back((*i)->CalcArea()); + new_area_energy += DSQR(cellareas.back()-(*i)->TargetArea()); + new_length_energy += DSQR((*i)->CalcLength()-(*i)->TargetLength()); + } + + double new_length=0; + for ( vector< pair< Node *, Node * > >::const_iterator e = length_edges.begin(); + e != length_edges.end(); + e++) { + new_length += DSQR(Node::target_length- + (*(e->first)-*(e->second)).Norm()); + } + + + dh += (new_area_energy - old_area_energy) + (new_length_energy - old_length_energy) * lambda_celllength + + par.lambda_length * (new_length - old_length); + + if (dh<0 || RANDOM()::const_iterator nb_it = neighbors.begin(); + for (vector::const_iterator ar_it = cellareas.begin(); + ar_it!=cellareas.end(); + ( ar_it++, nb_it++) ) { + ((Cell *)(*nb_it))->area = *ar_it; + (*nb_it)->SetIntegrals(); + } + + //cerr << endl; + + /*vector area1; + vector area2; + m->ExtractFromCells( mem_fun_ref(&Cell::Area), back_inserter(area1) ); + m->ExtractFromCells( mem_fun_ref(&Cell::CalcArea), back_inserter(area2)); + vector::iterator i=area1.begin(); + vector::iterator j=area2.begin(); + int c=0; + for (; + i!=area1.end(); + (i++, j++)) { + if ( (*i-*j) > 1e-10) { + cerr << c++ << " " << *i << " " << *j << endl; + abort(); + } + }*/ + + } else { + + Move ( -1*movement); + + } + + return dh; +} + + +void Cell::Displace (void) { + Displace(par.mc_cell_stepsize*(RANDOM()-0.5),par.mc_cell_stepsize*(RANDOM()-0.5),0); +} + +// Get energy level of whole cell (excluding length constraint?) +double Cell::Energy(void) const { + + double energy = 0.; + double length_contribution = 0.; + + for (list::const_iterator i=nodes.begin(); + i!=nodes.end(); + i++) { + + for (list::const_iterator n=(*i)->owners.begin(); + n!=(*i)->owners.end(); + n++) { + + if (n->getCell()==this) { + + length_contribution += + DSQR(Node::target_length-(*(*i)-*(n->nb1)).Norm())+ + DSQR(Node::target_length-(*(*i)-*(n->nb2)).Norm()); + + } + } + } + + // wall elasticity constraint + energy += par.lambda_length * length_contribution; + + // area constraint + energy += DSQR(CalcArea() - target_area); + + // cell length constraint + + + energy += lambda_celllength * DSQR(Length() - target_length); + + + return energy; +} + + + + + +bool Cell::SelfIntersect(void) { + + // The (obvious) O(N*N) algorithm + + // Compare each edge against each other edge + + // An O(N log(N)) algorithm by Shamos & Hoey (1976) supposedly exists; + // it was mentioned on comp.graphics.algorithms + + // But I haven't been able to lay my hand on the paper. + // Let's try whether we need it.... + + // method used: http://astronomy.swin.edu.au/~pbourke/geometry/lineline2d/ + + for (list::const_iterator i=nodes.begin(); + i!=nodes.end(); + i++) { + + list::const_iterator j=i; + ++j; + for (; + j!=nodes.end(); + j++) + { + + Vector v1 = *(*i); + list::const_iterator nb=i; + nb++; + if (nb == nodes.end()) { + nb = nodes.begin(); + } + Vector v2 = *(*nb); + Vector v3 = *(*j); + nb=j; + nb++; + if (nb == nodes.end()) { + nb = nodes.begin(); + } + Vector v4=*( *nb ); + + double denominator = + (v4.y - v3.y)*(v2.x - v1.x) - (v4.x - v3.x)*(v2.y - v1.y); + + double ua = + ((v4.x - v3.x)*(v1.y - v3.y) - (v4.y - v3.y)*(v1.x -v3.x))/denominator; + double ub = + ((v2.x - v1.x)*(v1.y-v3.y) - (v2.y- v1.y)*(v1.x - v3.x))/denominator; + + /* double intersec_x = v1.x + ua*(v2.x-v1.x); + double intersec_y = v1.y + ua*(v2.y-v1.y);*/ + + if ( ( TINY < ua && ua < 1.-TINY ) && ( TINY < ub && ub < 1.-TINY ) ) { + //cerr << "ua = " << ua << ", ub = " << ub << endl; + return true; + } + } + } + + return false; +} + + +bool Cell::MoveSelfIntersectsP(Node *moving_node_ind, Vector new_pos) { + + // Check whether the polygon will self-intersect if moving_node_ind + // were displaced to new_pos + + // Compare the two new edges against each other edge + + // O(2*N) + + // method used for segment intersection: + // http://astronomy.swin.edu.au/~pbourke/geometry/lineline2d/ + + Vector neighbor_of_moving_node[2]; + + //cerr << "list::const_iterator moving_node_ind_pos = find (nodes.begin(),nodes.end(),moving_node_ind);\n"; + list::const_iterator moving_node_ind_pos = find (nodes.begin(),nodes.end(),moving_node_ind); + + list::const_iterator nb = moving_node_ind_pos; + //cerr << "Done\n"; + nb++; + if (nb == nodes.end()) { + nb = nodes.begin(); + } + + neighbor_of_moving_node[0]=*(*nb); + + nb=moving_node_ind_pos; + if (nb == nodes.begin()) { + nb = nodes.end(); + } + nb--; + + neighbor_of_moving_node[1]=*( *nb ); + + + for (list::const_iterator i=nodes.begin(); + i!=nodes.end(); + i++) { + + for (int j=0;j<2;j++) { // loop over the two neighbors of moving node + list::const_iterator nb=i; + nb++; + if (nb == nodes.end()) { + nb = nodes.begin(); + } + if (*i == moving_node_ind || *nb == moving_node_ind) { + // do not compare to self + continue; + } + + Vector v3 = *(*i); + Vector v4 = *(*nb); + + double denominator = + (v4.y - v3.y)*(neighbor_of_moving_node[j].x - new_pos.x) - (v4.x - v3.x)*(neighbor_of_moving_node[j].y - new_pos.y); + + double ua = + ((v4.x - v3.x)*(new_pos.y - v3.y) - (v4.y - v3.y)*(new_pos.x -v3.x))/denominator; + double ub = + ((neighbor_of_moving_node[j].x - new_pos.x)*(new_pos.y-v3.y) - (neighbor_of_moving_node[j].y- new_pos.y)*(new_pos.x - v3.x))/denominator; + + /* double intersec_x = new_pos.x + ua*(neighbor_of_moving_node[j].x-new_pos.x); + double intersec_y = new_pos.y + ua*(neighbor_of_moving_node[j].y-new_pos.y);*/ + + if ( ( TINY < ua && ua < 1.-TINY ) && ( TINY < ub && ub < 1.-TINY ) ) { + //cerr << "ua = " << ua << ", ub = " << ub << endl; + return true; + } + } + } + + return false; +} + +/*! \brief Test if this cell intersects with the given line. + + */ +bool Cell::IntersectsWithLineP(const Vector v1, const Vector v2) { + + + // Compare the line against each edge + + // method used: http://astronomy.swin.edu.au/~pbourke/geometry/lineline2d/ + + + + for (list::const_iterator i=nodes.begin(); + i!=nodes.end(); + i++) + { + + Vector v3 = *(*i); + list::const_iterator nb=i; + nb++; + if (nb == nodes.end()) { + nb = nodes.begin(); + } + Vector v4 = *(*nb); + + double denominator = + (v4.y - v3.y)*(v2.x - v1.x) - (v4.x - v3.x)*(v2.y - v1.y); + + double ua = + ((v4.x - v3.x)*(v1.y - v3.y) - (v4.y - v3.y)*(v1.x -v3.x))/denominator; + double ub = + ((v2.x - v1.x)*(v1.y-v3.y) - (v2.y- v1.y)*(v1.x - v3.x))/denominator; + + /* double intersec_x = v1.x + ua*(v2.x-v1.x); + double intersec_y = v1.y + ua*(v2.y-v1.y);*/ + + if ( ( TINY < ua && ua < 1.-TINY ) && ( TINY < ub && ub < 1.-TINY ) ) { + return true; + } + } + + return false; + + +} +/*! \brief Constructs Walls, but only one per cell boundary. + + Standard method constructs a Wall for each cell wall element, + making transport algorithms computationally more intensive than needed. + + We can remove this? Well, let's leave it in the code in case we need it for something else. E.g. for importing leaf architectures in different formats than our own... :-) + + */ +void Cell::ConstructWalls(void) { + + return; + if (dead) return; + + walls.clear(); + neighbors.clear(); + + // Get "corner points; i.e. nodes where more than 2 cells are connected + list corner_points; + + for (list::const_iterator i=nodes.begin(); + i!=nodes.end();i++) { + + // look for nodes belonging to >2 cells + if ((*i)->owners.size()>2) { + + // push onto list + corner_points.push_back(*i); + } + + } + + // Construct Walls between corner points + + // previous one in list + list::const_iterator nb = (--corner_points.end()); + + // loop over list, + for (list::const_iterator i=corner_points.begin(); + i!=corner_points.end(); ( i++, nb++) ) { + + if (nb==corner_points.end()) nb=corner_points.begin(); + // add owning cells to a list + list owning_cells; + Node &n(*(*i)); + + for (list::const_iterator j=n.owners.begin(); + j!=n.owners.end(); + j++) { + owning_cells.push_back(j->cell); + } + + Node &n2(*(*nb)); + for (list::const_iterator j=n2.owners.begin(); + j!=n2.owners.end(); + j++) { + owning_cells.push_back(j->cell); + } + + // sort cell owners + owning_cells.sort( mem_fun( &Cell::Cmp )); + + // find duplicates + vector duplicates; + list::const_iterator prevj = (--owning_cells.end()); + for (list::const_iterator j=owning_cells.begin(); + j!=owning_cells.end(); + ( j++, prevj++) ) { + + if (prevj==owning_cells.end()) prevj=owning_cells.begin(); + if (*j==*prevj) duplicates.push_back(*j); + + } + + + if (duplicates.size()==3) { // ignore cell boundary (this occurs only after the first division, I think) + vector::iterator dup_it = find_if(duplicates.begin(),duplicates.end(),mem_fun(&Cell::BoundaryPolP) ); + if (dup_it!=duplicates.end()) + duplicates.erase(dup_it); + else { + return; + } + + } + + + // One Wall for each neighbor, so we should be able to correctly construct neighbor lists here. + if (duplicates[0]==this) { + //walls. new Wall(*nb,*i,duplicates[0],duplicates[1]) ); + AddWall( new Wall(*nb,*i,duplicates[0],duplicates[1]) ); + if (!duplicates[1]->BoundaryPolP()) { + + neighbors.push_back(duplicates[1]); + } + } else { + //walls.push_back( new Wall(*nb,*i,duplicates[1],duplicates[0]) ); + AddWall ( new Wall(*nb,*i,duplicates[1],duplicates[0]) ); + if (!duplicates[0]->BoundaryPolP()) { + neighbors.push_back(duplicates[0]); + + } + } + } + +} + + +void BoundaryPolygon::Draw(QGraphicsScene *c, QString tooltip) { + + // Draw the BoundaryPolygon on a QCanvas object + + + CellItem* p = new CellItem(this, c); + + QPolygonF pa(nodes.size()); + int cc=0; + + for (list::const_iterator n=nodes.begin(); + n!=nodes.end(); + n++) { + Node *i=*n; + + pa[cc++] = QPoint((int)((Offset().x+i->x)*Factor()), + (int)((Offset().y+i->y)*Factor()) ); + } + + + p->setPolygon(pa); + p->setPen(par.outlinewidth>=0?QPen( QColor(par.cell_outline_color),par.outlinewidth):QPen(Qt::NoPen)); + p->setBrush( Qt::NoBrush ); + p->setZValue(1); + + if (!tooltip.isEmpty()) + p->setToolTip(tooltip); + + p->show(); + +} + +void Cell::Flux(double *flux, double *D) { + + + // loop over cell edges + + for (int c=0;c::iterator i=walls.begin(); + i!=walls.end(); + i++) { + + + // leaf cannot take up chemicals from environment ("no flux boundary") + if ((*i)->c2->BoundaryPolP()) continue; + + + // flux depends on edge length and concentration difference + for (int c=0;clength * ( D[c] ) * ( ((Cell *)(*i)->c2)->chem[c] - chem[c] ); + + if ((*i)->c1!=this) { + cerr << "Warning, bad cells boundary: " << (*i)->c1->Index() << ", " << index << endl; + } + + flux[c] += phi; + } + } + +} + + +// graphics stuff, not compiled for batch versions +#ifdef QTGRAPHICS + +#include "canvas.h" + +void Cell::Draw(QGraphicsScene *c, QString tooltip) { + + // Draw the cell on a QCanvas object + + if (DeadP()) { + cerr << "Cell " << index << " not drawn, because dead.\n"; + return; + } + + CellItem* p = new CellItem(this, c); + + QPolygonF pa(nodes.size()); + int cc=0; + + for (list::const_iterator n=nodes.begin(); + n!=nodes.end(); + n++) { + Node *i=*n; + + pa[cc++] = QPoint((int)((offset[0]+i->x)*factor), + (int)((offset[1]+i->y)*factor) ); + } + + + QColor cell_color; + + m->plugin->SetCellColor(*this,cell_color); + + p->setPolygon(pa); + p->setPen(par.outlinewidth>=0?QPen( QColor(par.cell_outline_color),par.outlinewidth):QPen(Qt::NoPen)); + p->setBrush( cell_color ); + p->setZValue(1); + + if (!tooltip.isEmpty()) + p->setToolTip(tooltip); + + p->show(); + +} + + +void Cell::DrawCenter(QGraphicsScene *c) const { + // Maginfication derived similarly to that in nodeitem.cpp + // Why not use Cell::Magnification()? + const double mag = par.node_mag; + + // construct an ellipse + QGraphicsEllipseItem *disk = new QGraphicsEllipseItem ( -1*mag, -1*mag, 2*mag, 2*mag, 0, c ); + disk->setBrush( QColor("forest green") ); + disk->setZValue(5); + disk->show(); + Vector centroid=Centroid(); + disk -> setPos((offset[0]+centroid.x)*factor,(offset[1]+centroid.y)*factor); +} + +void Cell::DrawNodes(QGraphicsScene *c) const { + + for (list::const_iterator n=nodes.begin(); + n!=nodes.end(); + n++) { + Node *i=*n; + + //QCanvasEllipse *item = new QCanvasEllipse( 10, 10, c); + NodeItem *item = new NodeItem ( &(*i), c ); + //QGraphicsRectItem *item = new QGraphicsRectItem(-50, -50, 50, 50, 0, c); + //disk->setBrush( QColor("IndianRed") ); + + /*if (i->sam) { + item->setBrush( purple ); + } else { + if (i->boundary) { + item->setBrush( deep_sky_blue ); + } + else { + item->setBrush( indian_red ); + } + }*/ + item->setColor(); + + /*(if (item->getNode().DeadP()) { + item->setBrush( QBrush (Qt::Dense6Pattern) ); + }*/ + item->setZValue(5); + item->show(); + item ->setPos(((offset[0]+i->x)*factor), + ((offset[1]+i->y)*factor) ); + } + +} + +void Cell::DrawIndex(QGraphicsScene *c) const { + + // stringstream text; + // text << index; + // Vector centroid = Centroid(); + // QCanvasText *number = new QCanvasText ( QString (text.str()), c ); + // number->setColor( QColor(par.textcolor) ); + // number->setZ(20); + // number->setFont( QFont( "Helvetica", par.cellnumsize, QFont::Bold) ); + // number->show(); + // number -> move((int)((offset[0]+centroid.x)*factor), + // (int)((offset[1]+centroid.y)*factor) ); + DrawText( c, QString("%1").arg(index)); +} + +// Draw any text in the cell's center +void Cell::DrawText(QGraphicsScene *c, const QString &text) const { + + Vector centroid = Centroid(); + QGraphicsSimpleTextItem *ctext = new QGraphicsSimpleTextItem ( text, 0, c ); + ctext->setPen( QPen(QColor(par.textcolor)) ); + ctext->setZValue(20); + ctext->setFont( QFont( "Helvetica", par.cellnumsize, QFont::Bold) ); + //ctext->setTextFlags(Qt::AlignCenter); + ctext->show(); + ctext ->setPos(((offset[0]+centroid.x)*factor), + ((offset[1]+centroid.y)*factor) ); + +} + + +void Cell::DrawAxis(QGraphicsScene *c) const { + + Vector long_axis; + double width; + Length(&long_axis, &width); + + //cerr << "Length is " << length << endl; + long_axis.Normalise(); + Vector short_axis=long_axis.Perp2D(); + + + Vector centroid = Centroid(); + Vector from = centroid - 0.5 * width * short_axis; + Vector to = centroid + 0.5 * width *short_axis; + + + QGraphicsLineItem *line = new QGraphicsLineItem(0, c); + line->setPen( QPen(QColor(par.arrowcolor),2) ); + line->setZValue(2); + + line->setLine( ( (offset[0]+from.x)*factor ), + ( (offset[1]+from.y)*factor ), + ( (offset[0]+to.x)*factor ), + ( (offset[1]+to.y)*factor ) ); + line->setZValue(10); + line->show(); + +} + +void Cell::DrawStrain(QGraphicsScene *c) const { + + MyWarning::warning("Sorry, Cell::DrawStrain temporarily not implemented."); + /* Vector long_axis; + double width; + Length(&long_axis, &width); + + //cerr << "Length is " << length << endl; + long_axis.Normalise(); + Vector short_axis=long_axis.Perp2D(); + + // To test method "Strain" temporarily substitute "short_axis" for "strain" + Vector strain = Strain(); + //strain.Normalise(); + //static ofstream strainf("strain.dat"); + //strainf << strain.Norm() << endl; + Vector centroid = Centroid(); + // Vector from = centroid - 0.5 * width * short_axis; + // Vector to = centroid + 0.5 * width *short_axis; + Vector from = centroid - 0.5 * strain; + Vector to = centroid + 0.5 * strain; + + QGraphicsArrowItem *arrow = new QGraphicsArrowItem(0, c); + arrow->setPen( QPen(QColor(par.arrowcolor),100) ); + + arrow->setLine( ( (offset[0]+from.x)*factor ), + ( (offset[1]+from.y)*factor ), + ( (offset[0]+to.x)*factor ), + ( (offset[1]+to.y)*factor ) ); + arrow->setZValue(10.); + arrow->show(); + */ +} + +// Draw connecting lines to neighbors +/*void Cell::DrawTriangles(QCanvas &c) { + + for (list::const_iterator nb=nb_list.begin(); + nb!=nb_list.end(); + nb++) { + QCanvasLine *line = new QCanvasLine(&c); + line->setPen( QPen(QColor("black"),2) ); + line->setZ(2); + + line->setPoints((offset[0]+x)*factor,(offset[1]+y)*factor, + (offset[0]+nb->c->x)*factor,(offset[1]+nb->c->y)*factor); + line->setZ(10); + line->show(); + } + + }*/ + + + +void Cell::DrawFluxes(QGraphicsScene *c, double arrowsize) { + + // get the mean flux through this cell + //Vector vec_flux = ReduceWalls( mem_fun_ref( &Wall::VizFlux ), Vector() ); + Vector vec_flux = ReduceCellAndWalls( PINdir ); + + vec_flux.Normalise(); + + vec_flux *= arrowsize; + + QGraphicsArrowItem *arrow = new QGraphicsArrowItem(0,c); + + Vector centroid = Centroid(); + Vector from = centroid - vec_flux/2.; + Vector to = centroid + vec_flux/2.; + + + arrow->setPen( QPen(QColor(par.arrowcolor),par.outlinewidth)); + arrow->setZValue(2); + + arrow->setLine( ( (offset[0]+from.x)*factor ), + ( (offset[1]+from.y)*factor ), + ( (offset[0]+to.x)*factor ), + ( (offset[1]+to.y)*factor ) ); + arrow->setZValue(10); + arrow->show(); + +} + + +void Cell::DrawWalls(QGraphicsScene *c) const { + + for_each(walls.begin(), walls.end(), bind2nd ( mem_fun ( &Wall::Draw ) , c ) ); + + // to see the cells connected the each wall (for debugging), uncomment the following + //for_each(walls.begin(), walls.end(), bind2nd ( mem_fun ( &Wall::ShowStructure ), c ) ); +} + + +void Cell::DrawValence(QGraphicsScene *c) const { + + DrawText(c, QString("%1").arg(walls.size()) ); + +} + +#endif + +/*! \brief Recalculate the lengths of the cell's Walls. + + Call this function after the Monte Carlo updates, and before doing the reaction-diffusion iterations. + + */ +void Cell::SetWallLengths(void) { + + for (list::iterator de=walls.begin(); + de!=walls.end(); + de++) { + + // Step 1: find the path of nodes leading along the Wall. + // A Wall often represents a curved cell wall: we want the total + // length _along_ the wall here... + + + // Locate first and second nodes of the edge in list of nodes + list::const_iterator first_node_edge = find(nodes.begin(), nodes.end(), (*de)->n1); + list::const_iterator second_node_edge_plus_1 = ++find(nodes.begin(), nodes.end(), (*de)->n2); + + double sum_length = 0.; + + // Now, walk to the second node of the edge in the list of nodes + for (list::const_iterator n=++first_node_edge; + n!=second_node_edge_plus_1; + ++n ) { + + if (n==nodes.end()) n=nodes.begin(); /* wrap around */ + + + list::const_iterator prev_n = n; + if (prev_n==nodes.begin()) prev_n=nodes.end(); + --prev_n; + + + // Note that Node derives from a Vector, so we can do vector calculus as defined in vector.h + sum_length += (*(*prev_n) - *(*n)).Norm(); + + //cerr << "Node " << *prev_n << " to " << *n << ", cumulative length = " << sum_length << endl; + } + + // We got the total length of the Wall now, store it: + (*de)->length = sum_length; + + //cerr << endl; + // goto next de + } +} + + +//! Add Wall w to the list of Walls +void Cell::AddWall( Wall *w ) { + + // if necessary, we could try later inserting it at the correct position + if (w->c1 == w->c2 ){ + + cerr << "Wall between identical cells: " << w->c1->Index()<< endl; + + } + // Add Wall to Cell's list + walls.push_back( w ); + + // Add wall to Mesh's list if it isn't there yet + + if (find ( + m->walls.begin(), m->walls.end(), + w ) + == m->walls.end() ) { + m->walls.push_back(w); + } + +} + +//! Remove Wall w from the list of Walls +list::iterator Cell::RemoveWall( Wall *w ) { + + // remove wall from Mesh's list + m->walls.erase( + find( + m->walls.begin(), m->walls.end(), + w ) + ); + + // remove wall from Cell's list + return + walls.erase ( + find( + walls.begin(), walls.end(), + w ) + ); + +} + + + +void Cell::EmitValues(double t) { + + // cerr << "Attempting to emit " << t << ", " << chem[0] << ", " << chem[1] << endl; + //chem[3] = SumTransporters( 1 ); + emit ChemMonValue(t, chem); + +} diff --git a/src/cell.h b/src/cell.h new file mode 100644 --- /dev/null +++ b/src/cell.h @@ -0,0 +1,209 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#ifndef _CELL_H_ +#define _CELL_H_ + +#include +#include +#include +#include +#include "vector.h" +#include "parameter.h" +#include "wall.h" +#include "warning.h" +#include "cellbase.h" +#include "cell.h" + +#include +#include +#include + +#include +#include +#include + +class Cell : public CellBase +{ + + Q_OBJECT + friend class Mesh; + friend class FigureEditor; + +public: + Cell(double x, double y, double z = 0.); + Cell(void); + Cell(const Cell &src); + Cell operator=(const Cell &src); + bool Cmp(Cell*) const; + bool Eq(Cell*) const; + + inline bool IndexEquals(int i) { return i == index; } + + static void SetMagnification(const double &magn) { + factor=magn; + } + static Vector Offset(void) { + Vector offs; + offs.x=offset[0]; + offs.y=offset[1]; + return offs; + } + + static void Translate(const double &tx,const double &ty) { + offset[0]+=tx; + offset[1]+=ty; + } + + inline static double Factor(void) { + return factor; + } + static void setOffset(double ox, double oy) { + offset[0]=ox; + offset[1]=oy; + } + static double Magnification(void) { + return factor; + } + + static double Scale(const double scale) { + factor*=scale; + return factor; + } + + // return node "i" + // wrapped around, i.e. node n==node 0 + // will not work if i < -nodes.size() + //Node &getNode(int i) const; + void DivideOverAxis(Vector axis); // divide cell over axis + bool DivideOverGivenLine(const Vector v1, const Vector v2, bool wall_fixed = false, NodeSet *node_set = 0); // divide over the line (if line and cell intersect) + + void Divide(void) { // Divide cell over short axis + + Vector long_axis; + Length(&long_axis); + DivideOverAxis(long_axis.Perp2D()); + + } + + //void CheckForGFDrivenDivision(void); + inline int NNodes(void) const { return nodes.size(); } + + void Move(Vector T); + void Move(double dx, double dy, double dz=0) { + Move( Vector (dx, dy, dz) ); + } + + double Displace(double dx, double dy, double dh); + void Displace(void); + double Energy(void) const; + bool SelfIntersect(void); + bool MoveSelfIntersectsP(Node *nid, Vector new_pos); + bool IntersectsWithLineP(const Vector v1, const Vector v2); + + void XMLAdd(xmlNodePtr cells_node) const; + + void ConstructWalls(void); + void Flux(double *flux, double *D); + + /*! \brief Method called if a cell is clicked. + + Define this in the end-user code (e.g. meinhardt.cpp). + + */ + void OnClick(QMouseEvent *e); + inline Mesh& getMesh(void) const { return *m; } + double MeanArea(void); + + void Apoptose(void); // Cell kills itself + list::iterator RemoveWall( Wall *w ); + void AddWall( Wall *w ); + + void Draw(QGraphicsScene *c, QString tooltip = QString::Null()); + + // Draw a text in the cell's center + void DrawText(QGraphicsScene *c, const QString &text) const; + void DrawIndex(QGraphicsScene *c) const; + void DrawCenter(QGraphicsScene *c) const; + void DrawNodes(QGraphicsScene *c) const; + + void DrawAxis(QGraphicsScene *c) const; + void DrawStrain(QGraphicsScene *c) const; + void DrawFluxes(QGraphicsScene *c, double arrowsize = 1.); + void DrawWalls(QGraphicsScene *c) const; + void DrawValence(QGraphicsScene *c) const; + void EmitValues(double t); + +signals: + void ChemMonValue(double t, double *x); + + +protected: + void XMLAddCore(xmlNodePtr xmlcell) const; + int XMLRead(xmlNode *cur); + void DivideWalls(ItList new_node_locations, const Vector from, const Vector to, bool wall_fixed = false, NodeSet *node_set = 0); + +private: + + static QPen *cell_outline_pen; + static double offset[3]; + static double factor; + Mesh *m; + void ConstructConnections(void); + void SetWallLengths(void); + +}; + + +// Boundarypolygon is a special cell; will not increase ncells +// and will not be part of Mesh::cells +class BoundaryPolygon : public Cell { + +public: + BoundaryPolygon(void) : Cell() { + NCells()--; + index=-1; + } + + BoundaryPolygon(double x,double y,double z=0) : Cell (x,y,z) { + NCells()--; + index=-1; + } + + BoundaryPolygon &operator=(Cell &src) { + Cell::operator=(src); + index=-1; + return *this; + } + virtual void Draw(QGraphicsScene *c, QString tooltip = QString::Null()); + + virtual void XMLAdd(xmlNodePtr parent_node) const; + + virtual bool BoundaryPolP(void) const { return true; } + + +}; + + + +#endif diff --git a/src/cellbase.cpp b/src/cellbase.cpp new file mode 100644 --- /dev/null +++ b/src/cellbase.cpp @@ -0,0 +1,1021 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include +#include +#include +#include +#include +#ifdef QTGRAPHICS +#include +#include +#include +#include +#include +//Added by qt3to4: +#include +#include +#include "nodeitem.h" +#include "cellitem.h" +#include "qcanvasarrow.h" +#endif +#include "nodeset.h" + +//#include "cond_operator.h" +#include "cellbase.h" +//#include "node.h" +#include "wall.h" +#include "random.h" +#include "parameter.h" +#include "mesh.h" +#include "sqr.h" +#include "tiny.h" + +static const std::string _module_id("$Id$"); + +extern Parameter par; + +const char* CellBase::boundary_type_names[4] = {"None", "NoFlux", "SourceSink", "SAM"}; + +// These statics have moved to class "CellsStaticDatamembers" + +//double CellBase::static_base_area = 0.; +//int CellBase::ncells=0; +//int CellBase::NChem()=0; + +#ifndef VLEAFPLUGIN +CellsStaticDatamembers *CellBase::static_data_members = new CellsStaticDatamembers(); +#else +CellsStaticDatamembers *CellBase::static_data_members = 0; +#endif + +CellBase::CellBase(QObject *parent) : +QObject(parent), +Vector() +{ + + chem=new double[NChem()]; + for (int i=0;i=NChem()) { + stringstream error; + error << "SetChemical: value c = " << c << " is out of range\n"; + throw error.str().c_str(); + } + chem[c]=conc; +} + +ostream &CellBase::print(ostream &os) const { + + + os << "[ index = " << index << " {" << x << ", " << y << ", " << z << "}: {"; + + for (int i=0;i::const_iterator i = nodes.begin(); i!=nodes.end(); i++) { + os << (*i)->Index() << "( " << *i << ") "; + } + os << " } "; + + for (list::const_iterator i = walls.begin(); i!=walls.end(); i++) { + (*i)->print(os); + os << ", "; + } + os << endl; + + os << " [ area = " << area << " ]"; + os << " [ walls = "; + + for (list::const_iterator i= walls.begin(); + i!=walls.end(); + i++) { + os << (*i)->n1->Index() << " -> " << (*i)->n2->Index() << ", " << (*i)->c1->Index() << " | " << (*i)->c2->Index() << ", "; + } + os << " ] "; + os << "div_counter = " << div_counter << endl; + os << "cell_type = " << cell_type << endl; + os << endl; + return os; + +} + +ostream &operator<<(ostream &os, const CellBase &c) { + c.print(os); + return os; +} + + +double CellBase::CalcArea(void) const { + + double loc_area=0.; + + for (list::const_iterator i=nodes.begin(); + i!=(nodes.end()); + i++) { + + list::const_iterator i_plus_1=i; i_plus_1++; + if (i_plus_1==nodes.end()) + i_plus_1=nodes.begin(); + + loc_area+= (*i)->x * (*i_plus_1)->y; + loc_area-= (*i_plus_1)->x * (*i)->y; + } + + // http://technology.niagarac.on.ca/courses/ctec1335/docs/arrays2.pdf + //return loc_area/2.0; + return fabs(loc_area)/2.0; +} + +Vector CellBase::Centroid(void) const { + + double area=0.; + double integral_x_dxdy=0.,integral_y_dxdy=0.; + + for (list::const_iterator i=nodes.begin(); + i!=(nodes.end()); + i++) { + + list::const_iterator i_plus_1=i; i_plus_1++; + if (i_plus_1==nodes.end()) + i_plus_1=nodes.begin(); + + area+= (*i)->x * (*i_plus_1)->y; + area-= (*i_plus_1)->x * (*i)->y; + + integral_x_dxdy+= + ((*i_plus_1)->x+(*i)->x)* + ((*i)->x*(*i_plus_1)->y- + (*i_plus_1)->x*(*i)->y); + integral_y_dxdy+= + ((*i_plus_1)->y+(*i)->y)* + ((*i)->x*(*i_plus_1)->y- + (*i_plus_1)->x*(*i)->y); + } + + //area/=2.0; + area = fabs(area)/2.0; + + integral_x_dxdy/=6.; + integral_y_dxdy/=6.; + + Vector centroid(integral_x_dxdy,integral_y_dxdy,0); + centroid/=area; + return centroid; +} + +/*Node &CellBase::getNode(list::const_iterator i) const { + +if (i== + return m->getNode(i); + }*/ + + + + +void CellBase::SetIntegrals(void) const { + + // Set the initial values for the integrals over x^2, + // xy, yy, x, and y + + // these values will be updated after each move of the CellBase wall + + intgrl_xx=0.; intgrl_xy=0.; intgrl_yy=0.; + intgrl_x=0.; intgrl_y=0.; + area=0.; + list::const_iterator nb; + list::const_iterator i=nodes.begin(); + + for (; + i!=(nodes.end()); + i++) { + + nb = i; nb++; if (nb==nodes.end()) nb=nodes.begin(); + + area+=(*i)->x*(*nb)->y; + area-=(*nb)->x*(*i)->y; + intgrl_xx+= + ((*i)->x*(*i)->x+ + (*nb)->x*(*i)->x+ + (*nb)->x*(*nb)->x ) * + ((*i)->x*(*nb)->y- + (*nb)->x*(*i)->y); + intgrl_xy+= + ((*nb)->x*(*i)->y- + (*i)->x*(*nb)->y)* + ((*i)->x*(2*(*i)->y+(*nb)->y)+ + (*nb)->x*((*i)->y+2*(*nb)->y)); + intgrl_yy+= + ((*i)->x*(*nb)->y- + (*nb)->x*(*i)->y)* + ((*i)->y*(*i)->y+ + (*nb)->y*(*i)->y+ + (*nb)->y*(*nb)->y ); + intgrl_x+= + ((*nb)->x+(*i)->x)* + ((*i)->x*(*nb)->y- + (*nb)->x*(*i)->y); + intgrl_y+= + ((*nb)->y+(*i)->y)* + ((*i)->x*(*nb)->y- + (*nb)->x*(*i)->y); + } + + //area/=2.0; + area = fabs(area)/2.0; + + /* intgrl_x/=6.; + intgrl_y/=6.; + + intgrl_xx/=12.; + intgrl_xy/=24.; + intgrl_yy/=12.;*/ + + +} + +double CellBase::Length(Vector *long_axis, double *width) const { + + // Calculate length and axes of CellBase + + // Calculate inertia tensor + // see file inertiatensor.nb for explanation of this method + if (!lambda_celllength) { + + // Without length constraint we do not keep track of the cells' + // moments of inertia. So we must calculate them here. + SetIntegrals(); + } + + double intrx=intgrl_x/6.; + double intry=intgrl_y/6.; + double ixx=(intgrl_xx/12.)-(intrx*intrx)/area; + double ixy=(intgrl_xy/24.)+(intrx*intry)/area; + double iyy=(intgrl_yy/12.)-(intry*intry)/area; + + double rhs1=(ixx+iyy)/2., rhs2=sqrt( (ixx-iyy)*(ixx-iyy)+4*ixy*ixy )/2.; + + double lambda_b=rhs1+rhs2; + + // see: http://scienceworld.wolfram.com/physics/MomentofInertiaEllipse.html + // cerr << "n = " << n << "\n"; + + // Vector eigenvectors[2]; + // eigenvectors[0] = Vector(-(-ixx + iyy ) + rhs2, ixy, 0); + // eigenvectors[1] = Vector(-(-ixx + iyy ) - rhs2, ixy, 0); + if (long_axis) { + *long_axis = Vector(-ixy, lambda_b - ixx, 0); + // cerr << "ixx = " << ixx << ", ixy = " << ixy << ", iyy = " << iyy << ", area = " << area << endl; + } + + if (width) { + *width = 4*sqrt((rhs1-rhs2)/area); + } + + return 4*sqrt(lambda_b/area); + + + +} + +double CellBase::CalcLength(Vector *long_axis, double *width) const { + + // Calculate length and axes of CellBase, without touching cells raw moments + + // Calculate inertia tensor + // see file inertiatensor.nb for explanation of this method + + double my_intgrl_xx=0., my_intgrl_xy=0., my_intgrl_yy=0.; + double my_intgrl_x=0., my_intgrl_y=0., my_area=0.; + my_area=0.; + list::const_iterator nb; + list::const_iterator i=nodes.begin(); + + for (; + i!=(nodes.end()); + i++) { + + nb = i; nb++; if (nb==nodes.end()) nb=nodes.begin(); + + my_area+=(*i)->x*(*nb)->y; + my_area-=(*nb)->x*(*i)->y; + my_intgrl_xx+= + ((*i)->x*(*i)->x+ + (*nb)->x*(*i)->x+ + (*nb)->x*(*nb)->x ) * + ((*i)->x*(*nb)->y- + (*nb)->x*(*i)->y); + my_intgrl_xy+= + ((*nb)->x*(*i)->y- + (*i)->x*(*nb)->y)* + ((*i)->x*(2*(*i)->y+(*nb)->y)+ + (*nb)->x*((*i)->y+2*(*nb)->y)); + my_intgrl_yy+= + ((*i)->x*(*nb)->y- + (*nb)->x*(*i)->y)* + ((*i)->y*(*i)->y+ + (*nb)->y*(*i)->y+ + (*nb)->y*(*nb)->y ); + my_intgrl_x+= + ((*nb)->x+(*i)->x)* + ((*i)->x*(*nb)->y- + (*nb)->x*(*i)->y); + my_intgrl_y+= + ((*nb)->y+(*i)->y)* + ((*i)->x*(*nb)->y- + (*nb)->x*(*i)->y); + } + + + //my_area/=2.0; + my_area = fabs(my_area)/2.0; + + + double intrx=my_intgrl_x/6.; + double intry=my_intgrl_y/6.; + double ixx=(my_intgrl_xx/12.)-(intrx*intrx)/my_area; + double ixy=(my_intgrl_xy/24.)+(intrx*intry)/my_area; + double iyy=(my_intgrl_yy/12.)-(intry*intry)/my_area; + + double rhs1=(ixx+iyy)/2., rhs2=sqrt( (ixx-iyy)*(ixx-iyy)+4*ixy*ixy )/2.; + + double lambda_b=rhs1+rhs2; + + // see: http://scienceworld.wolfram.com/physics/MomentofInertiaEllipse.html + // cerr << "n = " << n << "\n"; + + // Vector eigenvectors[2]; + // eigenvectors[0] = Vector(-(-ixx + iyy ) + rhs2, ixy, 0); + // eigenvectors[1] = Vector(-(-ixx + iyy ) - rhs2, ixy, 0); + if (long_axis) { + *long_axis = Vector(-ixy, lambda_b - ixx, 0); + // cerr << "ixx = " << ixx << ", ixy = " << ixy << ", iyy = " << iyy << ", my_area = " << my_area << endl; + } + + if (width) { + *width = 4*sqrt((rhs1-rhs2)/my_area); + } + + return 4*sqrt(lambda_b/my_area); + + + +} + + +// void CellBase::NodeRemoved(int n) { +// for (list::iterator i=nodes.begin(); +// i!=nodes.end(); +// i++) { +// if ((*i)->Index()>n) { +// (*i)->index--; +// } +// } +// } + +void CellBase::ConstructNeighborList(void) { + + neighbors.clear(); + for (//list::const_reverse_iterator wit=walls.rbegin(); + list::const_iterator wit=walls.begin(); + // somehow the reverse_iterator returns by walls needs to be casted to const to let this work. + // it seems to me it is a bug in the STL implementation... + //wit!=(list::const_reverse_iterator)walls.rend(); + wit!=walls.end(); + wit++) { + + if ((*wit)->C1() != this) { + neighbors.push_back((*wit)->C1()); + } else { + neighbors.push_back((*wit)->C2()); + } + + } + + + /* + for (list::iterator e=neighbors.begin(); + e!=neighbors.end(); + e++) { + cerr << (*e)->Index() << " "; + if ((*e)->CellBase::BoundaryPolP()) { + cerr << " b "; + } + } + */ + // remove all boundary_polygons from the list + + + + list ::iterator e=neighbors.begin(); + at_boundary=false; + + do { + // Code crashes here after cutting off part of the leaf. I can't find the problem. + // Leaving the "Illegal" walls in the simulation helps. (c1=-1 && c2=-1) + // Work-around: define leaf primordium. Save to XML. Restart. Read XML file. + // Sorry about this; I hope to solve this annoying issue later. RM :-). + // All cells in neighbors seem to be okay (I might be messing some part of the memory elsewhere + // during the cutting operation?). + e = find_if(neighbors.begin(),neighbors.end(),mem_fun(&CellBase::BoundaryPolP)); + if (e!=neighbors.end()) { + e=neighbors.erase(e); + at_boundary=true; + } else { + break; + } + } while(1); + +} + +// CellBase constructs its neighbor list from its node lists +// Assumes, obviously, that the node lists are up to date +// (i.e. call ConstructConnections before calling this method) +// We'll keep this one private, anyway. +/* void CellBase::ConstructNeighborList(void) { + +// extern ofstream debug_stream; + +neighbors.clear(); + +// debug_stream << "Nodes: "; +// copy(nodes.begin(),nodes.end(),ostream_iterator(debug_stream, " ")); +//debug_stream << endl; + +for (list::const_iterator i=nodes.begin(); + i!=nodes.end(); + i++) { + + // collect all cells to which my nodes are connected on one list + //transform((*i)->cells.begin(),(*i)->cells.end(), back_inserter(neighbors), mem_fun_ref(&Neighbor::CellBase)); + + // index of next node + list::const_iterator nn=i; + ++nn; + if (nn==nodes.end()) + nn=nodes.begin(); + + // debug_stream << "Node " << *i << ", Neighbor " << *nn << endl; + // debug_stream << "Owners: "; + // copy((*i)->cells.begin(),(*i)->cells.end(),ostream_iterator(debug_stream, " ")); + // debug_stream << endl; + + for (list::const_iterator nb=(*i)->owners.begin(); + nb!=(*i)->owners.end(); + nb++) { + + // collect info about neighboring cells, not about myself + if (nb->CellBase!=this) { + + // make sure the whole edge touches this putative neighbor + // if (*nn == nb->nb1 || *nn == nb->nb2) { + //walls.push_back( new Wall(*i,*nn,this,nb->CellBase) ); + //debug_stream << "Adding edge " << walls.back() << " to CellBase " << index << endl; + //} + + neighbors.push_back( nb->CellBase ); + } + } + + +} + +neighbors.sort(); + +list::iterator e=unique(neighbors.begin(),neighbors.end()); + +// iterator e point to the end of the subsequence of unique elements +// remove every thing that comes after it + +neighbors.erase(e, neighbors.end()); + +// okay, now neighbors contains all neighbors of this CellBase, including itself + +// A future optimization for the diffusion algorithm: now we list +// each of the edges of a (curved) CellBase boundary separately. We +// could keep track just of the total length per CellBase boundary + +// the following is not necessary anymore. Is +// checked at earlier stage +// // remove myself from the list +// e = find(neighbors.begin(),neighbors.end(),index); +// if (e!=neighbors.end()) +// neighbors.erase(e); +// + +// remove boundary_polygon from the list (CellBase identity <0 ) +e=neighbors.begin(); +at_boundary=false; +do { + e = find_if(neighbors.begin(),neighbors.end(),mem_fun(&CellBase::BoundaryPolP)); + if (e!=neighbors.end()) { + e=neighbors.erase(e); + at_boundary=true; + } else { + break; + } +} while(1); + + +}*/ + + +/*void Cell::print_nblist(void) const { +// cerr << "{ "; + +for (list::const_iterator i=nb_list.begin(); + i!=nb_list.end(); + i++) { + // cerr << "(" << i->c->index << " " << i->Dij << ")"; + +} +// cerr << "}" << endl; +} +*/ + + +// Tests whether Cell p (given as Vector, remember that Cell is a +// Vector) is within polygon formed by nearest neighbor cells +// +// Based on algorithm and code by Paul Bourke, see +// http://astronomy.swin.edu.au/~pbourke/geometry/insidepoly/ +// +// Note: works for 2D only; projects everything on z=0; +/* +#define MIN(x,y) (x < y ? x : y) +#define MAX(x,y) (x > y ? x : y) + */ +/*bool Cell::CellInsidePolygonP(Vector &p) +{ + int counter = 0; + double xinters; + Vector p1,p2; + + //p1 = polygon[0]; + p1 = *(nb_list.begin()->c); + + int N=nb_list.size(); + list::const_iterator nb=nb_list.begin(); + + for (int i=1;i<=N;i++) { + + nb++; + + if (nb!=nb_list.end()) { + p2 = *(nb->c); + } else { + p2 = *(nb_list.begin()->c); + } + + if (p.y > MIN(p1.y,p2.y)) { + if (p.y <= MAX(p1.y,p2.y)) { + if (p.x <= MAX(p1.x,p2.x)) { + if (p1.y != p2.y) { + xinters = (p.y-p1.y)*(p2.x-p1.x)/(p2.y-p1.y)+p1.x; + if (p1.x == p2.x || p.x <= xinters) + counter++; + } + } + } + } + p1 = p2; + } + + if (counter % 2 == 0) + return false; + else + return true; + +}*/ + + +/* // at new position cell should be able to "see" all polygon sides +bool Cell::NewPointValidP(Vector &p) { + + //int ninvtri=0; + for (list::const_iterator nb=nb_list.begin(); + nb!=nb_list.end(); + nb++) { + + Vector p1=*(nb->c); // first neighbor + list::const_iterator nextv=nb; nextv++; + + + if (nextv==nb_list.end()) { + if (Boundary()==None) { + nextv=nb_list.begin(); + } else continue; + } + + Vector p2=*(nextv->c); + + Vector v1=(p1-p); + Vector v2=(p2-p1); + + Vector cross=v1*v2; + // //cerr << "[" << cross << "]" << endl; + + if (cross.z<0) { + // One of the triangles has "inverted". + //if (Boundary()==None || ninvtri) + return false; + //else + // accept one "inverted" triangle + //ninvtri++; + } + } + return true; + +}*/ + + + + +// void Cell::CheckForDivision(void) { +// // if (/* Chemical(0)<0.4 && */ /* differentiated cells do not divide */ area > 2*base_area /* || Length()>50 */) { + +// if (area > par.rel_cell_div_threshold * base_area ) { +// /* remark no longer valid? //m->IncreaseCellCapacityIfNecessary(); +// // Note that calling Divide as follows prevents trouble if cell +// // vector is relocated */ +// Divide(); +// } +//} + +/* void Cell::CheckForGFDrivenDivision(void) { +if (area > base_area && chem[0]>par.gf_div_threshold) { + //int ind=index; + if (index==1) return; // petiole does not divide + + // remark no longer valid? + //m->IncreaseCellCapacityIfNecessary(); + // Note that calling Divide as follows prevents trouble if cell + // vector is relocated + Vector horizontal(1,0,0); + Vector vertical(0,1,0); + double r; + if ((r=RANDOM())>par.vertdivprob) { + DivideOverAxis(horizontal); + } else { + cerr << "[" << r << "]"; + DivideOverAxis(vertical); + } +} +} +*/ + + + +// return (a measure of) the strain of this cell +/*Vector CellBase::Strain(void) const { + + cerr << "Sorry, CellBase::strain currently not implemented" << endl; + std::exit(1); + + // Reason: we do not want to include "Node" in the plugins (Node::target_length below), and we do need Strain anyway... + + + // go over all wall elements of the cell + Vector Fvec; + + for (list::const_iterator n=nodes.begin(); + n!=nodes.end(); + n++) { + + list::const_iterator nn=n; nn++; + if (nn==nodes.end()) nn=nodes.begin(); + + Vector wall_element = *(*n) - *(*nn); + + // assume k=1 (Hooke's constant), for now + double Fscal = (Node::target_length - wall_element.Norm())/Node::target_length; + + + Fvec += Fscal * wall_element.Normalised(); + + } + + return Fvec; +} */ + + + +/* void Cell::Flux(double *flux, double D) { + +// Algorithm according to Rudge & Haseloff 2005 +// (will we need to take cell area into account?) +// For the time being, we don't: assume cell area is +// mainly determined by vacuole. + +// Currently implements Rolland-Lagan-Mitchison algorithm +// Rolland-Lagan and Prusinkiewicz, The Plant Journal (2005), 44, 854-865 + +// currently I only implemented passive, diffusive transport +// active transport will be added later + +// loop over cell edges + +for (int c=0;c::iterator i=walls.begin(); + i!=walls.end(); + i++) { + + + // leaf cannot take up chemicals from environment ("no flux boundary") + if (i->c2 < 0) continue; + + // calculate edge length + // (will later be updated during node displacement for efficiency) + double edge_length = (m->nodes[i->n1]-m->nodes[i->n2]).Norm(); + + // D is "background diffusion coefficient" (Rolland-Lagan) + + + // flux depends on edge length and concentration difference */ + // i->phi = edge_length * ( /* i->D +*/ D ) * ( m->cells[i->c2].chem[0] - chem[0] ); + /* + if (m->cells[i->c1].index!=index) { + cerr << "Warning, bad cells boundary: " << m->cells[i->c1].index << ", " << index << endl; + } + + flux[0] += i->phi; + //double deltaD = par.alpha * (i->phi*i->phi) - par.gamma * i->D; // Note beta=0 + //i->D += par.dt*deltaD; + + //cerr << "[ i->D = " << i->D << ", deltaD = " << deltaD << "]"; + //if (i->D > par.Dmax) i->D=par.Dmax; + + // first calculate all fluxes, we update diffusion coefficient afterwards. + + // cerr << "[ " << edge_length << ", " << m->cells[i->c2].chem[0] << " - " << chem[0] << "]"; + +} + + +} +*/ + + // Save the cell to a stream so we can reconstruct its state later + void CellBase::Dump(ostream &os) const { + + + os << index << " " << nodes.size() << endl; + + Vector::Dump(os); + os << endl; + + for (list::const_iterator i=nodes.begin();i!=nodes.end();i++) { + os << *i << " "; + } + os << endl; + + + os << index << " " << neighbors.size() << endl; + for (list::const_iterator i=neighbors.begin();i!=neighbors.end();i++) { + os << *i << " "; + } + os << endl; + + os << walls.size() << endl; + /*for (list::const_iterator i=walls.begin();i!=walls.end(); i++) { + (*i)->Dump(os); + }*/ + + os << endl; + + os << NChem() << " "; + for (int i=0;i::const_iterator i=nodes.begin(); + i!=nodes.end(); + i++) { + (*i)->Unfix(); + } + + } + + void CellBase::FixNodes(void) { + + for (list::const_iterator i=nodes.begin(); + i!=nodes.end(); + i++) { + (*i)->Fix(); + } + + } + + // returns true if cell is at border + bool CellBase::AtBoundaryP(void) const { + + return at_boundary; + } + + + + + + + +QString CellBase::printednodelist(void) { + QString info_string = "Nodelist = { "; + for (list::const_iterator i = nodes.begin(); i!=nodes.end(); i++) { + info_string += QString("%1 ").arg((*i)->Index()); + } + info_string += " } "; + return info_string; +} + diff --git a/src/cellbase.h b/src/cellbase.h new file mode 100644 --- /dev/null +++ b/src/cellbase.h @@ -0,0 +1,473 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +// CellBase derives from Vector, where Vector is simply used as a Vertex + +#ifndef _CELLBASE_H_ +#define _CELLBASE_H_ + +#include +#include +#include +#include +#include "vector.h" +#include "parameter.h" +#include "wall.h" +#include "warning.h" + +extern Parameter par; +using namespace std; + +class Mesh; +class Node; +class CellBase; +class NodeSet; + +struct ParentInfo { + + Vector polarization; + double PINmembrane; + double PINendosome; + +}; + +// We need a little trick here, to make sure the plugin and the main application will see the same static datamembers +// otherwise each have their own instantation. +// My solution is as follow. I collect all original statics in a class. The main application instantiates it and +// has a static pointer to it. After loading the plugin I set a static pointer to the same class +class CellsStaticDatamembers { + +public: + CellsStaticDatamembers(void) { + ncells = 0; + nchem = 0; + base_area = 0.; + cerr << "Constructor of CellsStaticDatamembers\n"; + } + ~CellsStaticDatamembers() { + cerr << "Oops! Desctructor of CellsStaticDatamembers called\n"; + } + int ncells; + int nchem; + double base_area; + + +}; + +class CellBase : public QObject, public Vector +{ + + Q_OBJECT + + + friend class Mesh; + friend class CellInfo; + friend class Node; + friend class WallBase; + friend class SimPluginInterface; + + public: + CellBase(QObject *parent=0); + CellBase(double x,double y,double z=0); // constructor + + virtual ~CellBase() { + delete[] chem; + delete[] new_chem; + //cerr << "CellBase " << index << " is dying. " << endl; + } + + CellBase(const CellBase &src); // copy constructor + virtual bool BoundaryPolP(void) const { return false; } + + + // CellBase(const Vector &src); // not allowed (we cannot know to which mesh + /// the CellBase will belong...) + CellBase operator=(const CellBase &src); // assignment operator + CellBase operator=(const Vector &src); + + void SetChemical(int chem, double conc); + inline void SetNewChem(int chem, double conc) { + new_chem[chem] = conc; + } + void SetSource(int chem, double conc) { + source=true; + source_chem = chem; + source_conc = conc; + } + + void UnfixNodes(void); + void FixNodes(void); + void UnsetSource(void) { + source = false; + } + + inline bool Source(void) { return source; } + enum boundary_type {None, Noflux, SourceSink, SAM}; + static const char * boundary_type_names[4]; + + inline const char *BoundaryStr(void) { return boundary_type_names[boundary]; } + + ostream &print(ostream &os) const; + inline double Chemical(int c) const { // returns the value of chemical c + return chem[c]; + } + + + //void print_nblist(void) const; + + boundary_type SetBoundary(boundary_type bound) { + if (bound!=None) { + //area=0.; + //length=0.; + } + return boundary=bound; + } + + boundary_type ResetBoundary(void) { + return boundary=None; + } + boundary_type Boundary(void) const { + return boundary; + } + static int &NChem(void) { + return static_data_members->nchem; + } + + double CalcArea(void) const; + double RecalcArea(void) { + return area = CalcArea(); + } + + Vector Centroid(void) const; + + void SetIntegrals(void) const; + + double Length(Vector *long_axis = 0, double *width = 0) const; + double CalcLength(Vector *long_axis = 0, double *width = 0) const; + + + inline int Index(void) const { + return index; + } + + + void SetTargetArea(double tar_ar) { + target_area=tar_ar; + } + inline void SetTargetLength(double tar_l) { + target_length=tar_l; + } + inline void SetLambdaLength(double lambda_length) { + lambda_celllength = lambda_length; + } + inline double TargetArea(void) { + return target_area; + } + + inline void SetStiffness(double stiff) { + stiffness = stiff; + } + + inline double Stiffness(void) { + return stiffness; + } + inline double EnlargeTargetArea(double da) { + return target_area+=da; + } + + inline double Area(void) const { + return area; + } + + inline void Divide(void) { + flag_for_divide = true; + } + //Vector Strain(void) const; + + inline double Circumference(void) const { + double sum=0.; + for (list::const_iterator w=walls.begin(); + w!=walls.end(); + w++) { + sum += (*w)->Length(); + } + + return sum; + } + + QList getWalls(void) { + QList wall_list; + for (list::iterator i=walls.begin(); + i!=walls.end(); + i++) { + wall_list << *i; + } + return wall_list; + } + // void XFigPrint(std::ostream &os) const; + + void Dump(ostream &os) const; + + QString printednodelist(void); + + // void OnDivide(ParentInfo &parent_info, CellBase &daughter); + + + inline bool DeadP(void) { return dead; } + inline void MarkDead(void) { dead = true; } + + static double &BaseArea(void) { + return static_data_members->base_area; + } + + void CheckForDivision(void); + + + // write flux from neighboring cells into "flux" + void Flux(double *flux, double *D); + inline bool FixedP(void) { return fixed; } + inline bool Fix(void) { FixNodes(); return (fixed=true); } + inline bool Unfix(void) { UnfixNodes(); return (fixed=false);} + inline void setCellVec(Vector cv) { cellvec = cv; } + + bool AtBoundaryP(void) const; + + static inline int &NCells(void) { + return static_data_members->ncells; + } + + + + inline void Mark(void) { + marked=true; + } + inline void Unmark(void) { + marked=false; + } + inline bool Marked(void) const { + return marked; + } + + //! Returns the sum of chemical "chem" of this CellBase's neighbors + double SumChemicalsOfNeighbors(int chem) { + double sum=0.; + for (list::const_iterator w=walls.begin(); + w!=walls.end(); + w++) { + sum += (*w)->Length() * ( (*w)->c1!=this ? (*w)->c1->Chemical(chem) : (*w)->c2->Chemical(chem) ); + } + return sum; + } + + //! Generalization of the previous member function + template P ReduceNeighbors(Op f) { + P sum=0; + for (list::const_iterator w=walls.begin(); + w!=walls.end(); + w++) { + sum += (*w)->c1 != this ? f( *((*w)->c1) ) : f ( *((*w)->c2) ); + } + return sum; + } + + //! The same, but now for the walls + template P ReduceWalls(Op f, P sum) { + for (list::const_iterator w=walls.begin(); + w!=walls.end(); + w++) { + sum += f( **w ); + } + return sum; + } + + + + + //! The same, but now for the walls AND neighbors + template P ReduceCellAndWalls(Op f) { + P sum = 0; + for (list::const_iterator w=walls.begin(); + w!=walls.end(); + w++) { + sum += (*w)->c1 == this ? + f( *((*w)->c1), *((*w)->c2), **w ) : + f( *((*w)->c2), *((*w)->c1), **w ); + } + return sum; + } + + /* template void LoopWalls(Op f) { + for (list::const_iterator w=walls.begin(); + w!=walls.end(); + w++) { + ( **w)->f; + } + }*/ + + //! Sum transporters at this CellBase's side of the walls + double SumTransporters(int ch) { + double sum=0.; + for (list::const_iterator w=walls.begin(); + w!=walls.end(); + w++) { + sum += (*w)->getTransporter(this, ch); + + } + + return sum; + } + + inline int NumberOfDivisions(void) { return div_counter; } + + //! Sum transporters at this CellBase's side of the walls + double SumLengthTransporters(int ch) { + double sum=0.; + for (list::const_iterator w=walls.begin(); + w!=walls.end(); + w++) { + sum += (*w)->getTransporter(this, ch) * (*w)->Length(); + + } + + return sum; + } + + + + double SumLengthTransportersChemical(int trch, int ch) { + double sum=0.; + for (list::const_iterator w=walls.begin(); + w!=walls.end(); + w++) { + sum += (*w)->getTransporter(this, trch) * ( (*w)->c1!=this ? (*w)->c1->Chemical(ch) : (*w)->c2->Chemical(ch) ); + + } + + return sum; + } + inline int CellType(void) const { return cell_type; } + inline void SetCellType(int ct) { cell_type = ct; } + + + static void SetNChem(int new_nchem) { + if (NCells()) { + MyWarning::error("CellBase::SetNChem says: not permitted, call SetNChem after deleting all cells."); + } else { + NChem() = new_nchem; + } + } + + inline double TargetLength() const { return target_length; } + + static inline CellsStaticDatamembers *GetStaticDataMemberPointer(void) { return static_data_members; } + +protected: + // (define a list of Node* iterators) + typedef list < list::iterator > ItList; + + int index; + + inline void SetChemToNewchem(void) { + for (int c=0;c nodes; + void ConstructNeighborList(void); + long wall_list_index (Wall *elem) const; + + // DATA MEMBERS + + // list of nodes, in clockwise order + + // a (non-ordered) list of neighboring cells (actually I think the + // introduction of ConstructWalls() has made these + // lists ordered (clockwise), but I am not yet 100% sure...). + list neighbors; + + list walls; + + double *chem; + double *new_chem; + + boundary_type boundary; + mutable double area; + double target_area; + double target_length; + double lambda_celllength; + + double stiffness; // stiffness like in Hogeweg (2000) + + bool fixed; + bool pin_fixed; + bool at_boundary; + bool dead; + bool flag_for_divide; + + int cell_type; + + //double length; + //Vector meanflux; + //int valence; + + // for length constraint + mutable double intgrl_xx, intgrl_xy, intgrl_yy, intgrl_x, intgrl_y; + + bool source; + Vector cellvec; + + // STATIC DATAMEMBERS MOVED TO CLASS + static CellsStaticDatamembers *static_data_members; + double source_conc; + int source_chem; + + // PRIVATE MEMBER FUNCTIONS + inline static void ClearNCells(void) { + NCells()=0; + } + + bool marked; + int div_counter; +}; + +ostream &operator<<(ostream &os, const CellBase &v); + +inline Vector PINdir(CellBase &here, CellBase &nb, Wall &w) { + return w.getTransporter( &here, 1) * w.getInfluxVector(&here); +} + + +#endif + + + + + diff --git a/src/cellitem.cpp b/src/cellitem.cpp new file mode 100644 --- /dev/null +++ b/src/cellitem.cpp @@ -0,0 +1,45 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include +#include "cellitem.h" + +static const std::string _module_id("$Id$"); + +CellItem::CellItem( Cell *c, QGraphicsScene *canvas ) + : QGraphicsPolygonItem( 0, canvas ), SimItemBase( c, canvas){ +} + +void CellItem::userMove(double dx, double dy) { + QGraphicsPolygonItem::moveBy( dx, dy ); + + // also move the cell itself + class_cast(obj)->x += (dx/Cell::Magnification()); + class_cast(obj)->y += (dy/Cell::Magnification()); + + class_cast(obj)->Move( (dx/Cell::Magnification()), (dy/Cell::Magnification()) ); +} + + +QPainterPath CellItem::shape() const { return QGraphicsPolygonItem::shape(); } +QRectF CellItem::boundingRect() const { return QGraphicsPolygonItem::boundingRect(); } + diff --git a/src/cellitem.h b/src/cellitem.h new file mode 100644 --- /dev/null +++ b/src/cellitem.h @@ -0,0 +1,49 @@ +/* + * VirtualLeaf + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#ifndef _CELLITEM_H_ +#define _CELLITEM_H_ + +#include +#include +#include +#include "simitembase.h" +#include "cell.h" + + +class CellItem : public QGraphicsPolygonItem, public SimItemBase +{ +public: + CellItem( Cell *n, QGraphicsScene *canvas ); + virtual ~CellItem() {} + Cell &getCell(void) const { return *class_cast(obj); } + virtual void userMove(double dx, double dy); + QPainterPath shape() const; + QRectF boundingRect() const; + + private: + +}; + +#endif diff --git a/src/curvecolors.h b/src/curvecolors.h new file mode 100644 --- /dev/null +++ b/src/curvecolors.h @@ -0,0 +1,177 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + + +#ifndef _CURVECOLORS_H_ +#define _CURVECOLORS_H_ + +#include + +class CurveColors { + + public: + CurveColors(void) { + colors += QString("red"); + colors += QString("darkgreen"); + colors += QString("blue"); + colors += QString("purple"); + colors += QString("powderblue"); + colors += QString("darkgreen"); + colors += QString("darkcyan"); + colors += QString("burlywood"); + colors += QString("deeppink"); + colors += QString("deepskyblue"); + colors += QString("fuchsia"); + colors += QString("lawngreen"); + colors += QString("lavenderblush"); + colors += QString("pink"); + colors += QString("purple"); + colors += QString("navy"); + colors += QString("greenyellow"); + colors += QString("mintcream"); + colors += QString("saddlebrown"); + colors += QString("salmon"); + colors += QString("cornsilk"); + colors += QString("darkmagenta"); + colors += QString("chocolate"); + colors += QString("ivory"); + colors += QString("khaki"); + colors += QString("moccasin"); + colors += QString("mediumturquoise"); + colors += QString("mediumorchid"); + colors += QString("darkgray"); + colors += QString("lightseagreen"); + colors += QString("royalblue"); + colors += QString("darkred"); + colors += QString("violet"); + colors += QString("lavender"); + colors += QString("silver"); + colors += QString("slategray"); + colors += QString("ghostwhite"); + colors += QString("forestgreen"); + colors += QString("lightgrey"); + colors += QString("brown"); + colors += QString("lightgoldenrodyellow"); + colors += QString("darkblue"); + colors += QString("lime"); + colors += QString("darkkhaki"); + colors += QString("oldlace"); + colors += QString("springgreen"); + colors += QString("darkseagreen"); + colors += QString("skyblue"); + colors += QString("teal"); + colors += QString("lightsalmon"); + colors += QString("midnightblue"); + colors += QString("mediumslateblue"); + colors += QString("darkslateblue"); + colors += QString("indianred"); + colors += QString("honeydew"); + colors += QString("lemonchiffon"); + colors += QString("peru"); + colors += QString("snow"); + colors += QString("mistyrose"); + colors += QString("darkslategrey"); + colors += QString("gainsboro"); + colors += QString("mediumpurple"); + colors += QString("grey"); + colors += QString("palegoldenrod"); + colors += QString("linen"); + colors += QString("dimgrey"); + colors += QString("firebrick"); + colors += QString("lightcyan"); + colors += QString("steelblue"); + colors += QString("orangered"); + colors += QString("darkgoldenrod"); + colors += QString("turquoise"); + colors += QString("blueviolet"); + colors += QString("rosybrown"); + colors += QString("lightslategrey"); + colors += QString("lightgray"); + colors += QString("orchid"); + colors += QString("darkturquoise"); + colors += QString("darkorange"); + colors += QString("plum"); + colors += QString("dimgray"); + colors += QString("crimson"); + colors += QString("mediumaquamarine"); + colors += QString("chartreuse"); + colors += QString("limegreen"); + colors += QString("cadetblue"); + colors += QString("mediumblue"); + colors += QString("papayawhip"); + colors += QString("tan"); + colors += QString("dodgerblue"); + colors += QString("orange"); + colors += QString("lightblue"); + colors += QString("coral"); + colors += QString("blue"); + colors += QString("sandybrown"); + colors += QString("slategrey"); + colors += QString("lightpink"); + colors += QString("sienna"); + colors += QString("indigo"); + colors += QString("seashell"); + colors += QString("lightskyblue"); + colors += QString("paleturquoise"); + colors += QString("darkslategray"); + colors += QString("darkolivegreen"); + colors += QString("mediumspringgreen"); + colors += QString("floralwhite"); + colors += QString("lightyellow"); + colors += QString("palegreen"); + colors += QString("red"); + colors += QString("lightgreen"); + colors += QString("cyan"); + colors += QString("hotpink"); + colors += QString("cornflowerblue"); + colors += QString("maroon"); + colors += QString("magenta"); + colors += QString("lightsteelblue"); + colors += QString("gray"); + colors += QString("green"); + colors += QString("darkviolet"); + colors += QString("palevioletred"); + colors += QString("darksalmon"); + colors += QString("seagreen"); + colors += QString("tomato"); + colors += QString("olive"); + colors += QString("slateblue"); + colors += QString("peachpuff"); + colors += QString("gold"); + colors += QString("mediumseagreen"); + colors += QString("darkorchid"); + colors += QString("lightslategray"); + colors += QString("lightcoral"); + colors += QString("mediumvioletred"); + colors += QString("thistle"); + } + QString &operator[](int i) { + if (i>=colors.size() || i<0) { + throw("Color number out of range in curvecolors.h"); + } else + return colors[i]; + } + private: + QStringList colors; +}; +#endif diff --git a/src/cwi.xpm b/src/cwi.xpm new file mode 100644 --- /dev/null +++ b/src/cwi.xpm @@ -0,0 +1,619 @@ +/* XPM */ +static char * cwi_xpm[] = { +"61 61 555 2", +" c None", +". c #FFFFFF", +"+ c #FFFCFC", +"@ c #FDEEEF", +"# c #FCE4E6", +"$ c #FBDFE2", +"% c #FEF4F5", +"& c #FDE9EB", +"* c #F6B3BA", +"= c #F07E8A", +"- c #EB5363", +"; c #E94355", +"> c #E94052", +", c #E93F52", +"' c #E62535", +") c #F49FA9", +"! c #FEF9FA", +"~ c #F6B1B8", +"{ c #EC5967", +"] c #E83B4B", +"^ c #EB5564", +"/ c #F18690", +"( c #F7BBC2", +"_ c #FBDCDF", +": c #FDEFF1", +"< c #FEF8F9", +"[ c #F07C89", +"} c #F3929D", +"| c #FEF5F6", +"1 c #F397A0", +"2 c #E93E4D", +"3 c #EA4F5D", +"4 c #F5A5AD", +"5 c #FDEBED", +"6 c #F28894", +"7 c #E62736", +"8 c #EB5163", +"9 c #ED5F6E", +"0 c #F07A86", +"a c #F5AAB2", +"b c #FBD9DC", +"c c #F6B4BB", +"d c #E83949", +"e c #EB5563", +"f c #F8C6CB", +"g c #F28895", +"h c #EF717D", +"i c #F8C4CA", +"j c #F5AAB3", +"k c #F28C96", +"l c #EC5F6D", +"m c #E93F50", +"n c #E94253", +"o c #EF7782", +"p c #F9CDD2", +"q c #FDEDEF", +"r c #ED606E", +"s c #E93E4E", +"t c #F7BEC4", +"u c #FDEEF0", +"v c #F9CFD3", +"w c #F6B1B9", +"x c #F396A1", +"y c #E94251", +"z c #F3939F", +"A c #FFFBFB", +"B c #FAD0D4", +"C c #F18A93", +"D c #EA4A59", +"E c #F9CED3", +"F c #E83448", +"G c #EF7380", +"H c #FEF7F8", +"I c #F2919B", +"J c #EB5462", +"K c #E8374A", +"L c #E94154", +"M c #EC5B6B", +"N c #EF7281", +"O c #F07F87", +"P c #FACFD4", +"Q c #E94150", +"R c #EB505E", +"S c #F9CED2", +"T c #F7B7BE", +"U c #E6243A", +"V c #F5ABB3", +"W c #F8C5CA", +"X c #EC5C69", +"Y c #E83747", +"Z c #EE6975", +"` c #F7B6BC", +" . c #FCE7E9", +".. c #FFFDFE", +"+. c #F49DA6", +"@. c #E72E41", +"#. c #F28B96", +"$. c #FFFEFE", +"%. c #F6AFB7", +"&. c #E72C42", +"*. c #FBDEE2", +"=. c #EC5C6A", +"-. c #F398A1", +";. c #FDF1F2", +">. c #FCE3E6", +",. c #EA4D5E", +"'. c #EC5869", +"). c #FDF1F3", +"!. c #F8C1C8", +"~. c #F49EA7", +"{. c #E72B3F", +"]. c #F18893", +"^. c #FEF6F7", +"/. c #EF7381", +"(. c #FCE6E9", +"_. c #E7293F", +":. c #F7BDC3", +"<. c #FFFDFD", +"[. c #EF707F", +"}. c #E8384B", +"|. c #F1808D", +"1. c #E8394D", +"2. c #FDEFF0", +"3. c #E94054", +"4. c #F398A2", +"5. c #FEFAFB", +"6. c #EC5B6C", +"7. c #EB4F61", +"8. c #F07E8B", +"9. c #EA4759", +"0. c #F18491", +"a. c #ED6575", +"b. c #EB5364", +"c. c #FEFAFA", +"d. c #EC5C6C", +"e. c #FAD7DB", +"f. c #E72A40", +"g. c #FCE5E8", +"h. c #E94356", +"i. c #FEF3F4", +"j. c #E83348", +"k. c #F7B4BC", +"l. c #EC596A", +"m. c #F28F9A", +"n. c #F8C3C9", +"o. c #FCE8EA", +"p. c #F8C2C9", +"q. c #E72E44", +"r. c #FEF2F3", +"s. c #E83449", +"t. c #E83549", +"u. c #F6B4BC", +"v. c #ED6373", +"w. c #F18591", +"x. c #EE6777", +"y. c #F2909C", +"z. c #F28E99", +"A. c #EC5768", +"B. c #FBDDE0", +"C. c #E72F44", +"D. c #FDECEE", +"E. c #FDE8EA", +"F. c #FDF0F2", +"G. c #E83247", +"H. c #FBD8DC", +"I. c #ED5F70", +"J. c #F3909B", +"K. c #F5A3AD", +"L. c #EC5C6D", +"M. c #F49BA6", +"N. c #EDECED", +"O. c #BBBABB", +"P. c #9D9C9E", +"Q. c #8D8C8E", +"R. c #8F8D8F", +"S. c #D6D6D6", +"T. c #EBEAEB", +"U. c #DCDCDC", +"V. c #DDDDDE", +"W. c #DDDCDD", +"X. c #E3E3E3", +"Y. c #FEFEFE", +"Z. c #EFEFEF", +"`. c #DCDBDC", +" + c #E4E4E4", +".+ c #ECEBEC", +"++ c #DDDDDD", +"@+ c #DFDFDF", +"#+ c #F1F1F1", +"$+ c #E0DFE0", +"%+ c #FCFCFC", +"&+ c #EA4155", +"*+ c #F5A3AC", +"=+ c #B6B5B6", +"-+ c #322F33", +";+ c #131014", +">+ c #100D11", +",+ c #060307", +"'+ c #9F9E9F", +")+ c #878588", +"!+ c #171518", +"~+ c #201E21", +"{+ c #1B171C", +"]+ c #4F4D50", +"^+ c #FDFDFD", +"/+ c #6B696B", +"(+ c #181519", +"_+ c #1B181C", +":+ c #4E4B4E", +"<+ c #545154", +"[+ c #1A171B", +"}+ c #211E21", +"|+ c #5C5A5C", +"1+ c #9A999A", +"2+ c #1C191D", +"3+ c #434144", +"4+ c #F9F9F9", +"5+ c #E93C50", +"6+ c #FAD3D8", +"7+ c #E93B4F", +"8+ c #FCE0E3", +"9+ c #FBDCE0", +"0+ c #E8364B", +"a+ c #B5B4B6", +"b+ c #19171A", +"c+ c #151216", +"d+ c #18151A", +"e+ c #B3B2B3", +"f+ c #999799", +"g+ c #0F0C10", +"h+ c #19161A", +"i+ c #120E13", +"j+ c #585659", +"k+ c #EAEAEA", +"l+ c #2A272B", +"m+ c #171418", +"n+ c #484548", +"o+ c #DAD9DA", +"p+ c #1E1B1F", +"q+ c #181419", +"r+ c #AAA8AA", +"s+ c #727073", +"t+ c #110D12", +"u+ c #605E61", +"v+ c #EF717F", +"w+ c #F28A97", +"x+ c #FAD5D9", +"y+ c #E93D50", +"z+ c #FFFCFD", +"A+ c #E6E6E6", +"B+ c #2F2C30", +"C+ c #141115", +"D+ c #232024", +"E+ c #B9B8B9", +"F+ c #ABAAAB", +"G+ c #110E12", +"H+ c #130F14", +"I+ c #59575A", +"J+ c #AEADAE", +"K+ c #494649", +"L+ c #9A999B", +"M+ c #E7E7E7", +"N+ c #424043", +"O+ c #898789", +"P+ c #F6AEB7", +"Q+ c #EC5868", +"R+ c #EE6E7D", +"S+ c #F395A0", +"T+ c #7B797B", +"U+ c #1D1A1E", +"V+ c #969597", +"W+ c #E1E0E1", +"X+ c #D8D7D8", +"Y+ c #EEEEEE", +"Z+ c #120F13", +"`+ c #636164", +" @ c #615F61", +".@ c #49464A", +"+@ c #5B595C", +"@@ c #787779", +"#@ c #262327", +"$@ c #A9A8AA", +"%@ c #FBD9DD", +"&@ c #E93E52", +"*@ c #F28F9B", +"=@ c #EF7583", +"-@ c #F7BCC3", +";@ c #ECECEC", +">@ c #9B999B", +",@ c #C6C5C6", +"'@ c #747274", +")@ c #29262A", +"!@ c #4C494C", +"~@ c #E8E8E8", +"{@ c #CFCECF", +"]@ c #C8C8C9", +"^@ c #161317", +"/@ c #C9C8C9", +"(@ c #FDF0F1", +"_@ c #E8394E", +":@ c #FDEBEE", +"<@ c #F07987", +"[@ c #F28E9A", +"}@ c #EA4558", +"|@ c #FAD6DA", +"1@ c #B4B4B4", +"2@ c #444245", +"3@ c #D2D1D2", +"4@ c #818082", +"5@ c #AEADAF", +"6@ c #100C11", +"7@ c #282528", +"8@ c #383538", +"9@ c #514E51", +"0@ c #B4B3B4", +"a@ c #141015", +"b@ c #4A474A", +"c@ c #929193", +"d@ c #282529", +"e@ c #EA4356", +"f@ c #FBDADF", +"g@ c #EE6F7C", +"h@ c #F8C5CB", +"i@ c #F9C9CF", +"j@ c #F8C0C5", +"k@ c #E72D42", +"l@ c #FBE0E3", +"m@ c #848284", +"n@ c #8C8A8C", +"o@ c #221F23", +"p@ c #848385", +"q@ c #0C090D", +"r@ c #5C5A5D", +"s@ c #626063", +"t@ c #0D0A0E", +"u@ c #535154", +"v@ c #807E80", +"w@ c #676567", +"x@ c #3C393D", +"y@ c #F8F8F8", +"z@ c #EA4A5C", +"A@ c #FAD4D9", +"B@ c #F5A6B0", +"C@ c #EB515C", +"D@ c #E93F53", +"E@ c #E51930", +"F@ c #EB5161", +"G@ c #ED5C6C", +"H@ c #676467", +"I@ c #BAB9BA", +"J@ c #151116", +"K@ c #686669", +"L@ c #2B282C", +"M@ c #9F9EA0", +"N@ c #666467", +"O@ c #0E0B0F", +"P@ c #444144", +"Q@ c #151217", +"R@ c #E6E6E7", +"S@ c #FBFBFB", +"T@ c #413F42", +"U@ c #F0808D", +"V@ c #E94552", +"W@ c #E4132B", +"X@ c #E8384A", +"Y@ c #EB5263", +"Z@ c #FACCD0", +"`@ c #EB5062", +" # c #5F5C5F", +".# c #120F14", +"+# c #F2F2F2", +"@# c #333134", +"## c #272428", +"$# c #DEDEDF", +"%# c #6C6A6C", +"&# c #6C696C", +"*# c #252226", +"=# c #787679", +"-# c #EA4659", +";# c #E0000B", +"># c #E20018", +",# c #E10011", +"'# c #E30A22", +")# c #FAD3D7", +"!# c #EA4357", +"~# c #626062", +"{# c #8A898B", +"]# c #3D3B3E", +"^# c #6D6B6E", +"/# c #BEBDBF", +"(# c #C3C2C3", +"_# c #9E9C9E", +":# c #E9384C", +"<# c #E10014", +"[# c #E2001A", +"}# c #E20017", +"|# c #E8384D", +"1# c #FDF2F4", +"2# c #868587", +"3# c #B3B2B4", +"4# c #828183", +"5# c #F6F6F7", +"6# c #4D4A4D", +"7# c #888789", +"8# c #6C6A6D", +"9# c #9B9A9C", +"0# c #BFBEBF", +"a# c #E61F36", +"b# c #E20016", +"c# c #E10015", +"d# c #E73045", +"e# c #FFFBFC", +"f# c #FAD6DB", +"g# c #C5C5C5", +"h# c #292629", +"i# c #F5F5F5", +"j# c #CCCBCC", +"k# c #6F6D70", +"l# c #908F91", +"m# c #D7D6D7", +"n# c #E3061E", +"o# c #E20019", +"p# c #E10012", +"q# c #EC5162", +"r# c #ED5F6F", +"s# c #F5A5AF", +"t# c #211E22", +"u# c #E7E6E7", +"v# c #6A686B", +"w# c #3C3A3D", +"x# c #F6F6F6", +"y# c #777578", +"z# c #DEDEDE", +"A# c #464347", +"B# c #302D31", +"C# c #F0EFF0", +"D# c #E10010", +"E# c #F1818E", +"F# c #F49BA5", +"G# c #ED6070", +"H# c #5A585B", +"I# c #242125", +"J# c #757376", +"K# c #09050A", +"L# c #080509", +"M# c #757375", +"N# c #0A070B", +"O# c #0F0B10", +"P# c #0B070C", +"Q# c #413E42", +"R# c #EC5A6B", +"S# c #F7B8BF", +"T# c #FBDFE3", +"U# c #E8354A", +"V# c #B1B0B2", +"W# c #716F71", +"X# c #5D5B5D", +"Y# c #696769", +"Z# c #B4B3B5", +"`# c #E0E0E0", +" $ c #A8A7A8", +".$ c #575558", +"+$ c #5D5B5E", +"@$ c #585658", +"#$ c #5F5D60", +"$$ c #5A585A", +"%$ c #767476", +"&$ c #716F72", +"*$ c #727072", +"=$ c #9C9A9C", +"-$ c #E41129", +";$ c #E5152D", +">$ c #FDEAED", +",$ c #EB4D5F", +"'$ c #F6AAB2", +")$ c #F28D99", +"!$ c #F6ADB5", +"~$ c #EA475A", +"{$ c #E62238", +"]$ c #E10016", +"^$ c #F6B3BB", +"/$ c #F7BFC5", +"($ c #F5A8B1", +"_$ c #E40F24", +":$ c #EA485B", +"<$ c #FFFAFB", +"[$ c #E94053", +"}$ c #E6253B", +"|$ c #EE6B7B", +"1$ c #E20216", +"2$ c #F4A0AA", +"3$ c #F18390", +"4$ c #ED6272", +"5$ c #EF7886", +"6$ c #EA495B", +"7$ c #EE6F7D", +"8$ c #EE6B7A", +"9$ c #EB4B5D", +"0$ c #FEF1F3", +"a$ c #FBDDE1", +"b$ c #E51A2E", +"c$ c #FAD2D7", +"d$ c #EF7684", +"e$ c #F39BA4", +"f$ c #E51B2F", +"g$ c #E31125", +"h$ c #E6293D", +"i$ c #F4A0A8", +"j$ c #F6B0B7", +"k$ c #E93E4F", +"l$ c #E10013", +"m$ c #E4162A", +"n$ c #FAD2D6", +"o$ c #E94656", +"p$ c #F1808C", +"q$ c #E72A3E", +"r$ c #E62A3D", +"s$ c #E73344", +"t$ c #EC5D69", +"u$ c #F8C1C6", +"v$ c #EE6A78", +"w$ c #E51F34", +"x$ c #E2011A", +"y$ c #EC5B6A", +"z$ c #FDEAEC", +"A$ c #F399A1", +"B$ c #E94654", +"C$ c #F07D88", +"D$ c #F8BFC5", +"E$ c #E1000F", +"F$ c #E62337", +"G$ c #F5A4AC", +"H$ c #FFFEFF", +"I$ c #F9CDD1", +"J$ c #F1848F", +"K$ c #EA4D5D", +"L$ c #E93D4F", +"M$ c #EA4658", +"N$ c #E2041D", +"O$ c #E51C30", +"P$ c #F1838E", +"Q$ c #FAD1D6", +"R$ c #F49FA8", +"S$ c #E3041E", +"T$ c #E3071F", +"U$ c #E93C4E", +"V$ c #F397A1", +"W$ c #FCE8EB", +"X$ c #E3051E", +"Y$ c #E20015", +"Z$ c #E2031C", +"`$ c #E4132A", +" % c #E72A3F", +".% c #EC5666", +"+% c #F3949E", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . + @ # $ % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . & * = - ; > , ' ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . ! ~ { ] ^ / ( _ : < [ } . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . | 1 2 3 4 5 . . . . . . 6 7 8 9 0 a b + . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . c d e f . . . . . . . . . g h i j k l m n o p . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . q r s t . . . . . . . u v w x y z . . . . A B C s D c . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . E F G H . . . . . _ I J K L M N O P . . . . . . . ! a Q R S . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . T U V . . . . . W X Y Z ` .. . . . . . . . . . . . . . ..+.@.#.$.. . . . . . . . . . . . . . . . ", +". . . . . . . . . . . %.&.i . . . . *.=.Y -.;.. . . . . . . . . . . . . . . . . . . >.,.'.).. . . . . . . . . . . . . . . ", +". . . . . . . . . . !.U P . . . . ~.{.].^.. . . . . . . . . . . . . . . . . . . . . . $./.L (.. . . . . . . . . . . . . . ", +". . . . . . . . . $ _.:.. . . <.[.}.P . . . . . . . . . . . . . . . . . . . . . . . . . . |.1.2.. . . . . . . . . . . . . ", +". . . . . . . . ! 3.4.. . . 5.6.7.;.. . . . . . . . . . . . . . . . . . . . . . . . . . . . 8.9.H . . . . . . . . . . . . ", +". . . . . . . . 0.'.. . . . a.b.c.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . d./.. . . . . . . . . . . . ", +". . . . . . . e.f.g.. . . 0.h.H . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . i.j.k.. . . . . . . . . . . ", +". . . . . . . l.m.. . . n._.o.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p.q.r.. . . . . . . . . . ", +". . . . . . E s.% . . % t.u.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . v.w.. . . . . . . . . . ", +". . . . . . x.y.. . . z.A.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.D.. . . . . . . . . ", +". . . . . 5 j.E.. . F.G.H.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I.J.. . . . . . . . . ", +". . . . . K.L.. . . M.l.. . . . . N.O.P.Q.R.S.T.U.V.W.X.Y.. Z.`.W. +. . .+`.V.++@+#+W.V.++$+%+. . . i &++ . . . . . . . . ", +". . . . . 6.*+. . $.h.( . . . . =+-+;+>+>+,+'+)+!+~+{+]+^+. /+(+_+:+. ^+<+[+}+[+|+1+(+~+2+3+4+. . . A 5+6+. . . . . . . . ", +". . . . ^.7+8+. . 9+0+^.. . . a+b+c+[+[+d+>+e+f+g+h+i+j+. k+l+m+;+n+. o+p+q+h+>+r+s+>+h+t+u+. . . . . v+w+. . . . . . . . ", +". . . . x+y+z+. . ) d.. . . A+B+c+[+(+C+D+m+E+F+G+[+H+I+. J+;+[+C+K+. L+G+[+m+B+M+N+c+[+>+O+. . . . . P+Q+. . . . . . . . ", +". . . . %.'.. . . R+S+. . . T+g+[+(+U+V+W+X+Y+=+Z+[+i+`+. @i+[+C+.@. +@H+[+>+@@N.#@(+[+G+$@. . . . . %@&@+ . . . . . . . ", +". . . . *@=@. . . 8 -@. . ;@B+m+[+;+>@. . . . ,@m+h+G+'@Z.)@m+(+C+!@~@l+(+h+(+{@]@^@[+[+(+/@. . . . . (@_@:@. . . . . . . ", +". . . . <@[@. . . }@|@. . 1@;+[+c+2@4+. . . . 3@_+h+>+4@5@6@7@8@G+9@0@a@[+C+b@. c@>+[+(+d@A+. . . . . + e@f@. . . . . . . ", +". . . . <@g@h@i@j@k@l@. . m@>+[+>+n@. . . . . @+o@(+>+p@ @q@r@s@t@u@v@t+[+>+1+. w@Z+[+^@x@y@. . . . . . z@A@. . . . . . . ", +". . . . B@C@D@E@F@G@@ . . H@Z+[+;+I@. . . . . ~@)@(+J@K@L@g+M@N@O@P@P@Q@(+d@R@S@T@c+[+;+I+. . . . . U@V@W@X@Y@u . . . . . ", +". . . . . . Z@`@. . . . . #.#[+;+E+. . . . . +#@#m+[+##(+p+$#%#G+U+_+[+G+&#. +*#h+[+>+=#. . . . . -#;#>#,#'#g.. . . . . ", +". . . . . . )#!#<.. . . . ~#Z+[+>+{#. . . . . 4+]#^@[+(+C+K+. ^#G+h+[+[+c+/#. (#^@[+[+>+_#. . . . $.:#<#[#}#E@u . . . . . ", +". . . . . . o.|#1#. . . . 2#>+[+m+L@(#+#R@3#4#5#6#C+[+[+>+7#. 8#G+[+[+c+T@y@. 9#G+[+[+c+0#. . . . i.a#b#[#c#d#e#. . . . . ", +". . . . . . 5.&@f#. . . . g#c+h+[+m+U+@#h#G+l+i#j+;+[+h+(+j#. k#G+[+[+>+l#. . k#G+[+h+p+m#. . . . A@n#o#[#p#q#. . . . . . ", +". . . . . . . r#s#. . . . S@s@O@[+[+h+m+(+h+t#u#v#i+[+^@w#x#. y#G+[+(+D+z#. %+A#C+[+m+B#C#. . . . ~.,#[#[#D#E#. . . . . . ", +". . . . . . . F#G#. . . . . ;@H#G+>+Z+G+>+;+I#`.J#K#Z+L#y#. . M#K#Z+N#H#. . ~@t#O#G+P#Q#^+. . . . R#,#[#[#<#S#. . . . . . ", +". . . . . . . T#U#i.. . . . . S@V#W#X#Y#m@Z#`#^+ $.$+$+@j#. . r+@$#$$$=+. . k+%$&$*$^#=$. . . . (.-$>#[#}#;$>$. . . . . . ", +". . . . . . . . ,$'$. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . )$,#[#[#,#A.. . . . . . . ", +". . . . . . . . !$~$. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.{$]$[#[#<#^$. . . . . . . ", +". . . . . . . . ! 5+/$. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 0.D#[#[#<#&.< . . . . . . . ", +". . . . . . . . . ($5+z+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . )#_$>#[#[#p#~.. . . . . . . . ", +". . . . . . . . . <.:$x . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . <$[$p#[#[#<#f.% . . . . . . . . ", +". . . . . . . . . . x+}$)#. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . |$p#[#[#o#1$w . . . . . . . . . ", +". . . . . . . . . . . 2$G.@ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3$p#o#[#[#D#4$. . . . . . . . . . ", +". . . . . . . . . . . . 5$6$< . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.7$c#o#[#[#p#k@q . . . . . . . . . . ", +". . . . . . . . . . . . . 8$9$0$. . . . . . . . . . . . . . . . . . . . . . . . . a$,.,#o#[#[#<#b$c$. . . . . . . . . . . ", +". . . . . . . . . . . . . $.d$K x+. . . . . . . . . . . . . . . . . . . . . . e#e$f$,#[#[#[#c#g$p.. . . . . . . . . . . . ", +". . . . . . . . . . . . . . . +.h$i$. . . . . . . . . . . . . . . . . . . | j$k$l$b#[#[#[#l$m$-@. . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . n$o$R S . . . . . . . . . . . . . . ;.f p$q$c#l$[#[#[#[#,#r$P . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . + -.s$t$u$+ . . . . . . ;.4.E#v$6$w$x$D#c#[#[#[#[#c#p#y$z$. . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . u A$B$Q C$D$z$. . . >.>#E$D#l$}#o#[#[#[#[#}#D#F$G$H$. . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . H$I$J$K$L$M$A.8$v.N$[#[#[#[#[#[#o#<#,#O$P$:@. . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . : Q$T R$3$S$o#o#>#c#p#D#T$U$V$W$. . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . >.X$Y$Z$`$ %.%+%)#. . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . ^.n.u$E .! . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . "}; diff --git a/src/data_plot.cpp b/src/data_plot.cpp new file mode 100644 --- /dev/null +++ b/src/data_plot.cpp @@ -0,0 +1,225 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "data_plot.h" + +static const std::string _module_id("$Id$"); + +// +// Initialize main window +// +DataPlot::DataPlot(QWidget *parent, const QString title, const QStringList curvenames ): + QwtPlot(parent), + d_interval(0), + d_timerId(-1) +{ + + // Number of curves is number of names given + ncurves = curvenames.size(); + + // allocate data and curves + d_t = new double[PLOT_SIZE]; + d_x = new double *[ncurves]; + d_x[0] = new double[ncurves*PLOT_SIZE]; + for (int i=1;isetPaintAttribute(QwtPlotCanvas::PaintCached, false); + //canvas()->setPaintAttribute(QwtPlotCanvas::PaintPacked, false); + + alignScales(); + + // Initialize data + + + for (int i = 0; i< PLOT_SIZE; i++) { + d_t[i] = 0.; // time axis + } + + for (int i=0;iopen(QIODevice::WriteOnly | QIODevice::Text)) + return; + + datstream.setDevice(datfile); + */ + +} + +DataPlot::~DataPlot(void) { + delete[] d_t ; + delete[] d_x[0]; + delete[] d_x; + delete[] curves; +} +// +// Set a plain canvas frame and align the scales to it +// +void DataPlot::alignScales() +{ + // The code below shows how to align the scales to + // the canvas frame, but is also a good example demonstrating + // why the spreaded API needs polishing. + + canvas()->setFrameStyle(QFrame::Box | QFrame::Plain ); + canvas()->setLineWidth(1); + + for ( int i = 0; i < QwtPlot::axisCnt; i++ ) + { + QwtScaleWidget *scaleWidget = (QwtScaleWidget *)axisWidget(i); + if ( scaleWidget ) + scaleWidget->setMargin(0); + + QwtScaleDraw *scaleDraw = (QwtScaleDraw *)axisScaleDraw(i); + if ( scaleDraw ) + scaleDraw->enableComponent(QwtAbstractScaleDraw::Backbone, false); + } +} + +/* void DataPlot::setTimerInterval(double ms) +{ + d_interval = qRound(ms); + + if ( d_timerId >= 0 ) + { + killTimer(d_timerId); + d_timerId = -1; + } + if (d_interval >= 0 ) + d_timerId = startTimer(d_interval); +} +*/ + +// Generate new values +void DataPlot::AddValue(double t,double *x) +{ + + // std::cerr << "AddValue receives: " << t << ", " << y << ", " << z << std::endl; + + // Plot slowly fills up, then shifts to the left + if ( data_pos >= PLOT_SIZE ) { + + for ( int j = 0; j < PLOT_SIZE - 1; j++ ) + d_t[j] = d_t[j+1]; + + for ( int i=0;iresize(400,300); + + show(); + +} + +PlotDialog::~PlotDialog(void) { + + delete plot; +} + diff --git a/src/data_plot.h b/src/data_plot.h new file mode 100644 --- /dev/null +++ b/src/data_plot.h @@ -0,0 +1,86 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#ifndef _DATA_PLOT_H_ +#define _DATA_PLOT_H_ + +#include +#include +#include +#include +#include +#include +#include "curvecolors.h" + +const int PLOT_SIZE = 1000; + +class DataPlot : public QwtPlot +{ + Q_OBJECT + +public: + DataPlot(QWidget *parent, const QString title, const QStringList curvenames); + virtual ~DataPlot(void); +public slots: + // void setTimerInterval(double interval); + void AddValue(double t, double *x); + +private: + void alignScales(); + + double *d_t; + double **d_x; + + int d_interval; // timer in ms + int d_timerId; + + QwtPlotCurve *curves; + int data_pos; + int ncurves; + + CurveColors curvecolors; + + protected: + // to write contents of DataPlot to a file + QFile *datfile; + QTextStream datstream; + +}; + +class PlotDialog : public QDialog { + +Q_OBJECT + public: + PlotDialog(QWidget *parent, const QString title, const QStringList curvenames); + ~PlotDialog(void); + public slots: + // void setTimerInterval(double interval); + void AddValue(double t, double *x) { + //std::cerr << "AddValue receives: " << t << ", " << y << ", " << z << std::endl; + plot->AddValue(t,x); + } + private: + DataPlot *plot; +}; + +#endif diff --git a/src/far_mem_5.h b/src/far_mem_5.h new file mode 100644 --- /dev/null +++ b/src/far_mem_5.h @@ -0,0 +1,154 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + + +#ifndef _FAR_MEM_5_h_ +#define _FAR_MEM_5_h_ + +template + struct my_1_function + { +typedef _Arg1 argument_type1; +typedef _Result result_type; ///< result_type is the return type +}; +template +class far_1_arg_mem_fun_t : public my_1_function { +public: +explicit far_1_arg_mem_fun_t(Type &ir_typ, Result (Type::*i_pmf)(Param1)) { + m_ptyp = &ir_typ; + m_pmf = i_pmf; + }; +Result operator()(Param1 i_prm1) { return (m_ptyp->*(m_pmf))(i_prm1); }; + +protected: +Type *m_ptyp; +Result (Type::*m_pmf)(Param1); +}; + +template far_1_arg_mem_fun_t far_1_arg_mem_fun(Type &ir_typ, Result (Type::*i_pmf)(Param1) ) { + return far_1_arg_mem_fun_t(ir_typ,i_pmf); + } +template + struct my_2_function + { +typedef _Arg1 argument_type1; +typedef _Arg2 argument_type2; +typedef _Result result_type; ///< result_type is the return type +}; +template +class far_2_arg_mem_fun_t : public my_2_function { +public: +explicit far_2_arg_mem_fun_t(Type &ir_typ, Result (Type::*i_pmf)(Param1, Param2)) { + m_ptyp = &ir_typ; + m_pmf = i_pmf; + }; +Result operator()(Param1 i_prm1, Param2 i_prm2) { return (m_ptyp->*(m_pmf))(i_prm1, i_prm2); }; + +protected: +Type *m_ptyp; +Result (Type::*m_pmf)(Param1, Param2); +}; + +template far_2_arg_mem_fun_t far_2_arg_mem_fun(Type &ir_typ, Result (Type::*i_pmf)(Param1, Param2) ) { + return far_2_arg_mem_fun_t(ir_typ,i_pmf); + } +template + struct my_3_function + { +typedef _Arg1 argument_type1; +typedef _Arg2 argument_type2; +typedef _Arg3 argument_type3; +typedef _Result result_type; ///< result_type is the return type +}; +template +class far_3_arg_mem_fun_t : public my_3_function { +public: +explicit far_3_arg_mem_fun_t(Type &ir_typ, Result (Type::*i_pmf)(Param1, Param2, Param3)) { + m_ptyp = &ir_typ; + m_pmf = i_pmf; + }; +Result operator()(Param1 i_prm1, Param2 i_prm2, Param3 i_prm3) { return (m_ptyp->*(m_pmf))(i_prm1, i_prm2, i_prm3); }; + +protected: +Type *m_ptyp; +Result (Type::*m_pmf)(Param1, Param2, Param3); +}; + +template far_3_arg_mem_fun_t far_3_arg_mem_fun(Type &ir_typ, Result (Type::*i_pmf)(Param1, Param2, Param3) ) { + return far_3_arg_mem_fun_t(ir_typ,i_pmf); + } +template + struct my_4_function + { +typedef _Arg1 argument_type1; +typedef _Arg2 argument_type2; +typedef _Arg3 argument_type3; +typedef _Arg4 argument_type4; +typedef _Result result_type; ///< result_type is the return type +}; +template +class far_4_arg_mem_fun_t : public my_4_function { +public: +explicit far_4_arg_mem_fun_t(Type &ir_typ, Result (Type::*i_pmf)(Param1, Param2, Param3, Param4)) { + m_ptyp = &ir_typ; + m_pmf = i_pmf; + }; +Result operator()(Param1 i_prm1, Param2 i_prm2, Param3 i_prm3, Param4 i_prm4) { return (m_ptyp->*(m_pmf))(i_prm1, i_prm2, i_prm3, i_prm4); }; + +protected: +Type *m_ptyp; +Result (Type::*m_pmf)(Param1, Param2, Param3, Param4); +}; + +template far_4_arg_mem_fun_t far_4_arg_mem_fun(Type &ir_typ, Result (Type::*i_pmf)(Param1, Param2, Param3, Param4) ) { + return far_4_arg_mem_fun_t(ir_typ,i_pmf); + } +template + struct my_5_function + { +typedef _Arg1 argument_type1; +typedef _Arg2 argument_type2; +typedef _Arg3 argument_type3; +typedef _Arg4 argument_type4; +typedef _Arg5 argument_type5; +typedef _Result result_type; ///< result_type is the return type +}; +template +class far_5_arg_mem_fun_t : public my_5_function { +public: +explicit far_5_arg_mem_fun_t(Type &ir_typ, Result (Type::*i_pmf)(Param1, Param2, Param3, Param4, Param5)) { + m_ptyp = &ir_typ; + m_pmf = i_pmf; + }; +Result operator()(Param1 i_prm1, Param2 i_prm2, Param3 i_prm3, Param4 i_prm4, Param5 i_prm5) { return (m_ptyp->*(m_pmf))(i_prm1, i_prm2, i_prm3, i_prm4, i_prm5); }; + +protected: +Type *m_ptyp; +Result (Type::*m_pmf)(Param1, Param2, Param3, Param4, Param5); +}; + +template far_5_arg_mem_fun_t far_5_arg_mem_fun(Type &ir_typ, Result (Type::*i_pmf)(Param1, Param2, Param3, Param4, Param5) ) { + return far_5_arg_mem_fun_t(ir_typ,i_pmf); + } + +#endif diff --git a/src/forwardeuler.cpp b/src/forwardeuler.cpp new file mode 100644 --- /dev/null +++ b/src/forwardeuler.cpp @@ -0,0 +1,124 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include +#include "forwardeuler.h" +#include "warning.h" +#include "maxmin.h" +#include + +static const std::string _module_id("$Id$"); + +// The value Errcon equals (5/Safety) raised to the power (1/PGrow), see use below. + +/* static float maxarg1,maxarg2; + #define FMAX(a,b) (maxarg1=(a),maxarg2=(b),(maxarg1) > (maxarg2) ? \ + (maxarg1) : (maxarg2)) + static float minarg1,minarg2; + #define FMIN(a,b) (minarg1=(a),minarg2=(b),(minarg1) < (minarg2) ? \ + (minarg1) : (minarg2)) + #define SIGN(a,b) ((b) >= 0.0 ? fabs(a) : -fabs(a)) +*/ + + +const double ForwardEuler::Safety = 0.9; +const double ForwardEuler::PGrow = -0.2; +const double ForwardEuler::Pshrnk = -0.25; +const double ForwardEuler::Errcon = 1.89e-4; +const double ForwardEuler::Maxstp = 10000000; +const double ForwardEuler::Tiny = 1.0e-30; + + + +/* User storage for intermediate results. Preset kmax and dxsav in the calling program. If kmax = + 0 results are stored at approximate intervals dxsav in the arrays xp[1..kount], yp[1..nvar] + [1..kount], where kount is output by odeint. Defining declarations for these variables, with + memoryallo cations xp[1..kmax] and yp[1..nvar][1..kmax] for the arrays, should be in + the calling program.*/ + +void ForwardEuler::odeint(double *ystart, int nvar, double x1, double x2, double eps, double h1, double hmin, int *nok, int *nbad) +/* Runge-Kutta driver with adaptive stepsize control. Integrate starting values ystart[1..nvar] + from x1 to x2 with accuracy eps, storing intermediate results in global variables. h1 should + be set as a guessed first stepsize, hmin as the minimum allowed stepsize (can be zero). On + output nok and nbad are the number of good and bad (but retried and fixed) steps taken, and + ystart is replaced byv alues at the end of the integration interval. derivs is the user-supplied + routine for calculating the right-hand side derivative, while rkqs is the name of the stepper + routine to be used. */ +{ + static bool warning_issued = false; + if (!warning_issued) { + std::cerr << "Using inaccurate method ForwardEuler\n"; + warning_issued=true; + //MyWarning::warning("Using inaccurate method ForwardEuler"); + } + // N.B. Not for serious use and not fully usage compatible with RungeKutta + // simply for testing API of integrator. + + double *y,*dydx; + y=new double[nvar]; + dydx=new double[nvar]; + double x=x1; + + for (int i=0;i 0) xsav=x-dxsav*2.0; //Assures storage of first step. + + dydx=new double[nvar]; + + for (int nstp=0;nstp 0 && kount < kmax-1) { + xp[kount]=x; //Store intermediate results. + for (int i=0;i= 0.0) { //Are we done? + goto done; + } + + + } + + done: + for (int i=0;i. + * + * Copyright 2010 Roeland Merks. + * + */ + + +#ifndef _FORWARDEULER_H_ +#define _FORWARDEULER_H_ + +class ForwardEuler { + + public: + ForwardEuler(void) { + kmax=kount=0; + dxsav=0.; + xp=0; + yp=0; + } + + virtual ~ForwardEuler() {} + + void odeint(double ystart[], int nvar, double x1, double x2, double eps, double h1, + double hmin, int *nok, int *nbad); + + // implement this function in a derived class + protected: + virtual void derivs(double x, double *y, double *dxdy) = 0; + int kmax,kount; + double *xp,**yp,dxsav; + + private: + + static const double Safety; + static const double PGrow; + static const double Pshrnk; + static const double Errcon; + static const double Maxstp; + static const double Tiny; + + +}; +#endif diff --git a/src/infobar.h b/src/infobar.h new file mode 100644 --- /dev/null +++ b/src/infobar.h @@ -0,0 +1,61 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + + +#ifndef _INFOBAR_H_ +#define _INFOBAR_H_ + + +#include +#include +#include + +class InfoBar : public Q3DockWindow { + + Q_OBJECT +public: + + InfoBar(void) : Q3DockWindow() { + + virtleaf = new QLabel(); + SetText("undefined"); + + setHorizontalStretchable(true); + //dockwindow->boxLayout()->addWidget(viblab);//,Qt::AlignLeft); + boxLayout()->addStretch(); + boxLayout()->addWidget(virtleaf);//, Qt::AlignHCenter); + boxLayout()->addStretch(); + + //dockwindow->boxLayout()->addWidget(psblab);//, Qt::AlignRight); + } + + void SetText(QString text) { + virtleaf->setText(QString("

The Virtual Leaf

\n
Model: %1
").arg(text)); + } + +private: + //Q3DockWindow *dockwindow; + QLabel *virtleaf; +}; + +#endif \ No newline at end of file diff --git a/src/leaficon.icns b/src/leaficon.icns new file mode 100644 index 0000000000000000000000000000000000000000..223f0c6d653c1a26b4921348af93156f49edf629 GIT binary patch literal 50860 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 XJY0e3{EHi=U$igU7wwDoe`WhW_V&Q) diff --git a/src/leaficon.ico b/src/leaficon.ico new file mode 100644 index 0000000000000000000000000000000000000000..f7e64c9fabd1eddc3778ff9a519e1cefdc3552ad GIT binary patch literal 67646 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 diff --git a/src/libplugin.pro b/src/libplugin.pro new file mode 100644 --- /dev/null +++ b/src/libplugin.pro @@ -0,0 +1,72 @@ +# +# $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 . +# +# Copyright 2010 Roeland Merks. +# + +CONFIG += staticlib release +DEFINES = QTGRAPHICS # VLEAFPLUGIN +DESTDIR = build_models/lib +PERLDIR = ./perl +PARTMPL = VirtualLeafpar.tmpl +QT += qt3support +TARGET = vleaf +TEMPLATE = lib + +unix:LIBS += -lxml2 -lz -lm +win32:LIBS += -L\"c:\Lib\" -L\"c:\Bin\" -lxml2 -lz -lm -lwsock32 + +HEADERS = \ + cellbase.h \ + matrix.h \ + output.h \ + parameter.h \ + parse.h \ + random.h \ + simplugin.h \ + UniqueMessage.h \ + vector.h \ + wallbase.h \ + warning.h + +SOURCES = \ + cellbase.cpp \ + matrix.cpp \ + output.cpp \ + parameter.cpp \ + parse.cpp \ + random.cpp \ + simplugin.cpp \ + UniqueMessage.cpp \ + vector.cpp \ + wallbase.cpp \ + warning.cpp + +unix { + system(rm -f parameter.cpp parameter.h) # this is performed here when qmake is envoked and not in the resulting makefile. + system(perl $$PERLDIR/make_parameter_source.pl $$PARTMPL) + system(perl $$PERLDIR/make_pardialog_source.pl $$PARTMPL) + QMAKE_CXXFLAGS += -fexceptions -fPIC -I/usr/include/libxml2 + QMAKE_LFLAGS += -fPIC +} + +win32 { + QMAKE_CXXFLAGS += -fexceptions -IC:\Include +} + +# diff --git a/src/mainbase.cpp b/src/mainbase.cpp new file mode 100644 --- /dev/null +++ b/src/mainbase.cpp @@ -0,0 +1,260 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include + +#include "mainbase.h" +#include "xmlwrite.h" + +#include +#include + +static const std::string _module_id("$Id$"); + +xmlNode *MainBase::XMLSettingsTree(void) const { + + xmlNode *xmlsettings = xmlNewNode(NULL, BAD_CAST "settings"); + { + xmlNode *xmloption = xmlNewChild(xmlsettings, NULL, BAD_CAST "setting", NULL); + xmlNewProp(xmloption, BAD_CAST "name", BAD_CAST "show_cell_centers"); + ostringstream text; + text << bool_name(showcentersp); + xmlNewProp(xmloption, BAD_CAST "val", BAD_CAST text.str().c_str()); + } + { + xmlNode *xmloption = xmlNewChild(xmlsettings, NULL, BAD_CAST "setting", NULL); + xmlNewProp(xmloption, BAD_CAST "name", BAD_CAST "show_nodes"); + ostringstream text; + text << bool_name(showmeshp); + xmlNewProp(xmloption, BAD_CAST "val", BAD_CAST text.str().c_str()); + } + { + xmlNode *xmloption = xmlNewChild(xmlsettings, NULL, BAD_CAST "setting", NULL); + xmlNewProp(xmloption, BAD_CAST "name", BAD_CAST "show_node_numbers"); + ostringstream text; + text << bool_name(shownodenumbersp); + xmlNewProp(xmloption, BAD_CAST "val", BAD_CAST text.str().c_str()); + } + { + xmlNode *xmloption = xmlNewChild(xmlsettings, NULL, BAD_CAST "setting", NULL); + xmlNewProp(xmloption, BAD_CAST "name", BAD_CAST "show_cell_numbers"); + ostringstream text; + text << bool_name(showcellnumbersp); + xmlNewProp(xmloption, BAD_CAST "val", BAD_CAST text.str().c_str()); + } + { + xmlNode *xmloption = xmlNewChild(xmlsettings, NULL, BAD_CAST "setting", NULL); + xmlNewProp(xmloption, BAD_CAST "name", BAD_CAST "show_border_cells"); + ostringstream text; + text << bool_name(showbordercellp); + xmlNewProp(xmloption, BAD_CAST "val", BAD_CAST text.str().c_str()); + } + { + xmlNode *xmloption = xmlNewChild(xmlsettings, NULL, BAD_CAST "setting", NULL); + xmlNewProp(xmloption, BAD_CAST "name", BAD_CAST "show_cell_axes"); + ostringstream text; + text << bool_name(showcellsaxesp); + xmlNewProp(xmloption, BAD_CAST "val", BAD_CAST text.str().c_str()); + } + { + xmlNode *xmloption = xmlNewChild(xmlsettings, NULL, BAD_CAST "setting", NULL); + xmlNewProp(xmloption, BAD_CAST "name", BAD_CAST "show_cell_strain"); + ostringstream text; + text << bool_name(showcellstrainp); + xmlNewProp(xmloption, BAD_CAST "val", BAD_CAST text.str().c_str()); + } + { + xmlNode *xmloption = xmlNewChild(xmlsettings, NULL, BAD_CAST "setting", NULL); + xmlNewProp(xmloption, BAD_CAST "name", BAD_CAST "show_fluxes"); + ostringstream text; + text << bool_name(showfluxesp); + xmlNewProp(xmloption, BAD_CAST "val", BAD_CAST text.str().c_str()); + } + { + xmlNode *xmloption = xmlNewChild(xmlsettings, NULL, BAD_CAST "setting", NULL); + xmlNewProp(xmloption, BAD_CAST "name", BAD_CAST "show_walls"); + ostringstream text; + text << bool_name(showwallsp); + xmlNewProp(xmloption, BAD_CAST "val", BAD_CAST text.str().c_str()); + } + { + xmlNode *xmloption = xmlNewChild(xmlsettings, NULL, BAD_CAST "setting", NULL); + xmlNewProp(xmloption, BAD_CAST "name", BAD_CAST "show_apoplasts"); + ostringstream text; + text << bool_name(showapoplastsp); + xmlNewProp(xmloption, BAD_CAST "val", BAD_CAST text.str().c_str()); + } + { + xmlNode *xmloption = xmlNewChild(xmlsettings, NULL, BAD_CAST "setting", NULL); + xmlNewProp(xmloption, BAD_CAST "name", BAD_CAST "save_movie_frames"); + ostringstream text; + text << bool_name(movieframesp); + xmlNewProp(xmloption, BAD_CAST "val", BAD_CAST text.str().c_str()); + } + { + xmlNode *xmloption = xmlNewChild(xmlsettings, NULL, BAD_CAST "setting", NULL); + xmlNewProp(xmloption, BAD_CAST "name", BAD_CAST "show_only_leaf_boundary"); + ostringstream text; + text << bool_name(showboundaryonlyp); + xmlNewProp(xmloption, BAD_CAST "val", BAD_CAST text.str().c_str()); + } + { + xmlNode *xmloption = xmlNewChild(xmlsettings, NULL, BAD_CAST "setting", NULL); + xmlNewProp(xmloption, BAD_CAST "name", BAD_CAST "cell_growth"); + ostringstream text; + text << bool_name(dynamicscellsp); + xmlNewProp(xmloption, BAD_CAST "val", BAD_CAST text.str().c_str()); + } + { + xmlNode *xmloption = xmlNewChild(xmlsettings, NULL, BAD_CAST "setting", NULL); + xmlNewProp(xmloption, BAD_CAST "name", BAD_CAST "hide_cells"); + ostringstream text; + text << bool_name(hidecellsp); + xmlNewProp(xmloption, BAD_CAST "val", BAD_CAST text.str().c_str()); + } + + return xmlsettings; +} + +void MainBase::XMLReadSettings(xmlNode *settings) { + + // Many files have no settings section, so don't complain about it. + // Defaults will be used instead. + if (settings == 0) { + return; + } + + xmlNode *cur = settings->xmlChildrenNode; + + while (cur!=NULL) { + + if ((!xmlStrcmp(cur->name, (const xmlChar *)"setting"))){ + + xmlChar *name = xmlGetProp(cur, BAD_CAST "name"); + xmlChar *val = xmlGetProp(cur, BAD_CAST "val"); + if (!xmlStrcmp(name, (const xmlChar *)"show_cell_centers")) { + showcentersp = strtobool( (const char *)val ); + } + if (!xmlStrcmp(name, (const xmlChar *)"show_nodes")) { + showmeshp = strtobool( (const char *)val ); + } + if (!xmlStrcmp(name, (const xmlChar *)"show_node_numbers")) { + shownodenumbersp = strtobool( (const char *)val ); + } + if (!xmlStrcmp(name, (const xmlChar *)"show_cell_numbers")) { + showcellnumbersp = strtobool( (const char *)val ); + } + if (!xmlStrcmp(name, (const xmlChar *)"show_border_cells")) { + showbordercellp = strtobool( (const char *)val ); + } + if (!xmlStrcmp(name, (const xmlChar *)"show_cell_axes")) { + showcellsaxesp = strtobool( (const char *)val ); + } + if (!xmlStrcmp(name, (const xmlChar *)"show_cell_strain")) { + showcellstrainp = strtobool( (const char *)val ); + } + if (!xmlStrcmp(name, (const xmlChar *)"show_fluxes")) { + showfluxesp = strtobool( (const char *)val ); + } + if (!xmlStrcmp(name, (const xmlChar *)"show_walls")) { + showwallsp = strtobool( (const char *)val ); + } + if (!xmlStrcmp(name, (const xmlChar *)"show_apoplasts")) { + showapoplastsp = strtobool( (const char *)val ); + } + if (!xmlStrcmp(name, (const xmlChar *)"save_movie_frames")) { + movieframesp = strtobool( (const char *)val ); + } + if (!xmlStrcmp(name, (const xmlChar *)"show_only_leaf_boundary")) { + showboundaryonlyp = strtobool( (const char *)val ); + } + if (!xmlStrcmp(name, (const xmlChar *)"cell_growth")) { + dynamicscellsp = strtobool( (const char *)val ); + } + if (!xmlStrcmp(name,(const xmlChar *)"hide_cells")) { + hidecellsp = strtobool( (const char *)val ); + } + + xmlFree(name); + xmlFree(val); + + + } + cur=cur->next; + } + +} + +void MainBase::Save(const char *fname, const char *format, int sizex, int sizey) { + + Vector ll,ur; + mesh.BoundingBox(ll, ur); + + if (QString(fname).isEmpty()) { + MyWarning::warning("No output filename given. Saving nothing.\n"); + return; + } + + + 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 ); + + if (!QString(format).contains("pdf", Qt::CaseInsensitive)) { + + QImage *image = new QImage(QSize(sizex, sizey), QImage::Format_RGB32); + image->fill(QColor(Qt::white).rgb()); + QPainter *painter=new QPainter(image); + //canvas.render(painter,QRectF(),QRectF(-5000,-5000, 10000, 10000)); + canvas.render(painter); + if (!image->save(QString(fname))) { + MyWarning::warning("Image not saved successfully. Is the disk full or the extension not recognized?"); + }; + delete painter; + delete image; + } else { + QPrinter pdf(QPrinter::HighResolution); + pdf.setOutputFileName(fname); + pdf.setOutputFormat(QPrinter::PdfFormat); + //pdf.setPageSize(QPrinter::Custom); + QPainter painter(&pdf); + // cerr << "Cell::Magnification() = " << Cell::Magnification() << endl; + //if (sizex==0 || sizey==0) { + // always fit to page + // canvas.render(&painter); + //} else { + // canvas.render(&painter,QRectF(),QRectF(-5000,-5000, 10000, 10000)); + //} + canvas.render(&painter, QRectF(), QRectF(-5000,-5000, 10000, 10000)); + + cerr << "Rendering to printer\n"; + } +} + +void MainBase::CutSAM() { + + mesh.CutAwaySAM(); + +} diff --git a/src/mainbase.h b/src/mainbase.h new file mode 100644 --- /dev/null +++ b/src/mainbase.h @@ -0,0 +1,153 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + + +#ifndef _MAINBASE_H_ +#define _MAINBASE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#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 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 list = canvas.items(); + QList::Iterator it = list.begin(); + for (; it != list.end(); ++it) { + if ( *it ) + delete *it; + } + }; + virtual void XMLReadSettings(xmlNode *settings); + virtual double getFluxArrowsize(void) { return 10.;} + + /* void Save(const char *fname, const char *format, int width=640) { + + //cerr << "Initializing Pixmap\n"; + + cerr << "Saving to file " << fname << endl; + QPixmap *image=new QPixmap(width, (int) (((double)canvas.height()/ + (double)canvas.width())*((double)width))); + + //QPicture *image = new QPicture(); + + //cerr << "Done initializing QPixmap image(640, " << (((double)canvas.height()/(double)canvas.width())*640.) << ")\n"; + QPainter im(image); + //cerr << "Done initializing image\n"; + im.scale((double)width/canvas.width(),(double)width/canvas.width()); + canvas.render(&im, QRectF(0,0,canvas.width(),canvas.height()) ); + //im.end(); + image->save(QString(fname), format); + }*/ + + + 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; + + protected: + QGraphicsScene &canvas; + virtual xmlNode *XMLSettingsTree(void) 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 +#define TIMESTEP double MainBase::TimeStep(void) +#define INIT void MainBase::Init(char *leaffile) + +#endif diff --git a/src/matrix.cpp b/src/matrix.cpp new file mode 100644 --- /dev/null +++ b/src/matrix.cpp @@ -0,0 +1,178 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include +#include +#include "vector.h" +#include "matrix.h" +#include "tiny.h" + +static const std::string _module_id("$Id$"); + +Matrix::Matrix(const Vector &c1, const Vector &c2, const Vector &c3) { + + Alloc(); + + mat[0][0]=c1.x; mat[0][1]=c2.x; mat[0][2]=c3.x; + mat[1][0]=c1.y; mat[1][1]=c2.y; mat[1][2]=c3.y; + mat[2][0]=c1.z; mat[2][1]=c2.z; mat[2][2]=c3.z; + + +} + +void Matrix::Alloc(void) { + + // constructor +// mat=(double **)malloc(3*sizeof(double *)); +// mat[0]=(double *)malloc(9*sizeof(double)); + mat = new double*[3]; + mat[0] = new double[9]; + for (int i=1;i<3;i++) + mat[i]=mat[i-1]+3; + +} + +Matrix::~Matrix() { + + // destructor + //free(mat[0]); + //free(mat); + delete[] mat[0]; + delete[] mat; +} + +Matrix::Matrix(void) { + + // constructor + Alloc(); + + // clear matrix + for (int i=0;i<9;i++) { + mat[0][i]=0.; + } + +} + +Matrix::Matrix(const Matrix &source) { + + // copy constructor + Alloc(); + + for (int i=0;i<9;i++) { + mat[0][i]=source.mat[0][i]; + } + +} + + +void Matrix::operator=(const Matrix &source) { + + // assignment + + // don't assign to self + if (this==&source) return; + + // copy + for (int i=0;i<9;i++) + mat[0][i]=source.mat[0][i]; + +} + + +void Matrix::print(ostream *os) { + + *os << "{ { " << mat[0][0] << "," << mat[0][1] << "," << mat[0][2] << "},{" << mat[1][0] << "," << mat[1][1] << "," << mat[1][2] << "},{" << mat[2][0] << "," << mat[2][1] << "," << mat[2][2] << "} }"; + +} + +ostream &operator<<(ostream &os, Matrix &v) { + v.print(&os); + return os; +} + + +Vector Matrix::operator*(const Vector &v) const { + + // matrix * vector + Vector result; + + result.x = mat[0][0]*v.x+mat[0][1]*v.y+mat[0][2]*v.z; + result.y = mat[1][0]*v.x+mat[1][1]*v.y+mat[1][2]*v.z; + result.z = mat[2][0]*v.x+mat[2][1]*v.y+mat[2][2]*v.z; + + return result; + + +} + + +bool Matrix::operator==(Matrix &m) const { + + for (int i=0;i<9;i++) { + if ((mat[0][i]-m.mat[0][i])>TINY) + return false; + } + return true; + +} + +double Matrix::Det(void) const { + + return + - mat[0][2]*mat[0][4]*mat[0][6] + + mat[0][1]*mat[0][5]*mat[0][6] + + mat[0][2]*mat[0][3]*mat[0][7] + - mat[0][0]*mat[0][5]*mat[0][7] + - mat[0][1]*mat[0][3]*mat[0][8] + + mat[0][0]*mat[0][4]*mat[0][8]; + +} + +Matrix Matrix::Inverse(void) const { + + // return the Inverse of this matrix + double rd=1./Det(); // Reciproce Det; + Matrix inverse; + inverse.mat[0][0]=rd*(-mat[0][5]*mat[0][7]+mat[0][4]*mat[0][8]); + inverse.mat[0][1]=rd*( mat[0][2]*mat[0][7]-mat[0][1]*mat[0][8]); + inverse.mat[0][2]=rd*(-mat[0][2]*mat[0][4]+mat[0][1]*mat[0][5]); + inverse.mat[0][3]=rd*( mat[0][5]*mat[0][6]-mat[0][3]*mat[0][8]); + inverse.mat[0][4]=rd*(-mat[0][2]*mat[0][6]+mat[0][0]*mat[0][8]); + inverse.mat[0][5]=rd*( mat[0][2]*mat[0][3]-mat[0][0]*mat[0][5]); + inverse.mat[0][6]=rd*(-mat[0][4]*mat[0][6]+mat[0][3]*mat[0][7]); + inverse.mat[0][7]=rd*( mat[0][1]*mat[0][6]-mat[0][0]*mat[0][7]); + inverse.mat[0][8]=rd*(-mat[0][1]*mat[0][3]+mat[0][0]*mat[0][4]); + + + return inverse; +} + +void Matrix::Rot2D(double theta) { + + // make this matrix a rotation matrix over theta + // see http://mathworld.wolfram.com/RotationMatrix.html + + mat[0][0] = cos(theta); mat[0][1]=sin(theta); + mat[1][0] = -sin(theta); mat[1][1]=cos(theta); + mat[0][2] = mat[1][2] = mat[2][0] = mat[2][1] = mat[2][2] = 0.; + +} diff --git a/src/matrix.h b/src/matrix.h new file mode 100644 --- /dev/null +++ b/src/matrix.h @@ -0,0 +1,57 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + + +#ifndef _MATRIX_H_ +#define _MATRIX_H_ + +// three by three matrix (mostly for changes of basis of the vector object) +class Vector; + +class Matrix { + +public: + Matrix(const Vector &v1,const Vector &v2, const Vector &v3); // constructor + Matrix(void); + ~Matrix(); // destructor + + void print(ostream *os); + Matrix(const Matrix &source); // copy constructor + void operator=(const Matrix &source); // assignment operator + + Vector operator*(const Vector &v) const; // matrix * vector + bool operator==(Matrix &v) const; // comparison + double Det(void) const; // gives the "determinant" (| m |) of m + Matrix Inverse(void) const; // gives the inverse of m + void Rot2D(double theta); // make a matrix doing a 2D rotation over theta + // data members + double **mat; + +private: + + void Alloc(void); +}; + +ostream &operator<<(ostream &os, Matrix &v); + +#endif diff --git a/src/maxmin.h b/src/maxmin.h new file mode 100644 --- /dev/null +++ b/src/maxmin.h @@ -0,0 +1,35 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + + +#ifndef _MAXMIN_H_ +#define _MAXMIN_H_ + +#include + +inline double FMAX(double a, double b) { return a>b ? a : b; } +inline double FMIN(double a, double b) { return a=0.0 ? fabs(a) : -fabs(a); } + +#endif + diff --git a/src/mesh.cpp b/src/mesh.cpp new file mode 100644 --- /dev/null +++ b/src/mesh.cpp @@ -0,0 +1,2266 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include + +static const std::string _module_id("$Id$"); + +extern Parameter par; + +void Mesh::AddNodeToCellAtIndex(Cell *c, Node *n, Node *nb1, Node *nb2, list::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::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;cSetChemical(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 current_cells = cells; + for (vector::iterator j=current_cells.begin(); + j!=current_cells.end();j++) { + (*j)->DivideOverAxis(axis); + } + axis=axis.Perp2D(); + + } + + IncreaseCellCapacityIfNecessary(); + + axis=axis.Perp2D(); + + vector current_cells = cells; + for (vector::iterator j=current_cells.begin(); + j!=current_cells.end();j++) { + (*j)->DivideOverAxis(axis); + } + + + double sum_l=0; int n_l=0; + for (list::const_iterator i=cell->nodes.begin(); + i!=cell->nodes.end(); + i++) { + list::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;iboundary = true; + + } + + for (int i=0;im = 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; + + //boundary_polygon = c; + /* list::iterator nb; + for (list::iterator i=c->nodes.begin(); + i!=c->nodes.end(); + i++) { + + nb = i; nb++; + if (nb==c->nodes.end()) { + nb=c->nodes.begin(); + } + int next = *nb; + + nb = i; + if (nb==c->nodes.begin()) { + nb=c->nodes.end(); + } + nb--; + int previous = *nb; + + + getNode(*i).cells.push_back( Neighbor(boundary_polygon->index, next, previous) ); + }*/ + + c->SetIntegrals(); + //c->ConstructNeighborList(); + + //c->ConstructWalls(); + + // initial cell has one wall with the outside world + //c->walls.push_back( new Wall ( nodes.front(), nodes.front(), c, boundary_polygon )); + + 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 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::reverse_iterator it_n1=circle->nodes.rbegin(); + for (int i=0;i::reverse_iterator it_n2=--circle->nodes.rend(); + /* for (int i=0;iboundary=true; + n4->boundary=true; + + AddNodeToCell(petiole, *it_n1, + n4, + nodes[(*it_n2)->Index() + + (( (*it_n1)->Index() - (*it_n2)->Index() )-1+nnodes)%nnodes]); + + + + list::reverse_iterator i=it_n1; i++; + for (; + i!=it_n2; + //(++i) == circle->nodes.rend() ? i : i=circle->nodes.rbegin() ) { + ++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; + + //petiole.nodes.push_back(n3.Index()); + //petiole.nodes.push_back(n4.Index()); + AddNodeToCell(petiole, + n3, + n2, + n4); + AddNodeToCell(petiole, + n4, + n3, + n1); + + + + cerr << circle << endl; + cerr << petiole << endl; + + 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;iowners.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::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::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::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::iterator i=nodes.begin(); + i!=nodes.end(); + i++) { + delete *i; + } + + nodes.clear(); + Node::nnodes=0; + + node_insertion_queue.clear(); + // Clear NodeSets + for (vector::iterator i=node_sets.begin(); + i!=node_sets.end(); + i++) { + delete *i; + } + + node_sets.clear(); + time = 0; + + // clear cells + + for (vector::iterator i=cells.begin(); + i!=cells.end(); + i++) { + delete *i; + } + + cells.clear(); + Cell::NCells() = 0; + + delete boundary_polygon; + + // Clear walls + for (list::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; + */ + + cerr << "cells.size() = " << cells.size() << endl; + cerr << "walls.size() = " << walls.size() << endl; + cerr << "nodes.size() = " << nodes.size() << endl; +} + +double Mesh::DisplaceNodes(void) { + + MyUrand r(shuffled_nodes.size()); + random_shuffle(shuffled_nodes.begin(),shuffled_nodes.end(),r); + + double sum_dh=0; + + list delta_intgrl_list; + + for_each( node_sets.begin(), node_sets.end(), mem_fun( &NodeSet::ResetDone ) ); + + for (vector::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::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) { + cerr << node.index << ": " << bending_dh << " (" << r1 << ", " << r2 << ") " << cit->nb1 << ", " << cit->nb2 << ")" << endl; + }*/ + + //bending_dh += r1 - r2; + + } + + //const double bend_lambda = 100000; + //const double bend_lambda = 0; + + /* double dh = //(area_dev_sum_after - area_dev_sum_before) + + area_dh + par.lambda_celllength * cell_length_dh + + par.lambda_length * length_dh + par.bend_lambda * bending_dh + par.alignment_lambda * alignment_dh;*/ + + dh = //(area_dev_sum_after - area_dev_sum_before) + + area_dh + cell_length_dh + + par.lambda_length * length_dh + par.bend_lambda * bending_dh + par.alignment_lambda * alignment_dh; + + //cerr << "cell_length_dh = " << par.lambda_celllength * cell_length_dh << endl; + //(length_constraint_after - length_constraint_before); + + if (node.fixed) { + + // search the fixed cell to which this node belongs + // and displace these cells as a whole + // WARNING: undefined things will happen for connected fixed cells... + for (list::iterator c=node.owners.begin(); + c!=node.owners.end(); + c++) { + if (!c->cell->BoundaryPolP() && c->cell->FixedP()) { + sum_dh+=c->cell->Displace(rx,ry,0); + } + } + } else { + + + if (dh<-sum_stiff || RANDOM()::const_iterator di_it = delta_intgrl_list.begin(); + for (list::iterator cit=node.owners.begin(); + cit!=node.owners.end(); + ( cit++) ) { + //m->getCell(cit->cell).area -= *da_it; + //if (cit->cell>=0) { + if (!cit->cell->BoundaryPolP()) { + cit->cell->area -= di_it->area; + if (par.lambda_celllength) { + cit->cell->intgrl_x -= di_it->ix; + cit->cell->intgrl_y -= di_it->iy; + cit->cell->intgrl_xx -= di_it->ixx; + cit->cell->intgrl_xy -= di_it->ixy; + cit->cell->intgrl_yy -= di_it->iyy; + } + di_it++; + } + } + + double old_nodex, old_nodey; + + old_nodex=node.x; + old_nodey=node.y; + + node.x = new_p.x; + node.y = new_p.y; + + for (list::iterator cit=node.owners.begin(); + cit!=node.owners.end(); + ( cit++) ) { + + /* if (cit->cell >= 0 && cells[cit->cell].SelfIntersect()) { + node.x = old_nodex; + node.y = old_nodey; + goto next_node; + }*/ + } + sum_dh += dh; + } + } + } + next_node: + delta_intgrl_list.clear();//dA_list.clear(); + + } + + return sum_dh; + +} + + +void Mesh::InsertNode(Edge &e) { + + + // Construct a new node in the middle of the edge + Node *new_node = AddNode( new Node ( ( *e.first + *e.second )/2 ) ); + + // if new node is inserted into the boundary + // it will be part of the boundary, fixed, and source, too + + // The new node is part of the boundary only if both its neighbors are boundary nodes and the boundray proceeds from first to second. + new_node->boundary = (e.first->BoundaryP() && e.first->BoundaryP()) && ((findNextBoundaryNode(e.first))->Index() == e.second->Index()); + new_node->fixed = e.first->fixed && e.second->fixed; + new_node->sam = new_node->boundary && (e.first->sam || e.second->sam); + + // insert it into the boundary polygon; + /* if (new_node->boundary) { + + // find the position of the first node in the boundary + list::iterator ins_pos = find + (boundary_polygon->nodes.begin(), + boundary_polygon->nodes.end(), + e.first); + // ... second node comes before or after it ... + if (*(++ins_pos!=boundary_polygon->nodes.end()? + ins_pos:boundary_polygon->nodes.begin())!=e.second) { + + boundary_polygon->nodes.insert(((ins_pos--)!=boundary_polygon->nodes.begin()?ins_pos:(--boundary_polygon->nodes.end())), new_node); + + // .. set the neighbors of the new node ... + // in this case e.second and e.first are inverted + new_node->owners.push_back( Neighbor(boundary_polygon, e.second, e.first ) ); + //cerr << "pushing back " << Neighbor(boundary_polygon->index, e.second, e.first ) << endl; + } else { + // insert before second node, so leave ins_pos as it is, + // that is incremented + boundary_polygon->nodes.insert(ins_pos, new_node); + + // .. set the neighbors of the new node ... + new_node->owners.push_back( Neighbor(boundary_polygon, e.first, e.second ) ); + // cerr << "pushing back " << Neighbor(boundary_polygon->index, e.second, e.first ) << endl; + } + + }*/ + + + list owners; + + // push all cells owning the two nodes of the divided edges + // onto a list + copy(e.first->owners.begin(), + e.first->owners.end(), + back_inserter(owners)); + copy(e.second->owners.begin(), + e.second->owners.end(), + back_inserter(owners)); + + //copy(owners.begin(), owners.end(), ostream_iterator(cerr, " ")); + //cerr << endl; + + // sort the nodes + owners.sort( mem_fun_ref( &Neighbor::Cmp ) ); + + // extern ofstream debug_stream; + + // debug_stream << "Nodes " << e.first << " and " << e.second << endl; + // copy(owners.begin(), owners.end(), ostream_iterator(debug_stream, " ")); + // debug_stream << endl; + + // the duplicates in this list indicate cells owning this edge + list::iterator c=owners.begin(); + while (c!=owners.end()) { + c=adjacent_find(c,owners.end(), neighbor_cell_eq); + + + if (c!=owners.end()) { // else break; + + // debug_stream << "Cell " << c->cell << " owns Edge " << e << endl; + + //if (c->cell>=0) { + //if (!c->cell->BoundaryPolP()) { + // find the position of the edge's first node in cell c... + list::iterator ins_pos = find + (c->cell->nodes.begin(), + c->cell->nodes.end(), + e.first); + // ... second node comes before or after it ... + + // XXXX probably this test is always false XXXX: No, works okay. + if (*(++ins_pos!=c->cell->nodes.end()? + ins_pos:c->cell->nodes.begin())!=e.second) { + c->cell->nodes.insert(((ins_pos--)!=c->cell->nodes.begin()?ins_pos:(--c->cell->nodes.end())), new_node); + //cells[c->cell].nodes.insert(--ins_pos, new_node->index); + // .. set the neighbors of the new node ... + // in this case e.second and e.first are inverted + // cerr << "Inverted\n"; + new_node->owners.push_back( Neighbor(c->cell, e.second, e.first ) ); + } else { + // insert before second node, so leave ins_pos as it is, + // that is incremented + c->cell->nodes.insert(ins_pos, new_node); + // .. set the neighbors of the new node ... + // cerr << "Not inverted\n"; + new_node->owners.push_back( Neighbor(c->cell, e.first, e.second ) ); + } + + // redo the neighbors: + //} + + + // - find cell c among owners of Node e.first + list::iterator cpos= + find_if( e.first->owners.begin(), + e.first->owners.end(), + bind2nd( mem_fun_ref(&Neighbor::CellEquals), c->cell->Index()) ); + + // - correct the record + if (cpos->nb1 == e.second) { + cpos->nb1 = new_node; + } else + if (cpos->nb2 == e.second) { + cpos->nb2 = new_node; + } + + // - same for Node e.second + cpos= + find_if( e.second->owners.begin(), + e.second->owners.end(), + bind2nd( mem_fun_ref(&Neighbor::CellEquals), c->cell->Index()) ); + + // - correct the record + if (cpos->nb1 == e.first) { + cpos->nb1 = new_node; + } else + if (cpos->nb2 == e.first) { + cpos->nb2 = new_node; + } + + + } else break; + c++; + } + + // Repair neighborhood lists in a second loop, to make sure all + // `administration' is up to date + while (c!=owners.end()) { + c=adjacent_find(c,owners.end(), neighbor_cell_eq); + // repair neighborhood lists of cell and Wall lists + //if (c->cell>=0) { + if (!c->cell->BoundaryPolP()) { + c->cell->ConstructNeighborList(); + // debug_stream << "Repairing NeighborList of " << c->cell << endl; + } + c++; + } + // debug_stream.flush(); + +} + + +/* + Calculate circumcircle of triangle (x1,y1), (x2,y2), (x3,y3) + The circumcircle centre is returned in (xc,yc) and the radius in r + NOTE: A point on the edge is inside the circumcircle +*/ +void Mesh::CircumCircle(double x1,double y1,double x2,double y2,double x3,double y3, + double *xc,double *yc,double *r) +{ + double m1,m2,mx1,mx2,my1,my2; + double dx,dy,rsqr; + + /* Check for coincident points */ + /*if (abs(y1-y2) < TINY && abs(y2-y3) < TINY) + return(false);*/ + + if (abs(y2-y1) < TINY) { + m2 = - (x3-x2) / (y3-y2); + mx2 = (x2 + x3) / 2.0; + my2 = (y2 + y3) / 2.0; + *xc = (x2 + x1) / 2.0; + *yc = m2 * (*xc - mx2) + my2; + } else if (abs(y3-y2) < TINY) { + m1 = - (x2-x1) / (y2-y1); + mx1 = (x1 + x2) / 2.0; + my1 = (y1 + y2) / 2.0; + *xc = (x3 + x2) / 2.0; + *yc = m1 * (*xc - mx1) + my1; + } else { + m1 = - (x2-x1) / (y2-y1); + m2 = - (x3-x2) / (y3-y2); + mx1 = (x1 + x2) / 2.0; + mx2 = (x2 + x3) / 2.0; + my1 = (y1 + y2) / 2.0; + my2 = (y2 + y3) / 2.0; + *xc = (m1 * mx1 - m2 * mx2 + my2 - my1) / (m1 - m2); + *yc = m1 * (*xc - mx1) + my1; + } + + dx = x2 - *xc; + dy = y2 - *yc; + rsqr = dx*dx + dy*dy; + *r = sqrt(rsqr); + + return; + // Suggested + // return((drsqr <= rsqr + EPSILON) ? TRUE : FALSE); + +} + +// + +// return the total amount of chemical "ch" in the leaf +double Mesh::SumChemical(int ch) { + + double sum=0.; + for (vector::iterator i=cells.begin(); + i!=cells.end(); + i++) { + + sum+=(*i)->chem[ch]; + } + return sum; + +} + + + +void Mesh::CleanUpCellNodeLists(void) { + + typedef vector ::iterator> CellItVect; + + CellItVect cellstoberemoved; + vector cellind; + + // Start of by removing all stale walls. + //DeleteLooseWalls(); + // collect all dead cells that need to be removed from the simulation + for (vector::iterator i=cells.begin(); + i!=cells.end(); + i++) { + + if ((*i)->DeadP()) { + // collect the iterators + cellstoberemoved.push_back(i); + + // collect the indices + cellind.push_back((*i)->index); + } else { + // Remove pointers to dead Walls + for (list::iterator w=(*i)->walls.begin(); + w!=(*i)->walls.end(); + w++) { + if ((*w)->DeadP()) { + (*w)=0; + } + } + (*i)->walls.remove(0); + } + } + + // Remove pointers to dead Walls from BoundaryPolygon + for (list::iterator w=boundary_polygon->walls.begin(); + w!=boundary_polygon->walls.end(); + w++) { + if ((*w)->DeadP()) { + (*w)=0; + } + } + boundary_polygon->walls.remove(0); + + + // Renumber cells; this is most efficient if the list of dead cell indices is sorted + sort(cellind.begin(),cellind.end()); + + + // Reindexing of Cells + for (vector::reverse_iterator j=cellind.rbegin(); + j!=cellind.rend(); + j++) { + + for (vector::reverse_iterator i=cells.rbegin(); + i!=cells.rend(); + i++) { + + if (*j < (*i)->index) (*i)->index--; + + } + + } + + + // Actual deleting of Cells + // We must delete in reverse order, otherwise the iterators become redefined + for ( CellItVect::reverse_iterator i=cellstoberemoved.rbegin(); + i!=cellstoberemoved.rend(); + i++) { + Cell::NCells()--; + cells.erase(*i); + } + + + // same for nodes + typedef vector ::iterator> NodeItVect; + + NodeItVect nodestoberemoved; + vector nodeindlist; + + // collect iterators and indices of dead nodes + for (vector::iterator i=nodes.begin(); + i!=nodes.end(); + i++) { + + if ((*i)->DeadP()) { + nodestoberemoved.push_back( i ); + nodeindlist.push_back((*i)->index); + + } + } + + // sort the list of dead nodes for renumbering + sort(nodeindlist.begin(),nodeindlist.end()); + + + // Reindicing of Nodes + for (vector::reverse_iterator j=nodeindlist.rbegin(); + j!=nodeindlist.rend(); + j++) { + + for (vector::reverse_iterator i=nodes.rbegin(); + i!=nodes.rend(); + i++) { + + if (*j < (*i)->index) { + + (*i)->index--; + } + + + } + + } + + // Actual deleting of nodes + // We must delete in reverse order, otherwise the iterators become redefined + for ( NodeItVect::reverse_iterator i=nodestoberemoved.rbegin(); + i!=nodestoberemoved.rend(); + i++) { + Node::nnodes--; + nodes.erase(*i); + } + + + + for (list::iterator w=walls.begin(); + w!=walls.end(); + w++) { + if ((*w)->DeadP()) { + Wall::nwalls--; + delete *w; + *w = 0; + } + } + + walls.remove( 0 ); + + + + // Clean up all intercellular connections and redo everything + for (vector::iterator i=nodes.begin(); + i!=nodes.end(); + i++) { + + (*i)->owners.clear(); + } + + for (vector::iterator i=cells.begin(); + i!=cells.end(); + i++) { + + (*i)->ConstructConnections(); + + } + + boundary_polygon->ConstructConnections(); + + /* for (list::iterator w=walls.begin(); + w!=walls.end(); + w++) { + delete *w; + } + + walls.clear(); + cerr << "Cleared walls\n"; + for (vector::iterator i=cells.begin(); + i!=cells.end(); + i++) { + + (*i)->ConstructWalls(); + } + */ + + // remake shuffled_nodes and shuffled cells + shuffled_nodes.clear(); + shuffled_nodes = nodes; + + shuffled_cells.clear(); + shuffled_cells = cells; + +} + +void Mesh::CutAwayBelowLine( Vector startpoint, Vector endpoint) { + + // Kills all cells below the line startpoint -> endpoint + + Vector perp = (endpoint-startpoint).Perp2D().Normalised(); + + + cerr << "Before Apoptose\n"; + TestIllegalWalls(); + for (vector::iterator i=cells.begin(); + i!=cells.end(); + i++) { + + // do some vector geometry to check whether the cell is below the cutting line + Vector cellvec = ((*i)->Centroid()-startpoint); + + if ( InnerProduct(perp, cellvec) < 0 ) { + // remove those cells + (*i)->Apoptose(); + } + } + + cerr << "Before CleanUpCellNodeLists\n"; + TestIllegalWalls(); + + CleanUpCellNodeLists(); + +} + +void Mesh::CutAwaySAM(void) { + + for (vector::iterator i=cells.begin(); + i!=cells.end(); + i++) { + + if( (*i)->Boundary() == Cell::SAM ) { + + (*i)->Apoptose(); + } + } + + TestIllegalWalls(); + + CleanUpCellNodeLists(); + + +} +void Mesh::TestIllegalWalls(void) { + + for (list::iterator w = walls.begin(); + w!=walls.end(); + w++) { + if ((*w)->IllegalP() ) { + cerr << "Wall " << **w << " is illegal.\n"; + } + } + +} + + + +class node_owners_eq : public unary_function { + int no; +public: + + explicit node_owners_eq(int nn) { no=nn; } + + bool operator() (const Node &n) const { + if (n.CellsSize()==1) + return true; + else + return false; + } + +}; + + +void Mesh::RepairBoundaryPolygon(void) { + + // After serious manipulations (e.g. after cutting part off the + // leaf) repair the boundary polygon. It assumes the cut line has + // already been marked "boundary" and the original boundary marks + // were not removed. + // + // So, this function just puts boundary nodes into the boundary + // polygon in the right order; it cannot detect boundaries from + // scratch. + + Node *boundary_node=0, *next_boundary_node=0, *internal_node; + set original_boundary_nodes, repaired_boundary_nodes; + vector difference; // set difference result + + // Step 0: print out current boundary polygon +#ifdef QDEBUG + qDebug() << endl << "Original Boundary Polygon node indices: "; + foreach (Node* node, boundary_polygon->nodes) { + qDebug() << node->Index() << " " ; + } + qDebug() << endl << endl; +#endif + + // Step 1a: Create a set containing the current boundary polygon nodes' Indices. + foreach (Node* node, boundary_polygon->nodes) { + original_boundary_nodes.insert(node->Index()); + } + + // Step 1b: remove all nodes from boundary polygon + boundary_polygon->nodes.clear(); + + // Step 2: Remove all references to the boundary polygon from the Mesh's current list of nodes + foreach (Node* node, nodes) { + node->Unmark(); // remove marks, we need them to determine if we have closed the circle + list::iterator boundary_ref_pos; + if ((boundary_ref_pos = find_if (node->owners.begin(), node->owners.end(), + bind2nd(mem_fun_ref(&Neighbor::CellEquals), -1))) != node->owners.end()) { + // i.e. if one of the node's owners is the boundary polygon + node->owners.erase(boundary_ref_pos); // remove the reference + } + } + + // Step 3: Search for the first boundary node. We reconstruct the + // boundary polygon by moving along the boundary nodes until we've + // encircled the polygon. Since manually adding nodes may have + // turned nodes previously along the boundary into internal nodes, + // we search through all the node until we find first boundary node + // and proceed from there. If findNextBoundaryNode() returns a node + // other than the one passed to it, the original node is the first + // boundary node. + foreach (Node* node, nodes) { + if ((findNextBoundaryNode(node))->index != node->index){ + next_boundary_node = node; + break; + } + } + + // We have a problem if we arrive here without having found a boundary node. + if (!next_boundary_node) throw("Cannot find a boundary node!."); + + // Reconstruct the list of boundary polygon nodes. + do { + boundary_node = next_boundary_node; + boundary_node->Mark(); + boundary_polygon->nodes.push_back(boundary_node); + next_boundary_node = findNextBoundaryNode(boundary_node); + } while ( !next_boundary_node->Marked() ); + + + // Create a set containing the reconstructed boundary polygon nodes' Indices. + for (list::iterator it = boundary_polygon->nodes.begin(); it!=boundary_polygon->nodes.end(); ++it) { + repaired_boundary_nodes.insert((*it)->Index()); + } + + // Calculate the difference between the original and repaired sets of boundary nodes + // yielding the set of nodes that are no longer part of the boundary polygon. + set_difference(original_boundary_nodes.begin(), original_boundary_nodes.end(), + repaired_boundary_nodes.begin(), repaired_boundary_nodes.end(), back_inserter(difference)); + + // Tell each node in the difference that it's no longer part of the boundary polygon + vector::iterator internal_node_it; + foreach (int i, difference){ + internal_node_it = find_if (nodes.begin(), nodes.end(), bind2nd(mem_fun(&Node::IndexEquals), i)); + internal_node = *internal_node_it; // dereference the itterator to get to the node pointer + if (!internal_node) throw("Found a null Node pointer."); + internal_node->UnsetBoundary(); + } + + boundary_polygon->ConstructConnections(); + for (list::iterator w=boundary_polygon->walls.begin(); + w!=boundary_polygon->walls.end(); + w++) { + if ((*w)->DeadP()) { + (*w)=0; + } + } + boundary_polygon->walls.remove(0); + boundary_polygon->ConstructNeighborList(); + +#ifdef QDEBUG + cerr << "Repaired Boundary Polygon node indices: "; + foreach (Node* node, boundary_polygon->nodes){ + qDebug() << node->Index() << " " ; + } + qDebug() << endl ; + + #ifdef _undefined_ + qDebug() << "NODES:" << endl; + foreach(Node* node, nodes) { + qDebug() << *node; + } + qDebug() << endl; + + qDebug() << "WALLS:" << endl; + foreach(Wall* wall, walls) { + qDebug() << *wall; + } + qDebug() << endl; + + qDebug() << "CELLS:" << endl; + foreach(Cell* cell, cells) { + qDebug() << *cell; + } + qDebug() << endl; + #endif +#endif + +} + + +Node* Mesh::findNextBoundaryNode(Node* boundary_node) { + bool found_next_boundary_node = false; + Node *next_boundary_node; + set boundary_node_owners; // This is a list of the current boundary node's owners' Ids + vector neighborIds; // A list of the current boundary node's owners' 2nd neighbor Ids + vector *> nodeOwners; // A vector of set pointers where each set contains the owner Ids of the nodes in the neighborIds list. + vector intersection; // set intersection result + + // The next boundary node is that which has only one owner in common with the current boundary node + for (list::iterator it=boundary_node->owners.begin(); it!=boundary_node->owners.end(); ++it) { + if (it->cell->Index() != -1) boundary_node_owners.insert(it->cell->Index()); // Save each of the current boundary node's owners' Ids - except the boundary polygon + set *owners = new set; // create a set to hold a 2nd neighbor's owners' Ids + nodeOwners.push_back(owners); + neighborIds.push_back(it->nb2->Index()); + foreach(Neighbor neighbor, it->nb2->owners){ + if (neighbor.cell->Index() != -1) owners->insert(neighbor.cell->Index()); // Save second neighbors' owners' Ids - except the boundary polygon + } + } + vector::iterator itt = neighborIds.begin(); + vector *>::iterator it = nodeOwners.begin(); + + #ifdef QDEBUG + qDebug() << "Boundary node: " << boundary_node->Index() << " is owned by the following cells: "; + foreach (int i, boundary_node_owners){ + qDebug() << i << " "; + } + qDebug() << endl; + #endif + + for (; it < nodeOwners.end(); it++, itt++) { + intersection.clear(); + set_intersection(boundary_node_owners.begin(), boundary_node_owners.end(), (*it)->begin(), (*it)->end(), back_inserter(intersection)); + + #ifdef QDEBUG + qDebug() << "The intersection of the boundary node(" << boundary_node->Index() << ") owners and its 2nd neighbor(" << *itt << ") owners is: "; + foreach (int i, intersection){ + qDebug() << i << " "; + } + qDebug() << endl; + #endif + + if (intersection.size() == 1){ + found_next_boundary_node = true; + vector::iterator next_boundary_node_it = find_if (nodes.begin(), nodes.end(), bind2nd(mem_fun(&Node::IndexEquals), *itt)); + next_boundary_node = *next_boundary_node_it; // defeference the itterator to get to the node pointer + + #ifdef QDEBUG + qDebug() << "The Current boundary node is: " << boundary_node->Index() + << ". The Next boundary node is: " << *itt << ((next_boundary_node->Marked()) ? " Marked" : " Unmarked") << endl << endl; + #endif + + break; + } + } + + #ifdef QDEBUG + if (!found_next_boundary_node) { + qDebug() << "OOPS! Didn't find the next boundrary node!" << endl; + } + #endif + + return next_boundary_node; +} + + +void Mesh::CleanUpWalls(void) { + for (list::iterator w=walls.begin(); + w!=walls.end(); + w++) { + + if ((*w)->DeadP()) { + delete *w; + (*w)=0; + } + } + walls.remove(0); +} + +void Mesh::Rotate(double angle, Vector center) { + + // Rotate the mesh over the angle "angle", relative to center point "center". + + Matrix rotmat; + + rotmat.Rot2D(angle); + + for (vector::iterator n=nodes.begin(); + n!=nodes.end(); + n++) { + + (*n)->setPos ( rotmat * ( *(*n) - center ) + center ); + + } +} + + +void Mesh::PrintWallList( void ) { + + transform ( walls.begin(), walls.end(), ostream_iterator(cerr, "\n"), deref_ptr ); + +} + +#include +//#include "forwardeuler.h" +#include "rungekutta.h" + +class SolveMesh : public RungeKutta { + +private: + SolveMesh(void); + +public: + SolveMesh(Mesh *m_) { + + m = m_; + + kmax=0; + kount=0; + xp=0; yp=0; dxsav=0; + + + } + +protected: + virtual void derivs(double x, double *y, double *dydx) { + + // set mesh with new values given by ODESolver + // (we must do this, because only mesh knows the connections + // between the variables) + + m->setValues(x,y); + m->Derivatives(dydx); + + /*static int c=0; + QString fname("derivs%1.dat"); + + ofstream of(fname.arg(++c).ascii()); + + for (int i=0;iNEqs();i++) { + of << x << " " << dxdy[i] << endl; + } + of.close(); + */ + + //cerr << "Calculated derivatives at " << x << "\n"; + } + +private: + Mesh *m; + int kmax,kount; + double *xp,**yp,dxsav; + bool monitor_window; +}; + + + +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::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::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 &clean_chem, const vector &clean_transporters) { + + if (clean_chem.size()!=(unsigned)Cell::NChem() || clean_transporters.size()!=(unsigned)Cell::NChem()) { + throw "Run time error in Mesh::CleanChemicals: size of clean_chem and clean_transporters should be equal to Cell::NChem()"; + } + for (vector::iterator c=cells.begin(); + c!=cells.end(); + c++) { + + for (int i=0;iSetChemical(i,clean_chem[i]); + } + (*c)->SetNewChemToChem(); + + } + + // clean transporters + for (list::iterator w=walls.begin(); + w!=walls.end(); + w++) { + + for (int i=0;isetTransporters1(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 &max_chem, const vector &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::iterator c=cells.begin(); + c!=cells.end(); + c++) { + + for (int i=0;iSetChemical(i,max_chem[i]*RANDOM()); + } + (*c)->SetNewChemToChem(); + + } + + // randomize transporters + for (list::iterator w=walls.begin(); + w!=walls.end(); + w++) { + + for (int i=0;isetTransporters1(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::iterator c=cells.begin(); + c!=cells.end(); + c++) { + //(*cr)(*c, &(derivs[i])); + plugin->CellDynamics(*c, &(derivs[i])); + i+=nchems; + } + + for (list::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 + plugin->CelltoCellTransport(*w, &(derivs[(*w)->c1->Index() * nchems]), + &(derivs[(*w)->c2->Index() * nchems])); + + //(*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::iterator c=cells.begin(); + c!=cells.end(); + c++) { + for (int ch=0;chSetChemical(ch, y[i+ch]); + } + if ( !(emit_count%stride)) { + (*c)->EmitValues(x); + } + i+=nchems; + } + + for (list::iterator w=walls.begin(); + w!=walls.end(); + w++) { + + for (int ch=0;chsetTransporters1(ch,y[i+ch]); + } + i+=nchems; + + for (int ch=0;chsetTransporters2(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::iterator c=cells.begin(); + c!=cells.end(); + c++) { + for (int ch=0;chChemical(ch); + } + i+=nchems; + } + + for (list::iterator w=walls.begin(); + w!=walls.end(); + w++) { + + for (int ch=0;chTransporters1(ch); + } + i+=nchems; + + for (int ch=0;chTransporters2(ch); + } + i+=nchems; + + } + + return values; +} + +void Mesh::DrawNodes(QGraphicsScene *c) const { + + for (vector::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::const_iterator w=walls.begin(); + w!=walls.end(); + w++) { + sum_prot += (*w)->Transporters1(ch); + sum_prot += (*w)->Transporters2(ch); + } + + // At cells + for (vector::const_iterator c=cells.begin(); + c!=cells.end(); + c++) { + + sum_prot += (*c)->Chemical(ch); + } + + return sum_prot; + +} + +void Mesh::SettoInitVals(void) { + + vector clean_chem(Cell::NChem()); + vector clean_transporters(Cell::NChem()); + + for (int i=0;i Mesh::VertexAngles(void) { + QVector angles; + for (vector::const_iterator n=nodes.begin(); + n!=nodes.end(); + n++) { + if ((*n)->Value()>2 && !(*n)->BoundaryP() ) { + angles+=(*n)->NeighbourAngles(); + } + } + return angles; +} + +QVector< QPair > Mesh::VertexAnglesValues(void) { + + QVector< QPair > anglesvalues; + for (vector::const_iterator n=nodes.begin(); + n!=nodes.end(); + n++) { + if ((*n)->Value()>2 && !(*n)->BoundaryP() ) { + + QVector angles = (*n)->NeighbourAngles(); + int value_vertex = angles.size(); + for (QVector::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::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::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::iterator i=cells.begin(); + i!=cells.end(); + i++) { + delete *i; + } + //Cell::static_data_members = old_static_data_mem; + + cells.clear(); + Cell::NCells()=0; + + delete boundary_polygon; // (already deleted during cleaning of cells?) + + #ifdef QDEBUG + qDebug() << "Freeing walls" << endl; + #endif + for (list::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. + * + * Copyright 2010 Roeland Merks. + * + */ + + +// Cell derives from Vector, where Vector is simply used as a Vertex + +#ifndef _MESH_H_ +#define _MESH_H_ + +#include +#include +#include +#include +#include +#ifdef QTGRAPHICS +#include +#endif +#include "cell.h" +#include "node.h" +#include "simplugin.h" +#include +#include + +using namespace std; +// new queue which rejects duplicate elements +template > class unique_queue : public queue { + +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::c.begin(),queue::c.end(),x)==queue::c.end()) { + queue::c.push_back(x); + } + } + void clear(void) { + queue::c.clear(); + } +}; + +//template P& deref_ptr

( P *obj) { return *obj; } +template 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); + + //boundary_polygon = new BoundaryPolygon(); + + time = 0.; + plugin = 0; + }; + ~Mesh(void) { + delete boundary_polygon; + /* if (plugin) + delete plugin;*/ + }; + + void Clean(void); + //void Plane(int xwidth, int ywidth, int nx, int ny, bool randomP=false); + 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); + + /* void GMVoutput(ostream &os, + const char *codename=0, const char *codever=0, + const char *comments=0);*/ + //void RandPoints(int npoints); + + inline Cell &getCell(int i) { + if ((unsigned)i= nodes.size() || i < 0) { + // cerr << "Mesh::getNode: Warning. Index " << i << " out of range.\n"; + // } + return *nodes[i]; + } + + //double Diffusion(void); + inline int size(void) { + return cells.size(); + } + inline int nnodes(void) { + return nodes.size(); + } + //void SortNBLists(void); + + /*template void LoopCells(Op f) { + for (vector::iterator i=cells.begin(); + i!=cells.end(); + i++) { + f(*i); + } + }*/ + + /*! \brief Calls function f for all Cells f. + + Using this template requires some fiddling with function adaptors bind2nd and mem_fun_ref. + + Example usage for calling a member function on each cell: + + mesh.LoopCells( bind2nd (mem_fun_ref( &Cell::DrawDiffEdges), &canvas ) ); + + This calls Cell's member function DrawDiffEdges, taking one + argument canvas, on all Cell objects in the current Mesh. + + */ + template void LoopCells(Op f) { + for (vector ::iterator i=cells.begin(); + i!=cells.end(); + i++) { + f(**i); + } + //for_each(cells.begin(),cells.end(),f); + } + + template void LoopWalls(Op f) { + for (list ::iterator i=walls.begin(); + i!=walls.end(); + i++) { + f(**i); + } + } + + // if the amount of cells might increase, during looping, use this template + template void LoopCurrentCells(Op f) { + vector current_cells = cells; + for (vector ::iterator i=current_cells.begin(); + i!=current_cells.end(); + i++) { + f(**i); + + } + //for_each(cells.begin(),cells.end(),f); + } + + template void LoopNodes(Op f) { + for (vector::iterator i=nodes.begin(); + i!=nodes.end(); + i++) { + f(**i); + } + } + + template void RandomlyLoopNodes(Op f) { + + MyUrand r(shuffled_nodes.size()); + random_shuffle(shuffled_nodes.begin(),shuffled_nodes.end(),r); + + for (vector::const_iterator i=shuffled_nodes.begin(); + i!=shuffled_nodes.end(); + i++) { + f(*shuffled_nodes[*i]); + } + + } + + template void RandomlyLoopCells(Op f) { + + MyUrand r(shuffled_cells.size()); + random_shuffle(shuffled_cells.begin(),shuffled_cells.end(),r); + + for (vector::const_iterator i=shuffled_cells.begin(); + i!=shuffled_cells.end(); + i++) { + f(*shuffled_cells[*i]); + } + + + } + + template void LoopCells(Op1 f, Op2 &g) { + for (vector::iterator i=cells.begin(); + i!=cells.end(); + i++) { + f(**i,g); + } + } + + template void LoopCells(Op1 f, Op2 &g, Op3 &h) { + for (vector::iterator i=cells.begin(); + i!=cells.end(); + i++) { + f(**i,g,h); + } + } + + void DoCellHouseKeeping(void) { + vector current_cells = cells; + for (vector::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) { + (*i)->Divide(); + (*i)->flag_for_divide=false; + } + } + } +/* template void ExtractFromCells(Op1 f, Cont res) { + for (vector::iterator i=cells.begin(); + i!=cells.end(); + i++) { + *(res++) = ( f(*i) ); + } + }*/ + + // 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 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::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(); + } + //copy (node_insertion_queue.begin(),node_insertion_queue.end(),ostream_iterator(cerr, " ")); + } + + 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(); + + // template ReactDiffuse(const double D, ReactFunction& react) { + void ReactDiffuse( double delta_t = 1 ); + //void Diffuse(const double D); + double SumChemical(int ch); + void SetChemical(int ch, double value) { + for (vector::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 *tmp_cells); + void XMLReadWallsToCells(xmlNode *root, vector *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 &clean_chem, const vector &clean_transporters); + void RandomizeChemicals(const vector &max_chem, const vector &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 VertexAngles(void); + QVector< QPair > VertexAnglesValues(void); + void SetSimPlugin(SimPluginInterface *new_plugin) { + /* if (plugin) + delete plugin;*/ + plugin=new_plugin; + } + QString ModelID(void) { return plugin?plugin->ModelID():QString("undefined"); } + void StandardInit(void); + + Node* findNextBoundaryNode(Node*); + +private: + + // Data members + vector cells; + vector nodes; + list walls; // we need to erase elements from this container frequently, hence a list. +public: + vector node_sets; +private: + vector shuffled_nodes; + vector shuffled_cells; + unique_queue 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::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(cerr," ")); + cerr << endl;*/ + c->m=this; + return c; + } + + //int Delaunay(void); + void CircumCircle(double x1,double y1,double x2,double y2,double x3,double y3, + double *xc,double *yc,double *r); + + + // void RenumberCells(void); + +}; +#endif diff --git a/src/miscq.cpp b/src/miscq.cpp new file mode 100644 --- /dev/null +++ b/src/miscq.cpp @@ -0,0 +1,37 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include +#include + +static const std::string _module_id("$Id$"); + +// Returns the extension of a filename +QString getExtension(const QString fn) { + + // split on dots + QStringList parts = fn.split("."); + + // return last part, this should be the extension + return QString(parts.last()); + +} diff --git a/src/miscq.h b/src/miscq.h new file mode 100644 --- /dev/null +++ b/src/miscq.h @@ -0,0 +1,35 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + + +#ifndef _MISCQ_h_ +#define _MISCQ_h_ + +#include + +// Miscellaneous helper functions, using Qt + +QString getExtension(const QString fn); + +#endif + diff --git a/src/modelcatalogue.cpp b/src/modelcatalogue.cpp new file mode 100644 --- /dev/null +++ b/src/modelcatalogue.cpp @@ -0,0 +1,105 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include "modelcatalogue.h" +#include + +static const std::string _module_id("$Id$"); + +ModelCatalogue::ModelCatalogue(Mesh *_mesh, Main *_mainwin) { + mesh = _mesh; + mainwin = _mainwin; + 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 plugins; + foreach (QString fileName, pluginDir.entryList(QDir::Files)){ + QPluginLoader loader(pluginDir.absoluteFilePath(fileName)); + if (SimPluginInterface *plugin = + qobject_cast(loader.instance())) { + models.append(plugin); + } 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(); + 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...) + plugin->SetCellsStaticDatamembers(CellBase::GetStaticDataMemberPointer()); + + mesh->SetSimPlugin(plugin); + Cell::SetNChem(plugin->NChem()); + plugin->SetParameters(&par); + + mainwin->RefreshInfoBar(); +// mesh->StandardInit(); + mainwin->Init(0); +} diff --git a/src/modelcatalogue.h b/src/modelcatalogue.h new file mode 100644 --- /dev/null +++ b/src/modelcatalogue.h @@ -0,0 +1,59 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#ifndef _MODELCATALOGUE_H_ +#define _MODELCATALOGUE_H_ + +#include "warning.h" +#include "cell.h" +#include "mesh.h" +#include "simplugin.h" +#include "canvas.h" +#include +#include +#include +#include +#include +#include +#include +#include + +class ModelCatalogue : public QObject { +Q_OBJECT +public: + ModelCatalogue(Mesh *mesh, Main *mainwin); + void LoadPlugins(); + + void InstallFirstModel(); + void PopulateModelMenu(); + +public slots: + void InstallModel(SimPluginInterface *model); + void InstallModel(QAction *modelaction); +private: + QVector models; + Mesh *mesh; + Main *mainwin; + +}; +#endif \ No newline at end of file diff --git a/src/modelelement.h b/src/modelelement.h new file mode 100644 --- /dev/null +++ b/src/modelelement.h @@ -0,0 +1,38 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + + +#ifndef _MODELELEMENT_H_ +#define _MODELELEMENT_H_ + +#include "vector.h" + +// pure virtual base class for all model elements + +class ModelElement { + + public: + virtual void Displace(void) {}; + virtual ~ModelElement() {}; +}; +#endif diff --git a/src/node.cpp b/src/node.cpp new file mode 100644 --- /dev/null +++ b/src/node.cpp @@ -0,0 +1,225 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include +#ifdef QTGRAPHICS +#include +#include +#endif +#include "node.h" +#include "cell.h" +#include "mesh.h" +#include "random.h" +#include "parameter.h" +#include "sqr.h" +#include "pi.h" +#include "warning.h" + +static const std::string _module_id("$Id$"); + +extern Parameter par; + +int Node::nnodes=0; +double Node::target_length=1; + +ostream &Edge::print(ostream &os) const { + return os << "{ " << first->Index() << ", " << second->Index() << " }"; +} + +ostream &Neighbor::print(ostream &os) const { + + os << " {" << cell->Index() << " " << nb1->Index() << " " << nb2->Index() << "}"; + return os; + +} + +ostream &operator<<(ostream &os, const Neighbor &n) { + n.print(os); + return os; +} + + +Node::Node(void) : Vector() { + + index=(nnodes++); + node_set =0; + fixed=false; + boundary=false; + sam=false; + dead=false; +} + +Node::Node(int ind) : Vector() { + node_set =0; + index=ind; + fixed=false; + boundary=false; + sam=false; + dead=false; +} + +Node::Node(const Vector &src) : Vector(src) { + + node_set = 0; + index=(nnodes++); + fixed=false; + boundary=false; + sam=false; + dead = false; +} + +Node::Node(double x,double y, double z) : Vector (x,y,z) { + + node_set = 0; + index=(nnodes++); + fixed=false; + boundary=false; + sam=false; + dead = false; +} + +Node::Node(const Node &src) : Vector(src) { + + node_set=0; + owners=src.owners; + m=src.m; + index=src.index; + fixed = src.fixed; + boundary = src.boundary; + sam=src.sam; + dead = src.dead; +} + + +Cell &Node::getCell(const Neighbor &i) { + return *i.getCell(); // use accessor! +} + + +ostream &Node::print(ostream &os) const { + + if (!dead) + os << "Node "; + else + os << "DEAD NODE "; + + os << index << "[ {" << x << ", " << y << ", " << z << "}: {"; + + os << "Neighbors = { "; + + for (list::const_iterator i = owners.begin(); i!=owners.end(); i++) { + os << " {" << i->cell->Index() << " " << i->nb1->Index() << " " << i->nb2->Index() << "} "; + } + os << " } " << endl; + + return os; + +} + + +#ifdef QTGRAPHICS +void Node::DrawIndex(QGraphicsScene *c) const { + + //return DrawOwners(c); + stringstream text; + text << index; + QGraphicsSimpleTextItem *number = new QGraphicsSimpleTextItem ( QString (text.str().c_str()), 0, c ); + number->setFont( QFont( "Helvetica", par.nodenumsize, QFont::Bold) ); + number->setPen( QPen (par.textcolor) ); + number->setZValue(20); + number->show(); + Vector offs=Cell::Offset(); + number -> setPos( + ((offs.x+x)*Cell::Factor()), + ((offs.y+y)*Cell::Factor()) ); +} + +void Node::DrawOwners(QGraphicsScene *c) const { + + stringstream text; + + + + text << owners.size(); + + + QGraphicsSimpleTextItem *number = new QGraphicsSimpleTextItem ( QString (text.str().c_str()), 0, c ); + number->setFont( QFont( "Helvetica", par.nodenumsize, QFont::Bold) ); + number->setPen( QPen(par.textcolor) ); + number->setZValue(20); + number->show(); + Vector offs=Cell::Offset(); + + number ->setPos(((offs.x+x)*Cell::Factor()), + ((offs.y+y)*Cell::Factor()) ); +} + + + +QVector Node::NeighbourAngles(void) { + + QVector angles; + for (list::iterator i=owners.begin(); + i!=owners.end(); + i++) { + + Vector v1 = (*this - *i->nb1).Normalised(); + Vector v2 = (*this - *i->nb2).Normalised(); + + //angles.push_back(atan2(v2.y,v2.x)-atan2(v1.y,v1.x)); + //angles.push_back(atan2(v2.y,v2.x)-atan2(v1.y,v1.x)); + double angle = v1.SignedAngle(v2); + if (angle<0) { + //cerr << "Changing sign of angle " << angle << endl; + angle = angle + 2*Pi; + } + angles.push_back(angle); + + //cerr << "Cell " << i->cell->Index() << ": " << v1 << " and " << v2 << ": angle = " << angles.back() << ", " << v1.Angle(v2) << endl; + + } + + double sum=0.; + for (QVector::iterator i=angles.begin(); + i!=angles.end(); + i++) { + sum+=*i; + } + //cerr << "Angle sum = " << sum << endl; + // Check if the summed angle is different from 2 Pi + if (fabs(sum-(2*Pi))>0.0001) { + + MyWarning::warning("sum = %f",sum); + } + return angles; +} + + + +#endif + + +ostream &operator<<(ostream &os, const Node &n) { + n.print(os); + return os; +} + diff --git a/src/node.h b/src/node.h new file mode 100644 --- /dev/null +++ b/src/node.h @@ -0,0 +1,235 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + + +#ifndef _NODE_H_ +#define _NODE_H_ + +#include +//#include + +#include +#include + +#ifdef QTGRAPHICS +#include +#include +#include +#endif +#include "vector.h" +#include "random.h" +#include "parameter.h" +#include "cell.h" +#include "modelelement.h" + +#include + +#include "Neighbor.h" + +extern Parameter par; + +class Edge { + +public: + + Edge(void) { + first=0; + second=0; + } + + Edge(Node *f, Node *s) { + first=f; + second=s; + } + + // Copy Constructor + Edge(const Edge &src) { + first=src.first; + second=src.second; + } + + // Ambidextrous equivalence + inline bool operator==(const Edge &e) { + return ( (first==e.first && second==e.second) || + (first==e.second && second==e.first) ); + } + + + // Output the Edge + ostream &print(ostream &os) const; + + + Node *first, *second; +}; + + +class NodeSet; + +// this class is a node in a cell wall +class Node : public Vector { + + friend class Mesh; + friend class CellBase; + friend class Cell; + friend class WallBase; + friend class Wall; + friend class NodeSet; + friend class FigureEditor; + +public: + Node(void); + + Node(int index); // if want to construct a node, and not increase nnodes + + Node(double x,double y, double z=0); + + Node(const Node &src); + + explicit Node(const Vector &src); // "nonconverting" - explicit constructor syntax required + + virtual ~Node() {} + + inline int Index(void) const { return index; } + + inline bool IndexEquals(int i) { return i == index; } + + inline bool BoundaryP(void) const { return boundary; } + + inline void SetBoundary(void) { boundary = true; } + + inline void UnsetBoundary(void) { boundary = false; } + + inline void SetSAM(void) { sam = true; } + + inline void toggleBoundary(void) { + boundary = !boundary; + } + + //void Displace(void); + + Cell &getCell(const Neighbor &i); + + //Node &getNode(int i); + ostream &print(ostream &os) const; + void XMLAdd(xmlNodePtr nodes_node) const; + +#ifdef QTGRAPHICS + void Draw(QGraphicsScene &c, QColor color=QColor("black"), int size = 10) const; + void DrawIndex(QGraphicsScene *c) const; + void DrawOwners(QGraphicsScene *c) const; +#endif + + // temporary function for easier debugging + inline int CellsSize(void) const { + return owners.size(); + } + + inline int Value(void) const { + return owners.size(); + } + + void Fix(void) { + fixed=true; + } + inline bool Fixed(void) const { + return fixed; + } + inline void Unfix(void) { + fixed=false; + } + inline void MarkDead(void) { + dead=true; + } + inline bool DeadP(void) { + return dead; + } + + /*void UpdateAfterNodeRemoved(int n) { + for (list::iterator i=owners.begin(); + i!=owners.end(); + i++) { + if (i->nb1>=n) i->nb1--; + if (i->nb2>=n) i->nb2--; + } + } + + void UpdateAfterCellRemoved(int n) { + for (list::iterator i=owners.begin(); + i!=owners.end(); + i++) { + if (i->cell>=n) i->cell--; + } + } + */ + + inline void Mark(void) { + marked=true; + } + inline void Unmark(void) { + marked=false; + } + inline bool Marked(void) const { + return marked; + } + + inline void setPos( Vector p ) { + x = p.x; + y = p.y; + z = p.z; + } + inline bool SamP(void) const { return sam; } + //enum boundary_type {NoBoundary, Noflux, SourceSink, SAM}; + + //!\brief Calculate angles with neighboring vertices + //! Sum of angles should be 2*Pi + QVector NeighbourAngles(void); +private: + + // "owners" lists the cells to which this cell belong + // and the two neighboring nodes relative to each cell + list< Neighbor > owners; + + Mesh *m; + int index; + static int nnodes; + static double target_length; + + // if the node belongs to a NodeSet, node_set contains the pointer. Otherwise it is 0. + NodeSet *node_set; + // fixed nodes cannot move. E.g. to represent the petiole + bool fixed; + bool boundary; // true if node is at the edge of the leaf + bool sam; // true if node is connected to the shoot + bool dead; + bool marked; +}; + +ostream &operator<<(ostream &os, const Node &n); + +inline ostream &operator<<(ostream &os, const Edge &e) { + e.print(os); + return os; +} + + +#endif diff --git a/src/nodeitem.cpp b/src/nodeitem.cpp new file mode 100644 --- /dev/null +++ b/src/nodeitem.cpp @@ -0,0 +1,133 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include +#include +#include +#include +#include +#include "nodeitem.h" +#include "parameter.h" + +static const std::string _module_id("$Id$"); + +extern Parameter par; + +NodeItem::NodeItem( Node *n, QGraphicsScene *canvas ) + : QGraphicsItem( 0, canvas ), SimItemBase( n, canvas) { + + brush = Qt::darkGray; + + const double mag = par.node_mag; + ellipsesize=QRectF(-1*mag, -1*mag, 2*mag, 2*mag); + +} + +void NodeItem::userMove(double dx, double dy) { + QGraphicsItem::moveBy( dx, dy ); + + class_cast(obj)->x += (dx/Cell::Magnification()); + class_cast(obj)->y += (dy/Cell::Magnification()); +} + + + +void NodeItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) +{ + + // from Qt4.2 example: "elastic nodes" + //painter->setPen(Qt::NoPen); + //painter->setBrush(Qt::darkGray); + //painter->drawEllipse(-70, -70, 200, 200); + + /* QRadialGradient gradient(-30, -30, 100); + + if (option->state & QStyle::State_Sunken) { + gradient.setCenter(30, 30); + gradient.setFocalPoint(30, 30); + gradient.setColorAt(1, QColor(Qt::yellow).light(120)); + gradient.setColorAt(0, QColor(Qt::darkYellow).light(120)); + } else { + gradient.setColorAt(0, Qt::yellow); + gradient.setColorAt(1, Qt::darkYellow); + } + + painter->setBrush(gradient); */ + + painter->setBrush(brush); + painter->setPen(Qt::NoPen); + // const static double mag=20.; + painter->drawEllipse(ellipsesize); +} + + +QPainterPath NodeItem::shape() const +{ + QPainterPath path; + path.addEllipse(ellipsesize); + return path; +} + +QRectF NodeItem::boundingRect() const +{ + qreal penwidth = 0;// painter->pen()->widthF(); + return QRectF(ellipsesize.x()-penwidth/2.,ellipsesize.y()-penwidth/2., + ellipsesize.width()+penwidth, ellipsesize.height()+penwidth); +} + +// polymorphic OnClick functions +void NodeItem::OnClick(void) { + Node *n = &getNode(); + n->toggleBoundary(); + setColor(); + update(); +} + +void NodeItem::OnClick(const Qt::MouseButton &mb) { + if (mb == Qt::LeftButton) { + Node *n = &getNode(); + n->toggleBoundary(); + setColor(); + update(); + + } +} + +void NodeItem::setColor(void) { + + static QColor indian_red("IndianRed"); + static QColor deep_sky_blue("DeepSkyBlue"); + static QColor purple("Purple"); + + Node &n=getNode(); + + if (n.SamP()) { + setBrush( purple ); + } else { + if (n.BoundaryP()) { + setBrush( deep_sky_blue ); + } + else { + setBrush( indian_red ); + } + } +} diff --git a/src/nodeitem.h b/src/nodeitem.h new file mode 100644 --- /dev/null +++ b/src/nodeitem.h @@ -0,0 +1,58 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + + +#ifndef _NODEITEM_H_ +#define _NODEITEM_H_ + +#include +#include +#include "simitembase.h" +#include "node.h" +#include "cell.h" + +class NodeItem: public QGraphicsItem, public SimItemBase +{ +public: + NodeItem( Node *n, QGraphicsScene *canvas ); + ~NodeItem() {} + Node &getNode(void) const { return *class_cast(obj); } + virtual void userMove(double dx, double dy); + + QRectF boundingRect() const; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); + QPainterPath shape() const; + void setBrush( QBrush newbrush) { brush = newbrush; } + void setColor(void); + /*! We use this function internally, to (somewhat) interactively edit init configurations. + Simply put the property to be change upon clicking a node in this function. */ + void OnClick( const Qt::MouseButton &mb ); + void OnClick(void); +protected: + QBrush brush; + QRectF ellipsesize; + +private: +}; + +#endif diff --git a/src/nodeset.cpp b/src/nodeset.cpp new file mode 100644 --- /dev/null +++ b/src/nodeset.cpp @@ -0,0 +1,31 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include +#include "nodeset.h" + +static const std::string _module_id("$Id$"); + +ostream &operator<<(ostream &os, const NodeSet &ns) { + ns.print(os); + return os; +} diff --git a/src/nodeset.h b/src/nodeset.h new file mode 100644 --- /dev/null +++ b/src/nodeset.h @@ -0,0 +1,174 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + + +#ifndef _NODESET_H_ +#define _NODESET_H_ + +#include +#include +#include +#include +#include "node.h" + +//class xmlNode; + +class NodeSet : public list { + + public: + NodeSet(void) { + done = false; + } + + inline bool DoneP(void) { return done; } + inline void ResetDone(void) { done = false; } + + void AddNode(Node * n) { + push_back(n); + n->node_set = this; + } + + // list getCells(void) { + + list cellset; + + for (list::const_iterator i=begin(); + i!=end(); + i++) { + transform ( (*i)->owners.begin(), (*i)->owners.end(), back_inserter( cellset ) , mem_fun_ref ( &Neighbor::getCell )); + } + + cellset.sort(); + + // remove all non-unique elements + // (unique moves all unique elements to the front and returns an iterator to the end of the unique elements; + // so we simply erase all remaining elements. + cellset.erase(::unique(cellset.begin(), cellset.end() ), + cellset.end() ); + + // remove boundary_polygon + cellset.erase( find_if ( cellset.begin(), cellset.end(), mem_fun( &Cell::BoundaryPolP ) ) ); + return cellset; + } + + void CleanUp(void) { + + // remove double Nodes from the set + sort(); + erase(::unique(begin(), end() ), + end() ); + + + } + void print(ostream &os) const { + transform( begin(), end(), ostream_iterator( os, " " ), mem_fun( &Node::Index ) ); + } + + /*! Attempt a move over (rx, ry) + reject if energetically unfavourable. + */ + + void AttemptMove(double rx, double ry) { + + done = true; + // 1. Collect list of all attached to the nodes in the set + list celllist = getCells(); + + // 2. Sum the current energy of these cells + double old_energy=0.; + double sum_stiff = 0.; + for ( list::const_iterator i = celllist.begin(); + i!=celllist.end(); + ++i ) { + + old_energy += (*i)->Energy(); + sum_stiff += (*i)->Stiffness(); + } + + // 3. (Temporarily) move the set's nodes. + for ( list::iterator n = begin(); + n!=end(); + ++n ) { + + (*n)->x+=rx; + (*n)->y+=ry; + // (*n)->z += rz; + + } + + // 4. Recalculate the energy + double new_energy = 0.; + for ( list::const_iterator i = celllist.begin(); + i!=celllist.end(); + ++i ) { + + new_energy += (*i)->Energy(); + } + + + // 5. Accept or Reject DeltaH + double dh = new_energy - old_energy; + + list new_areas; + + // cerr << "Nodeset says: dh = " << dh << " ..."; + if (dh < -sum_stiff || RANDOM()::iterator n = begin(); + n!= end(); + ++n ) { + + (*n)->x-=rx; + (*n)->y-=ry; + // (*n)->z -= rz; + } + + + } + } + + void XMLAdd(xmlNode *root) const; + void XMLRead(xmlNode *root, Mesh *m); + private: + // list set; + bool done; +}; + +ostream &operator<<(ostream &os, const NodeSet &ns); +#endif diff --git a/src/output.cpp b/src/output.cpp new file mode 100644 --- /dev/null +++ b/src/output.cpp @@ -0,0 +1,466 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include +#include +#include +#include +//#include +#include +#include +#include "warning.h" +#include "parameter.h" +#include "output.h" + + +#include +#include + +static const std::string _module_id("$Id$"); + +using namespace MyWarning; + +#define FNAMESIZE 100 + +int OpenFileAndCheckExistance(FILE **fp,const char *fname,char *ftype) { + + *fp=fopen(fname,ftype); + if (*fp==NULL) + return FALSE; + + if (!strncmp(ftype,"a",1)) { + if (ftell(*fp)>0L) return TRUE; + else return FALSE; + } else return TRUE; +} + +int FileExistsP(const char *fname) { + + FILE *fp; + fp=fopen(fname,"r"); + if (fp==NULL) + return FALSE; + + fclose(fp); + return TRUE; + +} + +int YesNoP(const char *message) { + + char answer[100]; + + fprintf(stderr,"%s (y/n) ",message); + fflush(stderr); + + scanf("%s",answer); + while (strcmp(answer,"y") && strcmp(answer,"n")) { + fprintf(stderr,"\n\bPlease answer 'y' or 'n'. "); + fflush(stderr); + scanf("%s",answer); + } + + if (!strcmp(answer,"y")) return TRUE; + + return FALSE; + +} + +/* //FILE *OpenWriteFile(char *filename) +// { +// FILE *fp; +// fprintf(stderr,"Opening %s for writing\n",filename); + +// if((OpenFileAndCheckExistance(&fp,filename,"r"))==TRUE) { +// if (!YesNoP("File exists, overwrite?")) { +// fprintf(stderr," Could not open file %s for writing, exiting... \n" +// ,filename); +// exit(0); +// } +// } + +// if (fp!=NULL) // file existed, but user wants to overwrite +// fclose(fp); + +// if ((fp=fopen(filename,"w"))==NULL) { +// fprintf(stderr," Could not open file %s for writing, exiting... \n" +// ,filename); +// exit(0); +// } + +// return fp; +// } +*/ + +FILE *OpenWriteFile(const char *filename) +{ + + char fname[FNAMESIZE]; + + FILE *fp; + extern Parameter par; + + fprintf(stderr,"Opening %s for writing\n",filename); + + if(FileExistsP(filename)==TRUE) { + + if (false/*par.interactive*/) { + char *message=(char *)malloc(2000*sizeof(char)); + if (!YesNoP("File exists, overwrite?")) { + sprintf(message," Could not open file %s for writing, exiting... \n" + ,filename); + //exit(0); + throw(message); + } + } else { + /* Rename old file */ + sprintf(fname, "%s~",filename); + rename(filename, fname); + + } + } + + strncpy(fname, filename, FNAMESIZE-1); + + if ((fp=fopen(fname,"w"))==NULL) { + char *message=(char *)malloc(2000*sizeof(char)); + sprintf(message," Could not open file %s for writing: " + ,fname); + perror(""); + throw(message); + } + + return fp; +} + +/*FILE *OpenGZippedWriteFile(const char *filename) +{ + + // Open file that is zipped while it is written + // uses a pipe + + char fname[FNAMESIZE]; + char gzname[FNAMESIZE]; + + FILE *fp; + extern Parameter par; + + // step 1, add ".gz" to the filename + sprintf(gzname, "%s.gz", filename); + + // and check whether it already exists + fprintf(stderr,"Opening %s for writing\n",gzname); + + if(FileExistsP(gzname)==TRUE) { + + if (par.interactive) { + if (!YesNoP("File exists, overwrite?")) { + fprintf(stderr," Could not open file %s for writing, exiting... \n" + ,gzname); + exit(0); + } + } else { + // Rename old file + snprintf(fname, FNAMESIZE-1, "%s~",gzname); + rename(gzname, fname); + + } + } + + // strncpy(fname, gzname, FNAMESIZE-1); + char *command=new char[20+sizeof(gzname)]; + sprintf(command, "gzip -c > %s", gzname); + + if ((fp=popen(command,"w"))==NULL) { + fprintf(stderr," Could not open file %s for writing: " + ,fname); + perror(""); + exit(-1); + } + + delete[] command; + return fp; +} +*/ + +FILE *OpenReadFile(const char *filename) +{ + FILE *fp; + + fprintf(stderr,"Opening %s for reading\n",filename); + + if((OpenFileAndCheckExistance(&fp,filename,"r"))==FALSE) { + char *message=(char *)malloc(2000*sizeof(char)); + sprintf(message," File %s not found or empty, exiting... \n" + ,filename); + throw(message); + + } + return fp; +} + + +char *ReadLine(FILE *fp) +{ + /* does almost the same as fgetln(), but DEC Unix doesn't understand + fgetln(). Also I want my function to return a real C string, + terminated by a \0. */ + + /* The function reads a line from file *fp, and returns a pointer to the + line read, which can be freed with a normal free(). The length of the + string is written in *len */ + +#define INITIAL_BUFSIZE 100 + + char *tmpstring; + int character; + long bufsize; + char *line; + int pos; + + CheckFile(fp); + + /* first allocate a string with a standard length */ + bufsize=INITIAL_BUFSIZE; + MEMORYCHECK(tmpstring=(char *)malloc(bufsize*sizeof(char))); + + pos=0; + + while ((character=getc(fp))!=EOF && /* read a character and check */ + character!='\n') { + + + tmpstring[pos]=(char)character; + (pos)++; + + if (pos >= bufsize) { + /* line is longer than initial_bufsize, reallocate space */ + bufsize+=INITIAL_BUFSIZE; + MEMORYCHECK(tmpstring=(char *)realloc(tmpstring,bufsize*sizeof(char))); + + } + + } + + + if (character==EOF) { + + if (pos==0) { + /* EOF was reached, while no characters were read */ + free(tmpstring); + return NULL; + + } + if (ferror(fp)) { + error("I/O error in ReadLine(%ld): %s\n",fp, strerror(errno)); + } + + + } + + /* Allocate enough memory for the line */ + MEMORYCHECK(line=(char *)malloc((++(pos))*sizeof(char))); + + strncpy(line,tmpstring,(pos)-1); + free(tmpstring); + + line[pos-1]='\0'; + return line; + +} + + +void CheckFile(FILE *fp) +{ + if (ftell(fp)<0) { + /* file is probably not open, or another error occured */ + error("File error (fp=%ld): %d %s\n",fp,errno,strerror(errno)); + } + /* File pointer is ok */ +} + +char *Chext(char *filename) { + + /* Chop the extension from a filename */ + + /* Search backwards until a dot */ + + /* Remember to free the memory allocated by this function */ + /* ( free(result) ) */ + + /* not yet tested */ + + int i; + char *result; + + for (i=strlen(filename)-1;i>=0;i--) { + if (filename[i]=='.') + break; + + } + + /* No . found */ + if (i==0) { + + result=strdup(filename); + } else { + + /* . found */ + result=(char *)malloc((i+1)*sizeof(char)); + strncpy(result, filename, i); + } + return result; + + +} + +void MakeDir(const char *dirname) { + + cerr << "Entering MakeDir for name " << dirname << "\n"; +#ifdef QTGRAPHICS + QFileInfo file_info(dirname); + + //check for existance + if (file_info.exists()) { + + if (file_info.isDir()) { + // OK + cerr << "Using existing directory " << dirname << " for data storage.\n"; + return; + } else { + char *message = new char[MESS_BUF_SIZE+1]; + snprintf(message, MESS_BUF_SIZE, "%s is not a directory", dirname); + cerr << message << endl; + throw(message); + } + + } + + // make directory + QDir dir; + if (!dir.mkdir(QString(dirname))) { + char *message = new char[MESS_BUF_SIZE+1]; + snprintf(message,MESS_BUF_SIZE,"%s: Error in making directory %s",strerror(errno),dirname); + warning(message); + //strerror(message); + //exit(1); + return; + } + + // check + + +#else + + int status; + char message[MESS_BUF_SIZE]; + + status=mkdir(dirname, S_IRWXU); // S_IRWXU: Read, Write, Execute by Owner + + if (status<0) { // error occurred + + //check for existance + + if (errno==EEXIST) { + + // Check whether it is a directory + struct stat buf; + stat(dirname, &buf); + if (S_ISDIR(buf.st_mode)) { + // OK + extern Parameter par; + if (false /* && par.interactive*/) { + fprintf(stderr,"Using existing directory %s for data storage.\n",dirname); + if (!YesNoP("OK?")) { + // User doesn't agree. Exit + exit(1); + } + } else { + fprintf(stderr,"Using existing directory %s for data storage.\n",dirname); + } + } else { + snprintf(message, MESS_BUF_SIZE, "%s is not a directory", dirname); + perror(message); + exit(1); + } + } else { + // a different error occurred. Print error and quit + + snprintf(message,MESS_BUF_SIZE,"Error in making directory %s",dirname); + perror(message); + exit(1); + + } + + + + } + +#endif +} + +/*bool CanWeWriteP(char *filename) { + + // check for the existance of file "filename" + // if it exists, ask the user whether we may overwrite it + //false: do not overwrite. true: do overwrite + + + char message[MESS_BUF_SIZE]; + char fname[FNAMESIZE]; + + extern const Parameter par; + + int status; + status=access(filename, F_OK); + if (status < 0) {// file does not exist, or something else is wrong + // check error code + if (errno!=ENOENT) { + + // another error occured + snprintf(message, MESS_BUF_SIZE, "Error checking for existance of %s",filename); + perror(message); + exit(1); + } + + } else { + + // file exists, ask for permission to overwrite if interactive + if (par.interactive) { + snprintf(message, MESS_BUF_SIZE, "File %s exists. Overwrite? ",filename); + if (!YesNoP(message)) + return false; + else + return true; + } else { + // Rename old file + snprintf(fname, FNAMESIZE-1, "%s~",filename); + rename(filename, fname); + } + + return true; + } + + // file does not exist, or user permits overwriting + return true; + +}*/ diff --git a/src/output.h b/src/output.h new file mode 100644 --- /dev/null +++ b/src/output.h @@ -0,0 +1,55 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + + +#ifndef _OUTPUT_H_ +#define _OUTPUT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +int OpenFileAndCheckExistance(FILE **fp,const char *fname,char *ftype); +int YesNoP(const char *message); +FILE *OpenWriteFile(const char *filename); +FILE *OpenGZippedWriteFile(const char *filename); +FILE *OpenReadFile(const char *filename); +char *ReadLine(FILE *fp); +void CheckFile(FILE *fp); +int FileExistsP(const char *fname); +char *Chext(char *filename); +void MakeDir(const char *dirname); +bool CanWeWriteP(char *filename); + +#ifdef __cplusplus +} +#endif + + +#define MESS_BUF_SIZE 160 +#ifndef FALSE +#define FALSE 0 +#define TRUE 1 +#endif + +#endif diff --git a/src/parse.cpp b/src/parse.cpp new file mode 100644 --- /dev/null +++ b/src/parse.cpp @@ -0,0 +1,397 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include +#include +#include +#include +#include "warning.h" +#include "parse.h" +#include "output.h" +#include + +static const std::string _module_id("$Id$"); + +using namespace MyWarning; + +char *ParsePar(FILE *fp, char *parameter, bool wrapflag) +{ + char *line,*token; + char *value; + + line=SearchToken(fp,parameter, wrapflag); + if (line==NULL) { + warning("Warning: Token %s not found.",parameter); + value=0; + return value; + } + + /* parse the line on = sign */ + token=strtok(line,"="); + if (token==NULL) { + error("Parse error: no '=' sign found next to token %s, in line: \n %s.", + parameter,line); + } + + // warning("Reading value for token %s...",token); + fprintf(stderr, "[%s = ",token); + + token=strtok(NULL,"="); + if (token==NULL) + error("\nParse error: no value found after '=' sign, in line: \n %s", + line); + + value=strdup(token); + free(line); + + return value; + +} + + +int igetpar(FILE *fp,char *parameter, bool wrapflag) { + + // overloaded compatibility function. Doesn't need default parameter + // default = 0 + + return igetpar(fp, parameter, 0, wrapflag); +} + +int igetpar(FILE *fp,char *parameter, int default_val, bool wrapflag) +{ + char *token; + int value; + + /* Get token representing the value */ + token=ParsePar(fp,parameter, wrapflag); + + if (token==0) { + /* default value */ + warning("No token %s found. Using default value %d.\n", parameter, default_val); + return default_val; + } + /* read it */ + sscanf(token,"%d",&value); + fprintf(stderr, "%d]\n",value); + + free(token); + + return value; + +} + +float fgetpar(FILE *fp,char *parameter, bool wrapflag) { + + // overloaded compatibility function. Doesn't need default parameter + // default = 0 + + return fgetpar(fp, parameter, 0., wrapflag); + +} + +float fgetpar(FILE *fp, char *parameter, double default_val, bool wrapflag) +{ + char *token; + float value; + + /* Get token representing the value */ + token=ParsePar(fp,parameter, wrapflag); + + if (token==0) { + /* default value */ + warning("No token %s found. Using default value %f.\n", parameter, default_val); + return default_val; + } + + /* read it */ + sscanf(token,"%e",&value); + + fprintf(stderr,"%e]\n",value); + + free(token); + + return value; + +} + + +double *dgetparlist(FILE *fp,char *parameter, int n, bool wrapflag) +{ + /* Get a list of n comma separated doubles */ + char *token; + double *value; + char *number; + int i; + + value=(double *)malloc(n*sizeof(double)); + + /* Get token representing the value */ + token=ParsePar(fp,parameter, wrapflag); + + if (token==0) { + error("No token %s found.\n", parameter); + } + /* parse it */ + number=strtok(token,","); /* make a pointer to "token" */ + + i=0; + while (number!=NULL) { + + if (i>=n) { + error("\nToo many values found for parameterlist '%s' (%d expected).",parameter,n); + } + + sscanf(number,"%le",&value[i]); + fprintf(stderr,"[%f]",value[i]); + + /* next value */ + number=strtok(NULL,","); + i++; + } + + fprintf(stderr,"]\n"); + + if (i=initial_position)) { + + /* As long as the search was not wrapped and we are not + * back to where we were, continue searching */ + + /* Read a line, and check whether an EOF was found */ + if ((line=ReadLine(fp))==NULL) { + /* Yes? wrapflag on? => Wrap. */ + if (wrapflag) { + wrapped=true; + fseek(fp,0L,SEEK_SET); + continue; + } else + break; + } + + /* strip leading spaces */ + int pos=strspn(line," \t\n"); + + if (line[pos]=='#') { + + continue; + } + + len=strlen(line); + if (strlen(tokenplusspace)<=len) { + + /* only if the line is longer than the token, it might be found */ + // if (strstr(line,tokenplusspace)!=NULL) /* FOUND */ + if (strstr(line,tokenplusspace)==(&line[pos])) /* FOUND */ + { + free(tokenplusspace); + return line; + } + } + + + free(line); + + } + free(tokenplusspace); + return NULL; /* Token Not Found in the file */ +} + +int TokenInLineP(char *line,char *token) +{ + if (strstr(token, line)!=NULL) + return true; + else + return false; +} + + +void SkipLine(FILE *fp) { + + /* Just skips a line in FILE *fp */ + char *tmpstring; + tmpstring=ReadLine(fp); + free(tmpstring); + +} + +void SkipToken(FILE *fp,char *token, bool wrapflag) +{ + /* A very simple function: + call SearchToken() and get rid of the memory returned by + it. + Also, return an error if the desired token was not found in the file. + */ + char *tmppointer; + + tmppointer=SearchToken(fp,token, wrapflag); + + if (tmppointer==NULL) { + error("Token `%s' not found by function SkipToken.\n",token); + } + + free(tmppointer); + +} + diff --git a/src/parse.h b/src/parse.h new file mode 100644 --- /dev/null +++ b/src/parse.h @@ -0,0 +1,46 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + + +#ifndef _PARSE_H_ +#define _PARSE_H_ + +char *ParsePar(FILE *fp, char *parameter, bool wrapflag); +int igetpar(FILE *fp,char *parameter, bool wrapflag); +int igetpar(FILE *fp,char *parameter, int default_val, bool wrapflag); +float fgetpar(FILE *fp,char *parameter, bool wrapflag); +float fgetpar(FILE *fp,char *parameter, double default_val, bool wrapflag); + +/* Get a list of n comma separated doubles */ +double *dgetparlist(FILE *fp,char *parameter, int n, bool wrapflag); +char *sgetpar(FILE *fp,char *parameter, bool wrapflag); +char *sgetpar(FILE *fp,char *parameter,const char *default_val, bool wrapflag); +bool bgetpar(FILE *fp, char *parameter, bool wrapflag); +bool bgetpar(FILE *fp, char *parameter, int default_val, bool wrapflag); +char *SearchToken(FILE *fp, char *token, bool wrapflag); +int TokenInLineP(char *line,char *token); +void SkipToken(FILE *fp,char *token, bool wrapflag); +void SkipLine(FILE *fp); +char *bool_str(bool bool_var); + +#endif diff --git a/src/perl/deployapp.pl b/src/perl/deployapp.pl new file mode 100644 --- /dev/null +++ b/src/perl/deployapp.pl @@ -0,0 +1,114 @@ +#!/usr/bin/perl + +# +# $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 . +# +# Copyright 2010 Roeland Merks. +# +# + +# get dependencies for executable +sub get_frameworks { + my $exec = shift; + my @frameworks = (); + my $fw; +# print "get_frameworks says: exec = $exec\n"; + open deps, "otool -L $exec |"; + # omit first line + ; + + while () { + # look for lines with ".framework", that do not start with / or @. + if (/\.framework/ && !/^[ \t]*\// && !/^[ \t]*\@/) { + chomp; + @line = split; + $f = $line[0]; + # get framework directory name + @line = split(/\//,$f); + for $d (@line) { + if ($d =~ /\.framework/) { + $fw = $d; +# print "get_framework finds framework $d\n"; + last; + } + } + if (defined($fw)) { +# print "get_frameworks pushes $fw on stack\n"; + push @frameworks, $fw; + } + } + } + @frameworks; +} + +$app = $ARGV[0]; +$appdir = $app.".app"; +if ($appdir =~ /\.app\.app/) { + $appdir = $ARGV[0]; +} +$exec = $appdir."/Contents/MacOS/$app"; + +@frameworks = get_frameworks( $exec ); + +#for $f (@frameworks) { +# print "$f\n"; +#} + +# copy all frameworks to appdir/frameworks + +system ("mkdir $appdir/Contents/Frameworks"); +for $fw (@frameworks) { + system "cp -R /Library/Frameworks/$fw $appdir/Contents/Frameworks/."; +} + + +# set identification names for the frameworks + +for $fw (@frameworks) { + $lib = $fw; $lib =~ s/\.framework//g; + system "install_name_tool -id \@executable_path/../Frameworks/$fw/Versions/4.0/$lib $appdir/Contents/Frameworks/$fw/Versions/4.0/$lib"; + + + # tell dynamic linker where to look for the frameworks + system "install_name_tool -change $fw/Versions/4/$lib \@executable_path/../Frameworks/$fw/Versions/4.0/$lib $appdir/Contents/MacOs/$app"; + +} + + +# now, set the frameworks' reciprocal dependencies right +for $fw (@frameworks) { + $lib = $fw; $lib =~ s/\.framework//g; + my @frameworks_of_fw = get_frameworks ( "$appdir/Contents/Frameworks/$fw/Versions/4.0/$lib" ); + for $fwfw (@frameworks_of_fw) { +# print "$fwfw\n"; + $liblib = $fwfw; $liblib =~ s/\.framework//g; + system "install_name_tool -change $fwfw/Versions/4/$liblib \@executable_path/../Frameworks/$fwfw/Versions/4.0/$liblib $appdir/Contents/Frameworks/$fw/Versions/4.0/$lib"; + } +} + +# do the same for additional libs not in a framework depending on frameworks (e.g. libqwt...) +@additionallibs = ( "libqwt.dylib" ); + +for $lib (@additionallibs) { + my @frameworks_of_lib = get_frameworks ( "$appdir/Contents/Frameworks/$lib" ); + for $fwfw (@frameworks_of_lib) { + # print "$fwfw\n"; + $liblib = $fwfw; $liblib =~ s/\.framework//g; + system "install_name_tool -change $fwfw/Versions/4/$liblib \@executable_path/../Frameworks/$fwfw/Versions/4.0/$liblib $appdir/Contents/Frameworks/$lib"; + } +} diff --git a/src/perl/histogram.pl b/src/perl/histogram.pl new file mode 100644 --- /dev/null +++ b/src/perl/histogram.pl @@ -0,0 +1,47 @@ +#!/usr/bin/perl + +# +# $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 . +# +# Copyright 2010 Roeland Merks. +# +# + +$n_bins=100; +$min=0.; +$max=8*atan2(1,1); # 2 Pi +$binsize = ($max-$min)/$n_bins; + + +for ($i=0;$i<$n_bins;$i++) { + $bins[$i]=0; +} + +while (<>) { + @line=split; + $num=$line[0]; + $bin = int($num / $binsize); + # print "[$num, $bin] "; + $bins[$bin]++; +} + + +for ($bin=0;$bin<=$#bins;$bin++) { + $halfwaybin = $bin * $binsize + $binsize/2.; + print $halfwaybin." ".$bins[$bin]."\n"; +} diff --git a/src/perl/make_parameter_source.pl b/src/perl/make_parameter_source.pl new file mode 100644 --- /dev/null +++ b/src/perl/make_parameter_source.pl @@ -0,0 +1,435 @@ +#!/usr/bin/perl + +# +# $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 . +# +# Copyright 2010 Roeland Merks. +# + +# input: parameter file + types +# format: par_name = default_value/type + +# output: C++ source code of class Parameter +# and sample parameter file + +%funname = ( + "double" => fgetpar, + "float" => fgetpar, + "int" => igetpar, + "bool" => bgetpar, + "char *" => sgetpar, + "string" => sgetpar, + "double *" => dgetparlist, + ); + +%typetrans = ( + "double" => "double", + "float" => "double", + "int" => "int", + "bool" => "bool", + "char *" => "char *", + "string" => "char *", + "directory" => "char *", + "doublelist" => "double *", + "label" => "label", + "title" => "title", +); + +open parfile,"<$ARGV[0]"; +open cppfile,">parameter.cpp"; +$i=0; +while () { +#ignore comments + if (/^#/) { + next; + } + @line=split(/=/); +#ignore empty lines + if ($#line<1) { + next; + } + $param[$i]=$line[0]; + $value_type=$line[1]; + + @typel=split(/ \/ /,$value_type); + $value[$i] = $typel[0]; + $type[$i] = $typel[1]; + +#get rid of spaces + $param[$i] =~ s/ //g; + $value[$i] =~ s/ //g; + $type[$i] =~ s/ //g; + $type[$i] =~s/\n//g; + $convtype[$i]=$typetrans{$type[$i]}; + + if ($convtype[$i] eq "char *") { + $value[$i] = "\"$value[$i]\""; + } + #print cppfile "param = $param, value = $value, type = $type\n"; + + $i++; +} + +$lines=$i; + +print cppfile < +#include +#include +#include +#include +#include +#include "output.h" +#include "parse.h" +#include "xmlwrite.h" + +using namespace std; + + +Parameter::Parameter() { +END_HEADER + +for ($i=0;$i<$lines;$i++) { + if ($convtype[$i] ne "label" && $convtype[$i] ne "title") { + if ($convtype[$i] eq "char *") { + print cppfile " $param[$i] = strdup($value[$i]);\n"; + } else { + if ($convtype[$i] eq "double *") { + #comma separated list expected + @paramlist = split(/,/, $value[$i]); + $length = $#paramlist+1; + print cppfile " $param[$i] = new double\[$length\];\n"; + for ($j=0;$j<=$#paramlist;$j++) { + print cppfile " $param[$i]\[$j\] = $paramlist[$j];\n"; + } + } else { + print cppfile " $param[$i] = $value[$i];\n"; + } + } + } +} + + +print cppfile < valarray) {\n"; +print cppfile; + +for ($i=0;$i<$lines;$i++) { + + if ($convtype[$i] eq "double *") { + @paramlist = split(/,/,$value[$i]); + print cppfile "if (!strcmp(namec, \"$param[$i]\")) {\n"; + print cppfile " int i=0;\n"; + print cppfile " vector::const_iterator v=valarray.begin();\n"; + print cppfile " while (v!=valarray.end() && i <= $#paramlist ) {\n"; + print cppfile " $param[$i]\[i++\]=*(v++);\n"; + print cppfile " }\n"; + print cppfile "}\n"; + } +} +print cppfile "}\n"; + + +print cppfile <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 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; + } + +}*/ + +ostream &operator<<(ostream &os, Parameter &p) { + p.Write(os); + return os; +} + +END_TRAILER + + + +# parameter.h + +open hfile, ">parameter.h"; +print hfile < + +#include +#include + +class Parameter { + + public: + Parameter(); + ~Parameter(); + void CleanUp(void); + void Read(const char *filename); + void Write(ostream &os) const; + void XMLAdd(xmlNode *root) const; + void XMLRead(xmlNode *root); + void AssignValToPar(const char *namec, const char *valc); + void AssignValArrayToPar(const char *namec, vector valarray); +END_HEADER2 + + for ($i=0;$i<$lines;$i++) { + if ($convtype[$i] ne "label" && $convtype[$i] ne "title") { + print hfile " $convtype[$i] $param[$i];\n"; + } + } + +print hfile <default.par"; + +for ($i=0;$i<$lines;$i++) { + if ($type[$i] ne "title" && $type[$i] ne "label") { + $value[$i] =~ s/\"//g; + print parfile " $param[$i] = $value[$i]\n"; + } +} diff --git a/src/perl/make_pardialog_source.pl b/src/perl/make_pardialog_source.pl new file mode 100644 --- /dev/null +++ b/src/perl/make_pardialog_source.pl @@ -0,0 +1,303 @@ +#!/usr/bin/perl + +# +# $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 . +# +# Copyright 2010 Roeland Merks. +# + +# input: parameter file + types +# format: par_name = default_value/type + +# output: C++ source code of class Parameter +# and sample parameter file + +%funname = ( + "double" => "toDouble", + "int" => "toInt", +); + +%typetrans = ( + "double" => "double", + "float" => "double", + "int" => "int", + "bool" => "bool", + "char *" => "char *", + "string" => "char *", + "directory" => "char *", + "doublelist" => "double *", + "label" => "label", + "title" => "title", +); + +$basename = "pardialog"; + +open parfile,"<$ARGV[0]"; +open cppfile,">$basename.cpp"; + +$i=0; +while () { + if (/^#/) { + next; + } + @line=split(/=/); +#ignore empty lines + if ($#line<1) { + next; + } + $param[$i]=$line[0]; + $value_type=$line[1]; + + @typel=split(/ \/ /,$value_type); + $value[$i] = $typel[0]; + $type[$i] = $typel[1]; + + #get rid of spaces + $type[$i] =~ s/ //g; + $type[$i] =~s/\n//g; + $convtype[$i]=$typetrans{$type[$i]}; + $param[$i] =~ s/ //g; + if ($convtype[$i] ne "label" && $convtype[$i] ne "title") { + #get rid of spaces + $value[$i] =~ s/ //g; + } + if ($convtype[$i] eq "char *") { + $value[$i] = "\"$value[$i]\""; + } + #print cppfile "param = $param, value = $value, type = $type\n"; + + $i++; +} + +$lines=$i; + + +print cppfile < +#include +#include +#include +#include + +ParameterDialog::ParameterDialog(QWidget *parent, const char *name, Qt::WindowFlags f) : QDialog(parent,name,false,f) { + extern Parameter par; +END_HEADER + +for ($i=0;$i<$lines;$i++) { + if ($convtype[$i] eq "label" || $convtype[$i] eq "title") { + next; + } + if ($convtype[$i] eq "double *") { + print cppfile " QString $param[$i]_string(\""; + @paramlist = split(/,/,$value[$i]); + for ($j=1;$j<=$#paramlist;$j++) { + print cppfile "%$j,"; + } + print cppfile "%$j\");\n"; + print cppfile " $param[$i]_string = $param[$i]_string"; + for ($j=0;$j<=$#paramlist;$j++) { + print cppfile ".arg(par.$param[$i]\[$j\])"; + } + print cppfile ";\n"; + print cppfile " $param[$i]_edit = new QLineEdit( $param[$i]_string, this, \"$param[$i]_edit\" );\n"; + } else { + if ($convtype[$i] eq "bool") { + print cppfile " $param[$i]_edit = new QLineEdit( QString(\"%1\").arg(sbool(par.$param[$i])), this, \"$param[$i]_edit\" );\n"; + } else { + print cppfile " $param[$i]_edit = new QLineEdit( QString(\"%1\").arg(par.$param[$i]), this, \"$param[$i]_edit\" );\n"; + } + } +} + + +print cppfile <addWidget( new QLabel( \"$value[$i]\", this), $row, $col, 1, 2 );\n"; + $c++; + } else { + if ($convtype[$i] eq "title") { + if ($ntitles) { + print stderr "Sorry, only one title allowed. Aborting source construction...\n"; + exit(1); + } + print cppfile " setWindowTitle( QString( \"$value[$i]\") );\n"; + print cppfile " grid->addWidget( new QLabel( \"

$value[$i]

\",this), $row, $col, 1, -1, Qt::AlignCenter);\n"; + print cppfile " grid->addWidget( new QLabel( \"\", this), $row+1, $col, 1, -1);\n"; + $ntitles++; + } else { + print cppfile " grid->addWidget( new QLabel( \"$param[$i]\", this ),$row, $col );\n"; + print cppfile " grid->addWidget( $param[$i]_edit, $row, $col+1 );\n"; + $c++; + } + } +} + +$row = $numrows+1; +$col = 2*int($i/$numrows); + +print cppfile <addWidget(pb, $row, $col ); + connect( pb, SIGNAL( clicked() ), this, SLOT( write() ) ); + QPushButton *pb2 = new QPushButton( \"&Close\", this ); + grid->addWidget(pb2,$row, $col+1 ); + connect( pb2, SIGNAL( clicked() ), this, SLOT( close() ) ); + QPushButton *pb3 = new QPushButton( \"&Reset\", this ); + grid->addWidget(pb3, $row, $col+2 ); + connect( pb3, SIGNAL( clicked() ), this, SLOT( Reset() ) ); + show(); +}; + +ParameterDialog::~ParameterDialog(void) { +ANOTHER_LABEL + +for ($i=0;$i<$lines;$i++) { + if ($convtype[$i] ne "label" && $convtype[$i] ne "title") { + print cppfile "delete $param[$i]_edit;\n"; + } +} + +print cppfile <text().section(',', $j, $j);\n"; + print cppfile " par.$param[$i]\[$j\] = tmpval.toDouble();\n"; + } + } else { + if ($convtype[$i] eq "bool") { + print cppfile " tmpval = $param[$i]_edit->text().stripWhiteSpace();\n"; + print cppfile " if (tmpval == \"true\" || tmpval == \"yes\" ) par.$param[$i] = true;\n"; + print cppfile " else if (tmpval == \"false\" || tmpval == \"no\") par.$param[$i] = false;\n"; + print cppfile " else {\n"; + print cppfile " if (QMessageBox::question(this, \"Syntax error\", tr(\"Value %1 of parameter %2 is not recognized as Boolean.\\nDo you mean TRUE or FALSE?\").arg(tmpval).arg(\"$param[$i]\"),\"True\",\"False\", QString::null, 0, 1)==0) par.$param[$i]=true;\n"; + print cppfile " else par.$param[$i]=false;\n"; + print cppfile " }\n"; + } else { + if ($convtype[$i] eq "char *") { + print cppfile " par.$param[$i] = strdup((const char *)$param[$i]_edit->text());\n"; + } else { + print cppfile " par.$param[$i] = $param[$i]_edit->text().$funname{$convtype[$i]}();\n"; + } + } + } +} + + + +print cppfile <setText( $param[$i]_string );\n"; + } else { + if ($convtype[$i] eq "bool") { + print cppfile " $param[$i]_edit->setText( QString(\"%1\").arg(sbool(par.$param[$i])));\n"; + } else { + print cppfile " $param[$i]_edit->setText( QString(\"%1\").arg(par.$param[$i]) );\n"; + } + } +} + +print cppfile "}\n\n"; + +# qparameter.h + +open hfile, ">$basename.h"; +print hfile < +#include +#include +#include +#include +#include + +class ParameterDialog : public QDialog { + Q_OBJECT + + public: + ParameterDialog(QWidget *parent=0, const char *name = 0, Qt::WindowFlags f = 0); + virtual ~ParameterDialog(void); + public slots: + void Reset(void); + +private slots: + void write(void); + + private: +END_HEADER2 + +for ($i=0;$i<$lines;$i++) { + if ($convtype[$i] ne "label" && $convtype[$i] ne "title") { + print hfile " QLineEdit *$param[$i]_edit;\n"; + } +} + +print hfile <. +# +# Copyright 2010 Roeland Merks. + +# Store essential model source code in XML files, so the exact model +# used for the geometry can be retrieved later on The crucial model +# description is in the files "reactions.h" (or similar) and leaf.cpp +# (or similar). + +# First we should retrieve the name of the main code (i.e. leaf.cpp or +# so), and the version of reactions.h that we include there. + +# We can get this from the project file (possibly called "LeafGrowth.pro") + +# If more than one project file found, issue an error + +open prlist, "ls *.pro |"; + +@projectfiles = (); +while () { + chomp; + push @projectfiles, $_; +} + +if ($#projectfiles > 0) { + print stderr "Oops, I found several project files: "; + for $p (@projectfiles) { + print stderr "$p "; + } + print stderr "\n"; + print stderr "Please make sure you have only one, and try again\n"; + exit(1); +} + +if ($#projectfiles < 0) { + print stderr "No project files found. Please add a project file.\n"; + exit(1); +} + +$project = $projectfiles[0]; + +print stderr "Using project file \"$project\"\n"; + +# extract TARGET and REACTIONS from project file + +open pfile,"<$project"; +my @targets = (); +while () { + if (/^[ \t]*TARGET/) { + my @line = split(/=/); + my $target = $line[1]; + chomp $target; + $target =~ s/[ \t]//g; + push @targets, $target; + } + if (/^[ \t]*REACTIONS/) { + my @line = split(/=/); + my $reaction = $line[1]; + chomp $reaction; + $reaction =~ s/[ \t]//g; + push @reactions, $reaction; + } +} + + +if ($#targets > 0) { + print stderr "Too many targets found: "; + for $t (@targets) { + print stderr "$t "; + } + print stderr "\n"; + exit(1); +} + +if ($#targets < 0) { + print stderr "No targets found in project $project\n"; + exit(1); +} + +$target = $targets[0]; + +print "Target is $target\n"; + +if ($#reactions > 0) { + print stderr "Too many reactions found: "; + for $t (@reactions) { + print stderr "$t "; + } + print stderr "\n"; + exit(1); +} + +if ($#reactions < 0) { + print stderr "No reactions found in project $project\n"; + exit(1); +} + +$reaction = $reactions[0]; + +print "Reactions header is $reaction\n"; + +$mainsource = $target.".cpp"; + +# extract "reactions" source from mainsource +# assuming that the header file name starts with "reactions_" + +#open sfile, "<$mainsource"; +# +#while () { +# if (/^[ \t]*\#include/) { +# chomp; +# my @line = split(/\#include/); +# my $header = $line[1]; +# $header =~ s/[\t ]//g; +# if ($header =~ /reactions/) { +# $header =~ s/[\"\<\>]//g; +# push @headers, $header; +# } +# } +#} +# +#if ($#headers > 0) { +# print stderr "Oops, more than one reactions header found: "; +# for $h (@headers) { +# print stderr "$h "; +# } +# print stderr "\n"; +# print stderr "Don't know what to do now... Please rename a header in $mainsource, so only one will start with \"reactions\"\n"; +# exit(1); +#} +# +#if ($#headers < 0) { +# print stderr "Oops, no reactions header found in $mainsource.\n"; +# print stderr "Please make sure the name of your reactions header starts with \"reactions\"\n"; +# exit(1); +#} +# +#$reactions = $headers[0]; + + + +# now generating xmlcode to write the contents of these files (hard coded in the executables) + +open xmlsrc, ">xmlwritecode.cpp"; + +print xmlsrc < +#include +#include +#include +#include "xmlwrite.h" + + +void XMLIO::XMLWriteLeafSourceCode(xmlNode *parent) { + + // Embed the code in our xml file, so we can reconstruct the model we used + // to produce it... + +END_MARKER + +sub construct_string_constant_from_file { + + my $fname = shift; + my $str = ""; + + open srcfile, "<$fname"; + while () { + chomp; + s/\\/\\\\/g; + s/\"/\\\"/g; + s/\&/\&/g; + $str.=$_."\\n"; + } + $str .= "\";\n"; + return $str; +} + +print xmlsrc "xmlChar *sourcecode = (xmlChar *)\"".construct_string_constant_from_file( "$mainsource" ); + print xmlsrc <. + * + * Copyright 2010 Roeland Merks. + * + */ + + +#ifndef _PI_H_ +#define _PI_H_ + +const double Pi=3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170676; + +#endif diff --git a/src/psb.xpm b/src/psb.xpm new file mode 100644 --- /dev/null +++ b/src/psb.xpm @@ -0,0 +1,312 @@ +/* XPM */ +static const char * const PSB_xpm[] = { +/* columns rows colors chars-per-pixel */ +"133 50 256 2", +" c #020200", +". c #010B00", +"X c #090200", +"o c #0A0A03", +"O c #0C0C0A", +"+ c #06050A", +"@ c #001300", +"# c #081704", +"$ c #110A07", +"% c #13120C", +"& c #161606", +"* c #1B1B14", +"= c #1C1C1A", +"- c #151515", +"; c #0E0E10", +": c #1B2517", +"> c #18310A", +", c #1E3822", +"< c #221E18", +"1 c #24251A", +"2 c #282C12", +"3 c #2A2A24", +"4 c #282824", +"5 c #273524", +"6 c #35362C", +"7 c #393A33", +"8 c #373735", +"9 c #312D2A", +"0 c #3F3D41", +"q c #2B4A10", +"w c #334324", +"e c #394236", +"r c #365228", +"t c #3F4344", +"y c #413C37", +"u c #413F42", +"i c #44443C", +"p c #465434", +"a c #456829", +"s c #517034", +"d c #537926", +"f c #494945", +"g c #53544C", +"h c #5A5A54", +"j c #575755", +"k c #4D524D", +"l c #586A48", +"z c #615C5B", +"x c #765F5C", +"c c #63635D", +"v c #666D55", +"b c #667A4F", +"n c #6B6A64", +"m c #6C6B6A", +"M c #646663", +"N c #766965", +"B c #73726C", +"V c #737A69", +"C c #7B7B74", +"Z c #777674", +"A c #6D7869", +"S c #596162", +"D c #7E7C82", +"F c #598533", +"G c #679937", +"H c #659231", +"J c #6AA539", +"K c #6BB52A", +"L c #6DB335", +"P c #75AA3A", +"I c #74B43B", +"U c #77B73A", +"Y c #67A82A", +"T c #759653", +"R c #6E924B", +"E c #7B8277", +"W c #79936E", +"Q c #6DA344", +"! c #75AC44", +"~ c #7CAC4B", +"^ c #76A747", +"/ c #7AAA53", +"( c #74B242", +") c #7BB343", +"_ c #7BB44A", +"` c #7ABA42", +"' c #7DBA4B", +"] c #74B846", +"[ c #7AB453", +"{ c #7DA17D", +"} c #7BC345", +"| c #887872", +" . c #866A6A", +".. c #80B73F", +"X. c #83837C", +"o. c #898879", +"O. c #88916E", +"+. c #85AA58", +"@. c #81B24C", +"#. c #83B948", +"$. c #86B757", +"%. c #91BD5A", +"&. c #83AA49", +"*. c #88AB64", +"=. c #8AB666", +"-. c #93AB77", +";. c #92B46B", +":. c #93B968", +">. c #98B876", +",. c #8DB375", +"<. c #A2BC7A", +"1. c #99916D", +"2. c #83C13A", +"3. c #8BC459", +"4. c #8BC650", +"5. c #97C667", +"6. c #9BC476", +"7. c #9DD471", +"8. c #A2C86C", +"9. c #A5C778", +"0. c #A8D577", +"q. c #A9E079", +"w. c #858582", +"e. c #8A8A83", +"r. c #8A8886", +"t. c #8D8D94", +"y. c #8C9586", +"u. c #93928C", +"i. c #9A968A", +"p. c #949491", +"a. c #9A9A93", +"s. c #9C9C99", +"d. c #979597", +"f. c #908F8E", +"g. c #9B9AAA", +"h. c #9AA986", +"j. c #98AB8D", +"k. c #A49E97", +"l. c #AF978E", +"z. c #A2A29B", +"x. c #A5A597", +"c. c #A7B295", +"v. c #ADAD92", +"b. c #A4A4A2", +"n. c #ABABA4", +"m. c #ADACAA", +"M. c #A6A6A5", +"N. c #AAA7B5", +"B. c #B1ADA9", +"V. c #B3B3AB", +"C. c #BBBBAC", +"Z. c #B4B5A9", +"A. c #B4B5B2", +"S. c #BBBBB3", +"D. c #BBBBB9", +"F. c #B5B8B4", +"G. c #ADB4A9", +"H. c #BF9FA0", +"J. c #B8B7C0", +"K. c #9CC382", +"L. c #A8C786", +"P. c #B5CC95", +"I. c #B2D486", +"U. c #B7D595", +"Y. c #AFCC8E", +"T. c #B9C7A8", +"R. c #BCC2B6", +"E. c #B8D8B1", +"W. c #BBE596", +"Q. c #B6EC8C", +"!. c #BBE0A3", +"~. c #BFC1C0", +"^. c #C29B97", +"/. c #C1BCB9", +"(. c #CBAAA9", +"). c #C3BEC4", +"_. c #C1D99C", +"`. c #C4C4AB", +"'. c #C3C3BB", +"]. c #C7C7B8", +"[. c #C4D9A7", +"{. c #CAD6B6", +"}. c #D2D5B7", +"|. c #C5E697", +" X c #CDF099", +".X c #C7E7A8", +"XX c #CBE5B5", +"oX c #D2E9B8", +"OX c #D7F7B7", +"+X c #CBEFAC", +"@X c #E5EDBE", +"#X c #C4C3C2", +"$X c #CACBC3", +"%X c #CCCCC9", +"&X c #C6C6C4", +"*X c #CDCCD4", +"=X c #CCD2C4", +"-X c #D2CAC4", +";X c #D0CDD6", +":X c #D2D3CC", +">X c #D5DACC", +",X c #D6D6C8", +" -.aXLXg . # > / =.6.6.L.Y.fXCXlX4 % f.3XlX2X'.$XSX%X e.HXlXlX n ZXlX:X: * y % * kXJX#X% - X m $XD.B.% * i % = %XJXBXHX* o * 1 7 9 4 o o z.R.C.$XE . * 6 j SXBXBXGXFXPXPXPXPXPX", +"PXPXPXPXPXPXPXKXPXF.o B 1XPXw.. w F +.R . Z 2XKXPX3X&XF.D.p # T P %.Q # # W c.w @ y.cXNXqX% : s d T +.>.L.{.pXVXcXLXZ $ < m F.kXLXxXzXHXe 1 :XPXs. a.%XHXB B.m.a.B n.%X:XC + j B X.b.3X6 8 /.V.m.% h vXzXUX9 * S z.f M A.e. f F.'.1X9 4 b.X.A c G.$XtXkXLXPXPXPXPXPX", +"PXPXPXPXPXPXPXPXPXJ. 9 j 8 2 a H $.F @ k =XHXHXJXt.k t : . ,.@.' R . 5 T j.p . Z rXaXqX: : l R *.P.XX8XcXCXcXSXlX,Xc < = b.LXzXLXw. u.LXe 3 z.S.PXX. g w.z.b.b.:XJX| n w.i.B.'.$ f M c B : = #XBXBX4 * X.b.m C u.n.O 7 a.R.LXe X n s.F.n.M.F.D.XNX= 4 { E.hXuXk.h g 9 1X#X'.qXBXPXPXPXPXPX", +"PXPXPXKXPXPXPXPXPX~. S b.R.,XT.+.&.$.H . r G $.a . D M.&Xj . b Q [ T . q ,.|.R . v [.h.h.> 9 $XLXlXrX$XR.'.tXlXJXkXw.lXHXLXSXn E :XJXFXd. o i u.$XX'.J.y + A.b.#XBXSXPXPXPXPXPX", +"LXPXPXPXPXPXLXLXPXA. Z %XUXHXLXeX9.+.d @ r R +.R . # m f O . s G } R @ q [ I.d . A T >.iXr . l A yXbXT.T.P. l.SXu. | .<.I.[.hXBX:Xe.6 o & 1 c E X.n.:XlXLX7 : X.C.cXSXlXx.i * < f X.u.%XFXlXC 1 < .H.wX>Xe.3 * 4 h z.-XVXjXv p <.gXC.f e.2XBXM k { E.HXs.6 1 4 S r.g.g.).KXBXPXPXPXPXPX", +"PXPXPXPXPXPXPXLXPXJX*XN.4XIXPXPXPXHXLXPX&XG.oXhXdX5Xo.A R r a F Q ' @.Q F H P 0.=.R $.$._ ^ H s ,.,.+.:.8.8.<.[.dXbX>Xz.X.X.X.C C x.'.rXPX%Xo f a.=XcXNXyX$Xc.z.x.k.a.F.kXBXmX-Xi.1. .1.,XcX].M.a.b.u.z.].aXjX`.| v.uXgXZ.n.2XNX5Xj.{ E.nX7XG.x.M.f.M.J.N.;XFXJXPXPXPXPXPX", +"PXPXPXPXPXPXPXLXPXJX1X4XUXPXHXPXHXPXHXFX7XrXNXLXPXFX4X$XT.!./ G [ [ P P Y ..U #.G ~ @.P _ #.^ +.>.T >.8.8.8.0.L.6XnXXD.m.'.%XtXDXgX}.v.}.AXuX>X1XzXNX7XR.j.5XnXlX>XC.V.#X;X4XJ.4XUXBXPXPXPXPXPX", +"PXPXPXPXPXPXPXPXLXPXPXKXPXGXCXVXnXGXLXLXPXPXLXKXPXPXPXJXdXU.[ ] Q.0.#.U ) P ....) @._ ) ` U &.*./ ;.:.:.8.9.9.U.oXXX=X>X7X7X0XcX].C.'.:XxXC B a.$XvXBXSXxXm.b.zXmXmXZXvXZXZXlXDXyXgXgX`.rXBXLXvXBXBXlXBXaXDXjXeX{.,XSXmXBXBXlXLXnX5XG.5XGXzXcXcXmXMXUX4XN.;XJXZXPXPXPXPXPX", +"PXPXPXPXPXPXPXPXPXPXPXPXGXnXiX+XU.U. XsXSXLXHXPXKXPXKXLXiX+.&.&.$.q.5.` } U K 2./ [ [ ` ` #.U.oX8X!.9.:.9.9.L.U.P.[.{._.`.[.>X6X6X=X].-X>X'.s.n.7XPXBXHXPXD.d.zXBXJXBXBXvXCXvXNXNXVX,X`.>XSXcXvXBXBXBXNXBXSXCXaX:XcXmXZXGXBXMXcXBX6XT.>XvXBXvXDXJXMXJX;XN.;XIXZXPXPXPXPXPX", +"PXPXPXPXPXPXPXPXKXPXPXnXGX!.+.~ P &.&.*.oXVXSXnXLXLXPXSXsX8.&.) 0.#.` ` L U 2.P L.OX7.] I q.sXVXdXsX+X9.6.9.Y.Y.Y.P._._.[..XXX5X7X=X].=X:X].R.F.%X1XMXzX&XN.g.2XHXyXCXBXvXGXGXlXNXSX7Xv E GXnXvXZXNXSXvXBXMXFX2XD.zXJXBXlXBXcXLXcXhX{.aXGXZXCXNXmXBXPX).N.4XBXvXPXPXPXPXPX", +"PXPXPXPXPXPXPXPXPXPXPXGX6X[ _ #.) ' #.@.~ :.W.+X8XpXbXpXiX X%.^ 8.8.%...#.4.K U #.5.( ` U 3.W.P.L.9.>.<.9.6.6.U.I._._._._.!.XXoX}.& o o % k :X:X'.3Xf. t.A.J.HXBXvXFXcXyXZXSXcXcX9X: 5 yXcXBXvXaXBXmXIXzXIXA.d.;XPXvXZXBXNXNXNXuX`.wXBXvXaXSXZXZXlX&XN.;XJXGXPXPXPXPXPX", +"PXPXPXPXPXPXLXLXKXPXHXSXY.Q ) ` ` ( I ` #.) ! ] [ ,.>.*.;.%.:.Q. X|.&.&. X0.I #.2.7.0./ [ 6.>.-.c.[.[.XX8XsXoXU.Y.Y._._..X[.XX6X{.o e X.w.j h BXxX4XA.w.r.N.*XlXGXHXJXPXPXvXvXSXCX0X, 1 R.tXBXBXLXLXLXJXKXUXJ.g.4XIXUXLXLXLXLXLX`.l.wXLXPXBXBXBXLXLXqXD.kXGXBXPXPXPXPXPX", +"PXPXPXPXPXPXPXPXKXPXPXCXK.+.#.L ] ] ] ` U ` U ] ] _ &.@.W.OXOXQ.7./ +._.sXI.( I I 5.8X8X9XdXnXnXLXGXZXnXBXdXnXbXXX[.P._._.[..X6X].O 7 u.d.G.* 1 &XBXLXX. i A.1Xz.% f m.7XPXc.X z.#XD.2Xm 9 7 o 7 qXDXCX7X, : M.yXPX1X4 & k 9 t.J.g.4XKX8 u 8 X $ H.^.wXy : xXlXPXf o }.&XlXBXZXPXPXPXPXPX", +"PXLXPXPXPXPXPXPXPXPXSXVX6X/ J U U U L U U U U U ` ) U P U ` ] ( =.oXAXdXpX6.=./ +.I.&.:.8XSXcXBXDXBXNXjXNXnXZXvXBXBXNXhXjXuXcXvX7X% + < = X 5 p.Z.yXLXZ. e.:X7X$XX g s.w.M | mXSX0X, - n.qXKXm $ a.z.g.0 ; t.g.IXw.+ e.k.B.f $ .1.gXl.X u.vX7X v v./.rXSXMXPXPXPXPXPX", +"PXPXPXPXPXPXPXPXPXLXLXLXSX8XU.=.[ $.7.5._ ) #.) ' ) #...L U L I [.dXcXCX8X;.+X[.P.iX|.&.;.iXhXaXAXaXNXSXSXvXBXZXZXDXDXSXDXBXGXGX1X- * k h f + 6 =XtXLXZ. B $XLXz. X.u.m.b.% 3 V.qX7X, : n.qXLX9 3 a.s.).D + S g.KXf * d.p./.z $ .^.wXqX% 7 zXu.. 1.1./.uXSXNXPXPXPXPXPX", +"PXPXPXPXPXPXPXPXPXPXPXPXLXLXCXSXdXaXCXdX6.^ P ! / / [ [ Q.3.` $.iXDXBXnX+X+.eXVXCXdXhX_.+.%.@XsXjXZXZXJXFXJXZXZXDXNXBXDXZXBXBXZXqXX 7 e.k.a.i x.cXJXV. B $XPXy. X./.kXMX6 * k.Z.$X9 * k.tXLX: 6 z.:XBXN. k d.IX0 = d.;XGXB X N l.gXDXz X #X9 7 h.v.}.uXAXmXPXPXPXPXPX", +"PXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXHXPXPXiX.XI.9.K.!.+XiX0.! U %.@XDXUXHXU.=.hXcXcXBXMXAX<.P :.+XsXDXDXMXKXFXmXBXZXBXBXSXZXMXvXSX7Xo i x.&X:Xe. X..%.$.[ 6.U.oXbXnXcXBXBXBXZXBXMXZXJXtXC f y h n u.w.n.kXLX&Xc e.$XBXmX$Xm = 6 M e.a.%XMXHXf.z n.xXJX.J K K U G .XGXPXPXPXKXPXLXLXPXLXLXPXSXJXLXLXLXLXHXPXKXLXLXLXHXPXKXPXPXHXKXLXPXLXLXKXPXKXLXLXKXLXLXPXPXHXKXLXKXLXPXPXPXPXKXPXPXHXLXLXPXHXLXPXPXPXPXPXHXGXHXLXHXPXHXPXKXPXPXPXPXLXHXPXHXHXHXPXPXPXPXPXPX", +"PXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXLXLXPXLXPXPXPXPXPXPXPXPXPXHXGXL.Y K K Y ] 8XPXPXPXPXPXPXPXLXLXPXKXPXPXPXPXPXPXPXPXPXPXPXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXLXPXPXPXPXPXLXPXPXLXPXLXPXPXPXPXPXPXPXPXPX", +"PXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXLXLXPXPXPXPXPXPXPXPXPXPXHXUXpX$.H Y [ !.bXPXKXPXPXKXKXPXLXPXLXPXPXKXPXPXPXLXLXPXPXPXPXPXPXPXPXKXPXPXPXPXPXPXPXPXPXKXPXPXPXPXPXKXPXPXPXPXPXPXPXPXPXKXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXLXPXPXPXPXPXPXLXPXHXPXPXPXPXPXPXPXPXPXPX", +"PXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXHXGXsX[..XpXCXGXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPX", +"PXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXHXLXLXGXLXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPX" +}; diff --git a/src/qcanvasarrow.h b/src/qcanvasarrow.h new file mode 100644 --- /dev/null +++ b/src/qcanvasarrow.h @@ -0,0 +1,73 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + + +#ifndef _QCANVASARROW_H_ +#define _QCANVASARROW_H_ + +#include + +class QGraphicsArrowItem : public QGraphicsLineItem { + + public: + QGraphicsArrowItem(QGraphicsItem *parent, QGraphicsScene *c) : QGraphicsLineItem(parent, c) { + }; + + void paint ( QPainter *p, const QStyleOptionGraphicsItem *option, + QWidget *widget ) { + + // construct arrow head + QPointF start=line().p1(); + QPointF end=line().p2(); + QPointF mid=start + (3./4.)*(end-start); + + double vx=end.x()-start.x(); + double vy=end.y()-start.y(); + + double length = sqrt(vx*vx+vy*vy); + if (length==0) return; + + // perpendicular vector + double px=-vy/length; + double py=vx/length; + + // Arrow head lines go from end point + // to points about 3/4 of the total arrow, extending sideways about 1/4 + // of the arrow length. + + + QPointF arwp1 = mid + QPointF( (int)( (length/4.)*px ), + (int)( (length/4.)*py ) ); + QPointF arwp2 = mid - QPointF( (int)( (length/4.)*px ), + (int)( (length/4.)*py ) ); + + p->setPen(pen()); + // Draw arrow head + p->drawLine( end, arwp1 ); + p->drawLine( end, arwp2 ); + // Draw arrow line + p->drawLine( start, end); + } +}; + +#endif diff --git a/src/random.cpp b/src/random.cpp new file mode 100644 --- /dev/null +++ b/src/random.cpp @@ -0,0 +1,162 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include +#include +#include +#include +#include "random.h" + +static const std::string _module_id("$Id$"); + +static int idum = -1; +using namespace std; + +static int counter=0; +/*! \return A random double between 0 and 1 +**/ +double RANDOM(void) +/* Knuth's substrative method, see Numerical Recipes */ +{ + static int inext,inextp; + static long ma[56]; + static int iff=0; + counter++; + long mj,mk; + int i,ii,k; + + if (idum < 0 || iff == 0) { + iff=1; + mj=MSEED-(idum < 0 ? -idum : idum); + mj %= MBIG; + ma[55]=mj; + mk=1; + i=1; + do { + ii=(21*i) % 55; + ma[ii]=mk; + mk=mj-mk; + if (mk < MZ) mk += MBIG; + mj=ma[ii]; + } while ( ++i <= 54 ); + k=1; + do { + i=1; + do { + ma[i] -= ma[1+(i+30) % 55]; + if (ma[i] < MZ) ma[i] += MBIG; + } while ( ++i <= 55 ); + } while ( ++k <= 4 ); + inext=0; + inextp=31; + idum=1; + } + if (++inext == 56) inext=1; + if (++inextp == 56) inextp=1; + mj=ma[inext]-ma[inextp]; + if (mj < MZ) mj += MBIG; + ma[inext]=mj; + return mj*FAC; +} + +/*! \param An integer random seed + \return the random seed +**/ +int Seed(int seed) +{ + if (seed < 0) { + cerr << "Randomizing random generator, seed is "; + int rseed=Randomize(); + cerr << rseed << "\n"; + return rseed; + } else { + int i; + idum = -seed; + for (i=0; i <100; i++) + RANDOM(); + return seed; + } +} + + +/*! Returns a random integer value between 1 and 'max' + \param The maximum value (long) + \return A random integer (long) +**/ +long RandomNumber(long max) +{ + return((long)(RANDOM()*max+1)); +} + +/*! Interactively ask for the seed +\param void +\return void +**/ +void AskSeed(void) +{ + int seed; + printf("Please enter a random seed: "); + scanf("%d",&seed); + printf("\n"); + Seed(seed); +} + +int RandomCounter(void) { + return counter; +} + +/*! Make a random seed based on the local time +\param void +\return void +**/ + +int Randomize(void) { + + // Set the seed according to the local time + struct timeb t; + int seed; + + ftime(&t); + + seed=abs((int)((t.time*t.millitm)%655337)); + Seed(seed); + fprintf(stderr,"Random seed is %d\n",seed); + return seed; +} + +/** TESTING random generator +#include +#include +int main(void) { + + MyUrand r(-1); + + vector bucket(100); + for (int i = 0; i<1000000 ; i++) { + bucket[r(100)]++; + } + ofstream out("randomtest.dat"); + for (int i = 0; i<=100; i++) { + out << i << " " << bucket[i] << endl; + } +} +***/ diff --git a/src/random.h b/src/random.h new file mode 100644 --- /dev/null +++ b/src/random.h @@ -0,0 +1,66 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + + +#ifndef _RANDOM_H_ +#define _RANDOM_H_ + +#define MBIG 1000000000 +#define MSEED 161803398 +#define MZ 0 +#define FAC (1.0/MBIG) + +double RANDOM(); +int Seed(int seed); +long RandomNumber(long max); +void AskSeed(); +int Randomize(void); +int RandomCounter(void); + + + +// Class MyUrand, so we can pass the random generator to STL's random_shuffle, +// and get identical simulations for a given random seed. +class MyUrand { + + long n; + public: + MyUrand(long nn) { + n=nn; + } + MyUrand(void){}; + + void seed(long s) { + Seed(s); + } + + long operator()(long nn) { return RandomNumber(nn)-1; } + long operator()(void) { return RandomNumber(n); } +}; + + + + + + +#endif diff --git a/src/rseed.cpp b/src/rseed.cpp new file mode 100644 --- /dev/null +++ b/src/rseed.cpp @@ -0,0 +1,33 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include +#include "random.h" + +static const std::string _module_id("$Id$"); + +int main() { + + long rseed=Randomize(); + std::cout << rseed << "\n"; + +} diff --git a/src/rungekutta.cpp b/src/rungekutta.cpp new file mode 100644 --- /dev/null +++ b/src/rungekutta.cpp @@ -0,0 +1,205 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include +#include +#include "rungekutta.h" +#include "warning.h" +#include "maxmin.h" + +static const std::string _module_id("$Id$"); + +// The value Errcon equals (5/Safety) raised to the power (1/PGrow), see use below. + +/* +static float maxarg1,maxarg2; +#define FMAX(a,b) (maxarg1=(a),maxarg2=(b),(maxarg1) > (maxarg2) ?\ +(maxarg1) : (maxarg2)) +static float minarg1,minarg2; +#define FMIN(a,b) (minarg1=(a),minarg2=(b),(minarg1) < (minarg2) ?\ +(minarg1) : (minarg2)) +#define SIGN(a,b) ((b) >= 0.0 ? fabs(a) : -fabs(a)) +*/ + +const double RungeKutta::Safety = 0.9; +const double RungeKutta::PGrow = -0.2; +const double RungeKutta::Pshrnk = -0.25; +const double RungeKutta::Errcon = 1.89e-4; +const double RungeKutta::Maxstp = 10000; +const double RungeKutta::Tiny = 1.0e-30; + +void RungeKutta::rkqs(double *y, double *dydx, int n, double *x, double htry, double eps, + double *yscal, double *hdid, double *hnext) +/* Fifth-order Runge-Kutta step with monitoring of local truncation error to ensure accuracy and + adjust stepsize. Input are the dependent variable vector y[1..n] and its derivative dydx[1..n] + at the starting value of the independent variable x. Also input are the stepsize to be attempted + htry, the required accuracy eps, and the vector yscal[1..n] against which the error is + scaled. On output, y and x are replaced by their new values, hdid is the stepsize that was + actuallyac complished, and hnext is the estimated next stepsize. derivs is the user-supplied + routine that computes the right-hand side derivatives. */ +{ + int i; + double errmax,h,htemp,xnew,*yerr,*ytemp; + yerr=new double[n]; + ytemp=new double[n]; + + h=htry; // Set stepsize to the initial trial value. + for (;;) { + rkck(y,dydx,n,*x,h,ytemp,yerr); // Take a step. + errmax=0.0; //Evaluate accuracy. + for (i=0;i= 0.0 ? FMAX(htemp,0.1*h) : FMIN(htemp,0.1*h)); + //No more than a factor of 10. + xnew=(*x)+h; + if (xnew == *x) MyWarning::error("stepsize underflow in rkqs, with h = %f and htry = %f",h, htry); + } + if (errmax > Errcon) { + *hnext=Safety*h*pow(errmax,PGrow); + //std::cerr << "hnext = " << *hnext << std::endl; + } + else *hnext=5.0*h; //No more than a factor of 5 increase. + *x += (*hdid=h); + for (i=0;i 0) xsav=x-dxsav*2.0; //Assures storage of first step. + for (nstp=0;nstp 0 && kount < kmax-1 && fabs(x-xsav) > fabs(dxsav)) { + xp[kount]=x; //Store intermediate results. + for (i=0;i 0.0) h=x2-x;// If stepsize can overshoot, decrease. + + rkqs(y,dydx,nvar,&x,h,eps,yscal,&hdid,&hnext); + if (hdid == h) ++(*nok); else ++(*nbad); + if ((x-x2)*(x2-x1) >= 0.0) { //Are we done? + for (i=0;i. + * + * Copyright 2010 Roeland Merks. + * + */ + + +#ifndef _RUNGEKUTTA_H_ +#define _RUNGEKUTTA_H_ + +class RungeKutta { + + public: + RungeKutta(void) { + kmax=kount=0; + dxsav=0.; + xp=0; + yp=0; + } + virtual ~RungeKutta() {} + + void odeint(double ystart[], int nvar, double x1, double x2, double eps, double h1, + double hmin, int *nok, int *nbad); + + + protected: + // implement "derivs" in a derived class + virtual void derivs(double x, double *y, double *dxdy) = 0; + int kmax,kount; + double *xp,**yp,dxsav; + + private: + void rkqs(double *y, double *dydx, int n, double *x, double htry, double eps, + double *yscal, double *hdid, double *hnext); + + void rkck(double *y, double *dydx, int n, double x, double h, double yout[], + double *yerr); + + static const double Safety; + static const double PGrow; + static const double Pshrnk; + static const double Errcon; + static const double Maxstp; + static const double Tiny; + + +}; +#endif diff --git a/src/simitembase.cpp b/src/simitembase.cpp new file mode 100644 --- /dev/null +++ b/src/simitembase.cpp @@ -0,0 +1,37 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include +#include "simitembase.h" + +static const std::string _module_id("$Id$"); + +SimItemBase::SimItemBase( void *v, QGraphicsScene *canvas ) { + obj=v; +}; + +SimItemBase::~SimItemBase(void) {}; + + +void SimItemBase::userMove(double dx, double dy) { + +}; diff --git a/src/simitembase.h b/src/simitembase.h new file mode 100644 --- /dev/null +++ b/src/simitembase.h @@ -0,0 +1,67 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + + +#ifndef _SIMITEMBASE_H_ +#define _SIMITEMBASE_H_ + +#include +#include +#include +class Vector; + + +template +TYPE class_cast(void * symbol) +{ + assert(sizeof(void *) == sizeof(TYPE)); + union + { + void * symbol; + TYPE unknownclass; + } cast; + + cast.symbol = symbol; + return cast.unknownclass; +} + +class SimItemBase +{ +public: + SimItemBase( void *v, QGraphicsScene *canvas ); + virtual ~SimItemBase(void); + virtual void userMove(double dx, double dy); + + + protected: + + // I know which simulation object I represent, so if I am moved around + // the canvas, the real object can be moved as well + + // (both Cell and Node have Vector as base class...) + // Not proper design... sorry. + + void *obj; + }; + +#endif diff --git a/src/simplugin.cpp b/src/simplugin.cpp new file mode 100644 --- /dev/null +++ b/src/simplugin.cpp @@ -0,0 +1,35 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#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; +} + diff --git a/src/simplugin.h b/src/simplugin.h new file mode 100644 --- /dev/null +++ b/src/simplugin.h @@ -0,0 +1,82 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + + +#ifndef _SIMPLUGIN_H_ +#define _SIMPLUGIN_H_ + +#include +#include +#include "cellbase.h" +#include "wallbase.h" + +class Parameter; + +#include +#include + + +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; + + // 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.1") +Q_DECLARE_METATYPE(SimPluginInterface *) + + +#endif \ No newline at end of file diff --git a/src/sqr.h b/src/sqr.h new file mode 100644 --- /dev/null +++ b/src/sqr.h @@ -0,0 +1,60 @@ +/* + * + * $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 . + * + * From "Numerical recipes in C" + * + * Copyright 2010 Roeland Merks. + * + */ + +#ifndef _SQR_H_ +#define _SQR_H_ + +/* static float sqrarg; +#define SQR(a) ((sqrarg=(a)) == 0.0 ? 0.0 : sqrarg*sqrarg) + +static double dsqrarg; +#define DSQR(a) ((dsqrarg=(a)) == 0.0 ? 0.0 : dsqrarg*dsqrarg) +*/ + + +// redefine these macros as inline functions, to prevent scary +// behavior. (According to compiler warnings, the above macros from +// Numerical Recipes in C are officially undefined...) +// +// However, they seem to work, but it seems safer to redefine them. +// Inline functions will behave like macros anyhow. +inline double DSQR( double a ) { + + if (a == 0.0) { + return 0.0; + } else { + return a*a; + } + +} + +inline float SQR( float a ) { + if (a == 0.0) { + return 0.0; + } else { + return a*a; + } +} +#endif diff --git a/src/tiny.h b/src/tiny.h new file mode 100644 --- /dev/null +++ b/src/tiny.h @@ -0,0 +1,30 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + + +#ifndef _TINY_H_ +#define _TINY_H_ + +#define TINY 1e-5 + +#endif diff --git a/src/transporterdialog.cpp b/src/transporterdialog.cpp new file mode 100644 --- /dev/null +++ b/src/transporterdialog.cpp @@ -0,0 +1,106 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include +#include +#include "canvas.h" +#include "transporterdialog.h" + +static const std::string _module_id("$Id$"); + +TransporterDialog::TransporterDialog(Wall *w, CellBase *c, int wn, QWidget *parent) + : wall(w), cell(c), wall_num(wn), QDialog(parent) +{ + int frameStyle = QFrame::Plain | QFrame::NoFrame; + QPushButton *ok = new QPushButton(tr("Ok")); + QPushButton *cancel = new QPushButton(tr("Cancel")); + QGridLayout *t_grid = new QGridLayout; // transporter grid + + // connect the slots + connect(ok, SIGNAL(clicked()), this, SLOT(setTransporterValues())); + connect(cancel, SIGNAL(clicked()), this, SLOT(close())); + + // compose a label for the dialog box + std::stringstream label_text; + label_text << "C("<< wall->C1()->Index() << "," << wall->C2()->Index() << "), N(" << wall->N1()->Index() << "," << wall->N2()->Index() << ")"; + + // retrieve the current transporters + QVector transporters; // transporter vector + ntransporters = cell->NChem(); // size of transporter vector + for (int i=0; iTransporters1(i) : wall->Transporters2(i)); // dependent on the wall number of course. + } + + // iterate over the vector of transporter values making a label/edit line for each + for (int i=0; isetFrameStyle(frameStyle); + label->setText(QString("%1").arg(i+1)); // cardinal numbering + + // line editor + QLineEdit *editor = new QLineEdit(); + editors << editor; + editor->setValidator(new QDoubleValidator(editor)); // validator settings + + // assign the current transporter value + QString n; + editor->setText(n.setNum(transporters[i])); + + // insert the label and editor into the transporter grid + t_grid->addWidget(label, i, 0); + t_grid->addWidget(editor, i, 1); + } + + // add OK and Cancel buttons + QGridLayout *b_grid = new QGridLayout; // button grid + b_grid->addWidget(ok, 0, 0); + b_grid->addWidget(cancel, 0, 1); + + // add both transporter and button grids to the box layout widget + QVBoxLayout *layout = new QVBoxLayout; + layout->addLayout(t_grid); + layout->addLayout(b_grid); + setLayout(layout); + + setWindowTitle(tr(label_text.str().c_str())); +} + +void TransporterDialog::setTransporterValues(){ + + // iterate over the editor widgets soliciting their values and setting the wall's transporters accordingly. + for (int i=0; itext().toDouble() << endl; + #endif + if (wall_num == 1) + wall->setTransporters1(i, editors[i]->text().toDouble()); + else + wall->setTransporters2(i, editors[i]->text().toDouble()); + } + editors.resize(0); + close(); +} + +// finis + diff --git a/src/transporterdialog.h b/src/transporterdialog.h new file mode 100644 --- /dev/null +++ b/src/transporterdialog.h @@ -0,0 +1,57 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + + +#ifndef _TRANSPORTERDIALOG_H_ +#define _TRANSPORTERDIALOG_H_ + +#include +#include +#include + +class QLabel; +class QLineEdit; + +class Wall; +class CellBase; + +class TransporterDialog : public QDialog +{ + Q_OBJECT + + public: + TransporterDialog(Wall *w, CellBase *c, int wn, QWidget *parent = 0); + + private slots: + void setTransporterValues(); + + private: + Wall *wall; + CellBase *cell; + int wall_num; + QLabel *label; + int ntransporters; + QVector editors; +}; + +#endif diff --git a/src/vector.cpp b/src/vector.cpp new file mode 100644 --- /dev/null +++ b/src/vector.cpp @@ -0,0 +1,341 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include +#include +#include +#include +//#include +#include +#include "sqr.h" +#include "pi.h" +#include "vector.h" +#include "tiny.h" + +static const std::string _module_id("$Id$"); + +void Vector::operator=(const Vector &source) { + + // assignment + + // don't assign to self + if (this==&source) return; + + x=source.x; + y=source.y; + z=source.z; + +} + + + + + +ostream &Vector::print(ostream &os) const { + os << "(" << x << ", " << y << ", " << z << ")"; + return os; +} + +ostream &operator<<(ostream &os, const Vector &v) { + v.print(os); + return os; +} + + +Vector Vector::operator+(const Vector &v) const { + + Vector result; + result.x=x+v.x; + result.y=y+v.y; + result.z=z+v.z; + + return result; + +} + + +Vector& Vector::operator-=(const Vector &v) { + + x-=v.x; + y-=v.y; + z-=v.z; + + return *this; + +} + +Vector Vector::operator/(const double divisor) const { + + + Vector result; + + result.x=x/divisor; + result.y=y/divisor; + result.z=z/divisor; + + return result; + + +} + + +Vector Vector::operator*(const double multiplier) const { + + Vector result; + + result.x=x*multiplier; + result.y=y*multiplier; + result.z=z*multiplier; + + return result; + + +} + + +Vector operator*(const double multiplier, const Vector &v) { + + Vector result; + + result.x=v.x*multiplier; + result.y=v.y*multiplier; + result.z=v.z*multiplier; + + return result; + +} + +Vector &Vector::operator/=(const double divisor) { + x/=divisor; + y/=divisor; + z/=divisor; + + return *this; + +} + +Vector &Vector::operator*=(const double multiplier) { + + x*=multiplier; + y*=multiplier; + z*=multiplier; + + return *this; + +} + +Vector Vector::operator*(const Vector &v) const { + + // cross product ("uitproduct") + Vector result; + + result.x=y*v.z-z*v.y; + result.y=z*v.x-x*v.z; + result.z=x*v.y-y*v.x; + + return result; + + +} + + +double InnerProduct(const Vector &v1, const Vector &v2) { + + // Inner product ("inproduct") + double result; + result=v1.x*v2.x+v1.y*v2.y+v1.z*v2.z; + return result; + +} + +double Vector::Angle(const Vector &v) const { + + // angle between this vector and another vector + + // angle is within range of [0,pi] radians + + // angle is arccosine of the inner product over the product of the norms of the vectors + + double cos_angle=InnerProduct(*this,v)/(Norm()*v.Norm()); + + // check for computational inaccuracies + if (cos_angle<=-1) + return Pi; + + if (cos_angle>=1) + return 0.; + + double angle=acos(cos_angle); + + return angle; + +} + +double Vector::SignedAngle(const Vector &v) const { + + // angle between this vector and another vector + + // angle is within range of [-pi,pi] radians + + // angle is arccosine of the inner product over the product of the norms of the vectors + + double cos_angle=InnerProduct(*this,v)/(Norm()*v.Norm()); + + // check for computational inaccuracies + if (cos_angle<=-1) + return Pi; + + if (cos_angle>=1) + return 0.; + + double angle=acos(cos_angle); + + double sign = (InnerProduct ( Perp2D(), v ) )>0.?1.:-1.; + return angle * sign; + +} + + +bool Vector::operator==(const Vector &v) const { + + // "sloppy equal" + if ((fabs(x-v.x)0.) { // if the norm is 0, don't normalise + // (otherwise division by zero occurs) + + (*this)/=norm; + } + +} + +Vector Vector::Normalised(void) const { + + double norm; + norm=Norm(); // Absolute value; + + if (norm>0.) { // if the norm is 0, don't normalise + // (otherwise division by zero occurs) + return (*this)/norm; + } else { + return *this; + } + +} + +bool Vector::SameDirP(const Vector &v) { + + // return true if the two (parallel) vectors point in the same direction + // if (x*v.x>=0 && y*v.y>=0 && z*v.z>=0) + double angle=Angle(v); + + if (angle<(Pi/2)) + return true; + else + return false; + +} + +double Vector::Max(void) { + + // Find maximum value of vector + double max; + + max=x > y ? x : y; + max=max > z ? max : z; + + return max; +} + +double Vector::Min(void) { + + // Find minimum value of vector + double min; + + min=x < y ? x : y; + min=min < z ? min : z; + + return min; +} + +// test + +#ifdef TEST + +void main() { + + Vector v(1.,2.,3.); + + Vector d; + + d=5*v; + +// cerr << d << "\n"; + + d=v*5; + +// cerr << d << "\n"; + + d=v/5; + +// cerr << d << "\n"; + +// cerr << d.Norm() << "\n"; + + d.Normalise(); + +// cerr << d << " " << d.Norm() << "\n"; + + +} + +#endif diff --git a/src/vector.h b/src/vector.h new file mode 100644 --- /dev/null +++ b/src/vector.h @@ -0,0 +1,162 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + + +#ifndef _VECTOR_H_ +#define _VECTOR_H_ + +#include +#include +#include "sqr.h" +#ifdef QTGRAPHICS + #include +#endif + +// three dimensional vector +using namespace std; + +class Vector { + +public: + inline Vector(const double cx=0,const double cy=0,const double cz=0) { + x=cx; + y=cy; + z=cz; + }; + explicit inline Vector(const double m[3]) { + x=m[0]; + y=m[1]; + z=m[2]; + } + + // should be taken over by default arguments of Vector constructor + /* inline Vector(void) { */ + /* x=0.; */ + /* y=0.; */ + /* z=0.; */ + /* } */ + + inline Vector(const Vector &source) { + + // copy constructor + + x=source.x; + y=source.y; + z=source.z; + + } + // type conversion from and to QPointF (2D vector class from Qt library; we throw away "z" here!!) + #ifdef QTGRAPHICS + inline operator QPointF() const { + return QPointF( x, y ); + } + inline Vector(const QPointF &p) { + x = p.x(); + y = p.y(); + z = 0.; + } + #endif + virtual ~Vector() { + + }; + + virtual ostream& print(ostream &os) const; + void operator=(const Vector &source); // assignment operator + void operator=(const double &s) { + x=s; + y=s; + z=s; + } + Vector operator+(const Vector &v) const; // addition + inline Vector operator-(const Vector &v) const { + + Vector result; + + result.x=x-v.x; + result.y=y-v.y; + result.z=z-v.z; + + return result; + + } + Vector &operator-=(const Vector &v); + + inline Vector &operator+=(const Vector &v) { + + x+=v.x; + y+=v.y; + z+=v.z; + + return *this; + + } + + Vector operator/(const double divisor) const; // division by a double + Vector operator*(const double multiplier) const; // multiply by a scalar (double) + Vector &operator/=(const double divisor); + Vector &operator*=(const double multiplier); + Vector operator*(const Vector &v) const; // cross product + bool operator==(const Vector &v) const; // comparison + bool operator< (const Vector &v) const; // order x,y,z + //double Norm(void) const; // gives the "norm" (| v |) of v + inline double Norm(void) const { + + return sqrt(DSQR(x)+DSQR(y)+DSQR(z)); + + } + + // Quick and dirty Norm (i.e. Manhattan distance) + // (e.g. useful if we want to see if the vec is (0,0,0); + inline double ManhattanNorm(void) const { + return x+y+z; + } + + double SqrNorm(void) const; // gives the square of |v| + void Normalise(void); // normalise myself + Vector Normalised(void) const; // return me normalised + double Angle(const Vector &angle) const; // gives the angle between me and another vector + double SignedAngle(const Vector &v) const; + bool SameDirP(const Vector &v); + double Max(void); // Find max, resp. min value of vector + double Min(void); + Vector Perp2D(void) const { + return Vector(y,-x,0); + } + // data members + double x,y,z; + + void Dump(ostream &os) const { + os << x << " " << y << " " << z << " "; + } + void ReadDump(istream &is) { + is >> x >> y >> z; + } +private: + +}; + +ostream &operator<<(ostream &os, const Vector &v); +Vector operator*(const double multiplier, const Vector &v); +double InnerProduct(const Vector &v1, const Vector &v2); + +#endif diff --git a/src/wall.cpp b/src/wall.cpp new file mode 100644 --- /dev/null +++ b/src/wall.cpp @@ -0,0 +1,237 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include "wall.h" +#include "cell.h" +#include "wallitem.h" +#include "node.h" +#include "apoplastitem.h" +#include +#include + +static const std::string _module_id("$Id$"); + +/*! Check if this Wall still belongs to Cell a, otherwise gives it to Cell b. + \return true: Wall was corrected + \return false: Wall was still correct + */ +bool Wall::CorrectWall( void ) { + + + // collect all cells to which my nodes are connected on one list + list owners; + transform(n1->owners.begin(),n1->owners.end(), back_inserter(owners), mem_fun_ref(&Neighbor::getCell)); + transform(n2->owners.begin(),n2->owners.end(), back_inserter(owners), mem_fun_ref(&Neighbor::getCell)); + + // get the list of duplicates + list wall_owners; + owners.sort(); + //transform(owners.begin(), owners.end(), ostream_iterator(cerr, ", "), mem_fun(&Cell::Index)); + duplicates_copy( owners.begin(), owners.end(), back_inserter(wall_owners) ); + + // duplicates are the cells to which the Wall belongs + /* cerr << "Wall belongs to Cells: "; + transform(wall_owners.begin(), wall_owners.end(), ostream_iterator(cerr, ", "), mem_fun(&Cell::Index)); + cerr << endl; + */ + + //list::iterator f = adjacent_find (++e,owners.end()); + + // For the first division, wall finds three "owners", including the boundary_polygon. + // Remove that one from the list. + //cerr << "wall_owners.size() = " << wall_owners.size() << endl; + if (wall_owners.size() == 3 && nwalls==1 /* bug-fix 22/10/2007; confine this condition to first division only */) { + + cerr << "nwalls = " << nwalls << endl; + // special case for first cleavage + + // find boundary polygon in the wall owners list + list::iterator bpit = find_if ( + wall_owners.begin(), wall_owners.end(), + mem_fun( &CellBase::BoundaryPolP ) + ); + + if (bpit!=wall_owners.end()) { + + // add a Wall with the boundary_polygon to each cell + Wall *bp_wall1 = new Wall( n2, n1, c1, (*bpit) ); + bp_wall1->CopyWallContents(*this); + ((Cell *)c1)->AddWall( bp_wall1); + ((Cell *)(*bpit))->AddWall(bp_wall1); + + Wall *bp_wall2 = new Wall( n1, n2, c2, (*bpit) ); + bp_wall2->CopyWallContents(*this); + ((Cell *)c2)->AddWall( bp_wall2); + ((Cell *)(*bpit))->AddWall(bp_wall2); + + wall_owners.erase(bpit); + + }else { + + cerr << "Wall::CorrectWall says: Wall has three owners, but none of them is the BoundaryPolygon. I have no clue what to do with this case... Sorry!\n"; + cerr << "Wall: " << *this << endl; + cerr << "Owners are: "; + transform(wall_owners.begin(), wall_owners.end(), ostream_iterator(cerr, " "), mem_fun (&CellBase::Index) ); + cerr << endl; + cerr << "Owners node " << n1->Index() << ": "; + for (list::iterator i = n1->owners.begin(); + i!=n1->owners.end(); + i++) { + cerr << i->getCell()->Index() << " "; + } + cerr << endl; + cerr << "Owners node " << n2->Index() << ": "; + + for (list::iterator i = n2->owners.begin(); + i!=n2->owners.end(); + i++) { + cerr << i->getCell()->Index() << " "; + } + cerr << endl; + std::exit(1); + } + + } + + if (wall_owners.size() == 1) { + cerr << "Corner point. Special case.\n"; + } + + CellBase *cell1 = wall_owners.front(); + CellBase *cell2 = wall_owners.back(); + + if ( (c1 == cell1 && c2==cell2) || (c1 == cell2 && c2 == cell1) ) { + + return false; + } + + if ( c1 == cell1 ) { + //cerr << "Block 1\n"; + ((Cell *)c2) -> RemoveWall(this); + c2 = cell2; + ((Cell *)c2) -> AddWall(this); + } else { + if ( c1 == cell2 ) { + // cerr << "Block 2\n"; + ((Cell *)c2) -> RemoveWall(this); + c2 = cell1; + ((Cell *)c2) -> AddWall(this); + } else { + if ( c2 == cell1) { + ((Cell *)c1)->RemoveWall(this); + c1 = cell2; + ((Cell *)c1) -> AddWall(this); + // cerr << "Block 3\n"; + } else { + if ( c2 == cell2) { + ((Cell *)c1)->RemoveWall(this); + c1 = cell1; + ((Cell *)c1)->AddWall(this); + // cerr << "Block 3\n"; + } else { + cerr << "Warning, cell wall was not corrected.\n"; + return false; + } + } + } + } + + return true; + + + + +} + +void Wall::Draw(QGraphicsScene *c) { + + WallItem *wi1 = new WallItem(this, 1, c); + WallItem *wi2 = new WallItem(this, 2, c); + wi1->show(); + wi2->show(); + +} + +void Wall::DrawApoplast(QGraphicsScene *c) { + ApoplastItem *apo = new ApoplastItem(this, c); + apo->show(); + +} + +void Wall::ShowStructure(QGraphicsScene *c) { + + Vector offset = Cell::Offset(); + double factor = Cell::Factor(); + + Vector startpoint ( ((offset.x+n1->x)*factor),((offset.y+n1->y)*factor)), + endpoint (((offset.x+n2->x)*factor),((offset.y+n2->y)*factor)); + + Vector linevec = endpoint - startpoint; + Vector midline = startpoint + linevec/2.; + Vector perpvec = linevec.Normalised().Perp2D(); + Vector textpos1 = midline + 100 * perpvec; + Vector textpos2 = midline - 100 * perpvec; + + QGraphicsLineItem *line = new QGraphicsLineItem(0,c); + + line->setPen( QPen(QColor(par.arrowcolor),2) ); + line->setLine(startpoint.x, startpoint.y, endpoint.x, endpoint.y ); + line->setZValue(10); + line->show(); + + QGraphicsSimpleTextItem *text1 = new QGraphicsSimpleTextItem( QString("%1").arg(c2->Index()),0,c); + QGraphicsSimpleTextItem *text2 = new QGraphicsSimpleTextItem( QString("%1").arg(c1->Index()),0,c); + + text1 -> setPos( textpos1.x, textpos1.y ); + text2 -> setPos( textpos2.x, textpos2.y ); + text1->setZValue(20); text2->setZValue(20); + + text1->setFont( QFont( "Helvetica", par.nodenumsize, QFont::Bold) ); + text2->setFont( QFont( "Helvetica", par.nodenumsize, QFont::Bold) ); + + text1->setPen ( QColor(par.textcolor) ); + text2->setPen ( text1->pen() ); + + //text1->setTextFlags(Qt::AlignCenter); + //text2->setTextFlags(Qt::AlignCenter); + text1->show(); text2->show(); + +} +string Wall::WallTypetoStr(const WallType &wt) const { + + if (wt == Normal) { + return string("normal"); + } + + if (wt == AuxSource) { + return string("aux_source"); + } + + if (wt == AuxSink) { + return string("aux_sink"); + } + + return string(""); +} + + + diff --git a/src/wall.h b/src/wall.h new file mode 100644 --- /dev/null +++ b/src/wall.h @@ -0,0 +1,63 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + + +#ifndef _WALL_H_ +#define _WALL_H_ + +#include "wallbase.h" +#include +#include + +#include + +class Wall : public WallBase { + +public: + Wall(Node *sn1, Node *sn2, CellBase *sc1, CellBase *sc2) : WallBase(sn1, sn2, sc1, sc2) {} + + + void XMLAdd(xmlNodePtr parent_node) const; + bool CorrectWall(void); + + + // Graphics: + //! Visualize transport protein concentrations + void Draw(QGraphicsScene *c); + + //! Visualize contents of the apoplast + void DrawApoplast(QGraphicsScene *c); + /*! \brief Visualize the structure of the wall (Cell ID's etc.). + Used for debugging purposes. + */ + void ShowStructure(QGraphicsScene *c); + +private: + string WallTypetoStr(const WallType &wt) const; + + + + +}; + +#endif diff --git a/src/wallbase.cpp b/src/wallbase.cpp new file mode 100644 --- /dev/null +++ b/src/wallbase.cpp @@ -0,0 +1,309 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include "wall.h" +#include "wallbase.h" +#include "node.h" +#include "mesh.h" +#include "parameter.h" +#include +#include +#include "warning.h" + +#ifdef QTGRAPHICS +#include +#include +#include "wallitem.h" +#include "apoplastitem.h" +#endif + +static const std::string _module_id("$Id$"); + +int WallBase::nwalls=0; + +ostream &WallBase::print(ostream &os) const { + os << "{ " << n1->Index() << "->" << n2->Index() + << ", " << c1->Index() << " | " << c2->Index() << "} "; + return os; +} + +ostream &operator<<(ostream &os, const WallBase &w) { + w.print(os); + return os; +} + + +WallBase::WallBase(Node *sn1, Node *sn2, CellBase *sc1, CellBase *sc2) { + + if (sc1==sc2) { + cerr << "Attempting to build a wall between identical cells: " << sc1->Index() << endl; + } + + c1 = sc1; + c2 = sc2; + + n1 = sn1; + n2 = sn2; + + transporters1 = new double[CellBase::NChem()]; + transporters2 = new double[CellBase::NChem()]; + new_transporters1 = new double[CellBase::NChem()]; + new_transporters2 = new double[CellBase::NChem()]; + + for (int i=0;itransporters1[i]; + src->transporters1[i]=transporters1[i]; + transporters1[i]=tmp; + + } + if (transporters2) { + double tmp; + tmp=src->transporters2[i]; + src->transporters2[i]=transporters2[i]; + transporters2[i]=tmp; + } + if (new_transporters1) { + double tmp; + tmp=src->new_transporters1[i]; + src->new_transporters1[i]=new_transporters1[i]; + new_transporters1[i]=tmp; + } + if (new_transporters2) { + double tmp; + tmp=src->new_transporters2[i]; + src->new_transporters2[i]=new_transporters2[i]; + new_transporters2[i]=tmp; + } + + if (apoplast) { + double tmp; + tmp=src->apoplast[i]; + src->apoplast[i]=apoplast[i]; + apoplast[i]=tmp; + } + } + bool tmp_bool; + tmp_bool = src->dead; + src->dead=dead; + dead = tmp_bool; + + WallType tmp_wall_type; + tmp_wall_type = src->wall_type; + src->wall_type = wall_type; + wall_type = tmp_wall_type; + +} + +bool WallBase::SAM_P(void) { + + return N1()->sam || N2()->sam; + +} + +#include + +/* double WallBase::Length(void){ + return (*n2 - *n1).Norm(); + }*/ + +void WallBase::SetLength(void) { + + //static bool show_nodes = true; + + //double old_length = length; + // Step 1: find the path of nodes leading along the WallBase. + // A WallBase often represents a curved cell wall: we want the total + // length _along_ the wall here... + + // Locate first and second nodes of the edge in Cell's list of nodes + list::const_iterator first_node_edge = find(c1->nodes.begin(), c1->nodes.end(), n1); + list::const_iterator second_node_edge_plus_1 = ++find(c1->nodes.begin(), c1->nodes.end(), n2); + + // wrap around + if (second_node_edge_plus_1 == c1->nodes.end()) { + second_node_edge_plus_1 = c1->nodes.begin(); + } + + + length = 0.; + + // Now, walk to the second node of the edge in the list of nodes + stringstream deb_str; + + for (list::const_iterator n= + (++first_node_edge==c1->nodes.end()?c1->nodes.begin():first_node_edge); + n!=second_node_edge_plus_1; + (++n == c1->nodes.end()) ? (n=c1->nodes.begin()):n ) { + + /* if (n==c1->nodes.end()) { + + n=c1->nodes.begin(); // wrap around + }*/ + + list::const_iterator prev_n = n; + if (prev_n==c1->nodes.begin()) prev_n=c1->nodes.end(); + --prev_n; + + //cerr << "Node: " << (Vector)(**n) << endl; + + // Note that Node derives from a Vector, so we can do vector calculus as defined in vector.h + + + deb_str << "[ " << (*prev_n)->index << " to " << (*n)->index << "]"; + + length += (*(*prev_n) - *(*n)).Norm(); + + } + + /* if (length > 100) { + ostringstream warn; + warn << "Strange, length is " << length << "...: " << deb_str.str() << endl; + warn << "Strangeness in WallBase: " << *this << endl << endl << deb_str.str() << endl; + MyWarning::warning (warn.str().c_str()); + }*/ + + //cerr << deb_str .str() << ", length is " << length << endl; + //of << length << " " << ((*n2)-(*n1)).Norm() << endl; + + +} + +void WallBase::CorrectTransporters(double orig_length) { + + double length_factor = length / orig_length; + // cerr << "[ lf = " << length_factor << "]"; + //cerr << "Correcting amount of transporters on WallBase " << *this << ", length factor is " << orig_length << " / " << length << endl; + for (int ch=0;ch p2 +bool WallBase::IntersectsWithDivisionPlaneP(const Vector &p1, const Vector &p2) { + + // Algorithm of http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/ + double x1 = n1->x, y1 = n1->y; + double x2 = n2->x, y2 = n2->y; + double x3 = p1.x, y3 = p1.y; + double x4 = p2.x, y4 = p2.y; + + double ua = ( (x4 - x3) * (y1-y3) - (y4 - y3)*(x1-x3) ) / ( (y4 -y3) * (x2 -x1) - (x4-x3)*(y2-y1)); + // double ub = ( (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 -x3) ) / ( ( y4-y3) * (x2-x1) - (x4-x3)*(y2-y1)) ; + + // If ua is between 0 and 1, line p1 intersects the line segment + if ( ua >=0. && ua <=1.) return true; + else return false; + +} + + + diff --git a/src/wallbase.h b/src/wallbase.h new file mode 100644 --- /dev/null +++ b/src/wallbase.h @@ -0,0 +1,247 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + + + +#ifndef _WALLBASE_H_ +#define _WALLBASE_H_ + +#include +#include + + + +#include "vector.h" + + +class Node; +class CellBase; + +using namespace std; + +// warning, touches original sequence +template Out duplicates_copy(In first, In last, Out res) { + + In i = adjacent_find(first, last); + + while (i!=last) { + res++ = *i; + i = adjacent_find(++i, last); + } + + return res; +} + +/*! \class WallBase. A cell wall, which runs between cell "corner points", + and consists of wall elements. */ +class WallBase { + +protected: + friend class CellBase; + friend class Cell; + friend class Mesh; + //! Cells to which the wall belongs + CellBase *c1, *c2; + + //! A list of transporter protein concentrations associated with the wall + double *transporters1, *transporters2; + double *new_transporters1, *new_transporters2; + + bool IllegalP(void) { return c1 == c2; } + + //! The chemicals in the apoplast at this position + double *apoplast; + + //! Pointers to the wall's corner nodes + Node *n1, *n2; + + double length; + + double viz_flux; + + // bool aux_source; + + bool dead; + // disallow usage of empty constructor + WallBase(void) {} + + + enum WallType {Normal, AuxSource, AuxSink}; + static int nwalls; +protected: + int wall_index; + WallType wall_type; + + +public: + WallBase(Node *sn1, Node *sn2, CellBase *sc1, CellBase *sc2); + + // shallow copy + WallBase(const WallBase &src) { + c1 = src.c1; + c2 = src.c2; + transporters1 = src.transporters1; + transporters2 = src.transporters2; + new_transporters1 = src.new_transporters1; + new_transporters2 = src.new_transporters2; + apoplast = src.apoplast; + n1 = src.n1; + n2 = src.n2; + length = src.length; + viz_flux = src.viz_flux; + dead = src.dead; + wall_index = src.wall_index; + } + + inline int Index(void) const { return wall_index;} + inline bool DeadP(void) const { return dead; } + inline void Kill(void) { dead = true; } + // deep copying of chemicals and transporters + void CopyWallContents(const WallBase &src); + void SwapWallContents(WallBase *src); + bool is_wall_of_cell_p ( const CellBase *c ) { + return (c1==c || c2==c); + } + + inline CellBase *C1(void) const { return c1; } + inline CellBase *C2(void) const { return c2; } + inline Node *N1(void) const { return n1; } + inline Node *N2(void) const { return n2; } + inline void setTransporters1(int ch, double val) { transporters1[ch]=val; } + inline void setTransporters2(int ch, double val) { transporters2[ch]=val; } + inline void setNewTransporters1(int ch, double val) { new_transporters1[ch]=val; } + inline void setNewTransporters2(int ch, double val) { new_transporters2[ch]=val; } + inline double Transporters1(int ch) { return transporters1[ch]; } + inline double Transporters2(int ch) { return transporters2[ch]; } + + //! Return true if the WallBase adheres to the SAM (shoot apical meristem) + bool SAM_P(void); + // NB. Not checked. If cell is not found, it returns transporters2[ch]!! + //inline double getTransporter(int ch, Cell *c) { return c1 == c ? transporters1[ch] : transporters2[ch]; } + + //! Return true if the WallBase is a source of auxin + inline bool AuxinSource(void) const { return wall_type == AuxSource; } + inline bool AuxinSink(void) const { return wall_type == AuxSink; } + + inline void cycleWallType(void) { + + if (wall_type == Normal) + wall_type = AuxSource; + else + if (wall_type == AuxSource) + wall_type = AuxSink; + else + if (wall_type == AuxSink) { + wall_type = Normal; + } + } + // checked version. Use during debugging stage. + inline double getTransporter(CellBase *c, int ch) const { return c1 == c ? transporters1[ch] : ( c2 == c ? transporters2[ch] : throw "WallBase::getTransporter called with wrong cell") ; } + inline void setTransporter(CellBase *c, int ch, double val) { + if ( c1 == c ) { + transporters1[ch]=val; + } else + + if (c2 == c ) { + transporters2[ch]=val; + } else { + throw "WallBase::setTransporter called with wrong cell"; + } + } + inline double getApoplast(int ch) const { return apoplast[ch]; } + inline void setApoplast(int ch, double val) { apoplast[ch] = val; } + inline CellBase *getOtherCell(CellBase *c) { return c1 == c ? c2 : c1; } + Vector getInfluxVector(CellBase *c); + Vector getWallVector(CellBase *c); + void CorrectTransporters(double orig_length); + + inline double Length(void) { return length; } + //double Length(void); + + ostream &print(ostream &os) const; + + void SetLength(void); + void Transport(void); + + inline void setVizFlux( double value ) { viz_flux = value; } + + /*! Return vector containing the directional flux through the wall. + as defined by the value "viz_flux" which is supplied by the end-user + in the TransportFunction. + */ + Vector VizFlux(void); + bool IntersectsWithDivisionPlaneP(const Vector &p1, const Vector &p2); + + /*! Function to be defined in leaf.cpp */ + //void OnWallInsert(void); + + + + void SetTransToNewTrans( void ); + + // implemented in xmlwrite.cpp + //void XMLAdd(xmlNode *parent) const; +private: + +}; + +/* class TransportFunction { + + friend class Mesh; +public: + TransportFunction( void ); + virtual ~TransportFunction() { + //delete[] chem_change_c1; + //delete[] chem_change_c2; + } + virtual void operator()(WallBase *w, double *dchem_c1, double *dchem_c2) = 0;// { cerr << "This is base class TransportFunction.\n"; } + //virtual void operator()(WallBase *w, double *dchem_c1, double *dchem_c2, double *dapo) =0; +protected: + int wall_index; //double *chem_change_c1; + //double *chem_change_c2; +}; + +class CellReaction { + +public: + CellReaction(void) {}; + virtual ~CellReaction() {}; + + //! Implements the actual, intracellular chemical reactions. + virtual void operator()(CellBase * c, double *dchem ) = 0; + +}; + +class WallReaction { + +public: + WallReaction(void) {}; + virtual ~WallReaction() {}; + + //! Implements the actual, biochemical reactions occuring at the wall. + virtual void operator()(WallBase *, double *dw1, double *dw2) = 0; + +};*/ + +ostream &operator<<(ostream &os, const WallBase &w); +#endif diff --git a/src/wallitem.cpp b/src/wallitem.cpp new file mode 100644 --- /dev/null +++ b/src/wallitem.cpp @@ -0,0 +1,173 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include +#include +#include "canvas.h" +#include "wallitem.h" +#include "parameter.h" +#include "node.h" +#include "transporterdialog.h" + +static const std::string _module_id("$Id$"); + +WallItem::WallItem( Wall *w, int wallnumber, QGraphicsScene *canvas ) + : QGraphicsLineItem( 0, canvas ), SimItemBase( w, canvas){ + + wn = wallnumber; + + extern Parameter par; + //Wall *w=&getWall(); + + // Draw amount of "PIN1" + //double val = wn==1?(w->Transporters1(1)/par.Pi_tot)*255.:(w->Transporters2(1)/par.Pi_tot)*255.; + + /* if (val > 255 || val < 0 ) { + std::cerr << "val = " << val << endl; + if (wn == 1) { + std::cerr << "Transporters1(1) = " << w->Transporters1(1) << endl; + } else { + std::cerr << "Transporters2(1) = " << w->Transporters2(1) << endl; + } + + }*/ + + + setColor(); + + // line with "PIN1"is a bit inside the cell wall + Vector edgevec = (*(w->N2())) - (*(w->N1())); + Vector perp = edgevec.Normalised().Perp2D(); + + Vector offs = Cell::Offset(); + double factor = Cell::Factor(); + + Vector from = ( offs + *(w->N1()) ) * factor + (wn==1?-1:1) * par.outlinewidth * 0.5 * factor * perp; + Vector to = ( offs + *(w->N2()) ) *factor + (wn==1?-1:1) * par.outlinewidth * 0.5 * factor * perp; + + + Vector tmp_centroid = ( *(w->N2()) + *(w->N1()) )/2.; + Vector centroid = ( offs + tmp_centroid ) * factor; + + QString text=QString("%1").arg(w->Index()); + + /* if (0) { + QGraphicsSimpleTextItem *ctext = new QGraphicsSimpleTextItem ( text, 0, canvas ); + ctext->setPen( QPen(QColor("orange")) ); + ctext->setZValue(20); + ctext->setFont( QFont( "Helvetica", par.nodenumsize, QFont::Bold) ); + //ctext->setTextFlags(Qt::AlignCenter); + ctext->show(); + ctext ->setPos(centroid.x, + centroid.y ); + }*/ + + setLine(( from.x ), + ( from.y ), + ( to.x ), + ( to.y ) ); + setZValue(12); + } + + +void WallItem::setColor(void) { + + QColor diffcolor; + static const QColor purple("Purple"); + static const QColor blue("blue"); + + Wall *w=&getWall(); + double tr = wn==1?w->Transporters1(1):w->Transporters2(1); + CellBase *c = wn==1?w->C1():w->C2(); + diffcolor.setRgb( (int)( ( tr / (1 + tr) )*255.), 0, 0); + //diffcolor.setRgb( (int)( ((wn==1?w->Transporters1(1):w->Transporters2(1)))*255.), 0, 0); + /* if (wn==1) { + cerr << "Transporter: " << w->Transporters1(1) << endl; + } else { + cerr << "Transporter: " << w->Transporters2(1) << endl; + }*/ + + if (w->AuxinSource() && c->BoundaryPolP()) { + setPen( QPen(purple , par.outlinewidth) ); + } else { + if (w->AuxinSink() && c->BoundaryPolP()) { + setPen( QPen(blue, par.outlinewidth)); + } else { + setPen (QPen(diffcolor, par.outlinewidth) ); + } + } + +// if (c->BoundaryPolP()) { +// setPen(QPen(QColor("red"), 20)); +// } +} + +void WallItem::OnClick(QMouseEvent *e) { + + + Wall *w=&getWall(); + cerr << "Wall ID = " << w->Index() << ", this = " << w << "\n"; + cerr << "Wall item = " << this << "\n"; + cerr << "C1 = " << w->C1()->Index() << ", C2 = " << w->C2()->Index() << endl; + cerr << "N1 = " << w->N1()->Index() << ", N2 = " << w->N2()->Index() << endl; + //double tr = wn==1?w->Transporters1(1):w->Transporters2(1); + CellBase *c = wn==1?w->C1():w->C2(); + + TransporterDialog dialog(w, c, wn); + dialog.exec(); + + if (e->button() == Qt::RightButton) { + QString message; + if (wn==1) { + message=QString("Transporter 1 = %1, color = %2, length = %3\n").arg(w->Transporters1(1)).arg(pen().color().red()).arg(getWall().Length()); + } else { + message=QString("Transporter 2 = %1, color = %2, length = %3\n").arg(w->Transporters2(1)).arg(pen().color().red()).arg(getWall().Length()); + } + + //extern MainBase *main_window; + //((Main *)main_window)->UserMessage(message); + + } else { + if (e->button() == Qt::LeftButton) { + if (c->BoundaryPolP()) { + w->cycleWallType(); + } else { + if (e->modifiers() == Qt::ShiftModifier) { + wn==1?w->setTransporters1(1,0):w->setTransporters2(1,0); + + } else { + // set high amount of PIN1 + //cerr << "Setting PIN1\n"; + wn==1?w->setTransporters1(1,10):w->setTransporters2(1,10); + } + } + setColor(); + update(boundingRect()); + } + /* else { + if (e->button() == Qt::MidButton && (e->modifiers == Qt::ShiftModifier & Qt::ControlModifier)) { + // hidden feature for correcting + }*/ + + + } +} diff --git a/src/wallitem.h b/src/wallitem.h new file mode 100644 --- /dev/null +++ b/src/wallitem.h @@ -0,0 +1,52 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + + +#ifndef _WALLITEM_H_ +#define _WALLITEM_H_ + +#include +#include +#include +#include +#include +#include "simitembase.h" +#include "wall.h" + +//! Shows transporter concentrations at one side of the wall + +class WallItem : public QGraphicsLineItem, public SimItemBase +{ +public: + WallItem( Wall *n, int wallnumber, QGraphicsScene *canvas ); + virtual ~WallItem() {} + Wall &getWall(void) const { return *class_cast(obj); } + void OnClick(QMouseEvent *e); + //virtual void userMove(double dx, double dy); + //virtual void paint( QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0); + void setColor(void); + private: + int wn; +}; + +#endif diff --git a/src/warning.cpp b/src/warning.cpp new file mode 100644 --- /dev/null +++ b/src/warning.cpp @@ -0,0 +1,181 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include +#include +#include +#include +#include +#include "warning.h" +#ifdef QTGRAPHICS +#include +#include "canvas.h" +#endif +#include + +static const std::string _module_id("$Id$"); + +int Quiet=0; + +/* + * ERROR: scream and die quickly. + */ + +#ifndef QTGRAPHICS +void MyWarning::error(char * fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); /* invoke interface to printf */ + fprintf(stderr,"\n"); /* automatic \n by Roeland */ + fflush(stderr); /* drain std error buffer */ + va_end(ap); + exit(1); /* quit with error status */ +} +#else +//#include +#include "UniqueMessage.h" +void MyWarning::error(const char *fmt, ...) +{ + va_list ap; + if (Quiet) return; + char *message = new char[1000]; + + va_start(ap, fmt); + vsnprintf(message, 999, fmt, ap); /* invoke interface to printf */ + va_end(ap); + + QString qmess(message); + + bool batch = false; + + if (batch) { + // batch mode: print the message to stderr + fprintf(stderr, "Fatal error: %s\n",qmess.toStdString().c_str()); + exit(1); + } else { // issue a dialog box + /* Solve this with signal and slot...! */ + //extern MainBase *main_window; + //((Main *)main_window)->stopSimulation(); + + QMessageBox::critical( 0 , "Fatal error", qmess, QMessageBox::Abort, QMessageBox::NoButton, QMessageBox::NoButton ); + fprintf(stderr, "Error: %s\n",qmess.toStdString().c_str()); + QCoreApplication::exit(1); + std::exit(1); + } + delete[] message; + +} +#endif + + +/* + * EPRINTF: scream, but don't die yet. + * Roeland changed this to "warning" (21/10/1998) + * and added an automatic "\n" + */ + +#ifndef QTGRAPHICS +void MyWarning::warning(const char * fmt, ...) +{ + va_list ap; + if (Quiet) return; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); /* invoke interface to printf */ + fprintf(stderr,"\n"); /* automatic \n by Roeland */ + fflush(stderr); /* drain std error buffer */ + va_end(ap); +} +#else + +#include + + +void MyWarning::warning(const char *fmt, ...) +{ + va_list ap; + if (Quiet) return; + char *message = new char[1000]; + + va_start(ap, fmt); + vsnprintf(message, 999, fmt, ap); /* invoke interface to printf */ + va_end(ap); + + QString qmess(message); + + bool batch = false; + + if (batch) { + // batch mode: print the message to stderr + fprintf(stderr, "Warning: %s\n",qmess.toStdString().c_str()); + } else { // issue a dialog box + UniqueMessageBox msgBox( QString("Warning"), qmess ); + msgBox.exec(); + } + delete[] message; + +} +#endif + +/*! Issues a warning only once, + by comparing it to a list of warnings issued previously. */ +void MyWarning::unique_warning(const char *fmt, ...) { + + va_list ap; + if (Quiet) return; + char *message = new char[1000]; + + va_start(ap, fmt); + vsnprintf(message, 999, fmt, ap); /* invoke interface to printf */ + va_end(ap); + + static vector previous_warnings; + + // search warning in list + if (find( + previous_warnings.begin(),previous_warnings.end(), + string(message)) + ==previous_warnings.end()) { + + // new warning, store in list + previous_warnings.push_back(string(message)); + + // issue warning + warning("%s", message); + + } else { + // don't issue warning + return; + } +} + + +#ifdef TESTBED + +main(int argc, char * argv[]) +{ + eprintf("warning: foo=%f\tbar=%d\tfum=\"%s\"\n", 3.1415, 32768, "waldo"); + error("error: foo=%f\tbar=%d\tfum=\"%s\"\n", 3.1415, 32768, "waldo"); +} + +#endif diff --git a/src/warning.h b/src/warning.h new file mode 100644 --- /dev/null +++ b/src/warning.h @@ -0,0 +1,60 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + + +#ifndef _WARNING_H_ +#define _WARNING_H_ + +#define MEMORYCHECK(x) if ((x)==NULL) { fprintf(stderr, "Out of Memory error in "#x" \n"); exit(0); } + +#define UNIDENTIFIED 2353996 +//static int last_value=UNIDENTIFIED; +/*#define WATCH(x) if (last_value==UNIDENTIFIED) { last_value=x; +} else { if (x!=last_value) { fprintf(stderr,"WATCH value changed. Suspending execution. \n Interrupt within debugger to examine position in program.\n"); + last_value=x; + while(1); + } else { + last_value=x; + } + }*/ + + +/* These functions were a gift from Josh Barnes */ +/* I changed the name "eprintf" to "warning" */ + +#ifdef __cplusplus +extern "C" { +#endif + // namespace MyWarning needed because libxml2 also defines a warning token. + + namespace MyWarning { + void error(const char *, ...); + void warning(const char *, ...); + void unique_warning(const char *, ...); + } + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/xmlwrite.cpp b/src/xmlwrite.cpp new file mode 100644 --- /dev/null +++ b/src/xmlwrite.cpp @@ -0,0 +1,1603 @@ +/* + * + * 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 . + * + * Copyright 2010 Roeland Merks. + * + */ + +#include "mesh.h" +#include "parameter.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "xmlwrite.h" +#include "nodeset.h" +#include "warning.h" +#include "output.h" + +static const std::string _module_id("$Id$"); + +using namespace MyWarning; + +void ThrowStringStream(stringstream &s) { + + static char *msg = 0; + msg = (char *)malloc((1+s.str().length())*sizeof(char)); + strcpy(msg,s.str().c_str()); + throw(msg); +} + + +// Add this cell to the XML tree +void Cell::XMLAdd(xmlNodePtr cells_node) const { + + // Save the cell to a stream so we can reconstruct its state later + xmlNodePtr xmlcell = xmlNewChild(cells_node, NULL, BAD_CAST "cell",NULL); + XMLAddCore(xmlcell); + +} + +void BoundaryPolygon::XMLAdd(xmlNodePtr parent_node) const { + + xmlNodePtr xmlcell = xmlNewChild(parent_node, NULL, BAD_CAST "boundary_polygon",NULL); + XMLAddCore(xmlcell); + +} + +void NodeSet::XMLAdd(xmlNode *root) const { + + xmlNode *xmlnodeset = xmlNewChild(root, NULL, BAD_CAST "nodeset", NULL); + + for (list::const_iterator i=begin();i!=end();i++) { + { + ostringstream text; + xmlNodePtr node_xml = xmlNewChild(xmlnodeset, NULL, BAD_CAST "node", NULL); + text << (*i)->Index(); + xmlNewProp(node_xml, BAD_CAST "n", BAD_CAST text.str().c_str()); + } + } + +} + + + + +void Cell::XMLAddCore(xmlNodePtr xmlcell) const { + + // properties + + { + ostringstream text; + text << index; + xmlNewProp(xmlcell, BAD_CAST "index" , BAD_CAST text.str().c_str() ); + } + { + ostringstream text; + text << area; + xmlNewProp(xmlcell, BAD_CAST "area", BAD_CAST text.str().c_str()); + } + + { + ostringstream text; + text << target_area; + xmlNewProp(xmlcell, BAD_CAST "target_area", BAD_CAST text.str().c_str()); + } + + + { + ostringstream text; + text << target_length; + xmlNewProp(xmlcell, BAD_CAST "target_length", BAD_CAST text.str().c_str()); + } + + { + ostringstream text; + text << lambda_celllength; + xmlNewProp(xmlcell, BAD_CAST "lambda_celllength", BAD_CAST text.str().c_str()); + } + + { + ostringstream text; + text << stiffness; + xmlNewProp(xmlcell, BAD_CAST "stiffness", BAD_CAST text.str().c_str()); + } + + { + ostringstream text; + text << bool_name(fixed); + xmlNewProp(xmlcell, BAD_CAST "fixed", BAD_CAST text.str().c_str()); + } + + { + ostringstream text; + text << bool_name(pin_fixed); + xmlNewProp(xmlcell, BAD_CAST "pin_fixed", BAD_CAST text.str().c_str()); + } + + { + ostringstream text; + text << bool_name(at_boundary); + xmlNewProp(xmlcell, BAD_CAST "at_boundary", BAD_CAST text.str().c_str()); + } + + + { + ostringstream text; + text << bool_name(dead); + xmlNewProp(xmlcell, BAD_CAST "dead", BAD_CAST text.str().c_str()); + } + + { + ostringstream text; + text << bool_name(source); + xmlNewProp(xmlcell, BAD_CAST "source", BAD_CAST text.str().c_str()); + } + + + { + ostringstream text; + text << boundary_type_names[boundary]; + xmlNewProp(xmlcell, BAD_CAST "boundary", BAD_CAST text.str().c_str()); + } + + { + ostringstream text; + text << div_counter; + xmlNewProp(xmlcell, BAD_CAST "div_counter", BAD_CAST text.str().c_str()); + } + + { + ostringstream text; + text << cell_type; + xmlNewProp(xmlcell, BAD_CAST "cell_type", BAD_CAST text.str().c_str()); + } + + for (list::const_iterator i=nodes.begin();i!=nodes.end();i++) { + { + ostringstream text; + xmlNodePtr node_xml = xmlNewChild(xmlcell, NULL, BAD_CAST "node", NULL); + text << (*i)->Index(); + xmlNewProp(node_xml, BAD_CAST "n", BAD_CAST text.str().c_str()); + } + } + + for (list::const_iterator i=walls.begin();i!=walls.end();i++) { + { + ostringstream text; + xmlNodePtr wall_xml = xmlNewChild(xmlcell, NULL, BAD_CAST "wall", NULL); + //text <walls.begin(), m->walls.end(), *i ); + xmlNewProp(wall_xml, BAD_CAST "w", BAD_CAST text.str().c_str()); + } + } + + + + + xmlNodePtr chem_xml = xmlNewChild(xmlcell, NULL, BAD_CAST "chem", NULL); + { + ostringstream text; + text << NChem(); + xmlNewProp(chem_xml, BAD_CAST "n", BAD_CAST text.str().c_str()); + } + + for (int i=0;inode_sets.begin(),m->node_sets.end(),node_set); + xmlNewProp(xmlnode, BAD_CAST "nodeset", BAD_CAST text.str().c_str()); + } + + +} + +void Neighbor::XMLAdd(xmlNodePtr neighbors_node) const { + + xmlNodePtr xmlnode = xmlNewChild(neighbors_node, NULL, BAD_CAST "neighbor",NULL); + { + ostringstream text; + text << cell->Index(); + xmlNewProp(xmlnode, BAD_CAST "cell", BAD_CAST text.str().c_str()); + } + + { + ostringstream text; + text << nb1->Index(); + xmlNewProp(xmlnode, BAD_CAST "nb1", BAD_CAST text.str().c_str()); + } + + { + ostringstream text; + text << nb2->Index(); + xmlNewProp(xmlnode, BAD_CAST "nb2", BAD_CAST text.str().c_str()); + } + +} + + +// from example code on www.libxml.org: +xmlXPathObjectPtr +getnodeset (xmlDocPtr doc, xmlChar *xpath){ + + xmlXPathContextPtr context; + xmlXPathObjectPtr result; + + context = xmlXPathNewContext(doc); + if (context == NULL) { + //printf("Error in xmlXPathNewContext\n"); + return NULL; + } + result = xmlXPathEvalExpression(xpath, context); + xmlXPathFreeContext(context); + if (result == NULL) { + //printf("Error in xmlXPathEvalExpression\n"); + return NULL; + } + if(xmlXPathNodeSetIsEmpty(result->nodesetval)){ + xmlXPathFreeObject(result); + // printf("No result\n"); + return NULL; + } + return result; +} + + + +int Cell::XMLRead(xmlNode *cur) { + + xmlNode *n = cur->xmlChildrenNode; + + vector tmp_nodes; + + while(n!=NULL) { + if ((!xmlStrcmp(n->name, (const xmlChar *)"node"))) { + xmlChar *nc = xmlGetProp(n, BAD_CAST "n"); + + if (nc==0) { + unique_warning("Token \"n\" not found in xmlwrite.cpp at or around line no. 966"); + } + tmp_nodes.push_back(atoi( (char *)nc)); + xmlFree(nc); + + } + n = n->next; + } + + int nnodes = tmp_nodes.size(); + for (int i=0;iAddNodeToCell( this, + m->nodes[tmp_nodes[i]], + m->nodes[tmp_nodes[(nnodes+i-1)%nnodes]], + m->nodes[tmp_nodes[(i+1)%nnodes]] ); + + } + + n = cur->xmlChildrenNode; + while(n!=NULL) { + if ((!xmlStrcmp(n->name, (const xmlChar *)"chem"))) { + xmlChar *v_str = xmlGetProp(n, BAD_CAST "n"); + xmlNode *v_node = n->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 chemical values given for cell(s). Ignoring remaining values."; + //ThrowStringStream(text); + 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. 1002"); + } + double v = strtod( (char *)nc, 0 ); + chem[nv++]=v; + xmlFree(nc); + } + v_node = v_node->next; + } + } + n = n->next; + } + + // read cell properties + { + xmlChar *v_str = xmlGetProp(cur, BAD_CAST "area"); + + if (v_str==0) { + unique_warning("Token \"area\" not found in xmlwrite.cpp at or around line no. 1018"); + } + if (v_str != NULL) { + area = strtod( (char *)v_str, 0); + xmlFree(v_str); + } + } + + { + xmlChar *v_str = xmlGetProp(cur, BAD_CAST "target_area"); + + if (v_str==0) { + unique_warning("Token \"target_area\" not found in xmlwrite.cpp at or around line no. 1029"); + } + if (v_str != NULL) { + target_area = strtod( (char *)v_str, 0); + xmlFree(v_str); + } + } + + + { + xmlChar *v_str = xmlGetProp(cur, BAD_CAST "target_length"); + + if (v_str==0) { + unique_warning("Token \"target_length\" not found in xmlwrite.cpp at or around line no. 1041"); + } + if (v_str != NULL) { + target_length = strtod( (char *)v_str, 0); + xmlFree(v_str); + } + } + + { + xmlChar *v_str = xmlGetProp(cur, BAD_CAST "lambda_celllength"); + + if (v_str==0) { + unique_warning("Token \"lambda_celllength\" not found in xmlwrite.cpp at or around line no. 1052"); + } + if (v_str != NULL) { + lambda_celllength = strtod( (char *)v_str, 0); + xmlFree(v_str); + } + } + + { + xmlChar *v_str = xmlGetProp(cur, BAD_CAST "stiffness"); + + if (v_str==0) { + unique_warning("Token \"stiffness\" not found in xmlwrite.cpp at or around line no. 1063"); + } + if (v_str != NULL) { + stiffness = strtod( (char *)v_str, 0); + xmlFree(v_str); + } + } + + + { + xmlChar *v_str = xmlGetProp(cur, BAD_CAST "fixed"); + + if (v_str==0) { + unique_warning("Token \"fixed\" not found in xmlwrite.cpp at or around line no. 1075"); + } + if (v_str != NULL) { + fixed = strtobool( (char *)v_str); + xmlFree(v_str); + } + } + + { + xmlChar *v_str = xmlGetProp(cur, BAD_CAST "pin_fixed"); + + if (v_str==0) { + unique_warning("Token \"pin_fixed\" not found in xmlwrite.cpp at or around line no. 1086"); + } + if (v_str != NULL) { + pin_fixed = strtobool( (char *)v_str); + xmlFree(v_str); + } + } + + { + xmlChar *v_str = xmlGetProp(cur, BAD_CAST "at_boundary"); + + if (v_str==0) { + unique_warning("Token \"at_boundary\" not found in xmlwrite.cpp at or around line no. 1097"); + } + if (v_str != NULL) { + at_boundary = strtobool( (char *)v_str); + xmlFree(v_str); + } + } + + { + xmlChar *v_str = xmlGetProp(cur, BAD_CAST "dead"); + + if (v_str==0) { + unique_warning("Token \"dead\" not found in xmlwrite.cpp at or around line no. 1108"); + } + if (v_str != NULL) { + dead = strtobool( (char *)v_str); + xmlFree(v_str); + } + } + + { + xmlChar *v_str = xmlGetProp(cur, BAD_CAST "source"); + + if (v_str==0) { + unique_warning("Token \"source\" not found in xmlwrite.cpp at or around line no. 1119"); + } + if (v_str != NULL) { + source = strtobool( (char *)v_str); + xmlFree(v_str); + } + } + + { + xmlChar *v_str = xmlGetProp(cur, BAD_CAST "boundary"); + + if (v_str==0) { + unique_warning("Token \"boundary\" not found in xmlwrite.cpp at or around line no. 1130"); + } + if (v_str != NULL) { + for (int i=0;i<4;i++) { + if (!xmlStrcmp( v_str, (const xmlChar *)Cell::boundary_type_names[i])) { + boundary=(Cell::boundary_type)i; + break; + } + } + xmlFree(v_str); + } + } + + { + xmlChar *v_str = xmlGetProp(cur, BAD_CAST "div_counter"); + + if (v_str==0) { + unique_warning("Token \"div_counter\" not found in xmlwrite.cpp at or around line no. 1119"); + } + if (v_str != NULL) { + div_counter = atoi( (char *)v_str); + xmlFree(v_str); + } + } + + { + xmlChar *v_str = xmlGetProp(cur, BAD_CAST "cell_type"); + + if (v_str==0) { + unique_warning("Token \"cell_type\" not found in xmlwrite.cpp at or around line no. 1237"); + } + if (v_str != NULL) { + cell_type = atoi( (char *)v_str); + xmlFree(v_str); + } + } + + // Recalculate moments and area + SetIntegrals(); + return 0; + +} + +void NodeSet::XMLRead(xmlNode *root, Mesh *m) { + + xmlNode *n = root->xmlChildrenNode; + + vector tmp_nodes; + while(n!=NULL) { + if ((!xmlStrcmp(n->name, (const xmlChar *)"node"))) { + xmlChar *nc = xmlGetProp(n, BAD_CAST "n"); + + if (nc==0) { + unique_warning("Token \"n\" not found in xmlwrite.cpp at or around line no. 966"); + } + tmp_nodes.push_back(atoi( (char *)nc)); + xmlFree(nc); + } + n = n->next; + } + + int nnodes = tmp_nodes.size(); + for (int i=0;igetNode(tmp_nodes[i])) ); + } + +} + + + + +void Wall::XMLAdd(xmlNode *parent) const { + + // Save the node to a stream so we can reconstruct its state later + xmlNodePtr xmlwall = xmlNewChild(parent, NULL, BAD_CAST "wall",NULL); + + /* { + ostringstream text; + text << index; + xmlNewProp(xmlnode, BAD_CAST "index", BAD_CAST text.str().c_str()); + }*/ + + { + ostringstream text; + text << Index(); + xmlNewProp(xmlwall, BAD_CAST "index" , BAD_CAST text.str().c_str() ); + } + + { + ostringstream text; + text << c1->Index(); + xmlNewProp(xmlwall, BAD_CAST "c1", BAD_CAST text.str().c_str()); + } + + + { + ostringstream text; + text << c2->Index(); + xmlNewProp(xmlwall, BAD_CAST "c2", BAD_CAST text.str().c_str()); + } + + + { + ostringstream text; + text << n1->Index(); + xmlNewProp(xmlwall, BAD_CAST "n1", BAD_CAST text.str().c_str()); + } + + { + ostringstream text; + text << n2->Index(); + xmlNewProp(xmlwall, BAD_CAST "n2", BAD_CAST text.str().c_str()); + } + + { + ostringstream text; + text << length; + xmlNewProp(xmlwall, BAD_CAST "length", BAD_CAST text.str().c_str()); + } + + + { + ostringstream text; + text << viz_flux; + xmlNewProp(xmlwall, BAD_CAST "viz_flux", BAD_CAST text.str().c_str()); + } + + { + ostringstream text; + text << WallTypetoStr(wall_type); + xmlNewProp(xmlwall, BAD_CAST "wall_type", BAD_CAST text.str().c_str()); + } + + + xmlNodePtr tr1_xml = xmlNewChild(xmlwall, NULL, BAD_CAST "transporters1", NULL); + // { + // ostringstream text; + // text << Cell::nchem; + // xmlNewProp(tr1_xml, BAD_CAST "n", BAD_CAST text.str().c_str()); + // } + + if (transporters1) { + for (int i=0;i XMLIO::XMLReadValArray(xmlNode *cur) { + + vector result; + + xmlNode *valarray_node = cur->xmlChildrenNode; + while (valarray_node!=NULL) { + if (!xmlStrcmp(valarray_node->name, (const xmlChar *)"val")) { + xmlChar *vc = xmlGetProp(valarray_node, BAD_CAST "v"); + if (vc) { + result.push_back(strtod( (const char *)vc, 0)); + xmlFree(vc); + } + + } + valarray_node = valarray_node->next; + } + return result; +} + +void Mesh::XMLSave(const char *docname, xmlNode *options) const +{ + + // based on libxml2 example code "tree2.c" + + xmlDocPtr doc = NULL; /* document pointer */ + xmlNodePtr root_node = NULL;/* node pointers */ + //xmlDtdPtr dtd = NULL; /* DTD pointer */ + + // LIBXML_TEST_VERSION; + + /* + * Creates a new document, a node and set it as a root node + */ + doc = xmlNewDoc(BAD_CAST "1.0"); + root_node = xmlNewNode(NULL, BAD_CAST "leaf"); + xmlDocSetRootElement(doc, root_node); + + /* + * xmlNewProp() creates attributes, which is "attached" to an node. + * It returns xmlAttrPtr, which isn't used here. + */ + xmlNewProp(root_node, BAD_CAST "name", BAD_CAST docname); + + time_t t; + std::time(&t); + // asctime_r(localtime(&t),tstring); //Doesn't work for MinGW + char *tstring = strdup(asctime(localtime(&t))); // but this does + // replace "end of line character by '\0' + char *eol=strchr(tstring,'\n'); + if (eol!=NULL) + *eol='\0'; + + xmlNewProp(root_node, BAD_CAST "date", BAD_CAST tstring); + free(tstring); + + + QString simtime = QString("%1").arg(time); + xmlNewProp(root_node, BAD_CAST "simtime", BAD_CAST simtime.toStdString().c_str()); + /* + * Creates a DTD declaration. Isn't mandatory. + */ + //dtd = xmlCreateIntSubset(doc, BAD_CAST "root", NULL, BAD_CAST "tree2.dtd"); + par.XMLAdd(root_node); + XMLIO::XMLWriteLeafSourceCode(root_node); + XMLIO::XMLWriteReactionsCode(root_node); + + xmlNodePtr xmlnodes = xmlNewChild(root_node, NULL, BAD_CAST "nodes",NULL); + { ostringstream text; + text << NNodes(); + xmlNewProp(xmlnodes, BAD_CAST "n", BAD_CAST text.str().c_str()); + } + + { ostringstream text; + text << Node::target_length; + xmlNewProp(xmlnodes, BAD_CAST "target_length", BAD_CAST text.str().c_str()); + } + + + + for (vector::const_iterator i=nodes.begin(); + i!=nodes.end(); + i++) { + (*i)->XMLAdd(xmlnodes) ; + } + + + /* + * xmlNewChild() creates a new node, which is "attached" as child node + * of root_node node. + */ + xmlNodePtr xmlcells = xmlNewChild(root_node, NULL, BAD_CAST "cells",NULL); + { + ostringstream text; + text << NCells(); + xmlNewProp(xmlcells, BAD_CAST "n", BAD_CAST text.str().c_str()); + } + { + ostringstream text; + text << Cell::offset[0]; + xmlNewProp(xmlcells, BAD_CAST "offsetx", BAD_CAST text.str().c_str()); + } + + { + ostringstream text; + text << Cell::offset[1]; + xmlNewProp(xmlcells, BAD_CAST "offsety", BAD_CAST text.str().c_str()); + } + + { + ostringstream text; + text << Cell::factor; + xmlNewProp(xmlcells, BAD_CAST "magnification", BAD_CAST text.str().c_str()); + } + + { + ostringstream text; + text << cells.front()->BaseArea(); + xmlNewProp(xmlcells, BAD_CAST "base_area", BAD_CAST text.str().c_str()); + } + + { + ostringstream text; + text << Cell::NChem(); + xmlNewProp(xmlcells, BAD_CAST "nchem", BAD_CAST text.str().c_str()); + } + + for (vector::const_iterator i=cells.begin(); + i!=cells.end(); + i++) { + (*i)->XMLAdd(xmlcells) ; + } + + boundary_polygon->XMLAdd(xmlcells); + + xmlNodePtr xmlwalls = xmlNewChild(root_node, NULL, BAD_CAST "walls",NULL); + { + ostringstream text; + text << walls.size(); + xmlNewProp(xmlwalls, BAD_CAST "n", BAD_CAST text.str().c_str()); + } + + + for (list::const_iterator i=walls.begin(); + i!=walls.end(); + i++) { + (*i)->XMLAdd(xmlwalls) ; + } + + + xmlNodePtr xmlnodesets = xmlNewChild(root_node, NULL, BAD_CAST "nodesets",NULL); + { + ostringstream text; + text << node_sets.size(); + xmlNewProp(xmlnodesets, BAD_CAST "n", BAD_CAST text.str().c_str()); + } + + + for_each( node_sets.begin(), node_sets.end(), + bind2nd ( + mem_fun( &NodeSet::XMLAdd ), + xmlnodesets + ) ); + + + // Add option tree for interactive application + if (options) { + xmlAddChild(root_node, options); + } + + + + + /* + * Dumping document to stdio or file + */ + xmlSetDocCompressMode(doc,9); + xmlSaveFormatFileEnc(docname, doc, "UTF-8", 1); + + /*free the document */ + xmlFreeDoc(doc); + + /* + *Free the global variables that may + *have been allocated by the parser. + */ + xmlCleanupParser(); + + /* + * this is to debug memory for regression tests + */ + //xmlMemoryDump(); +} + + + +void Mesh::XMLReadSimtime(const xmlNode *a_node) { + + xmlNode *root_node; + root_node = (xmlNode *)a_node; + xmlNode *cur; + xmlChar *strsimtime = xmlGetProp(root_node, BAD_CAST "simtime"); + if (strsimtime) { + double simtime = strtod((const char *)strsimtime, 0); + time = simtime; + cerr << "Simtime = " << strsimtime << endl; + } else { + cerr << "No simtime found in file \n"; + time =0; + } +} + +void Mesh::XMLReadPars(const xmlNode * a_node) { + xmlNode *root_node; + root_node = (xmlNode *)a_node; + par.XMLRead(root_node); + Seed(par.rseed); + MakeDir(par.datadir); +} + +void Mesh::XMLReadGeometry(const xmlNode * a_node) +{ + xmlNode *root_node; + root_node = (xmlNode *)a_node; + xmlNode *cur; + + // allocate Nodes + cur = root_node->xmlChildrenNode; + while (cur!=NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"nodes"))){ + XMLReadNodes(cur); + } + cur=cur->next; + } + + cur = root_node->xmlChildrenNode; + while (cur!=NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"nodesets"))) { + XMLReadNodeSets(cur); + } + cur=cur->next; + } + + cur=root_node->xmlChildrenNode; + while (cur!=NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"nodes"))) { + XMLReadNodeSetsToNodes(cur); + } + cur = cur->next; + } + + //cur = root_node; + + // allocate Cells + cur = root_node; + + // allocate Cells + cur = cur->xmlChildrenNode; + while (cur!=NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"cells"))){ + xmlChar *offsetxc = xmlGetProp(cur, BAD_CAST "offsetx"); + xmlChar *offsetyc = xmlGetProp(cur, BAD_CAST "offsety"); + double ox = strtod((const char*)offsetxc, 0); + double oy = strtod((const char*)offsetyc, 0); + + Cell::setOffset(ox, oy); + xmlFree(offsetxc); + xmlFree(offsetyc); + + xmlChar *magnificationc = xmlGetProp(cur, BAD_CAST "magnification"); + Cell::SetMagnification(strtod((const char*)magnificationc, 0 )); + xmlFree(magnificationc); + + xmlChar *baseareac = xmlGetProp(cur, BAD_CAST "base_area"); + Cell::BaseArea()= strtod((const char *)baseareac, 0 ); + xmlFree(baseareac); + + + XMLReadCells(cur); + } + cur=cur->next; + } + + // allocate Walls (we need to have read the cells before constructing walls) + vector tmp_walls; + cur = root_node->xmlChildrenNode; + while (cur!=NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"walls"))){ + XMLReadWalls(cur, &tmp_walls); + } + cur=cur->next; + } + + // read walls to cells and boundary_polygon + cur = root_node->xmlChildrenNode; + while (cur!=NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"cells"))) { + XMLReadWallsToCells(cur, &tmp_walls); + } + cur=cur->next; + } + + boundary_polygon->ConstructNeighborList(); + boundary_polygon->ConstructConnections(); + + for (vector::iterator c=cells.begin(); + c!=cells.end(); + c++) { + + (*c)->ConstructNeighborList(); + (*c)->ConstructConnections(); + + } + + shuffled_cells.clear(); + shuffled_cells = cells; + + MyUrand r(shuffled_cells.size()); + random_shuffle(shuffled_cells.begin(),shuffled_cells.end(),r); + +} + +void Mesh::XMLParseTree(const xmlNode *root_node) { + + XMLReadSimtime(root_node); + XMLReadPars(root_node); + XMLReadGeometry(root_node); + +} + + +void Mesh::XMLReadNodes(xmlNode *root) { + + xmlNode *cur = root; + cur = cur->xmlChildrenNode; + + for (vector::iterator i=nodes.begin(); + i!=nodes.end(); + i++) { + delete *i; + } + + nodes.clear(); + Node::nnodes=0; + + xmlChar *tlc = xmlGetProp(root, BAD_CAST "target_length"); + + if (tlc != 0) { + Node::target_length = strtod( (const char *)tlc, 0 ); + xmlFree(tlc); + } else { + // note that libxml2 also defines a token "warning" + MyWarning::unique_warning("Warning: value found in XML file for Node::target_length."); + } + + while (cur!=NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"node"))){ + + xmlChar *xc = xmlGetProp(cur, BAD_CAST "x"); + + if (xc==0) { + unique_warning("Token \"x\" not found in xmlwrite.cpp at or around line no. 722"); + } + + xmlChar *yc = xmlGetProp(cur, BAD_CAST "y"); + + if (yc==0) { + unique_warning("Token \"y\" not found in xmlwrite.cpp at or around line no. 727"); + } + + xmlChar *fixedc = xmlGetProp(cur, BAD_CAST "fixed"); + if (fixedc==0) { + unique_warning("Token \"fixed\" not found in xmlwrite.cpp at or around line."); + } + + xmlChar *boundaryc = xmlGetProp(cur, BAD_CAST "boundary"); + if (boundaryc==0) { + unique_warning("Token \"boundary\" not found in xmlwrite.cpp at or around line."); + } + + xmlChar *samc = xmlGetProp(cur, BAD_CAST "sam"); + if (samc==0) { + unique_warning("Token \"sam\" not found in xmlwrite.cpp at or around line."); + } + + double x = strtod( (char *)xc , 0); + double y = strtod( (char *)yc , 0 ); + + 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 *tmp_walls) { + + xmlNode *cur = root; + cur = cur->xmlChildrenNode; + + for (list::iterator i=walls.begin(); + i!=walls.end(); + i++) { + delete *i; + } + + walls.clear(); + Wall::nwalls = 0; + tmp_walls->clear(); + //Node::nnodes=0; + + + while (cur!=NULL) { + + vector 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 = strtod( (char *)nc, 0); + xmlFree(nc); + + + nc = xmlGetProp(cur, BAD_CAST "viz_flux"); + + double viz_flux; + if (nc!=0) { + viz_flux = strtod( (char *)nc, 0); + } else { + // if the info is not there, we really don't care. + // It is just for visualization anyhow. + viz_flux = 0.; + } + 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."; + //ThrowStringStream(text); + 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 = strtod( (char *)nc, 0 ); + 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; + // ThrowStringStream(text); + } + } + + 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 = strtod( (char *)nc, 0 ); + 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."; + //ThrowStringStream(text); + 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 = strtod( (char *)nc, 0 ); + w->apoplast[nv++]=v; + xmlFree(nc); + } + v_node = v_node->next; + } + + } + w_node=w_node->next; + } + + } + cur = cur->next; + } + + } + // CleanUpWalls(); +} + + +void Mesh::XMLReadWallsToCells(xmlNode *root, vector *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) { + + //Cell *new_cell=0; + + if ((!xmlStrcmp(cur->name, (const xmlChar *)"cell")) || + (!xmlStrcmp(cur->name, (const xmlChar *)"boundary_polygon" ))) { + + vector 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;iwalls.push_back((*tmp_walls)[tmp_walls_ind[i]]); + } + } else { + + int nwalls = tmp_walls_ind.size(); + for (int i=0;iwalls.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::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::iterator i=cells.begin(); + i!=cells.end(); + i++) { + delete *i; + } + + cells.clear(); + Cell::NCells() = 0; + + delete boundary_polygon; + + + xmlNode *cur = root->xmlChildrenNode; + + while (cur!=NULL) { + + Cell *new_cell=0; + static int count=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); + + //XMLParseTree(root_element); + if (geometry) XMLReadGeometry(root_element); + if (pars) XMLReadPars(root_element); + if (simtime) XMLReadSimtime(root_element); + //print_element_names(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(); + +} + + +/* int Mesh::XMLReadWall(xmlNode *cur) { + + + return w; + }*/ +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 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; + } + +} + diff --git a/src/xmlwrite.h b/src/xmlwrite.h new file mode 100644 --- /dev/null +++ b/src/xmlwrite.h @@ -0,0 +1,70 @@ +/* + * + * $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 . + * + * Copyright 2010 Roeland Merks. + * + */ + + +#ifndef _XML_WRITE_H_ +#define _XML_WRITE_H_ + +#include +#include +#include + +namespace XMLIO { + + std::vector XMLReadValArray(xmlNode *cur); + void XMLWriteLeafSourceCode(xmlNode *parent); + void XMLWriteReactionsCode(xmlNode *parent); + template long list_index (For beg, For end, E *elem) { + + long n = 0; + for (For i=beg; + i!=end; + i++) { + if (*i == elem) { + return n; + } + n++; + } + return -1; + } + +} + + +static const char * bool_names[2] = {"true","false"}; + +inline const char *bool_name(bool q) { return q ? bool_names[0] : bool_names[1]; } + +inline bool strtobool(const char *str) { + if (!strcmp(str, "true")) { + return true; + } else { + if (!strcmp(str, "false")) { + return false; + } else { + throw("Error in xmlwrite.cpp : strtobool(const char *str). Parameter passed other than \"true\" or \"false\"."); + } + } +} + +#endif diff --git a/src/xmlwritecode.cpp b/src/xmlwritecode.cpp new file mode 100644 --- /dev/null +++ b/src/xmlwritecode.cpp @@ -0,0 +1,36 @@ +// Automatically produced by perl script "make_xmlwritecode.pl". DO NOT EDIT + +#include +#include +#include +#include +#include "xmlwrite.h" +#include + +static const std::string _module_id("$Id$"); + +void XMLIO::XMLWriteLeafSourceCode(xmlNode *parent) { + + // Embed the code in our xml file, so we can reconstruct the model we used + // to produce it... + +xmlChar *sourcecode = (xmlChar *)"#include \n#include \n#include \n#include \n#include \n#include \n#include \"mesh.h\"\n#include \"parameter.h\"\n#include \"random.h\"\n#include \"pi.h\"\n#include \"cellitem.h\"\n#include \"canvas.h\"\n#include \"cell.h\"\n#include \"output.h\"\n#include \n#include \n#include \n#include \n#include \n#include \n//Added by qt3to4:\n#include \n\n#include \n#include \n\n\n#ifdef HAVE_QWT\n#include \"data_plot.h\"\n#endif\n#include \n#include \n#include \n#include \"simplugin.h\"\n#include \"testplugin.h\"\n\n/* #define _xstr_(s) _str_(s)\n#define _str_(s) #s\n#include _xstr_(REACTIONS_HEADER)\n*/\nextern Parameter par;\n\nMainBase *main_window = 0;\ndouble auxin_account = 0.;\n\n\n\nTestPlugin *plugin = new TestPlugin();\n\n#ifdef XFIGGRAPHICS\n#define TIMESTEP double Graphics::TimeStep(void)\n#endif\n\nclass PrintNode {\npublic:\n void operator() (const Node &n) const \n {\n cerr << n.Index() << \": \" << n << endl;\n }\n};\n\n\nclass EdgeSource {\n \npublic:\n void operator() (Cell &c) {\n \n if (c.AtBoundaryP()) {\n cerr << \"Cell \" << c.Index() << \" is a source cell.\\n\";\n c.SetSource(0,par.source);\n } else {\n cerr << \"Cell \" << c.Index() << \" is _not_ a source cell.\\n\";\n }\n }\n \n};\n\n\n\nclass CellInfo {\npublic:\n void operator() (Cell &c,std::ostream &os) const {\n os << \"Cell \" << c.index << \" says: \" << endl;\n os << \"c.nodes.size() = \" << c.nodes.size() << endl;\n for (list::iterator i=c.nodes.begin();\n i!=c.nodes.end();\n i++) {\n cerr << (*i)->Index() << \" \";\n }\n cerr << endl;\n }\n};\n\ndouble PINSum(Cell &c) {\n \n return c.Chemical(1) + c.SumTransporters(1);// + c.ReduceCellAndWalls( complex_PijAj );\n \n}\n\n\nclass DrawCell {\npublic:\n void operator() (Cell &c,QGraphicsScene &canvas, MainBase &m) const {\n if (m.ShowBorderCellsP() || c.Boundary()==Cell::None) {\n if (!m.ShowBoundaryOnlyP() && !m.HideCellsP()) \n if (m.ShowToolTipsP()) {\n 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());\n \n info_string += \"\\n\" + c.printednodelist();\n \n c.Draw(&canvas, info_string);\n } else {\n c.Draw(&canvas);\n }\n if (m.ShowCentersP())\n c.DrawCenter(&canvas);\n if (m.ShowFluxesP())\n c.DrawFluxes(&canvas, par.arrowsize);\n \n }\n \n }\n \n};\n\nMesh mesh;\nbool batch=false;\n\n\nvoid MainBase::Plot(int resize_stride) {\n \n clear();\n \n \n static int count=0;\n if (resize_stride) {\n if ( !((++count)%resize_stride) ) {\n FitLeafToCanvas();\n }\n }\n mesh.LoopCells(DrawCell(),canvas,*this);\n \n if (ShowNodeNumbersP()) \n mesh.LoopNodes( bind2nd (mem_fun_ref ( &Node::DrawIndex), &canvas ) ) ;\n if (ShowCellNumbersP()) \n mesh.LoopCells( bind2nd (mem_fun_ref ( &Cell::DrawIndex), &canvas ) ) ;\n \n if (ShowCellAxesP()) \n mesh.LoopCells( bind2nd (mem_fun_ref ( &Cell::DrawAxis), &canvas ) );\n \n if (ShowCellStrainP()) \n mesh.LoopCells( bind2nd (mem_fun_ref ( &Cell::DrawStrain), &canvas ) );\n \n if (ShowWallsP())\n \n mesh.LoopWalls( bind2nd( mem_fun_ref( &Wall::Draw ), &canvas ) );\n \n if (ShowApoplastsP()) \n mesh.LoopWalls( bind2nd( mem_fun_ref( &Wall::DrawApoplast ), &canvas ) );\n \n if (ShowMeshP()) \n mesh.DrawNodes(&canvas);\n \n if (ShowBoundaryOnlyP()) \n mesh.DrawBoundary(&canvas);\n\n \n if ( ( batch || MovieFramesP() )) {\n \n static int frame = 0;\n // frame numbers are sequential for the most frequently written file type.\n // for the less frequently written file type they match the other type\n if (!(count%par.storage_stride) ) {\n \n stringstream fname;\n fname << par.datadir << \"/leaf.\";\n fname.fill('0');\n fname.width(6);\n \n /* \n fname << frame << \".pdf\";\n if (par.storage_stride <= par.xml_storage_stride) {\n frame++;\n }\n \n // Write high-res JPG snapshot every plot step\n Save(fname.str().c_str(), \"PDF\");\n */\n \n fname << frame << \".jpg\";\n if (par.storage_stride <= par.xml_storage_stride) {\n frame++;\n }\n \n // Write high-res JPG snapshot every plot step\n Save(fname.str().c_str(), \"JPEG\",1024,768);\n \n }\n \n if (!(count%par.xml_storage_stride)) {\n stringstream fname;\n fname << par.datadir << \"/leaf.\";\n fname.fill('0');\n fname.width(6);\n fname << frame << \".xml\";\n \n if (par.xml_storage_stride < par.storage_stride) {\n frame++;\n }\n // Write XML file every ten plot steps\n mesh.XMLSave(fname.str().c_str(), XMLSettingsTree());\n }\n \n }\n}\n\n\nvoid Cell::Flux(double *flux, double *D) {\n \n\n // loop over cell edges\n \n for (int c=0;c::iterator i=walls.begin();\n i!=walls.end();\n i++) {\n \n \n // leaf cannot take up chemicals from environment (\"no flux boundary\")\n if ((*i)->c2->BoundaryPolP()) continue;\n \n \n // flux depends on edge length and concentration difference\n for (int c=0;clength * ( D[c] ) * ( (*i)->c2->chem[c] - chem[c] );\n \n if ((*i)->c1!=this) {\n cerr << \"Warning, bad cells boundary: \" << (*i)->c1->index << \", \" << index << endl;\n }\n \n flux[c] += phi;\n } \n }\n \n}\n\nINIT {\n \n if (leaffile) { \n xmlNode *settings;\n mesh.XMLRead(leaffile, &settings);\n main_window->XMLReadSettings(settings);\n xmlFree(settings);\n main_window->UserMessage(QString(\"Ready. Time is %1\").arg(mesh.getTimeHours().c_str()));\n \n } else {\n \n Cell &circle=mesh.CircularCell(0,0,10,10);\n \n circle.SetTargetArea(circle.CalcArea());\n mesh.SetBaseArea();\n // clean up chemicals \n for (int c=0; cCellDynamics();\n WallReaction *wall_f = new WallDynamics();*/\n \n mesh.ReactDiffuse(plugin, par.rd_dt);\n \n \n t++;\n \n Plot(par.resize_stride);\n \n /*QVector< QPair > angles=mesh.VertexAnglesValues();\n QString afname=QString(\"Angles/anglesvalues%1.dat\").arg(t,6,10,QChar('0'));\n ofstream af(afname.toStdString().c_str());\n */\n \n /*for (QVector< QPair >::const_iterator v=angles.begin();\n v!=angles.end();\n v++) {\n af << v->first << \" \" << v->second << endl;\n }\n */\n }\n \n } else {\n \n /* TransportFunction *transport_f = new CelltoCellTransport();\n CellReaction *cellreaction_f = new CellDynamics();\n WallReaction *wall_f = new WallDynamics();\n \n mesh.ReactDiffuse_New(transport_f, cellreaction_f, wall_f, par.rd_dt);*/\n mesh.ReactDiffuse(plugin, par.rd_dt);\n \n Plot(par.resize_stride);\n \n }\n \n \n\n \n \n i++;\n return mesh.getTime();\n \n}\n \n \n \n/* Called if a cell is clicked */\nvoid Cell::OnClick(QMouseEvent *e) {\n \n}\n \n \n\nvoid Wall::OnWallInsert(void) {\n \n\n}\n\n \n \n \nint main(int argc,char **argv) {\n \n try {\n \n\n int c;\n\n \n char *leaffile=0;\n\n \n while (1) {\n \n //int this_option_optind = optind ? optind : 1;\n int option_index = 0;\n static struct option long_options[] = {\n {\"batch\", 0, 0, 0},\n {\"leaffile\", 2, 0, 0}\n };\n \n // short option 'p' creates trouble for non-commandline usage on MacOSX. Option -p changed to -P (capital)\n static char *short_options = \"bl\";\n c = getopt_long (argc, argv, \"bl:\",\n long_options, &option_index);\n if (c == -1)\n break;\n \n \n if (c==0) {\n printf (\"option %s\", long_options[option_index].name);\n if (optarg)\n printf (\" with arg %s\", optarg);\n printf (\"\\n\");\n \n c = short_options[option_index];\n }\n \n switch (c) {\n case 'b':\n cerr << \"Running in batch mode\\n\";\n batch=true;\n break;\n \n case 'l':\n leaffile=strdup(optarg);\n if (!leaffile) {\n throw(\"Out of memory\");\n }\n printf(\"Reading leaf state file '%s'\\n\", leaffile);\n break;\n \n case '?':\n break;\n \n default:\n printf (\"?? getopt returned character code 0%o ??\\n\", c);\n }\n }\n \n \n if (optind < argc) {\n printf (\"non-option ARGV-elements: \");\n while (optind < argc)\n printf (\"%s \", argv[optind++]);\n printf (\"\\n\");\n }\n \n MakeDir(\"Angles\");\n bool useGUI = !batch;\n QApplication app(argc,argv,useGUI);\n \n\n \n QPalette tooltippalette = QToolTip::palette();\n QColor transparentcolor = QColor(tooltippalette.brush(QPalette::Window).color());\n\n tooltippalette.setBrush (QPalette::Window, QBrush (transparentcolor) );\n QToolTip::setPalette( tooltippalette );\n\n QGraphicsScene canvas(0,0,8000,6000);\n\n if (useGUI) {\n main_window=new Main(canvas, mesh);\n if ( QApplication::desktop()->width() > ((Main *)main_window)->width() + 10\n && QApplication::desktop()->height() > ((Main *)main_window)->height() +30 ) {\n\n ((Main *)main_window)->show();\n ((Main *)main_window)->resize( ((Main *)main_window)->sizeHint());\n } else {\n ((Main *)main_window)->showMaximized();\n }\n } else {\n main_window=new MainBase(canvas, mesh);\n\n }\n\n \n \n canvas.setSceneRect(QRectF());\n if (!batch) {\n QObject::connect( qApp, SIGNAL(lastWindowClosed()), qApp, SLOT(quit()) );\n }\n\n \n \n main_window->Init(leaffile);\n \n Cell::SetMagnification(1);\n Cell::setOffset(0,0);\n \n main_window->FitLeafToCanvas();\n \n \n \n main_window->Plot();\n\n \n\n if (batch) {\n double t=0.;\n do {\n t = main_window->TimeStep();\n } while (t < par.maxt);\n \n } else\n return app.exec();\n \n \n } catch (const char *message) {\n if (batch) { \n cerr << \"Exception caught:\" << endl;\n cerr << message << endl;\n abort();\n } else {\n QString qmess=QString(\"Exception caught: %1\").arg(message);\n QMessageBox::critical(0, \"Critical Error\", qmess);\n abort();\n }\n } catch (ios_base::failure) {\n stringstream error_message;\n error_message << \"I/O failure: \" << strerror(errno);\n if (batch) {\n cerr << error_message.str() < par.rel_cell_div_threshold * c.BaseArea() ) {\n c.Divide();\n }\n }\n};\n\n// The number of chemical species in the cels\nconst int Cell::nchem = 0;\n\n// Differential equations describing transport of chemicals from cell to cell\nclass CelltoCellTransport : public TransportFunction {\n\n public:\n virtual void operator()(Wall *w, double *dchem_c1, double *dchem_c2) {}\n \n };\n\n// Differential equations describing chemical reactions taking place at or near the cell walls\n// (e.g. PIN accumulation)\nclass WallDynamics : public WallReaction {\n public:\n virtual void operator()(Wall *w, double *dw1, double *dw2) {}; \n \n};\n\n\n// Differential equations describing chemical reactions inside the cells\nclass CellDynamics : public CellReaction {\n public:\n virtual void operator()(Cell *c, double *dchem) {\n \n };\n \n};\n\n// Rules for cell coloring\nvoid Cell::SetColor(QColor &color) { }\n\n// To be executed after cell division\nvoid Cell::OnDivide(ParentInfo &parent_info, Cell &daughter) {}\n\n"; + xmlNodePtr xmlcode = xmlNewChild(parent, NULL, BAD_CAST "code", sourcecode); + + { + xmlNewProp(xmlcode, BAD_CAST "name", BAD_CAST "reactions_pce_growth.h"); + } + +} +