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
00026
00027
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
00151
00152
00153
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
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
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
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
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
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
00653
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
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
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
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
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
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
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
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
00859
00860
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;
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
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;
00986 }
00987 }