/* * * 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; } void CellBase::SetTransporters(int ch, double conc) { if (ch>=NChem()) { stringstream error; error << "SetChemical: value ch = " << ch << " is out of range\n"; throw error.str().c_str(); } for (list::iterator w=walls.begin(); w!=walls.end(); w++) { (*w)->setTransporter(this, ch, 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; }