cellsystem.h

Go to the documentation of this file.
00001 
00008 #ifndef VVELIB_ALGORITHMS_CELLSYSTEM_H
00009 #define VVELIB_ALGORITHMS_CELLSYSTEM_H
00010 
00011 #include <config.h>
00012 #include <algorithms/complex.h>
00013 #include <geometry/intersection.h>
00014 #include <geometry/area.h>
00015 #include <util/matrix.h>
00016 #include <model.h>
00017 #include <util/parms.h>
00018 #include <sstream>
00019 #include <algorithms/tissue.h>
00020 #include <storage/storage.h>
00021 #include <storage/complex.h>
00022 
00023 #include <iostream>
00024 
00028 namespace cell_system
00029 {
00030 #define EPSILON 0.00001
00031 
00032   using namespace vvcomplex;
00033 
00034   using geometry::Point3d;
00035 
00039   std::string removeWhitespace(std::string s);
00040 
00046   struct CellSystemDivisionParams
00047   {
00051     CellSystemDivisionParams(const Point3d& dir, double a = 0, double r = 0.5, double eps=0.000001,
00052                              bool pinch=false, tissue::CellPinchingParams pp = tissue::CellPinchingParams())
00053       : angle(a)
00054       , ratio(r)
00055       , direction(dir)
00056       , epsilon(eps)
00057       , minCellWall(0)
00058       , pinching(pinch)
00059       , pinchingParam(pp)
00060     { }
00061 
00065     CellSystemDivisionParams(const CellSystemDivisionParams& copy)
00066       : angle(copy.angle)
00067       , ratio(copy.ratio)
00068       , direction(copy.direction)
00069       , epsilon(copy.epsilon)
00070       , minCellWall(0)
00071       , pinching(copy.pinching)
00072       , pinchingParam(copy.pinchingParam)
00073     { }
00074 
00078     double angle;
00079 
00086     double ratio;
00087 
00091     Point3d direction;
00092 
00096     double epsilon;
00097 
00101     double minCellWall;
00102 
00106     bool pinching;
00107 
00111     tissue::CellPinchingParams pinchingParam;
00112   };
00113 
00125   template <class Complex>
00126   DivisionData<typename Complex::junction_content>
00127   findDivisionPoints(const typename Complex::cell& c, Point3d& center, Complex& T,
00128                      const Point3d& direction)
00129   {
00130     IMPORT_COMPLEX_VERTICES(Complex);
00131     IMPORT_COMPLEX_MODEL(Complex, T);
00132     DivisionData<typename Complex::junction_content> result;
00133 //    cout << "Finding points for center: " << center << endl;
00134     forall(const junction& v, T.S.neighbors(c))
00135     {
00136       const junction& n = T.S.nextTo(c, v);
00137       const Point3d& p1 = model.position(v);
00138       const Point3d& p2 = model.position(n);
00139       Point3d u;
00140       double s;
00141       if(geometry::planeLineIntersection(u, s, center, direction, p1, p2) && s >= 0 && s <= 1)
00142       {
00143 //        cout << "Intersection between " << p1 << " and " << p2 << " at " << u << " with s = " << s << endl;
00144         if((p1-center)*direction > 0)
00145         {
00146 //          cout << "Found v1 at " << model.vertexPosition(v) << " with pv = " << u << endl;
00147 //          if(result.v1)
00148 //          {
00149 //            cout << "Warning, finding v1 again" << endl;
00150 //          }
00151           result.v1 = v;
00152           result.pv = u;
00153         }
00154         else
00155         {
00156 //          cout << "Found u1 at " << model.vertexPosition(v) << " with pu = " << u << endl;
00157 //          if(result.u1)
00158 //          {
00159 //            cout << "Warning, finding v1 again" << endl;
00160 //          }
00161           result.u1 = v;
00162           result.pu = u;
00163         }
00164         if(result.v1 && result.u1)
00165           break;
00166       }
00167     }
00168     if(result.u1 && result.v1)
00169     {
00170       center = (result.pu+result.pv)/2;
00171     }
00172     return result;
00173   }
00174 
00186   template <class Complex>
00187   double volumeLeftCell(const typename Complex::cell& c, Complex& T, const Point3d& center,
00188                         const DivisionData<typename Complex::junction_content>& result)
00189   {
00190     IMPORT_COMPLEX_VERTICES(Complex);
00191     IMPORT_COMPLEX_MODEL(Complex, T);
00192     double area = 0;
00193     junction v = result.v1;
00194     Point3d prev = result.pv;
00195     do
00196     {
00197       v = T.S.nextTo(c, v);
00198       const Point3d& cur = model.position(v);
00199       area += geometry::triangleArea(center, prev, cur);
00200       prev = cur;
00201     } while(v != result.u1);
00202     area += geometry::triangleArea(center, prev, result.pu);
00203     return area;
00204   }
00205 
00211   template <class Complex>
00212   DivisionData<typename Complex::junction_content>
00213   findDivisionPoints(const typename Complex::cell& c, Complex& T,
00214                      const CellSystemDivisionParams& params)
00215   {
00216     IMPORT_COMPLEX_VERTICES(Complex);
00217     IMPORT_COMPLEX_MODEL(Complex, T);
00218     const Point3d& cell_normal = model.normal(c);
00219     Point3d direction = util::Matrix<3,3,double>::rotation(cell_normal, params.angle) * params.direction;
00220 //    cout << "Z = " << params.direction << " - angle = " << params.angle << " => direction = " << direction << endl;
00221 //    cout << "Polygon: ";
00222 //    forall(const vertex& j, S.neighbors(cell))
00223 //    {
00224 //      cout << model.vertexPosition(j) << "; ";
00225 //    }
00226 //    cout << endl;
00227     Point3d center = model.position(c);
00228     // First, find the division points going through the center
00229     DivisionData<typename Complex::junction_content> result =
00230       findDivisionPoints(c, center, T, direction);
00231     if(!result)
00232     {
00233       return DivisionData<typename Complex::junction_content>();
00234     }
00235 
00236     // Then, if ratio != 0 then adjust volume by dichotomy
00237     if(params.ratio)
00238     {
00239       // Volume of the cell
00240       double total_volume = model.area(c);
00241       double target_volume = total_volume*params.ratio;
00242       double vol_epsilon = params.epsilon * target_volume;
00243       double proj_min = HUGE_VAL, proj_max = -HUGE_VAL;
00244       Point3d pos_max, pos_min;
00245       forall(const junction& j, T.S.neighbors(c))
00246       {
00247         const Point3d& jpos = model.position(j);
00248         double proj = (jpos - center) * direction;
00249         if(proj > proj_max)
00250         {
00251           proj_max = proj;
00252           pos_max = jpos;
00253         }
00254         if(proj < proj_min)
00255         {
00256           proj_min = proj;
00257           pos_min = jpos;
00258         }
00259       }
00260       double current_volume = volumeLeftCell(c, T, center, result);
00261       Point3d delta;
00262       if(current_volume < target_volume)
00263       {
00264         delta = proj_max*direction;
00265       }
00266       else
00267       {
00268         delta = -proj_min*direction;
00269       }
00270 //      cout << endl << endl;
00271 //      cout << "Total volume = " << total_volume << " => target_volume = " << target_volume << endl;
00272 //      cout << "Pos max = " << pos_max << " - Pos min = " << pos_min << endl;
00273 //      cout << "delta = " << delta << endl;
00274       while(std::abs(current_volume - target_volume) > vol_epsilon)
00275       {
00276         delta /= 2;
00277         if(current_volume < target_volume)
00278         {
00279           center += delta;
00280         }
00281         else
00282         {
00283           center -= delta;
00284         }
00285 //        cout << "New delta = " << delta << " - new center = " << center << 
00286 //        endl;
00287         result = findDivisionPoints(c, center, T, direction);
00288         current_volume = volumeLeftCell(c, T, center, result);
00289 //        cout << "pu = " << result.pu << " - pv = " << result.pv << endl;
00290       }
00291 //      cout << "Volume = " << current_volume << endl;
00292     }
00293     if(params.pinching)
00294     {
00295       tissue::cellPinching(c, T, result, params.pinchingParam);
00296     }
00297     testDivisionOnVertices(c, result, T, std::max(params.epsilon,params.minCellWall));
00298     return result;
00299   }
00300 
00304   typedef int label_t;
00305 
00311   struct CellSystemCell
00312   {
00316     double area;
00320     Point3d pos;
00324     label_t label;
00325 
00329     bool serialize(storage::VVEStorage&);
00330   };
00331 
00337   struct CellSystemJunction
00338   {
00342     Point3d pos;
00346     Point3d force;
00350     Point3d velocity;
00351 
00355     bool serialize(storage::VVEStorage&);
00356   };
00357 
00363   struct DivisionParams
00364   {
00365     DivisionParams()
00366       : left_label(0)
00367       , right_label(0)
00368       , angle(0)
00369       , ratio(0)
00370     { }
00371 
00372     DivisionParams(label_t ll, label_t rl, double a, double r = 0.5)
00373       : left_label(ll)
00374       , right_label(rl)
00375       , angle(a)
00376       , ratio(r)
00377     { }
00378 
00379     DivisionParams(const DivisionParams& copy)
00380       : left_label(copy.left_label)
00381       , right_label(copy.right_label)
00382       , angle(copy.angle)
00383       , ratio(copy.ratio)
00384     { }
00385 
00389     label_t left_label;
00393     label_t right_label;
00397     double angle;
00403     double ratio;
00404   };
00405 
00417   template <class Complex, class MyModel>
00418   struct CellSystem : public Model
00419   {
00420     IMPORT_COMPLEX_VERTICES(Complex);
00421     IMPORT_COMPLEX_EDGES(Complex);
00422     IMPORT_COMPLEX_GRAPHS(Complex);
00423 
00427     Complex T;
00431     double dt;
00435     double showInterval;
00439     double showTime;
00443     double ke;
00447     double ki;
00451     double pressure;
00455     double damping;
00459     double restLength;
00463     double stability;
00467     double mass;
00471     double time;
00472 
00476     typedef std::map<label_t, label_t> label_change_rules_t;
00480     typedef std::map<label_t, DivisionParams> division_rules_t;
00484     label_change_rules_t label_change_rules;
00488     division_rules_t division_rules;
00492     std::map<QString, label_t> label_numbers;
00496     std::vector<QString> label_names;
00497 
00503     DivisionParams current_div;
00504 
00508     bool viewRulesDefinitions;
00512     bool viewRulesApplication;
00513 
00517     void updateCellsArea()
00518     {
00519       forall_named( const cell& c, T.S, cells)
00520       {
00521         Point3d center;
00522         double np = 0;
00523         c->area = 0.0;
00524         forall(const junction& n, T.S.neighbors(c))
00525         {
00526           // Find area
00527           const junction& m = T.S.nextTo(c, n);
00528           c->area += geometry::triangleArea(c->pos, m->pos, n->pos);
00529           center += n->pos;
00530           np++;
00531         }
00532         center /= np;
00533         c->pos = center;
00534         if(c->area <= EPSILON ) // Avoid inf
00535         {
00536           cout << "Bad area " << c->area << endl;
00537           c->area = 0.1;
00538         }
00539       }
00540     }
00541 
00560     void readMechanicParam(util::Parms& parms, const QString& section)
00561     {
00562       parms(section, "dt", dt);
00563       parms(section, "ki", ki);
00564       parms(section, "ke", ke);
00565       parms(section, "ShowInterval", showInterval);
00566       if(showInterval < dt)
00567         showInterval = dt;
00568       parms(section, "Pressure", pressure);
00569       parms(section, "Damping", damping);
00570       parms(section, "RestLength", restLength);
00571       parms(section, "Stability", stability);
00572       parms(section, "Mass", mass);
00573     }
00574 
00582     label_t label(QString name)
00583     {
00584       std::map<QString,label_t>::const_iterator found = label_numbers.find(name);
00585       if(found != label_numbers.end())
00586       {
00587         return found->second;
00588       }
00589       cout << "Error, symbol '" << name.toStdString() << "' used but not defined" << endl;
00590       return -1;
00591     }
00592 
00593 
00599     void readSymbols(util::Parms& parms, const QString& section)
00600     {
00601       typedef std::map<QString, std::vector<QString> > rmap;
00602       rmap symbols;
00603       parms.all(section, symbols);
00604       if(viewRulesDefinitions)
00605         util::out << "Reading " << symbols.size() << " symbols from section [" << section << "]" << endl;
00606       for(rmap::iterator it = symbols.begin() ; it != symbols.end() ; ++it)
00607       {
00608         QString name = it->first;
00609         registerSymbol(name);
00610       }
00611     }
00612 
00622     label_t registerSymbol(QString name)
00623     {
00624       std::map<QString, label_t>::iterator found = label_numbers.find(name);
00625       if(found == label_numbers.end())
00626       {
00627         label_t new_label = label_names.size();
00628         label_names.push_back(name);
00629         label_numbers[name] = new_label;
00630         if(viewRulesDefinitions)
00631           cout << "Defining symbol '" << name.toStdString() << "' as value " << new_label << endl;
00632         return new_label;
00633       }
00634       return found->second;
00635     }
00636 
00656     void readRules(util::Parms& parms, const QString& section)
00657     {
00658       typedef std::map<QString, std::vector<QString> > rmap;
00659       rmap rules;
00660       parms.all(section, rules);
00661 
00662       for(rmap::iterator it = rules.begin() ; it != rules.end() ; ++it)
00663       {
00664         QString name = it->first;
00665         label(name);
00666       }
00667 
00668       label_change_rules.clear();
00669       division_rules.clear();
00670 
00671       for(rmap::iterator it = rules.begin() ; it != rules.end() ; ++it)
00672       {
00673         label_t lhs = label_numbers[it->first];
00674         if(it->second.size() > 1)
00675         {
00676           cout << "Error, more than one rule for '" << lhs << "' ... ignoring" << endl;
00677           continue;
00678         }
00679         QString rhs = it->second[0];
00680         int pos = rhs.indexOf('|');
00681         if(pos == -1)
00682         {
00683           label_t new_label = label(rhs);
00684           label_change_rules[lhs] = new_label;
00685           if(viewRulesDefinitions)
00686             cout << "Rule: " << label_names[lhs].toStdString() << " --> " << label_names[new_label].toStdString() << endl;
00687         }
00688         else
00689         {
00690           if(pos == -1)
00691           {
00692            cout << "Error, division rule without '|': " << lhs << " --> " << rhs.toStdString() << endl;
00693            continue;
00694           }
00695           QString left_name = rhs.mid(0, pos).trimmed();
00696           label_t left_label = label(left_name);
00697           int pos_left_paren, pos_right_paren, pos_comma;
00698           pos_left_paren = rhs.indexOf('(', pos);
00699           pos_comma = rhs.indexOf(',', pos_left_paren);
00700           pos_right_paren = rhs.indexOf(')', pos_left_paren);
00701           if(pos_left_paren == -1)
00702           {
00703            cout << "Error, division rule without '(': " << lhs << " --> " << rhs.toStdString() << endl;
00704            continue;
00705           }
00706           if(pos_right_paren == -1)
00707           {
00708            cout << "Error, division rule without ')': " << lhs << " --> " << rhs.toStdString() << endl;
00709            continue;
00710           }
00711           QString right_name = rhs.mid(pos_right_paren+1).trimmed();
00712           label_t right_label = label(right_name);
00713           if(pos_comma != -1)
00714           {
00715             QString angle_str = rhs.mid(pos_left_paren+1, pos_comma-pos_left_paren-1);
00716             QString ratio_str = rhs.mid(pos_comma+1, pos_right_paren-pos_comma-1);
00717             bool ok;
00718             double angle = angle_str.toDouble(&ok);
00719             if(!ok)
00720             {
00721               cout << "Error, cannot read angle (" << angle_str.toStdString() << ") in " << lhs << " --> " << rhs.toStdString() << endl;
00722               continue;
00723             }
00724             angle *= M_PI/180;
00725             double ratio = ratio_str.toDouble(&ok);
00726             if(!ok)
00727             {
00728               cout << "Error, cannot read ratio (" << ratio_str.toStdString() << ") in " << lhs << " --> " << rhs.toStdString() << endl;
00729               continue;
00730             }
00731             division_rules[lhs] = DivisionParams(left_label, right_label, angle, ratio);
00732           }
00733           else
00734           {
00735             QString angle_str = rhs.mid(pos_left_paren+1, pos_right_paren-pos_left_paren-1);
00736             bool ok;
00737             double angle = angle_str.toDouble(&ok);
00738             if(!ok)
00739             {
00740               cout << "Error, cannot read angle (" << angle_str.toStdString() << ") in " << lhs << " --> " << rhs.toStdString() << endl;
00741               continue;
00742             }
00743             angle *= M_PI/180;
00744             division_rules[lhs] = DivisionParams(left_label, right_label, angle);
00745           }
00746           const DivisionParams& dp = division_rules[lhs];
00747           if(viewRulesDefinitions)
00748             cout << "Rule: " << label_names[lhs].toStdString() << " --> " << label_names[dp.left_label].toStdString() << " |("
00749               << (dp.angle*180/M_PI) << ","
00750               << dp.ratio << ") " << label_names[dp.right_label].toStdString() << endl;
00751         }
00752       }
00753       cout << endl;
00754     }
00755 
00761     virtual void readParam()
00762     {
00763       util::Parms parms("view.v");
00764       readMechanicParam(parms, "Mechanic");
00765       readSymbols(parms, "Symbols");
00766       readRules(parms, "Rules");
00767     }
00768 
00769     void modifiedFiles( const std::set<QString>& filenames )
00770     {
00771       if(filenames.count("view.v"))
00772         readParam();
00773     }
00774 
00778     CellSystem( QObject* parent )
00779       : Model( parent )
00780       , T((MyModel*)this)
00781       , showTime(0)
00782       , time(0)
00783       , viewRulesDefinitions(false)
00784       , viewRulesApplication(false)
00785     {
00786     }
00787 
00791     void step()
00792     {
00793       step_cellsystem();
00794     }
00795 
00801     bool step_cellsystem_meca()
00802     {
00803       time += dt;
00804       bool stable;
00805       do
00806       {
00807         showTime += dt;
00808         // First, mechanical model
00809         updateCellsArea();
00810         forall(const junction& v, T.W)
00811         {
00812           v->force = 0;
00813         }
00814         forall_named(const cell& c, T.S, cells)
00815         {
00816           double area= c->area;
00817           // First, compute the forces due to the pressure
00818           forall(const junction& j, T.S.neighbors(c))
00819           {
00820             const junction& k = T.S.nextTo(c, j);
00821             Point3d u = k->pos - j->pos;
00822             double l = util::norm(u);
00823             u /= l;
00824             Point3d n(u.y(), -u.x(), 0);
00825             n *= pressure*l/area/2;
00826             j->force += n;
00827             k->force += n;
00828           }
00829         }
00830         // Second, compute the forces due to the springs
00831         forall(const junction& j, T.W)
00832         {
00833           const Point3d& jpos = j->pos;
00834           forall(const junction& k, T.W.neighbors(j))
00835           {
00836             double ks = ki;
00837             if(T.border(j, k))
00838               ks = ke;
00839             Point3d u = k->pos - jpos;
00840             double l = util::norm(u);
00841             u *= ks*(1 - restLength/l);
00842             j->force += u;
00843           }
00844         }
00845 
00846         // Now, integrate and check for stability
00847         stable = true;
00848         forall(const junction& j, T.W)
00849         {
00850           if(util::norm(j->force) > stability) stable = false;
00851           j->velocity += (j->force - damping*j->velocity)/mass*dt;
00852           j->pos += j->velocity*dt;
00853         }
00854       } while(showTime < showInterval);
00855       showTime -= showInterval;
00856 
00857       return stable;
00858     }
00859 
00864     void step_cellsystem_division()
00865     {
00866       updateCellsArea();
00867 
00868       // Second, division
00869       std::vector<cell> cells(T.C.begin(), T.C.end());
00870       forall(const cell& c, cells)
00871       {
00872         label_t label = c->label;
00873         label_change_rules_t::const_iterator label_found = label_change_rules.find(label);
00874         division_rules_t::const_iterator division_found = division_rules.find(label);
00875         if(label_found != label_change_rules.end())
00876         {
00877           if(viewRulesApplication)
00878             cout << "Applying rule: " << c->label << " --> " << label_found->second << endl;
00879           c->label = label_found->second;
00880         }
00881         else if(division_found != division_rules.end())
00882         {
00883           const DivisionParams& div = division_found->second;
00884           if(viewRulesApplication)
00885             cout << "Applying rule: " << c->label << " --> "
00886               << div.left_label << " |(" << (div.angle*180/M_PI) << "," << div.ratio << ") "
00887               << div.right_label << endl;
00888           current_div = div;
00889           Point3d ref = ((MyModel*)this)->referenceVector(c);
00890           ref.normalize();
00891           CellSystemDivisionParams parms(ref, div.angle, div.ratio, EPSILON);
00892           T.divideCell(c, parms, c);
00893         }
00894       }
00895     }
00896 
00903     bool step_cellsystem()
00904     {
00905       if(!this->step_cellsystem_meca())
00906         return false;
00907 
00908       step_cellsystem_division();
00909       return true;
00910    }
00911 
00912     // Methods required by cell system library
00913 
00914     double area(const cell& c) const
00915     {
00916       return c->area;
00917     }
00918 
00919     Point3d position( const cell& v) const
00920     {
00921       return v->pos;
00922     }
00923 
00924     Point3d position( const junction& v) const
00925     {
00926       return v->pos;
00927     }
00928 
00929     void setPosition( const cell& v, const Point3d& pos)
00930     {
00931       v->pos = pos;
00932     }
00933 
00934     void setPosition( const junction& v, const Point3d& pos)
00935     {
00936       v->pos = pos;
00937     }
00938 
00939     void setPositionHint( const junction& , const junction& , const junction& , double )
00940     {
00941     }
00942 
00943     Point3d normal( const junction& ) const
00944     {
00945       return Point3d(0,0,1);
00946     }
00947 
00948     Point3d normal( const cell& ) const
00949     {
00950       return Point3d(0,0,1);
00951     }
00952 
00953     void updateFromOld( const cell& cl, const cell& cr, const cell& c,
00954                         const typename Complex::division_data&, Complex& T)
00955     {
00956       cl->area = 0;
00957       cl->label = current_div.left_label;
00958       forall( const junction& n, T.S.neighbors(cl))
00959       {
00960         // Find area
00961         const junction& m = T.S.nextTo(cl, n);
00962         cl->area += geometry::triangleArea(cl->pos, m->pos, n->pos);
00963       }
00964       cr->area = 0;
00965       cr->label = current_div.right_label;
00966       forall( const junction& n, T.S.neighbors(cr))
00967       {
00968         // Find area
00969         const junction& m = T.S.nextTo(cr, n);
00970         cr->area += geometry::triangleArea(cr->pos, m->pos, n->pos);
00971       }
00972       if(current_div.ratio > 0)
00973       {
00974         double target_volume = current_div.ratio*c->area;
00975         double vol_epsilon = target_volume*EPSILON;
00976         if(std::abs(cl->area - target_volume) > vol_epsilon)
00977         {
00978           cout << "Error, bad division volume : " << cl->area << " instead of " << target_volume << endl;
00979         }
00980         if(std::abs(cl->area+cr->area-c->area)/c->area > 0.001)
00981         {
00982           cout << "Error, sum of cl and cr is not c: " << cl->area << " + " << cr->area << " = " << (cl->area +cr->area) << " != " << c->area << endl;
00983         }
00984       }
00985     }
00986 
00987     int versionNumber(const QString& version)
00988     {
00989       return storage::versionNumber(version);
00990     }
00991 
00992     QString version() const
00993     {
00994       return "CellSystemModel 1.0";
00995     }
00996 
00997     bool serialize(storage::VVEStorage& store)
00998     {
00999       if(!store.field("T", T))
01000       {
01001         return false;
01002       }
01003       if(!store.field("Time", time))
01004       {
01005         std::cerr << "Warning, cell system saved without time. Setting time to 0" << endl;
01006         time = 0;
01007       }
01008       return true;
01009     }
01010 
01011   };
01012 
01013 #undef EPSILON
01014 }
01015 #endif // VVELIB_ALGORITHMS_CELLSYSTEM_H
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines
Generated on Fri May 31 15:37:49 2013 for VVE by  doxygen 1.6.3