$treeview $search $mathjax
|
Palabos
Version 1.1
$projectbrief
|
$projectbrief
|
$searchbox |
00001 /* This file is part of the Palabos library. 00002 * 00003 * Copyright (C) 2011 FlowKit Sarl 00004 * Avenue de Chailly 23 00005 * 1012 Lausanne, Switzerland 00006 * E-mail contact: contact@flowkit.com 00007 * 00008 * The most recent release of Palabos can be downloaded at 00009 * <http://www.palabos.org/> 00010 * 00011 * The library Palabos is free software: you can redistribute it and/or 00012 * modify it under the terms of the GNU Affero General Public License as 00013 * published by the Free Software Foundation, either version 3 of the 00014 * License, or (at your option) any later version. 00015 * 00016 * The library is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 * GNU Affero General Public License for more details. 00020 * 00021 * You should have received a copy of the GNU Affero General Public License 00022 * along with this program. If not, see <http://www.gnu.org/licenses/>. 00023 */ 00024 00025 /* Main author: Dimitrios Kontaxakis */ 00026 00027 #ifndef TRIANGULAR_SURFACE_MESH_HH 00028 #define TRIANGULAR_SURFACE_MESH_HH 00029 00030 #include <algorithm> 00031 #include <limits> 00032 #include <cmath> 00033 #include <cstdio> 00034 #include <cstdlib> 00035 #include <cstring> 00036 #include "offLattice/triangularSurfaceMesh.h" 00037 #include "latticeBoltzmann/geometricOperationTemplates.h" 00038 #include "core/globalDefs.h" 00039 #include "core/plbDebug.h" 00040 #include "parallelism/mpiManager.h" 00041 #include "core/util.h" 00042 #include "io/parallelIO.h" 00043 00044 #define FPEQUAL_ABS(x, y, eps) (fabs((x)-(y)) <= (eps)) // Macro definition for the function 00045 // util::fpequal_abs<T>(T x, T y, T eps) 00046 00047 namespace plb { 00048 00049 template<typename T> 00050 const T TriangularSurfaceMesh<T>::eps0 = std::numeric_limits<T>::epsilon(); 00051 00052 template<typename T> 00053 const T TriangularSurfaceMesh<T>::eps1 = 00054 (sizeof(T) == sizeof(float)) ? 00055 std::numeric_limits<float>::epsilon() : 00056 100.0 * std::numeric_limits<T>::epsilon(); 00057 00058 template<typename T> 00059 TriangularSurfaceMesh<T>::TriangularSurfaceMesh ( 00060 std::vector<Array<T,3> >& vertexList_, 00061 std::vector<plint>& emanatingEdgeList_, 00062 std::vector<typename TriangularSurfaceMesh<T>::Edge>& edgeList_, 00063 plint numVertices_ ) 00064 : vertexList(&vertexList_), 00065 emanatingEdgeList(&emanatingEdgeList_), 00066 edgeList(&edgeList_), 00067 numTriangles((plint)edges().size()/(plint)3), 00068 numVertices(numVertices_>=0 ? numVertices_ : ( (plint)vertices().size() ) ) 00069 { 00070 avoidIntegerPositions(); 00071 } 00072 00073 template<typename T> 00074 void TriangularSurfaceMesh<T>::replaceVertex(plint iVertex, Array<T,3> const& newPosition) 00075 { 00076 PLB_PRECONDITION( iVertex<getNumVertices() ); 00077 (*vertexList)[iVertex] = newPosition; 00078 // avoidIntegerPosition(iVertex); 00079 } 00080 00081 template<typename T> 00082 void TriangularSurfaceMesh<T>::resetVertices(Array<T,3> const& defaultVertex) 00083 { 00084 for (plint iVertex=0; iVertex<getNumVertices(); ++iVertex) { 00085 vertices()[iVertex] = defaultVertex; 00086 } 00087 } 00088 00089 template<typename T> 00090 inline void TriangularSurfaceMesh<T>::assertVertex(Array<T,3> const& vertex) const 00091 { 00092 #ifdef PLB_DEBUG 00093 if (global::IOpolicy().stlFilesHaveLowerBound()) { 00094 double bound = global::IOpolicy().getLowerBoundForStlFiles(); 00095 PLB_ASSERT( !(vertex[0]<bound && vertex[1]<bound && vertex[2]<bound) ); 00096 } 00097 #endif // PLB_DEBUG 00098 } 00099 00100 template<typename T> 00101 inline Array<T,3> const& TriangularSurfaceMesh<T>::getVertex ( 00102 plint iTriangle, int localVertex) const 00103 { 00104 PLB_ASSERT(iTriangle >= 0 && iTriangle < numTriangles && 00105 (localVertex == 0 || localVertex == 1 || localVertex == 2)); 00106 Array<T,3> const& vertex ( 00107 vertices()[ edges()[ 3*iTriangle + ((localVertex == 0) ? 2 : localVertex-1) ].pv ] ); 00108 assertVertex(vertex); 00109 return vertex; 00110 } 00111 00112 template<typename T> 00113 inline Array<T,3>& TriangularSurfaceMesh<T>::getVertex ( 00114 plint iTriangle, int localVertex ) 00115 { 00116 PLB_ASSERT(iTriangle >= 0 && iTriangle < numTriangles && 00117 (localVertex == 0 || localVertex == 1 || localVertex == 2)); 00118 Array<T,3>& vertex ( 00119 vertices()[ edges()[ 3*iTriangle + ((localVertex == 0) ? 2 : localVertex-1) ].pv ] ); 00120 assertVertex(vertex); 00121 return vertex; 00122 } 00123 00124 template<typename T> 00125 bool TriangularSurfaceMesh<T>::isValidVertex ( 00126 plint iTriangle, int localVertex) const 00127 { 00128 PLB_ASSERT(iTriangle >= 0 && iTriangle < numTriangles && 00129 (localVertex == 0 || localVertex == 1 || localVertex == 2)); 00130 if (global::IOpolicy().stlFilesHaveLowerBound()) { 00131 double bound = global::IOpolicy().getLowerBoundForStlFiles(); 00132 Array<T,3> const& vertex ( 00133 vertices()[ edges()[ 3*iTriangle + ((localVertex == 0) ? 2 : localVertex-1) ].pv ] ); 00134 return !(vertex[0]<bound && vertex[1]<bound && vertex[2]<bound); 00135 } 00136 else { 00137 return true; 00138 } 00139 } 00140 00141 template<typename T> 00142 inline Array<T,3> const& TriangularSurfaceMesh<T>::getVertex ( 00143 plint iVertex) const 00144 { 00145 PLB_ASSERT(iVertex >= 0 && iVertex < numVertices); 00146 Array<T,3> const& vertex(vertices()[iVertex]); 00147 assertVertex(vertex); 00148 return vertex; 00149 } 00150 00151 template<typename T> 00152 inline Array<T,3>& TriangularSurfaceMesh<T>::getVertex ( 00153 plint iVertex) 00154 { 00155 PLB_ASSERT(iVertex >= 0 && iVertex < numVertices); 00156 Array<T,3>& vertex(vertices()[iVertex]); 00157 assertVertex(vertex); 00158 return vertex; 00159 } 00160 00161 template<typename T> 00162 inline bool TriangularSurfaceMesh<T>::isValidVertex ( 00163 plint iVertex) const 00164 { 00165 PLB_ASSERT(iVertex >= 0 && iVertex < numVertices); 00166 if (global::IOpolicy().stlFilesHaveLowerBound()) { 00167 Array<T,3> const& vertex(vertices()[iVertex]); 00168 double bound = global::IOpolicy().getLowerBoundForStlFiles(); 00169 return !(vertex[0]<bound && vertex[1]<bound && vertex[2]<bound); 00170 } 00171 else { 00172 return true; 00173 } 00174 } 00175 00176 template<typename T> 00177 void TriangularSurfaceMesh<T>::computeBoundingBox ( 00178 Array<T,2>& xRange, Array<T,2>& yRange, Array<T,2>& zRange ) const 00179 { 00180 T minVal = std::numeric_limits<T>::min(); 00181 T maxVal = std::numeric_limits<T>::max(); 00182 xRange = Array<T,2>(maxVal, minVal); 00183 yRange = Array<T,2>(maxVal, minVal); 00184 zRange = Array<T,2>(maxVal, minVal); 00185 for (plint iVertex=0; iVertex<getNumVertices(); ++iVertex) { 00186 Array<T,3> const& vertex = getVertex(iVertex); 00187 xRange[0] = std::min(xRange[0], vertex[0]); 00188 xRange[1] = std::max(xRange[1], vertex[0]); 00189 yRange[0] = std::min(yRange[0], vertex[1]); 00190 yRange[1] = std::max(yRange[1], vertex[1]); 00191 zRange[0] = std::min(zRange[0], vertex[2]); 00192 zRange[1] = std::max(zRange[1], vertex[2]); 00193 } 00194 } 00195 00196 template<typename T> 00197 void TriangularSurfaceMesh<T>::translate(Array<T,3> const& vector) 00198 { 00199 if (util::fpequal(norm(vector), T(), eps0)) 00200 return; 00201 00202 for (plint i = 0; i < numVertices; i++) 00203 getVertex(i) += vector; 00204 } 00205 00206 template<typename T> 00207 void TriangularSurfaceMesh<T>::scale(T alpha) 00208 { 00209 00210 PLB_ASSERT(! util::fpequal(alpha, T(), eps0)); 00211 if (util::fpequal(alpha, (T) 1.0, eps0)) 00212 return; 00213 00214 for (plint i = 0; i < numVertices; i++) 00215 getVertex(i) *= alpha; 00216 } 00217 00218 template<typename T> 00219 void TriangularSurfaceMesh<T>::rotate(T phi, T theta, T psi) 00220 { 00221 static const T pi = acos(-1.0); 00222 00223 PLB_ASSERT((theta > T() || util::fpequal(theta, T(), eps0)) && 00224 (theta < pi || util::fpequal(theta, pi, eps0))); 00225 00226 T a[3][3]; 00227 a[0][0] = 1.0; 00228 a[0][1] = 0.0; 00229 a[0][2] = 0.0; 00230 a[1][0] = 0.0; 00231 a[1][1] = cos(theta); 00232 a[1][2] = sin(theta); 00233 a[2][0] = 0.0; 00234 a[2][1] = -sin(theta); 00235 a[2][2] = cos(theta); 00236 00237 T b[3][3]; 00238 b[0][0] = cos(phi); 00239 b[0][1] = sin(phi); 00240 b[0][2] = 0.0; 00241 b[1][0] = -sin(phi); 00242 b[1][1] = cos(phi); 00243 b[1][2] = 0.0; 00244 b[2][0] = 0.0; 00245 b[2][1] = 0.0; 00246 b[2][2] = 1.0; 00247 00248 T c[3][3]; 00249 for (int i = 0; i < 3; i++) { 00250 for (int j = 0; j < 3; j++) { 00251 c[i][j] = 0.0; 00252 for (int k = 0; k < 3; k++) { 00253 c[i][j] += a[i][k]*b[k][j]; 00254 } 00255 } 00256 } 00257 00258 b[0][0] = cos(psi); 00259 b[0][1] = sin(psi); 00260 b[0][2] = 0.0; 00261 b[1][0] = -sin(psi); 00262 b[1][1] = cos(psi); 00263 b[1][2] = 0.0; 00264 b[2][0] = 0.0; 00265 b[2][1] = 0.0; 00266 b[2][2] = 1.0; 00267 00268 for (int i = 0; i < 3; i++) { 00269 for (int j = 0; j < 3; j++) { 00270 a[i][j] = 0.0; 00271 for (int k = 0; k < 3; k++) { 00272 a[i][j] += b[i][k]*c[k][j]; 00273 } 00274 } 00275 } 00276 00277 for (plint iVertex = 0; iVertex < numVertices; iVertex++) { 00278 Array<T,3> x = getVertex(iVertex); 00279 for (int i = 0; i < 3; i++) { 00280 getVertex(iVertex)[i] = 0.0; 00281 for (int j = 0; j < 3; j++) { 00282 getVertex(iVertex)[i] += a[i][j]*x[j]; 00283 } 00284 } 00285 } 00286 } 00287 00288 template<typename T> 00289 void TriangularSurfaceMesh<T>::smooth(plint maxiter, T relax, bool isMeasureWeighted) 00290 { 00291 PLB_ASSERT(maxiter >= 0); 00292 PLB_ASSERT((relax > (T) 0.0 || fabs(relax) <= eps0) && 00293 (relax < (T) 1.0 || fabs(relax - 1.0) <= eps0)); 00294 00295 if (maxiter <= 0) 00296 return; 00297 00298 std::vector<Array<T,3> > *bp0; 00299 bp0 = vertexList; 00300 00301 std::vector<Array<T,3> > buf; 00302 buf.resize(numVertices); 00303 00304 std::vector<Array<T,3> > *bp1; 00305 bp1 = &buf; 00306 00307 if (isMeasureWeighted) { 00308 for (plint iter = 0; iter < maxiter; iter++) { 00309 for (plint iVertex = 0; iVertex < numVertices; iVertex++) { 00310 Array<T,3> iVertexPos = (*bp0)[iVertex]; 00311 std::vector<plint> neighborVertexIds = getNeighborVertexIds(iVertex); 00312 00313 if (isInteriorVertex(iVertex)) { 00314 pluint sizeIds = neighborVertexIds.size(); 00315 T area = (T) 0.0; 00316 Array<T,3> tmp((T) 0.0, (T) 0.0, (T) 0.0); 00317 for (pluint i = 0; i < sizeIds; i++) { 00318 plint jVertex = neighborVertexIds[i]; 00319 plint kVertex = (i + 1 < sizeIds) ? neighborVertexIds[i + 1] : neighborVertexIds[0]; 00320 00321 Array<T,3> jVertexPos = (*bp0)[jVertex]; 00322 Array<T,3> kVertexPos = (*bp0)[kVertex]; 00323 00324 Array<T,3> middleVertexPos = (iVertexPos + jVertexPos + kVertexPos) / (T) 3.0; 00325 00326 Array<T,3> eij = jVertexPos - iVertexPos; 00327 Array<T,3> eik = kVertexPos - iVertexPos; 00328 Array<T,3> n; 00329 crossProduct(eij, eik, n); 00330 T locArea = (T) 0.5 * norm(n); 00331 00332 area += locArea; 00333 tmp += middleVertexPos * locArea; 00334 } 00335 (*bp1)[iVertex] = (1.0 - relax) * (*bp0)[iVertex] + relax * tmp / area; 00336 } else { 00337 plint jVertex = -1, kVertex = -1; 00338 int counter = 0; 00339 for (pluint i = 0; i < neighborVertexIds.size(); i++) { 00340 plint vertexId = neighborVertexIds[i]; 00341 if (isBoundaryVertex(vertexId)) { 00342 if (isBoundaryEdge(iVertex, vertexId)) { 00343 counter++; 00344 if (counter == 1) { 00345 jVertex = vertexId; 00346 } else if (counter == 2) { 00347 kVertex = vertexId; 00348 } else { 00349 PLB_ASSERT(false); // Problem with the boundary of the surface mesh. 00350 } 00351 } 00352 } 00353 } 00354 00355 PLB_ASSERT(counter == 2); // Problem with the boundary of the surface mesh. 00356 00357 Array<T,3> jVertexPos = (*bp0)[jVertex]; 00358 Array<T,3> kVertexPos = (*bp0)[kVertex]; 00359 00360 Array<T,3> middleVertexPos0 = (iVertexPos + jVertexPos) / (T) 2.0; 00361 Array<T,3> middleVertexPos1 = (iVertexPos + kVertexPos) / (T) 2.0; 00362 00363 T length0 = norm(jVertexPos - iVertexPos); 00364 T length1 = norm(kVertexPos - iVertexPos); 00365 00366 (*bp1)[iVertex] = (1.0 - relax) * (*bp0)[iVertex] + 00367 relax * (middleVertexPos0*length0 + middleVertexPos1*length1) / (length0 + length1); 00368 } 00369 } 00370 // Swap buffers. 00371 std::vector<Array<T,3> > *tmp = bp0; 00372 bp0 = bp1; 00373 bp1 = tmp; 00374 } 00375 } else { 00376 for (plint iter = 0; iter < maxiter; iter++) { 00377 for (plint iVertex = 0; iVertex < numVertices; iVertex++) { 00378 Array<T,3> iVertexPos = (*bp0)[iVertex]; 00379 std::vector<plint> neighborVertexIds = getNeighborVertexIds(iVertex); 00380 00381 if (isInteriorVertex(iVertex)) { 00382 pluint sizeIds = neighborVertexIds.size(); 00383 Array<T,3> tmp((T) 0.0, (T) 0.0, (T) 0.0); 00384 for (pluint i = 0; i < sizeIds; i++) { 00385 tmp += (*bp0)[neighborVertexIds[i]]; 00386 } 00387 (*bp1)[iVertex] = (1.0 - relax) * (*bp0)[iVertex] + relax * tmp / (T) sizeIds; 00388 } else { 00389 plint jVertex = -1, kVertex = -1; 00390 int counter = 0; 00391 for (pluint i = 0; i < neighborVertexIds.size(); i++) { 00392 plint vertexId = neighborVertexIds[i]; 00393 if (isBoundaryVertex(vertexId)) { 00394 if (isBoundaryEdge(iVertex, vertexId)) { 00395 counter++; 00396 if (counter == 1) { 00397 jVertex = vertexId; 00398 } else if (counter == 2) { 00399 kVertex = vertexId; 00400 } else { 00401 PLB_ASSERT(false); // Problem with the boundary of the surface mesh. 00402 } 00403 } 00404 } 00405 } 00406 00407 PLB_ASSERT(counter == 2); // Problem with the boundary of the surface mesh. 00408 00409 (*bp1)[iVertex] = (1.0 - relax) * (*bp0)[iVertex] + 00410 relax * ((*bp0)[jVertex] + (*bp0)[kVertex]) / (T) 2.0; 00411 } 00412 } 00413 // Swap buffers. 00414 std::vector<Array<T,3> > *tmp = bp0; 00415 bp0 = bp1; 00416 bp1 = tmp; 00417 } 00418 } 00419 00420 // Final copy of vertex positions. 00421 if (vertexList != bp0) 00422 for (plint iVertex = 0; iVertex < numVertices; iVertex++) 00423 (*vertexList)[iVertex] = (*bp0)[iVertex]; 00424 } 00425 00426 template<typename T> 00427 inline plint TriangularSurfaceMesh<T>::getVertexId(plint iTriangle, plint localVertex) const { 00428 PLB_ASSERT(iTriangle >= 0 && iTriangle < numTriangles && 00429 (localVertex == 0 || localVertex == 1 || localVertex == 2)); 00430 return edges() [ 3*iTriangle + ((localVertex == 0) ? 2 : localVertex-1) ].pv; 00431 } 00432 00433 template<typename T> 00434 std::vector<plint> TriangularSurfaceMesh<T>::getNeighborVertexIds( 00435 plint iVertex) const 00436 { 00437 PLB_ASSERT(iVertex >= 0 && iVertex < numVertices); 00438 00439 std::vector<plint> neighborVertexIds; 00440 00441 plint ee = emanatingEdges()[iVertex]; 00442 neighborVertexIds.push_back(edges()[ee].pv); 00443 00444 plint pe = prev(ee); 00445 plint e = pe; 00446 if ((e = edges()[e].ne) < 0) { 00447 neighborVertexIds.push_back(edges()[prev(pe)].pv); 00448 e = ee; 00449 } 00450 00451 while (e != ee) { 00452 neighborVertexIds.push_back(edges()[e].pv); 00453 00454 e = pe = prev(e); 00455 if ((e = edges()[e].ne) < 0) { 00456 neighborVertexIds.push_back(edges()[prev(pe)].pv); 00457 e = ee; 00458 } 00459 } 00460 00461 PLB_ASSERT(neighborVertexIds.size() > 1); // Problem with the topology of the surface mesh. 00462 00463 return neighborVertexIds; 00464 } 00465 00466 template<typename T> 00467 std::vector<plint> TriangularSurfaceMesh<T>::getNeighborVertexIds( 00468 plint iVertex, plint jVertex) const 00469 { 00470 std::vector<plint> neighborVertexIds; 00471 std::vector<plint> adjacentTriangleIds = getAdjacentTriangleIds(iVertex, jVertex); 00472 std::vector<plint>::iterator tit = adjacentTriangleIds.begin(); 00473 for (; tit != adjacentTriangleIds.end(); ++tit) { 00474 plint iTriangle = *tit; 00475 plint id0 = getVertexId(iTriangle, 0); 00476 plint id1 = getVertexId(iTriangle, 1); 00477 plint id2 = getVertexId(iTriangle, 2); 00478 plint kVertex = (iVertex != id0 && jVertex != id0) ? id0 : 00479 ((iVertex != id1 && jVertex != id1) ? id1 : id2); 00480 neighborVertexIds.push_back(kVertex); 00481 } 00482 00483 plint size = neighborVertexIds.size(); 00484 PLB_ASSERT(size == 1 || size == 2); // Problem with the topology of the surface mesh. 00485 00486 return neighborVertexIds; 00487 } 00488 00489 template<typename T> 00490 std::vector<plint> TriangularSurfaceMesh<T>::getNeighborTriangleIds( 00491 plint iVertex ) const 00492 { 00493 PLB_ASSERT(iVertex >= 0 && iVertex < numVertices); 00494 00495 std::vector<plint> neighborTriangleIds; 00496 00497 plint ee = emanatingEdges()[iVertex]; 00498 neighborTriangleIds.push_back(ee/3); 00499 00500 plint e = prev(ee); 00501 if ((e = edges()[e].ne) < 0) 00502 e = ee; 00503 00504 while (e != ee) { 00505 neighborTriangleIds.push_back(e/3); 00506 00507 e = prev(e); 00508 if ((e = edges()[e].ne) < 0) 00509 e = ee; 00510 } 00511 00512 PLB_ASSERT(neighborTriangleIds.size() != 0); // Problem with the topology of the surface mesh. 00513 00514 return neighborTriangleIds; 00515 } 00516 00517 template<typename T> 00518 std::vector<plint> TriangularSurfaceMesh<T>::getAdjacentTriangleIds( 00519 plint iTriangle) const 00520 { 00521 PLB_ASSERT(iTriangle >= 0 && iTriangle < numTriangles); 00522 00523 std::vector<plint> adjacentTriangleIds; 00524 for (int localEdge = 0; localEdge < 3; localEdge++) { 00525 plint e = edges()[3*iTriangle + localEdge].ne; 00526 if (e >= 0) 00527 adjacentTriangleIds.push_back(e/3); 00528 } 00529 return adjacentTriangleIds; 00530 } 00531 00532 template<typename T> 00533 std::vector<plint> TriangularSurfaceMesh<T>::getAdjacentTriangleIds( 00534 plint iVertex, plint jVertex) const 00535 { 00536 PLB_ASSERT(iVertex >= 0 && iVertex < numVertices && 00537 jVertex >= 0 && jVertex < numVertices && 00538 iVertex != jVertex); 00539 00540 #ifdef PLB_DEBUG 00541 std::vector<plint> neighborVertexIds = getNeighborVertexIds(iVertex); 00542 std::vector<plint>::iterator vit; 00543 vit = find(neighborVertexIds.begin(), neighborVertexIds.end(), jVertex); 00544 PLB_ASSERT(vit != neighborVertexIds.end()); // Vertices do not belong to the same edge. 00545 #endif // PLB_DEBUG 00546 00547 std::vector<plint> adjacentTriangleIds; 00548 std::vector<plint> neighborTriangleIds = getNeighborTriangleIds(iVertex); 00549 std::vector<plint>::iterator tit = neighborTriangleIds.begin(); 00550 for (; tit != neighborTriangleIds.end(); ++tit) { 00551 plint iTriangle = *tit; 00552 plint id0 = getVertexId(iTriangle, 0); 00553 plint id1 = getVertexId(iTriangle, 1); 00554 plint id2 = getVertexId(iTriangle, 2); 00555 if ((iVertex == id0 || iVertex == id1 || iVertex == id2) && 00556 (jVertex == id0 || jVertex == id1 || jVertex == id2)) 00557 adjacentTriangleIds.push_back(iTriangle); 00558 } 00559 00560 plint size = adjacentTriangleIds.size(); 00561 PLB_ASSERT(size == 1 || size == 2); // Problem with the topology of the surface mesh. 00562 00563 return adjacentTriangleIds; 00564 } 00565 00566 template<typename T> 00567 Array<T,3> TriangularSurfaceMesh<T>::computeTriangleNormal( 00568 plint iTriangle, bool isAreaWeighted) const 00569 { 00570 PLB_ASSERT(iTriangle >= 0 && iTriangle < numTriangles); 00571 00572 Array<T,3> v0 = getVertex(iTriangle, 0); 00573 Array<T,3> v1 = getVertex(iTriangle, 1); 00574 Array<T,3> v2 = getVertex(iTriangle, 2); 00575 00576 Array<T,3> e01 = v1 - v0; 00577 Array<T,3> e02 = v2 - v0; 00578 00579 Array<T,3> n; 00580 crossProduct(e01, e02, n); 00581 if (!isAreaWeighted) 00582 n /= norm(n); 00583 00584 return n; 00585 } 00586 00587 template<typename T> 00588 Array<T,3> TriangularSurfaceMesh<T>::computeTriangleNormal( 00589 plint iVertex, plint jVertex, plint kVertex, bool isAreaWeighted) const 00590 { 00591 PLB_ASSERT(iVertex >= 0 && iVertex < numVertices && 00592 jVertex >= 0 && jVertex < numVertices && 00593 kVertex >= 0 && kVertex < numVertices && 00594 iVertex != jVertex && iVertex != kVertex && 00595 jVertex != kVertex); 00596 00597 plint id0 = iVertex; 00598 plint id1, id2; 00599 00600 std::vector<plint> neighborVertexIds = getNeighborVertexIds(iVertex); 00601 std::vector<plint>::iterator it; 00602 it = find(neighborVertexIds.begin(), neighborVertexIds.end(), jVertex); 00603 PLB_ASSERT(it != neighborVertexIds.end()); // Vertices do not belong to the same triangle. 00604 00605 plint prevVertex, nextVertex; 00606 if (jVertex == neighborVertexIds[0]) { 00607 prevVertex = neighborVertexIds[neighborVertexIds.size()-1]; 00608 nextVertex = neighborVertexIds[1]; 00609 } 00610 else if (jVertex == neighborVertexIds[neighborVertexIds.size()-1]) { 00611 prevVertex = neighborVertexIds[neighborVertexIds.size()-2]; 00612 nextVertex = neighborVertexIds[0]; 00613 } 00614 else { 00615 prevVertex = *(it - 1); 00616 nextVertex = *(it + 1); 00617 } 00618 00619 if (kVertex == prevVertex) { 00620 id1 = kVertex; 00621 id2 = jVertex; 00622 } 00623 else if (kVertex == nextVertex) { 00624 id1 = jVertex; 00625 id2 = kVertex; 00626 } 00627 else { 00628 PLB_ASSERT(false); // Vertices do not belong to the same triangle. 00629 } 00630 00631 Array<T,3> v0 = getVertex(id0); 00632 Array<T,3> v1 = getVertex(id1); 00633 Array<T,3> v2 = getVertex(id2); 00634 00635 Array<T,3> e01 = v1 - v0; 00636 Array<T,3> e02 = v2 - v0; 00637 00638 Array<T,3> n; 00639 crossProduct(e01, e02, n); 00640 if (!isAreaWeighted) 00641 n /= norm(n); 00642 00643 return n; 00644 } 00645 00646 template<typename T> 00647 Array<T,3> TriangularSurfaceMesh<T>::computeEdgeNormal( 00648 plint iVertex, plint jVertex, bool isAreaWeighted) const 00649 { 00650 Array<T,3> n; 00651 std::vector<plint> adjacentTriangleIds = getAdjacentTriangleIds(iVertex, jVertex); 00652 std::vector<plint>::iterator tit = adjacentTriangleIds.begin(); 00653 for (n.resetToZero(); tit != adjacentTriangleIds.end(); ++tit) 00654 n += computeTriangleNormal(*tit, isAreaWeighted); 00655 00656 n /= norm(n); 00657 return n; 00658 } 00659 00660 template<typename T> 00661 Array<T,3> TriangularSurfaceMesh<T>::computeVertexNormal( 00662 plint iVertex, bool isAreaWeighted) const 00663 { 00664 std::vector<plint> neighborTriangleIds = getNeighborTriangleIds(iVertex); 00665 Array<T,3> n; 00666 std::vector<plint>::iterator it = neighborTriangleIds.begin(); 00667 for (n.resetToZero(); it != neighborTriangleIds.end(); ++it) 00668 n += computeTriangleNormal(*it, isAreaWeighted); 00669 00670 n /= norm(n); 00671 return n; 00672 } 00673 00674 template<typename T> 00675 Array<T,3> TriangularSurfaceMesh<T>::computeContinuousNormal( 00676 Array<T,3> const& p, plint iTriangle, bool isAreaWeighted) const 00677 { 00678 plint id0 = getVertexId(iTriangle, 0); 00679 plint id1 = getVertexId(iTriangle, 1); 00680 plint id2 = getVertexId(iTriangle, 2); 00681 00682 Array<T,3> v0 = getVertex(id0); 00683 Array<T,3> v1 = getVertex(id1); 00684 Array<T,3> v2 = getVertex(id2); 00685 00686 Array<T,3> ep0 = v0 - p; 00687 Array<T,3> ep1 = v1 - p; 00688 Array<T,3> ep2 = v2 - p; 00689 00690 Array<T,3> n; 00691 crossProduct(ep1, ep2, n); 00692 T area0 = 0.5 * norm(n); 00693 crossProduct(ep2, ep0, n); 00694 T area1 = 0.5 * norm(n); 00695 00696 T area = computeTriangleArea(iTriangle); 00697 00698 T u = area0 / area; 00699 T v = area1 / area; 00700 00701 Array<T,3> n0 = computeVertexNormal(id0, isAreaWeighted); 00702 Array<T,3> n1 = computeVertexNormal(id1, isAreaWeighted); 00703 Array<T,3> n2 = computeVertexNormal(id2, isAreaWeighted); 00704 00705 n = u * n0 + v * n1 + ((T)1. - u - v) * n2; 00706 n /= norm(n); 00707 return n; 00708 } 00709 00710 template<typename T> 00711 T TriangularSurfaceMesh<T>::computeTriangleArea(plint iTriangle) const 00712 { 00713 Array<T,3> v0 = getVertex(iTriangle, 0); 00714 Array<T,3> v1 = getVertex(iTriangle, 1); 00715 Array<T,3> v2 = getVertex(iTriangle, 2); 00716 00717 Array<T,3> e01 = v1 - v0; 00718 Array<T,3> e02 = v2 - v0; 00719 00720 Array<T,3> n; 00721 crossProduct(e01, e02, n); 00722 return 0.5 * norm(n); 00723 } 00724 00725 template<typename T> 00726 T TriangularSurfaceMesh<T>::computeTriangleArea( 00727 plint iVertex, plint jVertex, plint kVertex) const 00728 { 00729 PLB_ASSERT(iVertex >= 0 && iVertex < numVertices && 00730 jVertex >= 0 && jVertex < numVertices && 00731 kVertex >= 0 && kVertex < numVertices && 00732 iVertex != jVertex && iVertex != kVertex && 00733 jVertex != kVertex); 00734 00735 #ifdef PLB_DEBUG 00736 std::vector<plint> neighborVertexIds = getNeighborVertexIds(iVertex); 00737 std::vector<plint>::iterator it; 00738 it = find(neighborVertexIds.begin(), neighborVertexIds.end(), jVertex); 00739 PLB_ASSERT(it != neighborVertexIds.end()); // Vertices do not belong to the same triangle. 00740 00741 plint prevVertex, nextVertex; 00742 if (jVertex == neighborVertexIds[0]) { 00743 prevVertex = neighborVertexIds[neighborVertexIds.size()-1]; 00744 nextVertex = neighborVertexIds[1]; 00745 } 00746 else if (jVertex == neighborVertexIds[neighborVertexIds.size()-1]) { 00747 prevVertex = neighborVertexIds[neighborVertexIds.size()-2]; 00748 nextVertex = neighborVertexIds[0]; 00749 } 00750 else { 00751 prevVertex = *(it - 1); 00752 nextVertex = *(it + 1); 00753 } 00754 00755 PLB_ASSERT(kVertex == prevVertex || kVertex == nextVertex); // Vertices do not belong to the same triangle. 00756 #endif // PLB_DEBUG 00757 00758 Array<T,3> v0 = getVertex(iVertex); 00759 Array<T,3> v1 = getVertex(jVertex); 00760 Array<T,3> v2 = getVertex(kVertex); 00761 00762 return plb::computeTriangleArea(v0,v1,v2); 00763 } 00764 00765 template<typename T> 00766 T TriangularSurfaceMesh<T>::computeEdgeArea(plint iVertex, plint jVertex) const 00767 { 00768 T area = T(); 00769 std::vector<plint> adjacentTriangleIds = getAdjacentTriangleIds(iVertex, jVertex); 00770 std::vector<plint>::iterator tit = adjacentTriangleIds.begin(); 00771 for (; tit != adjacentTriangleIds.end(); ++tit) 00772 area += computeTriangleArea(*tit); 00773 00774 return area/3.0; 00775 } 00776 00777 template<typename T> 00778 T TriangularSurfaceMesh<T>::computeVertexArea(plint iVertex) const 00779 { 00780 std::vector<plint> neighborTriangleIds = getNeighborTriangleIds(iVertex); 00781 T area = T(); 00782 std::vector<plint>::iterator it = neighborTriangleIds.begin(); 00783 for (; it != neighborTriangleIds.end(); ++it) 00784 { 00785 area += computeTriangleArea(*it); 00786 } 00787 00788 return area/3.0; 00789 } 00790 00791 template<typename T> 00792 T TriangularSurfaceMesh<T>::computeEdgeLength(plint iVertex, plint jVertex) const 00793 { 00794 #ifdef PLB_DEBUG 00795 std::vector<plint> neighborVertexIds = getNeighborVertexIds(iVertex); 00796 std::vector<plint>::iterator it; 00797 it = find(neighborVertexIds.begin(), neighborVertexIds.end(), jVertex); 00798 PLB_ASSERT(it != neighborVertexIds.end()); // Vertices do not belong to the same edge. 00799 #endif // PLB_DEBUG 00800 00801 return norm(getVertex(iVertex) - getVertex(jVertex)); 00802 } 00803 00804 template<typename T> 00805 T TriangularSurfaceMesh<T>::computeDihedralAngle(plint iVertex, plint jVertex) const 00806 { 00807 std::vector<plint> adjacentTriangleIds = getAdjacentTriangleIds(iVertex, jVertex); 00808 if (adjacentTriangleIds.size() == 1) 00809 return T(); 00810 00811 return angleBetweenVectors(computeTriangleNormal(adjacentTriangleIds[0]), 00812 computeTriangleNormal(adjacentTriangleIds[1])); 00813 } 00814 00815 template<typename T> 00816 T TriangularSurfaceMesh<T>::computeEdgeTileSpan(plint iVertex, plint jVertex) const 00817 { 00818 std::vector<plint> neighborVertexIds = getNeighborVertexIds(iVertex, jVertex); 00819 00820 Array<T,3> v0 = getVertex(neighborVertexIds[0]); 00821 Array<T,3> v1 = getVertex(iVertex); 00822 Array<T,3> v2 = getVertex(jVertex); 00823 00824 Array<T,3> v01 = v1 - v0; 00825 Array<T,3> v21 = v1 - v2; 00826 T angle_012 = angleBetweenVectors(v21, v01); 00827 00828 T span = fabs(sin(angle_012)) * norm(v01); 00829 00830 if (neighborVertexIds.size() == 1) 00831 return span/6.0; 00832 00833 Array<T,3> v3 = getVertex(neighborVertexIds[1]); 00834 00835 Array<T,3> v32 = v2 - v3; 00836 Array<T,3> v12 = -v21; 00837 T angle_321 = angleBetweenVectors(v12, v32); 00838 00839 span += fabs(sin(angle_321)) * norm(v32); 00840 00841 return span/6.0; 00842 } 00843 00844 template<typename T> 00845 void TriangularSurfaceMesh<T>::writeAsciiSTL(std::string fname) const 00846 { 00847 // Output only from one MPI process. 00848 if (!global::mpi().isMainProcessor()) { 00849 return; 00850 } 00851 FILE *fp = fopen(fname.c_str(), "w"); 00852 PLB_ASSERT(fp != NULL); 00853 00854 char fmt1[64] = " facet normal "; 00855 char fmt2[64] = " vertex "; 00856 if (sizeof(T) == sizeof(long double)) { 00857 strcat(fmt1, "% Le % Le % Le\n"); 00858 strcat(fmt2, "% Le % Le % Le\n"); 00859 } 00860 else if (sizeof(T) == sizeof(float) || 00861 sizeof(T) == sizeof(double)) { 00862 strcat(fmt1, "% e % e % e\n"); 00863 strcat(fmt2, "% e % e % e\n"); 00864 } 00865 else { 00866 PLB_ASSERT(false); 00867 } 00868 00869 fprintf(fp, "solid surface\n"); 00870 for (plint i = 0; i < numTriangles; i++) { 00871 Array<T,3> n = computeTriangleNormal(i); 00872 Array<T,3> v; 00873 fprintf(fp, fmt1, n[0], n[1], n[2]); 00874 fprintf(fp, " outer loop\n"); 00875 v = getVertex(i, 0); 00876 fprintf(fp, fmt2, v[0], v[1], v[2]); 00877 v = getVertex(i, 1); 00878 fprintf(fp, fmt2, v[0], v[1], v[2]); 00879 v = getVertex(i, 2); 00880 fprintf(fp, fmt2, v[0], v[1], v[2]); 00881 fprintf(fp, " endloop\n"); 00882 fprintf(fp, " endfacet\n"); 00883 } 00884 fprintf(fp, "endsolid surface\n"); 00885 00886 fclose(fp); 00887 } 00888 00889 template<typename T> 00890 void TriangularSurfaceMesh<T>::writeBinarySTL(std::string fname) const 00891 { 00892 // Output only from one MPI process. 00893 if (!global::mpi().isMainProcessor()) { 00894 return; 00895 } 00896 FILE *fp = fopen(fname.c_str(), "wb"); 00897 PLB_ASSERT(fp != NULL); 00898 00899 unsigned int nt = (unsigned int) numTriangles; 00900 unsigned short abc = 0; 00901 char buf[80]; 00902 00903 for (int i = 0; i < 80; i++) 00904 buf[i] = '\0'; 00905 00906 fwrite(buf, sizeof(char), 80, fp); 00907 fwrite(&nt, sizeof(unsigned int), 1, fp); 00908 for (plint i = 0; i < numTriangles; i++) { 00909 Array<T,3> vertex; 00910 Array<T,3> normal = computeTriangleNormal(i); 00911 float n[3]; 00912 n[0] = normal[0]; 00913 n[1] = normal[1]; 00914 n[2] = normal[2]; 00915 fwrite((void *) n, sizeof(float), 3, fp); 00916 vertex = getVertex(i, 0); 00917 float v[3]; 00918 v[0] = vertex[0]; 00919 v[1] = vertex[1]; 00920 v[2] = vertex[2]; 00921 fwrite((void *) v, sizeof(float), 3, fp); 00922 vertex = getVertex(i, 1); 00923 v[0] = vertex[0]; 00924 v[1] = vertex[1]; 00925 v[2] = vertex[2]; 00926 fwrite((void *) v, sizeof(float), 3, fp); 00927 vertex = getVertex(i, 2); 00928 v[0] = vertex[0]; 00929 v[1] = vertex[1]; 00930 v[2] = vertex[2]; 00931 fwrite((void *) v, sizeof(float), 3, fp); 00932 fwrite(&abc, sizeof(unsigned short), 1, fp); 00933 } 00934 00935 fclose(fp); 00936 } 00937 00938 template<typename T> 00939 inline bool TriangularSurfaceMesh<T>::isBoundaryVertex(plint iVertex) const 00940 { 00941 PLB_ASSERT(iVertex >= 0 && iVertex < numVertices); 00942 if ( edges()[ emanatingEdges()[iVertex] ].ne < 0) 00943 return true; 00944 return false; 00945 } 00946 00947 template<typename T> 00948 inline bool TriangularSurfaceMesh<T>::isInteriorVertex(plint iVertex) const 00949 { 00950 PLB_ASSERT(iVertex >= 0 && iVertex < numVertices); 00951 if ( edges()[ emanatingEdges()[iVertex] ].ne >= 0) 00952 return true; 00953 return false; 00954 } 00955 00956 template<typename T> 00957 inline bool TriangularSurfaceMesh<T>::isBoundaryEdge(plint iVertex, plint jVertex) const 00958 { 00959 if (getAdjacentTriangleIds(iVertex, jVertex).size() == 2) 00960 return false; 00961 00962 return true; 00963 } 00964 00965 template<typename T> 00966 inline bool TriangularSurfaceMesh<T>::isInteriorEdge(plint iVertex, plint jVertex) const 00967 { 00968 return !isBoundaryEdge(iVertex, jVertex); 00969 } 00970 00971 template<typename T> 00972 int TriangularSurfaceMesh<T>::pointOnTriangle ( 00973 Array<T,3> const& point1, Array<T,3> const& point2, int flag, 00974 plint iTriangle, Array<T,3>& intersection, Array<T,3>& normal, 00975 T& distance) const 00976 { 00977 if(!((flag == 0 || flag == 1 || flag == 2) &&iTriangle >= 0 && iTriangle < numTriangles)) { 00978 pcout << "iTriangle=" << iTriangle << std::endl; 00979 pcout << "numTriangles=" << numTriangles << std::endl; 00980 pcout << "flag=" << flag << std::endl; 00981 } 00982 PLB_ASSERT((flag == 0 || flag == 1 || flag == 2) && 00983 iTriangle >= 0 && iTriangle < numTriangles); 00984 00985 Array<T,3> v0 = getVertex(iTriangle, 0); // Triangle vertex coordinates 00986 Array<T,3> v1 = getVertex(iTriangle, 1); 00987 Array<T,3> v2 = getVertex(iTriangle, 2); 00988 00989 Array<T,3> e0 = v1 - v0; // Triangle edge vectors starting at v0 00990 Array<T,3> e1 = v2 - v0; 00991 00992 crossProduct(e0, e1, normal); // Triangle unit normal 00993 normal /= norm(normal); 00994 00995 Array<T,3> direction = point2 - point1; 00996 00997 T num = dot(v0, normal) - dot(point1, normal); 00998 T denom = dot(direction, normal); 00999 01000 T t = num / denom; 01001 01002 // The function pointOnTriangle is essentially to verify crossings 01003 // through the surface from inside to outside or vice versa. When 01004 // one of the end-points of a segment is right on top of the surface 01005 // this creates an awkward ambiguity. To remove this ambiguity, the 01006 // triangle is slightly shifted in direction of its normal vector. 01007 if ( ( (flag==0||flag==1) && util::fpequal_abs(t, T(), eps1) ) 01008 || ( (flag==0) && util::fpequal_abs(t, (T) 1.0, eps1) ) 01009 ) 01010 { 01011 // Shift the vertex, and recompute everything that needs to be 01012 // recomputed. v1 and v2 are not used any more, so there is no 01013 // need to recompute them. 01014 v0 += (T)2*eps1*normal; 01015 num = dot(v0, normal) - dot(point1, normal); 01016 t = num / denom; 01017 } 01018 01019 if (util::fpequal_abs(denom, T(), eps1)) { 01020 if (util::fpequal_abs(num, T(), eps1)) { 01021 return -1; // Line belongs to the plane 01022 } 01023 else { 01024 return 0; // Line does not intersect the plane 01025 } 01026 } 01027 01028 if (flag == 0) { // For intersection with a line segment 01029 if ((t < 0.0 && !util::fpequal_abs(t, T(), eps1)) || 01030 (t > 1.0 && !util::fpequal_abs(t, (T) 1.0, eps1))) { 01031 return 0; 01032 } 01033 } 01034 else if (flag == 1) { // For intersection with a half-line 01035 if (t < 0.0 && !util::fpequal_abs(t, T(), eps1)) { 01036 return 0; 01037 } 01038 } 01039 01040 intersection = point1 + direction*t; // Intersection point with the plane 01041 01042 T a[2][2]; 01043 a[0][0] = dot(e0, e0); 01044 a[0][1] = dot(e0, e1); 01045 a[1][0] = a[0][1]; 01046 a[1][1] = dot(e1, e1); 01047 01048 T tmp[3]; 01049 tmp[0] = intersection[0] - v0[0]; 01050 tmp[1] = intersection[1] - v0[1]; 01051 tmp[2] = intersection[2] - v0[2]; 01052 01053 T b[2]; 01054 b[0] = dot(tmp, e0); 01055 b[1] = dot(tmp, e1); 01056 01057 T det = a[0][0]*a[1][1] - a[0][1]*a[1][0]; 01058 01059 T u = (a[1][1]*b[0] - a[0][1]*b[1]) / det; 01060 T v = (a[0][0]*b[1] - a[1][0]*b[0]) / det; 01061 01062 T upv = u + v; 01063 01064 int ueq0 = util::fpequal_abs(u, T(), eps1); 01065 int ueq1 = util::fpequal_abs(u, (T) 1.0, eps1); 01066 int veq0 = util::fpequal_abs(v, T(), eps1); 01067 int veq1 = util::fpequal_abs(v, (T) 1.0, eps1); 01068 int upveq1 = util::fpequal_abs(upv, (T) 1.0, eps1); 01069 01070 int is_vertex = -1; 01071 int is_in_edge = -1; 01072 01073 if ((u < 0.0 && !ueq0) || (u > 1.0 && !ueq1) || 01074 (v < 0.0 && !veq0) || (v > 1.0 && !veq1) || 01075 (upv > 1.0 && !upveq1)) 01076 return 0; // The point does not belong to the triangle 01077 else { // The point belongs to the triangle (boundary or interior) 01078 distance = fabs(t) * norm(direction); 01079 // Distance between intersection and point1 01080 01081 if (!ueq0 && !veq0 && !upveq1) 01082 return 1; // Point is in the triangle interior 01083 else if (ueq0 && veq0) 01084 is_vertex = 0; 01085 else if (ueq1 && veq0) 01086 is_vertex = 1; 01087 else if (ueq0 && veq1) 01088 is_vertex = 2; 01089 else if (veq0 && !ueq0 && !ueq1) 01090 is_in_edge = 0; 01091 else if (ueq0 && !veq0 && !veq1) 01092 is_in_edge = 2; 01093 else if (upveq1 && !ueq1 && !veq1) 01094 is_in_edge = 1; 01095 } 01096 01097 if (is_in_edge != -1) { // The point belongs to an edge 01098 plint iVertex = getVertexId(iTriangle, is_in_edge); 01099 plint jVertex = (is_in_edge == 2) ? getVertexId(iTriangle, 0) : 01100 getVertexId(iTriangle, is_in_edge+1); 01101 01102 normal = computeEdgeNormal(iVertex, jVertex); 01103 } else if (is_vertex != -1) { // The point is a vertex 01104 normal = computeVertexNormal(getVertexId(iTriangle, is_vertex)); 01105 } 01106 01107 return 1; 01108 } 01109 01110 template<typename T> 01111 bool TriangularSurfaceMesh<T>::segmentIntersectsTriangle ( 01112 Array<T,3> const& point1, Array<T,3> const& point2, plint iTriangle ) const 01113 { 01114 PLB_ASSERT(iTriangle >= 0 && iTriangle < numTriangles); 01115 01116 T v0[3], v1[3], v2[3]; // Triangle vertex coordinates 01117 01118 Array<T,3> tv0 = getVertex(iTriangle, 0); tv0.to_cArray(v0); 01119 Array<T,3> tv1 = getVertex(iTriangle, 1); tv1.to_cArray(v1); 01120 Array<T,3> tv2 = getVertex(iTriangle, 2); tv2.to_cArray(v2); 01121 01122 T e0[3], e1[3]; // Triangle edge vectors starting at v0 01123 01124 e0[0] = v1[0] - v0[0]; 01125 e0[1] = v1[1] - v0[1]; 01126 e0[2] = v1[2] - v0[2]; 01127 01128 e1[0] = v2[0] - v0[0]; 01129 e1[1] = v2[1] - v0[1]; 01130 e1[2] = v2[2] - v0[2]; 01131 01132 T normal[3]; // Triangle normal 01133 01134 normal[0] = e0[1]*e1[2] - e0[2]*e1[1]; 01135 normal[1] = e0[2]*e1[0] - e0[0]*e1[2]; 01136 normal[2] = e0[0]*e1[1] - e0[1]*e1[0]; 01137 01138 T direction[3], p1[3], p2[3]; 01139 01140 point1.to_cArray(p1); 01141 point2.to_cArray(p2); 01142 01143 direction[0] = p2[0] - p1[0]; 01144 direction[1] = p2[1] - p1[1]; 01145 direction[2] = p2[2] - p1[2]; 01146 01147 T denom = direction[0]*normal[0] + direction[1]*normal[1] + direction[2]*normal[2]; 01148 01149 if (FPEQUAL_ABS(denom, T(), eps1)) 01150 return false; // The segment belongs to the plane or it is parallel to it 01151 // and does not intersect it. 01152 01153 T num = (v0[0]-p1[0])*normal[0] + (v0[1]-p1[1])*normal[1] + (v0[2]-p1[2])*normal[2]; 01154 01155 T t = num / denom; 01156 01157 // The function segmentIntersectsTriangle is essentially to verify crossings 01158 // through the surface from inside to outside or vice versa. When 01159 // one of the end-points of a segment is right on top of the surface 01160 // this creates an awkward ambiguity. To remove this ambiguity, the 01161 // triangle is slightly shifted in direction of its normal vector. 01162 if ( ( FPEQUAL_ABS(t, T(), eps1) ) 01163 || ( FPEQUAL_ABS(t, (T) 1.0, eps1) ) 01164 ) 01165 { 01166 T norm_normal = sqrt(util::sqr(normal[0])+util::sqr(normal[1])+util::sqr(normal[2])); 01167 normal[0] /= norm_normal; 01168 normal[1] /= norm_normal; 01169 normal[2] /= norm_normal; 01170 // Shift the vertex, and recompute everything that needs to be 01171 // recomputed. v1 and v2 are not used any more, so there is no 01172 // need to recompute them. 01173 T tmp = (T)2*eps1; 01174 v0[0] += tmp*normal[0]; 01175 v0[1] += tmp*normal[1]; 01176 v0[2] += tmp*normal[2]; 01177 num = (v0[0]-p1[0])*normal[0] + (v0[1]-p1[1])*normal[1] + (v0[2]-p1[2])*normal[2]; 01178 t = num / denom; 01179 } 01180 01181 if ((t < 0.0 && !FPEQUAL_ABS(t, T(), eps1)) || 01182 (t > 1.0 && !FPEQUAL_ABS(t, (T) 1.0, eps1))) 01183 return false; 01184 01185 T intersection[3]; 01186 01187 intersection[0] = p1[0] + direction[0]*t; // Intersection point with the plane 01188 intersection[1] = p1[1] + direction[1]*t; 01189 intersection[2] = p1[2] + direction[2]*t; 01190 01191 T a[2][2]; 01192 a[0][0] = e0[0]*e0[0] + e0[1]*e0[1] + e0[2]*e0[2]; 01193 a[0][1] = e0[0]*e1[0] + e0[1]*e1[1] + e0[2]*e1[2]; 01194 a[1][0] = a[0][1]; 01195 a[1][1] = e1[0]*e1[0] + e1[1]*e1[1] + e1[2]*e1[2]; 01196 01197 T tmp[3]; 01198 tmp[0] = intersection[0] - v0[0]; 01199 tmp[1] = intersection[1] - v0[1]; 01200 tmp[2] = intersection[2] - v0[2]; 01201 01202 T b[2]; 01203 b[0] = tmp[0]*e0[0] + tmp[1]*e0[1] + tmp[2]*e0[2]; 01204 b[1] = tmp[0]*e1[0] + tmp[1]*e1[1] + tmp[2]*e1[2]; 01205 01206 T det = a[0][0]*a[1][1] - a[0][1]*a[1][0]; 01207 01208 T u = (a[1][1]*b[0] - a[0][1]*b[1])/det; 01209 T v = (a[0][0]*b[1] - a[1][0]*b[0])/det; 01210 01211 T upv = u + v; 01212 01213 if ((u < 0.0 && !FPEQUAL_ABS(u, T(), eps1)) || 01214 (u > 1.0 && !FPEQUAL_ABS(u, (T) 1.0, eps1)) || 01215 (v < 0.0 && !FPEQUAL_ABS(v, T(), eps1)) || 01216 (v > 1.0 && !FPEQUAL_ABS(v, (T) 1.0, eps1)) || 01217 (upv > 1.0 && !FPEQUAL_ABS(upv, (T) 1.0, eps1))) 01218 return false; // The point does not belong to the triangle 01219 01220 return true; 01221 } 01222 01223 template<typename T> 01224 void TriangularSurfaceMesh<T>::distanceToEdgeLine ( 01225 Array<T,3> const& point, plint iTriangle, plint whichEdge, 01226 T& distance, bool& intersectionIsInside ) const 01227 { 01228 PLB_ASSERT(iTriangle >= 0 && iTriangle < numTriangles && 01229 (whichEdge == 0 || whichEdge == 1 || whichEdge == 2)); 01230 01231 plint iVertex1 = edges()[3*iTriangle+((whichEdge==0) ? 2:whichEdge-1)].pv; 01232 plint iVertex2 = edges()[3*iTriangle+whichEdge].pv; 01233 Array<T,3> vertex1 = getVertex(iVertex1); 01234 Array<T,3> vertex2 = getVertex(iVertex2); 01235 Array<T,3> pv = point-vertex1; 01236 Array<T,3> e = vertex2-vertex1; 01237 01238 T u = dot(pv,e) / dot(e,e); 01239 Array<T,3> x = vertex1+u*e; 01240 distance = norm(point-x); 01241 01242 int ueq0 = util::fpequal_abs(u, T(), eps1); 01243 int ueq1 = util::fpequal_abs(u, (T) 1.0, eps1); 01244 intersectionIsInside = (u >= 0.0 || ueq0) && (u <= 1.0 || ueq1); 01245 } 01246 01247 template<typename T> 01248 void TriangularSurfaceMesh<T>::distanceToTrianglePlane ( 01249 Array<T,3> const& point, plint iTriangle, 01250 T& distance, bool& intersectionIsInside, bool& pointIsBehind ) const 01251 { 01252 Array<T,3> normal = computeTriangleNormal(iTriangle); 01253 Array<T,3> intersection; 01254 int flag = 2; // Line. 01255 intersectionIsInside = 01256 pointOnTriangle( point, point+normal, flag, iTriangle, 01257 intersection, normal, distance ) == 1; 01258 T projection = dot(normal, point-intersection); 01259 01260 pointIsBehind = projection <= (T)0. || 01261 util::fpequal_abs(projection, (T)0., eps1); 01262 } 01263 01264 template<typename T> 01265 void TriangularSurfaceMesh<T>::distanceToTriangle ( 01266 Array<T,3> const& point, plint iTriangle, 01267 T& distance, bool& pointIsBehind ) const 01268 { 01269 bool intersectionIsInside; 01270 // First possibility: The projection of the point is inside the triangle. 01271 distanceToTrianglePlane( point, iTriangle, distance, 01272 intersectionIsInside, pointIsBehind ); 01273 if (intersectionIsInside) { 01274 return; 01275 } 01276 // Second possibility: The projection of the point is inside an edge. 01277 bool intersectsWithEdge = false; 01278 for (plint iEdge=0; iEdge<=2; ++iEdge) { 01279 T newDistance; 01280 distanceToEdgeLine(point, iTriangle, iEdge, newDistance, intersectionIsInside); 01281 if (iEdge==0 || newDistance<distance) { 01282 distance = newDistance; 01283 // The edge-line to which the point is closest should be selected if 01284 // and only if the intersection is inside the edge. Otherwise 01285 // the closest distance to the triangle is on one of the vertices 01286 // which is selected further down. 01287 intersectsWithEdge = intersectionIsInside; 01288 } 01289 } 01290 if (intersectsWithEdge) { 01291 return; 01292 } 01293 // Default: Compute the closest distance to one of the vertices. 01294 T d0Sqr = normSqr(point-getVertex(iTriangle, 0)); 01295 T d1Sqr = normSqr(point-getVertex(iTriangle, 1)); 01296 T d2Sqr = normSqr(point-getVertex(iTriangle, 2)); 01297 T minDistSqr = std::min(d0Sqr, std::min(d1Sqr,d2Sqr)); 01298 distance = sqrt(minDistSqr); 01299 } 01300 01301 template<typename T> 01302 void TriangularSurfaceMesh<T>::reverseOrientation() 01303 { 01304 // Rearrange the emanating edge list. 01305 for (plint iVertex = 0; iVertex < numVertices; iVertex++) 01306 if (isInteriorVertex(iVertex)) { 01307 emanatingEdges()[iVertex] = changeEdgeId(edges()[emanatingEdges()[iVertex]].ne); 01308 } 01309 else { 01310 plint ee = emanatingEdges()[iVertex]; 01311 plint pe = prev(ee); 01312 plint e = pe; 01313 if ((e = edges()[e].ne) < 0) { 01314 emanatingEdges()[iVertex] = changeEdgeId(pe); 01315 e = ee; 01316 } 01317 while (e != ee) { 01318 e = pe = prev(e); 01319 if ((e = edges()[e].ne) < 0) { 01320 emanatingEdges()[iVertex] = changeEdgeId(pe); 01321 e = ee; 01322 } 01323 } 01324 } 01325 01326 // Rearrange the neighboring edge list. 01327 std::vector<Edge> tmp = edges(); 01328 for (plint iEdge = 0; iEdge < 3*numTriangles; iEdge++) 01329 if (edges()[iEdge].ne < 0) { 01330 tmp[changeEdgeId(iEdge)].ne = -1; 01331 } 01332 else { 01333 tmp[changeEdgeId(iEdge)].ne = changeEdgeId(edges()[iEdge].ne); 01334 } 01335 01336 edges().swap(tmp); 01337 01338 // Rearrange the pointing vertex list. 01339 for (plint iTriangle = 0; iTriangle < numTriangles; iTriangle++) { 01340 plint id0 = getVertexId(iTriangle, 0); 01341 plint id1 = getVertexId(iTriangle, 1); 01342 plint id2 = getVertexId(iTriangle, 2); 01343 01344 edges()[3*iTriangle].pv = id2; 01345 edges()[3*iTriangle + 1].pv = id1; 01346 edges()[3*iTriangle + 2].pv = id0; 01347 } 01348 } 01349 01350 template<typename T> 01351 std::vector<typename TriangularSurfaceMesh<T>::Lid> TriangularSurfaceMesh<T>::closeHoles() { 01352 std::vector<std::vector<plint> > holes = detectHoles(); 01353 std::vector<Lid> lids(holes.size()); 01354 for (pluint iHole=0; iHole<holes.size(); ++iHole) { 01355 lids[iHole] = closeHole(holes[iHole]); 01356 } 01357 return lids; 01358 } 01359 01360 template<typename T> 01361 void TriangularSurfaceMesh<T>::avoidIntegerPositions() { 01362 for (plint iVertex=0; iVertex<getNumVertices(); ++iVertex) { 01363 avoidIntegerPosition(iVertex); 01364 } 01365 } 01366 01367 template<typename T> 01368 void TriangularSurfaceMesh<T>::avoidIntegerPosition(plint iVertex) { 01369 Array<T,3>& vertex = getVertex(iVertex); 01370 Array<T,3> normal = computeVertexNormal(iVertex); 01371 if ( vertex[0]-(plint)vertex[0] < 1.e-12 || 01372 vertex[1]-(plint)vertex[1] < 1.e-12 || 01373 vertex[2]-(plint)vertex[2] < 1.e-12 ) 01374 { 01375 vertex += (T)1.e-12 * normal; 01376 } 01377 } 01378 01379 template<typename T> 01380 void TriangularSurfaceMesh<T>::inflate(T amount) { 01381 for (plint iVertex=0; iVertex<getNumVertices(); ++iVertex) { 01382 Array<T,3>& vertex = getVertex(iVertex); 01383 Array<T,3> normal = computeVertexNormal(iVertex); 01384 vertex += amount * normal; 01385 } 01386 } 01387 01388 template<typename T> 01389 inline plint TriangularSurfaceMesh<T>::prev(plint iEdge) const 01390 { 01391 PLB_ASSERT(iEdge >= 0 && iEdge < 3*numTriangles); 01392 return (iEdge%3 == 0) ? iEdge+2 : iEdge-1; 01393 } 01394 01395 template<typename T> 01396 inline plint TriangularSurfaceMesh<T>::next(plint iEdge) const 01397 { 01398 PLB_ASSERT(iEdge >= 0 && iEdge < 3*numTriangles); 01399 return (iEdge%3 == 2) ? iEdge-2 : iEdge+1; 01400 } 01401 01402 template<typename T> 01403 inline plint TriangularSurfaceMesh<T>::changeEdgeId(plint iEdge) const 01404 { 01405 plint iTriangle = iEdge/3; 01406 plint mod = iEdge%3; 01407 return (mod == 0) ? 3*iTriangle+2 : ((mod == 1) ? iEdge : 3*iTriangle); 01408 } 01409 01410 template<typename T> 01411 std::vector<std::vector<plint> > 01412 TriangularSurfaceMesh<T>::detectHoles() 01413 { 01414 std::vector<std::vector<plint> > holes; 01415 std::vector<bool> check(getNumVertices()); 01416 // Whenever a vertex is identified as part of a given hole boundary, it is 01417 // opted out in the "check" array to avoid that it is mistakingly 01418 // identified later on as the starting vertex for a new hole. 01419 std::fill(check.begin(), check.end(), false); 01420 // Check all vertices ... 01421 for(plint iVertex=0; iVertex<numVertices; ++iVertex) { 01422 // ... unless they've been previously opted out. 01423 if (isBoundaryVertex(iVertex) && !check[iVertex]) { 01424 check[iVertex] = true; 01425 std::vector<plint> newHole; 01426 newHole.push_back(iVertex); 01427 Edge nextEdge = edges()[emanatingEdges()[iVertex]]; 01428 // Loop around the hole, until we're back to the first vertex. 01429 while (nextEdge.pv != iVertex) { 01430 plint nextVertex = nextEdge.pv; 01431 check[nextVertex] = true; 01432 newHole.push_back(nextVertex); 01433 nextEdge = edges()[emanatingEdges()[nextVertex]]; 01434 } 01435 holes.push_back(newHole); 01436 } 01437 } 01438 return holes; 01439 } 01440 01441 template<typename T> 01442 typename TriangularSurfaceMesh<T>::Lid TriangularSurfaceMesh<T>::closeHole( 01443 std::vector<plint> const& hole) 01444 { 01445 plint numHoleVertices = (plint) hole.size(); 01446 01447 // 1. Create a new vertex, corresponding to the barycenter. 01448 Array<T,3> baryCenter; baryCenter.resetToZero(); 01449 for (plint iHole=0; iHole<numHoleVertices; ++iHole) { 01450 plint iVertex = hole[iHole]; 01451 baryCenter += getVertex(iVertex); 01452 } 01453 baryCenter /= (T)numHoleVertices; 01454 vertices().push_back(baryCenter); 01455 plint iCenter = numVertices; 01456 ++numVertices; // Don't forget to update this class variable. 01457 01458 // 2. Create the new triangles. 01459 plint edgeIndex = edges().size()-1; 01460 plint firstAddedEdge = edgeIndex+1; 01461 // There is exactly one new triangle for each vertex along the hole. 01462 for (plint iTriangle=0; iTriangle<numHoleVertices; ++iTriangle) { 01463 plint iVertex = hole[iTriangle]; 01464 plint nextVertex = hole[iTriangle==numHoleVertices-1 ? 0 : iTriangle+1]; 01465 01466 // a) Add the new edge which is horizontal to the hole boundary. 01467 Edge counterEdge; ++edgeIndex; 01468 counterEdge.pv = iVertex; 01469 counterEdge.ne = emanatingEdges()[iVertex]; 01470 edges().push_back(counterEdge); 01471 // Convert this former boundary edge to a bulk edge. 01472 edges()[counterEdge.ne].ne = edgeIndex; 01473 01474 // b) Add the edge that goes from a boundary vertex to the barycenter. 01475 Edge firstEdge; ++edgeIndex; 01476 firstEdge.pv = iCenter; 01477 if (iTriangle>0) { 01478 firstEdge.ne = edgeIndex-2; 01479 // Convert this former boundary edge to a bulk edge. 01480 edges()[firstEdge.ne].ne = edgeIndex; 01481 } 01482 edges().push_back(firstEdge); 01483 01484 // c) Add the edge that goes from the barycenter to a boundary vertex. 01485 Edge secondEdge; ++edgeIndex; 01486 secondEdge.pv = nextVertex; 01487 if (iTriangle==numHoleVertices-1) { 01488 secondEdge.ne = firstAddedEdge+1; 01489 // Convert this former boundary edge to a bulk edge. 01490 edges()[secondEdge.ne].ne = edgeIndex; 01491 } 01492 edges().push_back(secondEdge); 01493 01494 ++numTriangles; // Don't forget to update this class variable. 01495 } 01496 // For the newly created barycenter, define the emanating edge to 01497 // be the one corresponding to the first added triangle. 01498 emanatingEdges().push_back(firstAddedEdge+2); 01499 01500 // 3. Make sure this will not become a degenerate case. 01501 avoidIntegerPosition(iCenter); 01502 01503 // 4. Keep track of the newly created triangles with help of a "Lid" structure. 01504 Lid lid; 01505 lid.firstTriangle = firstAddedEdge/3; 01506 lid.numTriangles = numHoleVertices; 01507 lid.boundaryVertices = hole; 01508 lid.centerVertex = iCenter; 01509 return lid; 01510 } 01511 01512 01513 template<typename T> 01514 void toLatticeUnits ( 01515 TriangularSurfaceMesh<T>& mesh, 01516 plint resolution, plint referenceDirection, 01517 Array<T,3>& location, T& dx) 01518 { 01519 PLB_ASSERT(referenceDirection>=0 && referenceDirection <=2); 01520 Array<T,2> xRange, yRange, zRange; 01521 mesh.computeBoundingBox(xRange, yRange, zRange); 01522 T deltaX = T(); 01523 switch (referenceDirection) { 01524 case 0: deltaX = xRange[1] - xRange[0]; 01525 break; 01526 case 1: deltaX = yRange[1] - yRange[0]; 01527 break; 01528 case 2: deltaX = zRange[1] - zRange[0]; 01529 break; 01530 } 01531 01532 T scalingFactor = (T)(resolution)/deltaX; 01533 01534 // Transform mesh into the frame of coordinate of the multiScalarField. 01535 location = Array<T,3>(xRange[0],yRange[0],zRange[0]); 01536 mesh.translate(Array<T,3>(-location)); 01537 mesh.scale(scalingFactor); 01538 dx = 1./scalingFactor; 01539 } 01540 01541 01542 /* ******* Lid operations ************************************************** */ 01543 01544 template<typename T> 01545 Array<T,3> computeBaryCenter ( 01546 TriangularSurfaceMesh<T> const& mesh, 01547 typename TriangularSurfaceMesh<T>::Lid const& lid ) 01548 { 01549 Array<T,3> baryCenter; baryCenter.resetToZero(); 01550 for ( plint iBoundary=0; 01551 iBoundary<(plint)lid.boundaryVertices.size(); ++iBoundary) 01552 { 01553 plint iVertex = lid.boundaryVertices[iBoundary]; 01554 baryCenter += mesh.getVertex(iVertex); 01555 } 01556 baryCenter /= (T)lid.boundaryVertices.size(); 01557 return baryCenter; 01558 } 01559 01560 template<typename T> 01561 Array<T,3> computeGeometricCenter ( 01562 TriangularSurfaceMesh<T> const& mesh, 01563 typename TriangularSurfaceMesh<T>::Lid const& lid ) 01564 { 01565 typedef typename TriangularSurfaceMesh<T>::Edge Edge; 01566 Array<T,3> center; center.resetToZero(); 01567 T circumference = T(); 01568 for ( plint iBoundary=0; 01569 iBoundary<(plint)lid.boundaryVertices.size(); ++iBoundary) 01570 { 01571 plint iVertex = lid.boundaryVertices[iBoundary]; 01572 Edge edge = mesh.edges()[mesh.emanatingEdges()[iVertex]]; 01573 plint iNextVertex = edge.pv; 01574 Array<T,3> v1 = mesh.getVertex(iVertex); 01575 Array<T,3> v2 = mesh.getVertex(iNextVertex); 01576 T l = norm(v2-v1); 01577 center += (v1+v2)*l; 01578 circumference += l; 01579 } 01580 center /= 2.*circumference; 01581 return center; 01582 } 01583 01584 template<typename T> 01585 T computeGeometricRadius ( 01586 TriangularSurfaceMesh<T> const& mesh, 01587 typename TriangularSurfaceMesh<T>::Lid const& lid ) 01588 { 01589 typedef typename TriangularSurfaceMesh<T>::Edge Edge; 01590 Array<T,3> center= computeGeometricCenter(mesh, lid); 01591 T radius = T(); 01592 T circumference = T(); 01593 for ( plint iBoundary=0; 01594 iBoundary<(plint)lid.boundaryVertices.size(); ++iBoundary) 01595 { 01596 plint iVertex = lid.boundaryVertices[iBoundary]; 01597 Edge edge = mesh.edges()[mesh.emanatingEdges()[iVertex]]; 01598 plint iNextVertex = edge.pv; 01599 Array<T,3> v1 = mesh.getVertex(iVertex); 01600 Array<T,3> v2 = mesh.getVertex(iNextVertex); 01601 T l = norm(v2-v1); 01602 radius += norm(v1-center)*l; 01603 circumference += l; 01604 } 01605 radius /= circumference; 01606 return radius; 01607 } 01608 01609 template<typename T> 01610 void computeBoundingBox ( 01611 TriangularSurfaceMesh<T> const& mesh, 01612 typename TriangularSurfaceMesh<T>::Lid const& lid, 01613 Array<T,2>& xLim, Array<T,2>& yLim, Array<T,2>& zLim ) 01614 { 01615 xLim[0] = yLim[0] = zLim[0] = std::numeric_limits<T>::max(); 01616 xLim[1] = yLim[1] = zLim[1] = std::numeric_limits<T>::min(); 01617 for ( plint iBoundary=0; 01618 iBoundary<(plint)lid.boundaryVertices.size(); ++iBoundary ) 01619 { 01620 plint iVertex = lid.boundaryVertices[iBoundary]; 01621 Array<T,3> const& vertex = mesh.getVertex(iVertex); 01622 xLim[0] = std::min(xLim[0], vertex[0]); 01623 xLim[1] = std::max(xLim[1], vertex[0]); 01624 yLim[0] = std::min(yLim[0], vertex[1]); 01625 yLim[1] = std::max(yLim[1], vertex[1]); 01626 zLim[0] = std::min(zLim[0], vertex[2]); 01627 zLim[1] = std::max(zLim[1], vertex[2]); 01628 } 01629 } 01630 01631 template<typename T> 01632 T computeInnerRadius ( 01633 TriangularSurfaceMesh<T> const& mesh, 01634 typename TriangularSurfaceMesh<T>::Lid const& lid ) 01635 { 01636 if (lid.boundaryVertices.empty()) { 01637 return T(); 01638 } 01639 else { 01640 T innerRadius = mesh.computeEdgeLength ( 01641 lid.centerVertex, lid.boundaryVertices[0] ); 01642 for ( plint iBoundary=1; 01643 iBoundary<(plint)lid.boundaryVertices.size(); ++iBoundary) 01644 { 01645 T nextLength = mesh.computeEdgeLength ( 01646 lid.centerVertex, lid.boundaryVertices[iBoundary] ); 01647 innerRadius = std::min(innerRadius, nextLength); 01648 } 01649 return innerRadius; 01650 } 01651 } 01652 01653 template<typename T> 01654 T computeOuterRadius ( 01655 TriangularSurfaceMesh<T> const& mesh, 01656 typename TriangularSurfaceMesh<T>::Lid const& lid ) 01657 { 01658 if (lid.boundaryVertices.empty()) { 01659 return T(); 01660 } 01661 else { 01662 T innerRadius = mesh.computeEdgeLength ( 01663 lid.centerVertex, lid.boundaryVertices[0] ); 01664 for ( plint iBoundary=1; 01665 iBoundary<(plint)lid.boundaryVertices.size(); ++iBoundary) 01666 { 01667 T nextLength = mesh.computeEdgeLength ( 01668 lid.centerVertex, lid.boundaryVertices[iBoundary] ); 01669 innerRadius = std::max(innerRadius, nextLength); 01670 } 01671 return innerRadius; 01672 } 01673 } 01674 01675 template<typename T> 01676 T computeArea ( 01677 TriangularSurfaceMesh<T> const& mesh, 01678 typename TriangularSurfaceMesh<T>::Lid const& lid ) 01679 { 01680 T area = T(); 01681 plint numVertices = (plint)lid.boundaryVertices.size(); 01682 plint centerVertex = lid.centerVertex; 01683 for ( plint iBoundary=0; iBoundary<numVertices; ++iBoundary) 01684 { 01685 plint vertex1 = lid.boundaryVertices[iBoundary]; 01686 plint vertex2 = lid.boundaryVertices[(iBoundary+1)%numVertices]; 01687 area += mesh.computeTriangleArea(centerVertex, vertex1, vertex2); 01688 } 01689 return area; 01690 } 01691 01692 template<typename T> 01693 Array<T,3> computeNormal ( 01694 TriangularSurfaceMesh<T> const& mesh, 01695 typename TriangularSurfaceMesh<T>::Lid const& lid ) 01696 { 01697 return mesh.computeVertexNormal(lid.centerVertex); 01698 } 01699 01700 01701 template<typename T> 01702 void reCenter ( 01703 TriangularSurfaceMesh<T>& mesh, 01704 typename TriangularSurfaceMesh<T>::Lid const& lid ) 01705 { 01706 Array<T,3> newCenter = computeBaryCenter(mesh,lid); 01707 mesh.replaceVertex(lid.centerVertex, newCenter); 01708 } 01709 01710 } // namespace plb 01711 01712 #endif // TRIANGULAR_SURFACE_MESH_HH
1.6.3
1.6.3