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
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
00144 if((p1-center)*direction > 0)
00145 {
00146
00147
00148
00149
00150
00151 result.v1 = v;
00152 result.pv = u;
00153 }
00154 else
00155 {
00156
00157
00158
00159
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
00221
00222
00223
00224
00225
00226
00227 Point3d center = model.position(c);
00228
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
00237 if(params.ratio)
00238 {
00239
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
00271
00272
00273
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
00286
00287 result = findDivisionPoints(c, center, T, direction);
00288 current_volume = volumeLeftCell(c, T, center, result);
00289
00290 }
00291
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
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 )
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
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
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
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
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
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
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
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
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