diff --git a/src/mesh.h b/src/mesh.h new file mode 100644 --- /dev/null +++ b/src/mesh.h @@ -0,0 +1,474 @@ +/* + * + * $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. + * + */ + + +// 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