triangle_growth.cpp

00001 #include "triangle_growth.h"
00002 
00003 #include <QFile>
00004 #include <QTextStream>
00005 #include <QStringList>
00006 #include <QFileInfo>
00007 #include <QDir>
00008 
00009 #include <geometry/coordinates.h>
00010 #include <geometry/intersection.h>
00011 #include <geometry/projection.h>
00012 #include <util/assert.h>
00013 
00014 #include <storage/vector.h>
00015 
00016 #include <cmath>
00017 
00018 using std::max;
00019 
00020 #define tr(x) QObject::tr(x)
00021 
00022 static QTextStream out(stdout);
00023 static QTextStream err(stderr);
00024 
00025 //#define debug
00026 
00027 // This is needed for Visual C++, as windows defines the OUT macro
00028 #ifdef OUT
00029 #  undef OUT
00030 #endif
00031 
00032 #ifdef debug
00033 #  define OUT(s) out << s << endl
00034 #else
00035 #  define OUT(s) (void)0
00036 #endif
00037 #define ERR(s) err << s
00038 
00039 namespace algorithms
00040 {
00041   bool TriangleSurface::Position::serialize(storage::VVEStorage& store)
00042   {
00043     if(!store.field("Baryventric", barycentric))
00044       return false;
00045     if(!store.field("V1", v1))
00046       return false;
00047     if(!store.field("V2", v2))
00048       return false;
00049     if(!store.field("V3", v3))
00050       return false;
00051     if(!store.field("Face", face))
00052       return false;
00053     return true;
00054   }
00055 
00056   TriangleSurface::TriangleSurface()
00057     : S()
00058     , time(-HUGE_VAL)
00059   {
00060   }
00061 
00062   Point3d TriangleSurface::point3d(const Position& pos) const
00063   {
00064     const Point3d& p1 = vertices[pos.v1]->pos;
00065     const Point3d& p2 = vertices[pos.v2]->pos;
00066     const Point3d& p3 = vertices[pos.v3]->pos;
00067     return geometry::barycentricToCartesian(pos.barycentric, p1, p2, p3);
00068   }
00069 
00070   Point3d TriangleSurface::normal(const Position& pos) const
00071   {
00072     return faces[pos.face]->normal;
00073   }
00074 
00075   Point3d TriangleSurface::smoothNormal(const Position& pos) const
00076   {
00077     const Point3d& p1 = vertices[pos.v1]->normal;
00078     const Point3d& p2 = vertices[pos.v2]->normal;
00079     const Point3d& p3 = vertices[pos.v3]->normal;
00080     return geometry::barycentricToCartesian(pos.barycentric, p1, p2, p3);
00081   }
00082 
00083   Point3d TriangleSurface::vector3d(const Position& pos) const
00084   {
00085     const Point3d& p = point3d(pos);
00086     const Point3d& ref = vertices[pos.v1]->pos;
00087     return p-ref;
00088   }
00089 
00090   TriangleSurface::Position TriangleSurface::centerPosition(int face) const
00091   {
00092     const cell& f = S.reference(faces[face]);
00093     const junction& j1 = S.anyIn(f);
00094     const junction& j2 = S.nextTo(f, j1);
00095     const junction& j3 = S.nextTo(f, j2);
00096     return Position(Point3d(1,1,1)/3, j1->id, j2->id, j3->id, face);
00097   }
00098 
00099   Point3d TriangleSurface::center3d(int face) const
00100   {
00101     const cell& f = S.reference(faces[face]);
00102     const junction& j1 = S.anyIn(f);
00103     const junction& j2 = S.nextTo(f, j1);
00104     const junction& j3 = S.nextTo(f, j2);
00105     return (j1->pos + j2->pos + j3->pos)/3;
00106   }
00107 
00108   TriangleSurface::Position TriangleSurface::vector(const Position& ref, const Point3d& pos) const
00109   {
00110     const Point3d& p1 = vertices[ref.v1]->pos;
00111     const Point3d& p2 = vertices[ref.v2]->pos;
00112     const Point3d& p3 = vertices[ref.v3]->pos;
00113     const Point3d& pt = p1 + pos;
00114     const Point3d& vec = geometry::cartesianToBarycentric(pt, p1, p2, p3);
00115     Position result = ref;
00116     result.barycentric = vec;
00117     return result;
00118   }
00119 
00120   TriangleSurface::Position TriangleSurface::vector(const Point3d& ref, const Point3d& pos) const
00121   {
00122     const Position& ref_p = position(ref);
00123     return vector(ref_p, pos);
00124   }
00125 
00126   void TriangleSurface::clear()
00127   {
00128     S.clear();
00129     vertices.clear();
00130     faces.clear();
00131     time = -HUGE_VAL;
00132     time_index = -1;
00133   }
00134 
00135   TriangleSurface::Position TriangleSurface::position(const Point3d& pos) const
00136   {
00137     Position result;
00138     double best_dist = HUGE_VAL;
00139     cell best_cell(0);
00140     Point3d best_pos;
00141     OUT("New matching for point " << pos);
00142     forall(const cell& c, S.cells())
00143     {
00144       const junction& v1 = S.anyIn(c);
00145       const junction& v2 = S.nextTo(c, v1);
00146       const junction& v3 = S.nextTo(c, v2);
00147       const Point3d& p1 = v1->pos;
00148       const Point3d& p2 = v2->pos;
00149       const Point3d& p3 = v3->pos;
00150 //      if(util::normsq(pos-p1) > max_sq_dist and
00151 //         util::normsq(pos-p2) > max_sq_dist and
00152 //         util::normsq(pos-p3) > max_sq_dist)
00153 //        continue;
00154       double s;
00155       Point3d u = geometry::projectPointOnTriangle(pos, p1, p2, p3, s);
00156       if(best_dist > s)
00157       {
00158         OUT("Improved distance to: " << s << " for cell " << c.id());
00159         best_dist = s;
00160         best_cell = c;
00161         best_pos = u;
00162       }
00163     }
00164     OUT("Best distance = " << best_dist);
00165     // Now, find the barycentric coordinates
00166     vvassert(best_cell);
00167     if(best_cell)
00168     {
00169       const junction& v1 = S.anyIn(best_cell);
00170       const junction& v2 = S.nextTo(best_cell, v1);
00171       const junction& v3 = S.nextTo(best_cell, v2);
00172       const Point3d& p1 = v1->pos;
00173       const Point3d& p2 = v2->pos;
00174       const Point3d& p3 = v3->pos;
00175       result.barycentric = geometry::cartesianToBarycentric(best_pos, p1, p2, p3);
00176       result.v1 = v1->id;
00177       result.v2 = v2->id;
00178       result.v3 = v3->id;
00179       result.face = best_cell->id;
00180     }
00181     return result;
00182   }
00183 
00184   TriangleSurface::Position TriangleSurface::find_position(const cell& c, const Point3d& pos, cell_set_t& visited) const
00185   {
00186     const junction& j1 = S.anyIn(c);
00187     const junction& j2 = S.nextTo(c, j1);
00188     const junction& j3 = S.nextTo(c, j2);
00189     const Point3d& p1 = j1->pos;
00190     const Point3d& p2 = j2->pos;
00191     const Point3d& p3 = j3->pos;
00192     Point3d u = geometry::projectPointOnPlane(pos, p1, (p2-p1)^(p3-p1));
00193     Point3d bar = geometry::cartesianToBarycentric(u, p1, p2, p3);
00194     // If in triangle
00195     if(bar.x() > -VVE_EPSILON and
00196        bar.y() > -VVE_EPSILON and
00197        bar.z() > -VVE_EPSILON)
00198     {
00199       return Position(bar, j1->id, j2->id, j3->id, c->id);
00200     }
00201     else
00202     {
00203       if(bar.x() < bar.y() and bar.x() < bar.z())
00204       {
00205         const cell& n = S.adjacentCell(c, j2);
00206         if(n and not visited.count(n))
00207         {
00208           visited.insert(n);
00209           return find_position(n, pos, visited);
00210         }
00211         else if(bar.x() < 0)
00212         {
00213           bar.x() = 0;
00214           if(bar.y() < 0)
00215           {
00216             bar.y() = 0;
00217             bar.z() = 1;
00218           }
00219           else
00220           {
00221             Point3d proj = geometry::projectPointOnLine(pos, p2, p3);
00222             double s = util::norm(proj-p2)/util::norm(p3-p2);
00223             bar.y() = 1-s;
00224             bar.z() = s;
00225           }
00226         }
00227       }
00228       else if(bar.y() < bar.z())
00229       {
00230         const cell& n = S.adjacentCell(c, j3);
00231         if(n and not visited.count(n))
00232         {
00233           visited.insert(n);
00234           return find_position(n, pos, visited);
00235         }
00236         else if(bar.y() < 0)
00237         {
00238           bar.y() = 0;
00239           if(bar.z() < 0)
00240           {
00241             bar.z() = 0;
00242             bar.x() = 1;
00243           }
00244           else if(bar.x() < 0)
00245           {
00246             bar.x() = 0;
00247             bar.z() = 1;
00248           }
00249           else
00250           {
00251             Point3d proj = geometry::projectPointOnLine(pos, p1, p3);
00252             double s = util::norm(proj-p1)/util::norm(p3-p1);
00253             bar.x() = 1-s;
00254             bar.z() = s;
00255           }
00256         }
00257       }
00258       else
00259       {
00260         const cell& n = S.adjacentCell(c, j1);
00261         if(n and not visited.count(n))
00262         {
00263           visited.insert(n);
00264           return find_position(n, pos, visited);
00265         }
00266         else if(bar.z() < 0)
00267         {
00268           bar.z() = 0;
00269           if(bar.y() < 0)
00270           {
00271             bar.y() = 0;
00272             bar.x() = 1;
00273           }
00274           else if(bar.x() < 0)
00275           {
00276             bar.x() = 0;
00277             bar.y() = 1;
00278           }
00279           else
00280           {
00281             Point3d proj = geometry::projectPointOnLine(pos, p1, p2);
00282             double s = util::norm(proj-p1)/util::norm(p2-p1);
00283             bar.x() = 1-s;
00284             bar.y() = s;
00285           }
00286         }
00287       }
00288       return Position(bar, j1->id, j2->id, j3->id, c->id);
00289     }
00290   }
00291 
00292   TriangleSurface::Position TriangleSurface::position(const Position& start, const Point3d& pos) const
00293   {
00294     if(start)
00295     {
00296       cell_set_t visited;
00297       const cell& c = faces[start.face];
00298       visited.insert(c);
00299       return find_position(c, pos, visited);
00300     }
00301     else
00302       return position(pos);
00303   }
00304 
00305   TriangleGrowth::TriangleGrowth()
00306     : S()
00307     , _time(0)
00308     , _time_pos(0)
00309     , _valid(false)
00310     , _errorMsg("Structure not initialized.")
00311   {}
00312 
00313   bool TriangleGrowth::readOBJs(QString desc_filepath)
00314   {
00315     desc_filepath = QDir::toNativeSeparators(desc_filepath);
00316     _valid = false;
00317     std::vector<QString> data_files;
00318     std::vector<double> data_scales;
00319     times.clear();
00320     QFileInfo info(desc_filepath);
00321     if(not (info.exists() and info.isFile()))
00322     {
00323       if(info.exists())
00324         _errorMsg = QString(tr("Path '%1' exists but is not a file.")).arg(desc_filepath);
00325       else
00326         _errorMsg = QString(tr("Path '%1' does not exists.")).arg(desc_filepath);
00327       return false;
00328     }
00329     QDir main_dir = info.dir();
00330     QFile desc_file(desc_filepath);
00331     if(!desc_file.open(QIODevice::ReadOnly))
00332     {
00333       _errorMsg = QString(tr("Error while opening file '%1':\n%2")).arg(desc_filepath).arg(desc_file.errorString());
00334       return false;
00335     }
00336     QTextStream desc(&desc_file);
00337     double current_time = -HUGE_VAL;
00338     size_t ln = 0;
00339     while(!desc.atEnd())
00340     {
00341       QString line = desc.readLine().simplified();
00342       int comment = line.indexOf('#');
00343       if(comment != -1)
00344         line.truncate(comment);
00345       ln++;
00346       if(!line.isEmpty())
00347       {
00348         QString data_file;
00349         double data_time;
00350         double data_scale = 1;
00351         QStringList fields = line.split(" ");
00352         bool ok;
00353         switch(fields.size())
00354         {
00355           case 3:
00356             data_scale = fields[2].toDouble(&ok);
00357             if(!ok)
00358             {
00359               _errorMsg = QString(tr("Line %1 of file '%2', the scaling field cannot be parsed as a double.")).arg(ln).arg(desc_filepath);
00360               return false;
00361             }
00362             if(data_scale <= 0)
00363             {
00364               _errorMsg = QString(tr("Line %1 of file '%2', the scaling field must be strictly positive.")).arg(ln).arg(desc_filepath);
00365               return false;
00366             }
00367           case 2:
00368             data_time = fields[0].toDouble(&ok);
00369             if(!ok)
00370             {
00371               _errorMsg = QString(tr("Line %1 of file '%2', the time field cannot be parsed as a double.")).arg(ln).arg(desc_filepath);
00372               return false;
00373             }
00374             if(data_time <= current_time)
00375             {
00376               _errorMsg = QString(tr("Line %1 of file '%2', the time field is smaller or equal than the previous one.")).arg(ln).arg(desc_filepath);
00377               desc_file.close();
00378               return false;
00379             }
00380             data_file = fields[1];
00381             if(!main_dir.exists(data_file))
00382             {
00383               _errorMsg = QString(tr("Line %1 of file '%2', the file '%3' does not exist.")).arg(ln).arg(desc_filepath).arg(data_file);
00384               return false;
00385             }
00386             times.push_back(data_time);
00387             data_files.push_back(data_file);
00388             data_scales.push_back(data_scale);
00389             break;
00390           default:
00391             _errorMsg = QString(tr("Line %1 of file '%2' is invalid.")).arg(ln).arg(desc_filepath);
00392             return false;
00393         }
00394       }
00395     }
00396     desc_file.close();
00397     vertices.clear();
00398     faces.clear();
00399     if(times.empty())
00400       return false;
00401     // Read the first OBJ file to create the cell complex
00402     QString first_filepath = main_dir.filePath(data_files.front());
00403     _time = times.front();
00404     _time_pos = 0;
00405     QFile first_file(first_filepath);
00406     if(!first_file.open(QIODevice::ReadOnly))
00407     {
00408       _errorMsg = QString(tr("Impossible to open file '%1' for reading")).arg(first_filepath);
00409       times.clear();
00410       return false;
00411     }
00412     ln = 0;
00413     QTextStream first(&first_file);
00414     size_t nb_timepoints = times.size();
00415     size_t current_normal = 0;
00416     while(!first.atEnd())
00417     {
00418       QString line = first.readLine().simplified();
00419       ln++;
00420       int comment = line.indexOf('#');
00421       if(comment != -1)
00422         line.truncate(comment);
00423       if(!line.isEmpty())
00424       {
00425         QStringList fields = line.split(' ');
00426         QString field_type = fields[0];
00427         if(field_type == "v")
00428         {
00429           if(fields.size() != 4)
00430           {
00431             _errorMsg = QString(tr("Error in OBJ file '%1', line %2: vertex position must 3D")).arg(first_filepath).arg(ln);
00432             return false;
00433           }
00434           junction j;
00435           double x,y,z;
00436           bool ok;
00437           x = fields[1].toDouble(&ok);
00438           if(!ok)
00439           {
00440             _errorMsg = QString(tr("Error in OBJ file '%1', line %2: x coordinate is not convertible to a double number")).arg(first_filepath).arg(ln);
00441             return false;
00442           }
00443           y = fields[2].toDouble(&ok);
00444           if(!ok)
00445           {
00446             _errorMsg = QString(tr("Error in OBJ file '%1', line %2: y coordinate is not convertible to a double number")).arg(first_filepath).arg(ln);
00447             return false;
00448           }
00449           z = fields[3].toDouble(&ok);
00450           if(!ok)
00451           {
00452             _errorMsg = QString(tr("Error in OBJ file '%1', line %2: z coordinate is not convertible to a double number")).arg(first_filepath).arg(ln);
00453             return false;
00454           }
00455           j->pos.resize(nb_timepoints, Point3d(x,y,z));
00456           j->id = (int)vertices.size();
00457           vertices.push_back(j);
00458           S.insert(j);
00459         }
00460         else if(field_type == "vn")
00461         {
00462           if(current_normal >= vertices.size())
00463           {
00464             _errorMsg = QString(tr("Error in OBJ file '%1', line %2: the vertex normal is specified before the vertex")).arg(first_filepath).arg(ln);
00465             return false;
00466           }
00467           if(fields.size() != 4)
00468           {
00469             _errorMsg = QString(tr("Error in OBJ file '%1', line %2: vertex normal must 3D")).arg(first_filepath).arg(ln);
00470             return false;
00471           }
00472           double x,y,z;
00473           bool ok;
00474           x = fields[1].toDouble(&ok);
00475           if(!ok)
00476           {
00477             _errorMsg = QString(tr("Error in OBJ file '%1', line %2: x coordinate is not convertible to a double number")).arg(first_filepath).arg(ln);
00478             return false;
00479           }
00480           y = fields[2].toDouble(&ok);
00481           if(!ok)
00482           {
00483             _errorMsg = QString(tr("Error in OBJ file '%1', line %2: y coordinate is not convertible to a double number")).arg(first_filepath).arg(ln);
00484             return false;
00485           }
00486           z = fields[3].toDouble(&ok);
00487           if(!ok)
00488           {
00489             _errorMsg = QString(tr("Error in OBJ file '%1', line %2: z coordinate is not convertible to a double number")).arg(first_filepath).arg(ln);
00490             return false;
00491           }
00492           const junction& j = vertices[current_normal];
00493           j->normal.resize(nb_timepoints, Point3d(x,y,z));
00494         }
00495         else if(field_type == "f")
00496         {
00497           if(fields.size() != 4)
00498           {
00499             _errorMsg = QString(tr("Error in OBJ file '%1', line %2: face must be a triangle")).arg(first_filepath).arg(ln);
00500             return false;
00501           }
00502           int i1, i2, i3;
00503           bool ok;
00504           i1 = fields[1].toInt(&ok);
00505           if(!ok)
00506           {
00507             _errorMsg = QString(tr("Error in OBJ file '%1', line %2: first vertex index is not an integer")).arg(first_filepath).arg(ln);
00508             return false;
00509           }
00510           if(i1 > (int)vertices.size())
00511           {
00512             _errorMsg = QString(tr("Error in OBJ file '%1', line %2: first vertex index doesn't reference an existing vertex")).arg(first_filepath).arg(ln);
00513             return false;
00514           }
00515           i2 = fields[2].toInt(&ok);
00516           if(!ok)
00517           {
00518             _errorMsg = QString(tr("Error in OBJ file '%1', line %2: second vertex index is not an integer")).arg(first_filepath).arg(ln);
00519             return false;
00520           }
00521           if(i2 > (int)vertices.size())
00522           {
00523             _errorMsg = QString(tr("Error in OBJ file '%1', line %2: second vertex index doesn't reference an existing vertex")).arg(first_filepath).arg(ln);
00524             return false;
00525           }
00526           i3 = fields[3].toInt(&ok);
00527           if(!ok)
00528           {
00529             _errorMsg = QString(tr("Error in OBJ file '%1', line %2: third vertex index is not an integer")).arg(first_filepath).arg(ln);
00530             return false;
00531           }
00532           if(i3 > (int)vertices.size())
00533           {
00534             _errorMsg = QString(tr("Error in OBJ file '%1', line %2: third vertex index doesn't reference an existing vertex")).arg(first_filepath).arg(ln);
00535             return false;
00536           }
00537           cell f;
00538           f->id = (int)faces.size();
00539           S.insert(f);
00540           junction v1 = vertices[i1-1];
00541           junction v2 = vertices[i2-1];
00542           junction v3 = vertices[i3-1];
00543           S.insertEdge(f, v1);
00544           S.insertEdge(f, v2);
00545           S.spliceAfter(f, v2, v3);
00546           faces.push_back(f);
00547         }
00548       }
00549     }
00550     first_file.close();
00551     // Read the other positions
00552     for(size_t i = 1 ; i < times.size() ; ++i)
00553     {
00554       QString filepath = main_dir.filePath(data_files[i]);
00555       QFile file(filepath);
00556       if(!file.open(QIODevice::ReadOnly))
00557       {
00558         _errorMsg = QString(tr("Impossible to open file '%1' for reading")).arg(filepath);
00559         times.clear();
00560         return false;
00561       }
00562       ln = 0;
00563       QTextStream fstream(&file);
00564       current_normal = 0;
00565       size_t current_vertex = 0;
00566       while(!fstream.atEnd())
00567       {
00568         QString line = fstream.readLine().simplified();
00569         ln++;
00570         int comment = line.indexOf('#');
00571         if(comment != -1)
00572           line.truncate(comment);
00573         if(!line.isEmpty())
00574         {
00575           QStringList fields = line.split(' ');
00576           QString field_type = fields[0];
00577           if(field_type == "v")
00578           {
00579             if(fields.size() != 4)
00580             {
00581               _errorMsg = QString(tr("Error in OBJ file '%1', line %2: vertex position must 3D")).arg(filepath).arg(ln);
00582               return false;
00583             }
00584             double x,y,z;
00585             bool ok;
00586             x = fields[1].toDouble(&ok);
00587             if(!ok)
00588             {
00589               _errorMsg = QString(tr("Error in OBJ file '%1', line %2: x coordinate is not convertible to a double number")).arg(filepath).arg(ln);
00590               return false;
00591             }
00592             y = fields[2].toDouble(&ok);
00593             if(!ok)
00594             {
00595               _errorMsg = QString(tr("Error in OBJ file '%1', line %2: y coordinate is not convertible to a double number")).arg(filepath).arg(ln);
00596               return false;
00597             }
00598             z = fields[3].toDouble(&ok);
00599             if(!ok)
00600             {
00601               _errorMsg = QString(tr("Error in OBJ file '%1', line %2: z coordinate is not convertible to a double number")).arg(filepath).arg(ln);
00602               return false;
00603             }
00604             vertices[current_vertex++]->pos[i] = Point3d(x,y,z);
00605           }
00606           else if(field_type == "vn")
00607           {
00608             if(fields.size() != 4)
00609             {
00610               _errorMsg = QString(tr("Error in OBJ file '%1', line %2: vertex normal must 3D")).arg(filepath).arg(ln);
00611               return false;
00612             }
00613             double x,y,z;
00614             bool ok;
00615             x = fields[1].toDouble(&ok);
00616             if(!ok)
00617             {
00618               _errorMsg = QString(tr("Error in OBJ file '%1', line %2: x coordinate is not convertible to a double number")).arg(filepath).arg(ln);
00619               return false;
00620             }
00621             y = fields[2].toDouble(&ok);
00622             if(!ok)
00623             {
00624               _errorMsg = QString(tr("Error in OBJ file '%1', line %2: y coordinate is not convertible to a double number")).arg(filepath).arg(ln);
00625               return false;
00626             }
00627             z = fields[3].toDouble(&ok);
00628             if(!ok)
00629             {
00630               _errorMsg = QString(tr("Error in OBJ file '%1', line %2: z coordinate is not convertible to a double number")).arg(filepath).arg(ln);
00631               return false;
00632             }
00633             vertices[current_normal++]->normal[i] = Point3d(x,y,z);
00634           }
00635         }
00636       }
00637     }
00638     // Reconstruct the graphs
00639     forall(const junction& j, S.junctions())
00640     {
00641       if(!S.iEmpty(j))
00642       {
00643         cell left = S.iAnyIn(j);
00644         cell right = left;
00645         S.insertEdge(j, left);
00646         while(S.valence(j) != S.iValence(j))
00647         {
00648           forall(const cell& c, S.iNeighbors(j))
00649           {
00650             if(S.edge(j,c))
00651               continue;
00652             //              const junction& right_j = S.prevTo(right, j);
00653             //              const junction& left_j = S.nextTo(left, j);
00654             if(S.edge(c, S.prevTo(right, j)))
00655             {
00656               S.spliceAfter(j, right, c);
00657               right = c;
00658             }
00659             else if(S.edge(c, S.nextTo(left, j)))
00660             {
00661               S.spliceBefore(j, left, c);
00662               left = c;
00663             }
00664           }
00665         }
00666       }
00667     }
00668     // For all vertices that do not have their normals ... compute them !
00669     forall(const junction& j, S.junctions())
00670     {
00671       if(j->normal.empty())
00672       {
00673         std::vector<Point3d> &normal = j->normal;
00674         normal.resize(nb_timepoints);
00675         const std::vector<Point3d>& jpos = j->pos;
00676         forall(const cell& c, S.neighbors(j))
00677         {
00678           const junction& jp = S.prevTo(c, j);
00679           const junction& jn = S.nextTo(c, j);
00680           const std::vector<Point3d>& jppos = jp->pos;
00681           const std::vector<Point3d>& jnpos = jn->pos;
00682           for(size_t i = 0 ; i < nb_timepoints ; ++i)
00683           {
00684             normal[i] += (jnpos[i]-jpos[i]) ^ (jppos[i]-jpos[i]);
00685           }
00686         }
00687         for(size_t i = 0 ; i < nb_timepoints ; ++i)
00688         {
00689           normal[i].normalize();
00690         }
00691       }
00692     }
00693     _valid = true;
00694     return true;
00695   }
00696 
00697   size_t TriangleGrowth::size() const
00698   {
00699     return times.size();
00700   }
00701 
00702   void TriangleGrowth::setTime(double t)
00703   {
00704     if(!_valid)
00705       return;
00706     if(t < times.front())
00707       t = times.front();
00708     else if(t > times.back())
00709       t = times.back();
00710     _time = t;
00711     for(size_t i = 0 ; i < times.size()-1 ; ++i)
00712     {
00713       if(times[i+1] > t)
00714       {
00715         _time_pos = (int)i;
00716         return;
00717       }
00718     }
00719     _time_pos = (int)times.size()-1;
00720   }
00721 
00722   const TriangleSurface& TriangleGrowth::surface() const
00723   {
00724     if(TS.time != _time)
00725     {
00726       TS.time = _time;
00727       TS.time_index = _time_pos;
00728       setSurface(TS);
00729     }
00730     return TS;
00731   }
00732 
00733   TriangleSurface TriangleGrowth::surface(double time)
00734   {
00735     TriangleSurface ts;
00736     if(time < times.front())
00737       time = times.front();
00738     else if(time > times.back())
00739       time = times.back();
00740     ts.time = time;
00741     ts.time_index = (int)times.size()-1;
00742     for(size_t i = 0 ; i < times.size()-1 ; ++i)
00743     {
00744       if(times[i+1] > time)
00745       {
00746         ts.time_index = (int)i;
00747         break;
00748       }
00749     }
00750     setSurface(ts);
00751     return ts;
00752   }
00753 
00754   void TriangleGrowth::setSurface(TriangleSurface& ts) const
00755   {
00756     if(ts.S.empty())
00757     {
00758       OUT("Reset the structure");
00759       // Create the structure
00760       ts.vertices.resize(vertices.size(), TriangleSurface::junction(0));
00761       ts.faces.resize(faces.size(), TriangleSurface::cell(0));
00762       OUT("Create the faces " << flush);
00763       // Create and insert faces
00764       for(size_t i = 0 ; i < faces.size() ; ++i)
00765       {
00766         OUT('.' << flush);
00767         TriangleSurface::cell c;
00768         c->id = (int)i;
00769         ts.S.insert(c);
00770         ts.faces[i] = c;
00771       }
00772       OUT(" OK");
00773       OUT("Create the vertices " << flush);
00774       // Create and insert vertices
00775       for(size_t i = 0 ; i < vertices.size() ; ++i)
00776       {
00777         OUT('.' << flush);
00778         TriangleSurface::junction j;
00779         j->id = (int)i;
00780         ts.S.insert(j);
00781         ts.vertices[i] = j;
00782       }
00783       OUT(" OK");
00784       // Copy cell to vertex structure
00785       forall(const cell& c, S.cells())
00786       {
00787         const TriangleSurface::cell& ts_c = ts.S.reference(ts.faces[c->id]);
00788         TriangleSurface::junction prev(0);
00789         forall(const junction& n, S.neighbors(c))
00790         {
00791           if(prev)
00792             ts.S.spliceAfter(ts_c, prev, ts.vertices[n->id]);
00793           else
00794             ts.S.insertEdge(ts_c, ts.vertices[n->id]);
00795           prev = ts.vertices[n->id];
00796         }
00797       }
00798       // Copy vertex to cell structure
00799       forall(const junction& c, S.junctions())
00800       {
00801         const TriangleSurface::junction& ts_c = ts.S.reference(ts.vertices[c->id]);
00802         TriangleSurface::cell prev(0);
00803         forall(const cell& n, S.neighbors(c))
00804         {
00805           if(prev)
00806             ts.S.spliceAfter(ts_c, prev, ts.faces[n->id]);
00807           else
00808             ts.S.insertEdge(ts_c, ts.faces[n->id]);
00809           prev = ts.faces[n->id];
00810         }
00811       }
00812     }
00813     double dt = ts.time - times[ts.time_index];
00814     size_t time_index = ts.time_index;
00815     Point3d pmin(HUGE_VAL, HUGE_VAL, HUGE_VAL);
00816     Point3d pmax(-HUGE_VAL, -HUGE_VAL, -HUGE_VAL);
00817     if(dt == 0)
00818     {
00819       forall(const TriangleSurface::junction& v, ts.S.junctions())
00820       {
00821         const Point3d& pos = vertices[v->id]->pos[time_index];
00822         v->pos = pos;
00823         v->normal = vertices[v->id]->normal[time_index];
00824         if(pos.x() < pmin.x()) pmin.x() = pos.x();
00825         if(pos.y() < pmin.y()) pmin.y() = pos.y();
00826         if(pos.z() < pmin.z()) pmin.z() = pos.z();
00827         if(pos.x() > pmax.x()) pmax.x() = pos.x();
00828         if(pos.y() > pmax.y()) pmax.y() = pos.y();
00829         if(pos.z() > pmax.z()) pmax.z() = pos.z();
00830       }
00831     }
00832     else
00833     {
00834       dt /= times[time_index+1]-times[time_index];
00835       forall(const TriangleSurface::junction& v, ts.S.junctions())
00836       {
00837         Point3d pos = (1-dt)*vertices[v->id]->pos[time_index] + dt*vertices[v->id]->pos[time_index+1];
00838         v->pos = pos;
00839         v->normal = (1-dt)*vertices[v->id]->normal[time_index] + dt*vertices[v->id]->normal[time_index+1];
00840         if(pos.x() < pmin.x()) pmin.x() = pos.x();
00841         if(pos.y() < pmin.y()) pmin.y() = pos.y();
00842         if(pos.z() < pmin.z()) pmin.z() = pos.z();
00843         if(pos.x() > pmax.x()) pmax.x() = pos.x();
00844         if(pos.y() > pmax.y()) pmax.y() = pos.y();
00845         if(pos.z() > pmax.z()) pmax.z() = pos.z();
00846       }
00847     }
00848     // Then, reconstruct the normals for each face
00849     forall(const TriangleSurface::cell& c, ts.S.cells())
00850     {
00851       const TriangleSurface::junction& j1 = ts.S.anyIn(c);
00852       const TriangleSurface::junction& j2 = ts.S.nextTo(c, j1);
00853       const TriangleSurface::junction& j3 = ts.S.nextTo(c, j2);
00854       c->normal = util::normalized((j2->pos - j1->pos)^(j3->pos - j1->pos));
00855     }
00856     ts.pmin = pmin;
00857     ts.pmax = pmax;
00858     //Point3d delta = pmax - pmin;
00859 //    ts.max_sq_dist = max(max(pmax.x(), pmax.y()), pmax.z())/8;
00860 //    ts.max_sq_dist *= ts.max_sq_dist;
00861   }
00862 
00863   template <typename ComplexGraph>
00864   static TriangleSurface::Position getVertexPosition(const typename ComplexGraph::junction& j, const ComplexGraph& S)
00865   {
00866     TriangleSurface::Position result;
00867     const typename ComplexGraph::cell& c = S.anyIn(j);
00868     result.face = c->id;
00869     const typename ComplexGraph::junction& v2 = S.nextTo(c, j);
00870     const typename ComplexGraph::junction& v3 = S.nextTo(c, v2);
00871     result.v1 = j->id;
00872     result.v2 = v2->id;
00873     result.v3 = v3->id;
00874     result.barycentric = Point3d(1,0,0);
00875     return result;
00876   }
00877 
00878   TriangleGrowth::Position TriangleGrowth::vertexPosition(const junction& j) const
00879   {
00880     return getVertexPosition(j, S);
00881   }
00882 
00883   TriangleSurface::Position TriangleSurface::vertexPosition(const junction& j) const
00884   {
00885     return getVertexPosition(j, S);
00886   }
00887 
00888   std::vector<TriangleSurface::Position> TriangleSurface::nextContourVertex(const junction& first, const cell& c, const junction& j, std::vector<Position> acc) const
00889   {
00890     const junction& n = S.nextTo(c, j);
00891     if(n == first)
00892       return acc;
00893     int nb_common_cells = 0;
00894     forall(const cell& cn, S.neighbors(j))
00895     {
00896       if(S.edge(cn, n))
00897         nb_common_cells++;
00898     }
00899     if(nb_common_cells == 1)
00900     {
00901       acc.push_back(vertexPosition(n));
00902       return nextContourVertex(first, c, n, acc);
00903     }
00904     const cell& cn = S.nextTo(j, c);
00905     const junction& jn = S.nextTo(cn, j);
00906     if(jn == first)
00907       return acc;
00908     acc.push_back(vertexPosition(jn));
00909     return nextContourVertex(first, cn, jn, acc);
00910   }
00911 
00912   std::vector<TriangleGrowth::Position> TriangleGrowth::contour() const
00913   {
00914     return surface().contour();
00915   }
00916 
00917   std::vector<TriangleSurface::Position> TriangleSurface::contour() const
00918   {
00919     std::vector<Position> result;
00920     switch(faces.size())
00921     {
00922       case 0:
00923         return result; // No contour !
00924       case 1:
00925         {
00926           const cell& c = S.any_cell();
00927           const junction& j1 = S.anyIn(c);
00928           const junction& j2 = S.nextTo(c, j1);
00929           const junction& j3 = S.nextTo(c, j2);
00930           result.push_back(vertexPosition(j1));
00931           result.push_back(vertexPosition(j2));
00932           result.push_back(vertexPosition(j3));
00933         }
00934         break;
00935       case 2:
00936         {
00937           const cell& c1 = S.reference(faces[0]);
00938           const cell& c2 = S.reference(faces[1]);
00939           forall(const junction& j, S.neighbors(c1))
00940           {
00941             if(S.valence(j) == 1)
00942             {
00943               result.push_back(vertexPosition(S.prevTo(c1, j)));
00944               result.push_back(vertexPosition(j));
00945               result.push_back(vertexPosition(S.nextTo(c1, j)));
00946               break;
00947             }
00948           }
00949           forall(const junction& j, S.neighbors(c2))
00950           {
00951             if(S.valence(j) == 1)
00952             {
00953               result.push_back(vertexPosition(j));
00954               break;
00955             }
00956           }
00957         }
00958         break;
00959       default:
00960         // First, find one point of the contour
00961         forall(const junction& j, S.junctions())
00962         {
00963           if(S.border(j))
00964           {
00965             result.push_back(vertexPosition(j));
00966             forall(const cell& c, S.neighbors(j))
00967             {
00968               const junction& jn = S.nextTo(c, j);
00969               int nb_common_cells = 0;
00970               forall(const cell& cn, S.neighbors(jn))
00971               {
00972                 if(S.edge(j, cn))
00973                   nb_common_cells++;
00974               }
00975               if(nb_common_cells == 1)
00976               {
00977                 result.push_back(vertexPosition(jn));
00978                 return nextContourVertex(j, c, jn, result);
00979               }
00980             }
00981             return result;
00982           }
00983         }
00984     }
00985     return result; // Should not be reached!
00986   }
00987 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines
Generated on Fri May 31 15:37:50 2013 for VVE by  doxygen 1.6.3