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