$treeview $search $mathjax
Palabos  Version 1.1
$projectbrief
$projectbrief
$searchbox

triangleSetGenerator.hh

Go to the documentation of this file.
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 TRIANGLE_SET_GENERATOR_HH
00028 #define TRIANGLE_SET_GENERATOR_HH
00029 
00030 #include "core/globalDefs.h"
00031 #include "offLattice/triangleSetGenerator.h"
00032 
00033 namespace plb {
00034 
00035 template<typename T>
00036 TriangleSet<T> constructSphere(Array<T,3> const& center, T radius, plint minNumOfTriangles)
00037 {
00038     static const T eps = std::numeric_limits<T>::epsilon();
00039     std::vector<typename TriangleSet<T>::Triangle> triangles;
00040     PLB_ASSERT(radius > 0.0 && !util::fpequal(radius, (T) 0.0, eps) &&
00041                minNumOfTriangles >= 8);
00042 
00043     // Create a triangularized unit sphere
00044 
00045     // Initial 6 vertices
00046 
00047     Array<T,3> va;
00048     va[0] = 1.0;
00049     va[1] = 0.0;
00050     va[2] = 0.0;
00051 
00052     Array<T,3> vb;
00053     vb[0] = 0.0;
00054     vb[1] = 1.0;
00055     vb[2] = 0.0;
00056 
00057     Array<T,3> vc;
00058     vc[0] = -1.0;
00059     vc[1] =  0.0;
00060     vc[2] =  0.0;
00061 
00062     Array<T,3> vd;
00063     vd[0] =  0.0;
00064     vd[1] = -1.0;
00065     vd[2] =  0.0;
00066 
00067     Array<T,3> ve;
00068     ve[0] = 0.0;
00069     ve[1] = 0.0;
00070     ve[2] = 1.0;
00071 
00072     Array<T,3> vf;
00073     vf[0] =  0.0;
00074     vf[1] =  0.0;
00075     vf[2] = -1.0;
00076 
00077     // Initial 8 triangles
00078 
00079     typename TriangleSet<T>::Triangle tmp;
00080 
00081     tmp[0] = ve;
00082     tmp[1] = va;
00083     tmp[2] = vb;
00084     triangles.push_back(tmp);
00085 
00086     tmp[0] = ve;
00087     tmp[1] = vb;
00088     tmp[2] = vc;
00089     triangles.push_back(tmp);
00090 
00091     tmp[0] = ve;
00092     tmp[1] = vc;
00093     tmp[2] = vd;
00094     triangles.push_back(tmp);
00095 
00096     tmp[0] = ve;
00097     tmp[1] = vd;
00098     tmp[2] = va;
00099     triangles.push_back(tmp);
00100 
00101     tmp[0] = vf;
00102     tmp[1] = vb;
00103     tmp[2] = va;
00104     triangles.push_back(tmp);
00105 
00106     tmp[0] = vf;
00107     tmp[1] = vc;
00108     tmp[2] = vb;
00109     triangles.push_back(tmp);
00110 
00111     tmp[0] = vf;
00112     tmp[1] = vd;
00113     tmp[2] = vc;
00114     triangles.push_back(tmp);
00115 
00116     tmp[0] = vf;
00117     tmp[1] = va;
00118     tmp[2] = vd;
00119     triangles.push_back(tmp);
00120 
00121     // Perform refinement iterations
00122 
00123     plint size;
00124     while ((size = triangles.size()) < minNumOfTriangles) {
00125         for (plint i = 0; i < size; i++) {
00126             va = triangles[i][0];
00127             vb = triangles[i][1];
00128             vc = triangles[i][2];
00129 
00130             vd = 0.5 * (va + vb);
00131             ve = 0.5 * (vb + vc);
00132             vf = 0.5 * (vc + va);
00133 
00134             vd /= norm(vd);
00135             ve /= norm(ve);
00136             vf /= norm(vf);
00137 
00138             triangles[i][0] = vd;
00139             triangles[i][1] = ve;
00140             triangles[i][2] = vf;
00141 
00142             tmp[0] = va;
00143             tmp[1] = vd;
00144             tmp[2] = vf;
00145             triangles.push_back(tmp);
00146 
00147             tmp[0] = vd;
00148             tmp[1] = vb;
00149             tmp[2] = ve;
00150             triangles.push_back(tmp);
00151 
00152             tmp[0] = vf;
00153             tmp[1] = ve;
00154             tmp[2] = vc;
00155             triangles.push_back(tmp);
00156         }
00157     }
00158 
00159     // Scale and translate the mesh
00160 
00161     TriangleSet<T> triangleSet(triangles);
00162 
00163     triangleSet.scale(radius);
00164     triangleSet.translate(center);
00165 
00166     return triangleSet;
00167 }
00168 
00169 template<typename T>
00170 TriangleSet<T> constructCylinder( Array<T,3> const& inletCenter, T inletRadius, T outletRadius,
00171                                   T length, plint nAxial, plint nCirc,
00172                                   std::vector<Array<T,3> >& inletPoints )
00173 {
00174     static const T eps = std::numeric_limits<T>::epsilon();
00175     std::vector<typename TriangleSet<T>::Triangle> triangles;
00176     PLB_ASSERT( inletRadius > 0.0 && !util::fpequal( inletRadius, (T) 0.0, eps) &&
00177                outletRadius > 0.0 && !util::fpequal(outletRadius, (T) 0.0, eps) &&
00178                      length > 0.0 && !util::fpequal(      length, (T) 0.0, eps) &&
00179                nAxial >= 2 && nCirc >= 2);
00180 
00181     inletPoints.resize(0);
00182     triangles.resize(0);
00183 
00184     // Construction of the cylindrical grid and triangulation
00185 
00186     T pi = (T) acos(-1.0);
00187 
00188     T dtheta = 2.0*pi / nCirc;
00189     T dx = length / (nAxial-1.0);
00190     T dr = (outletRadius-inletRadius) / length * dx;
00191 
00192     T x0 = inletCenter[0];
00193     T y0 = inletCenter[1];
00194     T z0 = inletCenter[2];
00195 
00196     for (plint i = 0; i < nAxial-1; i++) {
00197         T x = x0 + i*dx;
00198         T r = (outletRadius-inletRadius) * (x-x0) / length + inletRadius;
00199         for (plint j = 0; j < nCirc; j++) {
00200             T theta = j * dtheta;
00201 
00202             Array<Array<T,3>, 4> v;
00203 
00204             v[0][0] = x;
00205             v[0][1] = y0 + r * cos(theta);
00206             v[0][2] = z0 + r * sin(theta);
00207 
00208             v[1][0] = x;
00209             v[1][1] = y0 + r * cos(theta + dtheta);
00210             v[1][2] = z0 + r * sin(theta + dtheta);
00211 
00212             v[2][0] = x + dx;
00213             v[2][1] = y0 + (r + dr) * cos(theta + dtheta);
00214             v[2][2] = z0 + (r + dr) * sin(theta + dtheta);
00215 
00216             v[3][0] = x + dx;
00217             v[3][1] = y0 + (r + dr) * cos(theta);
00218             v[3][2] = z0 + (r + dr) * sin(theta);
00219 
00220             Array<T,3> vc;
00221 
00222             vc[0] = x + 0.5 * dx;
00223             vc[1] = y0 + (r + 0.5 * dr) * cos(theta + 0.5 * dtheta);
00224             vc[2] = z0 + (r + 0.5 * dr) * sin(theta + 0.5 * dtheta);
00225 
00226             Array<Array<T,3>, 4> vce;
00227 
00228 
00229             vce[0][0] = x;
00230             vce[0][1] = y0 + r * cos(theta + 0.5 * dtheta);
00231             vce[0][2] = z0 + r * sin(theta + 0.5 * dtheta);
00232 
00233             if (i==0) {
00234                 inletPoints.push_back(v[0]);
00235                 inletPoints.push_back(vce[0]);
00236             }
00237 
00238             vce[1][0] = x + 0.5 * dx;
00239             vce[1][1] = y0 + (r + 0.5 * dr) * cos(theta + dtheta);
00240             vce[1][2] = z0 + (r + 0.5 * dr) * sin(theta + dtheta);
00241 
00242             vce[2][0] = x + dx;
00243             vce[2][1] = y0 + (r + dr) * cos(theta + 0.5 * dtheta);
00244             vce[2][2] = z0 + (r + dr) * sin(theta + 0.5 * dtheta);
00245 
00246             vce[3][0] = x + 0.5 * dx;
00247             vce[3][1] = y0 + (r + 0.5 * dr) * cos(theta);
00248             vce[3][2] = z0 + (r + 0.5 * dr) * sin(theta);
00249 
00250             typename TriangleSet<T>::Triangle tmp;
00251 
00252             tmp[0] = vc;
00253             tmp[1] = v[0];
00254             tmp[2] = vce[0];
00255             triangles.push_back(tmp);
00256 
00257             tmp[0] = vc;
00258             tmp[1] = vce[0];
00259             tmp[2] = v[1];
00260             triangles.push_back(tmp);
00261 
00262             tmp[0] = vc;
00263             tmp[1] = v[1];
00264             tmp[2] = vce[1];
00265             triangles.push_back(tmp);
00266 
00267             tmp[0] = vc;
00268             tmp[1] = vce[1];
00269             tmp[2] = v[2];
00270             triangles.push_back(tmp);
00271 
00272             tmp[0] = vc;
00273             tmp[1] = v[2];
00274             tmp[2] = vce[2];
00275             triangles.push_back(tmp);
00276 
00277             tmp[0] = vc;
00278             tmp[1] = vce[2];
00279             tmp[2] = v[3];
00280             triangles.push_back(tmp);
00281 
00282             tmp[0] = vc;
00283             tmp[1] = v[3];
00284             tmp[2] = vce[3];
00285             triangles.push_back(tmp);
00286 
00287             tmp[0] = vc;
00288             tmp[1] = vce[3];
00289             tmp[2] = v[0];
00290             triangles.push_back(tmp);
00291         }
00292     }
00293     return TriangleSet<T>(triangles);
00294 }
00295 template<typename T>
00296 TriangleSet<T> constructCylinder(Array<T,3> const& inletCenter, T inletRadius, T outletRadius,
00297                                  T length, plint nAxial, plint nCirc)
00298 {
00299     static const T eps = std::numeric_limits<T>::epsilon();
00300     std::vector<typename TriangleSet<T>::Triangle> triangles;
00301     PLB_ASSERT( inletRadius > 0.0 && !util::fpequal( inletRadius, (T) 0.0, eps) &&
00302                outletRadius > 0.0 && !util::fpequal(outletRadius, (T) 0.0, eps) &&
00303                      length > 0.0 && !util::fpequal(      length, (T) 0.0, eps) &&
00304                nAxial >= 2 && nCirc >= 2);
00305 
00306     triangles.resize(0);
00307 
00308     // Construction of the cylindrical grid and triangulation
00309 
00310     T pi = (T) acos(-1.0);
00311 
00312     T dtheta = 2.0*pi / nCirc;
00313     T dx = length / (nAxial-1.0);
00314     T dr = (outletRadius-inletRadius) / length * dx;
00315 
00316     T x0 = inletCenter[0];
00317     T y0 = inletCenter[1];
00318     T z0 = inletCenter[2];
00319 
00320     for (plint i = 0; i < nAxial-1; i++) {
00321         T x = x0 + i*dx;
00322         T r = (outletRadius-inletRadius) * (x-x0) / length + inletRadius;
00323         for (plint j = 0; j < nCirc; j++) {
00324             T theta = j * dtheta;
00325 
00326             Array<Array<T,3>, 4> v;
00327 
00328             v[0][0] = x;
00329             v[0][1] = y0 + r * cos(theta);
00330             v[0][2] = z0 + r * sin(theta);
00331 
00332             v[1][0] = x;
00333             v[1][1] = y0 + r * cos(theta + dtheta);
00334             v[1][2] = z0 + r * sin(theta + dtheta);
00335 
00336             v[2][0] = x + dx;
00337             v[2][1] = y0 + (r + dr) * cos(theta + dtheta);
00338             v[2][2] = z0 + (r + dr) * sin(theta + dtheta);
00339 
00340             v[3][0] = x + dx;
00341             v[3][1] = y0 + (r + dr) * cos(theta);
00342             v[3][2] = z0 + (r + dr) * sin(theta);
00343 
00344             Array<T,3> vc;
00345 
00346             vc[0] = x + 0.5 * dx;
00347             vc[1] = y0 + (r + 0.5 * dr) * cos(theta + 0.5 * dtheta);
00348             vc[2] = z0 + (r + 0.5 * dr) * sin(theta + 0.5 * dtheta);
00349 
00350             Array<Array<T,3>, 4> vce;
00351 
00352             vce[0][0] = x;
00353             vce[0][1] = y0 + r * cos(theta + 0.5 * dtheta);
00354             vce[0][2] = z0 + r * sin(theta + 0.5 * dtheta);
00355 
00356             vce[1][0] = x + 0.5 * dx;
00357             vce[1][1] = y0 + (r + 0.5 * dr) * cos(theta + dtheta);
00358             vce[1][2] = z0 + (r + 0.5 * dr) * sin(theta + dtheta);
00359 
00360             vce[2][0] = x + dx;
00361             vce[2][1] = y0 + (r + dr) * cos(theta + 0.5 * dtheta);
00362             vce[2][2] = z0 + (r + dr) * sin(theta + 0.5 * dtheta);
00363 
00364             vce[3][0] = x + 0.5 * dx;
00365             vce[3][1] = y0 + (r + 0.5 * dr) * cos(theta);
00366             vce[3][2] = z0 + (r + 0.5 * dr) * sin(theta);
00367 
00368             typename TriangleSet<T>::Triangle tmp;
00369 
00370             tmp[0] = vc;
00371             tmp[1] = v[0];
00372             tmp[2] = vce[0];
00373             triangles.push_back(tmp);
00374 
00375             tmp[0] = vc;
00376             tmp[1] = vce[0];
00377             tmp[2] = v[1];
00378             triangles.push_back(tmp);
00379 
00380             tmp[0] = vc;
00381             tmp[1] = v[1];
00382             tmp[2] = vce[1];
00383             triangles.push_back(tmp);
00384 
00385             tmp[0] = vc;
00386             tmp[1] = vce[1];
00387             tmp[2] = v[2];
00388             triangles.push_back(tmp);
00389 
00390             tmp[0] = vc;
00391             tmp[1] = v[2];
00392             tmp[2] = vce[2];
00393             triangles.push_back(tmp);
00394 
00395             tmp[0] = vc;
00396             tmp[1] = vce[2];
00397             tmp[2] = v[3];
00398             triangles.push_back(tmp);
00399 
00400             tmp[0] = vc;
00401             tmp[1] = v[3];
00402             tmp[2] = vce[3];
00403             triangles.push_back(tmp);
00404 
00405             tmp[0] = vc;
00406             tmp[1] = vce[3];
00407             tmp[2] = v[0];
00408             triangles.push_back(tmp);
00409         }
00410     }
00411     return TriangleSet<T>(triangles);
00412 }
00413 
00414 template<typename T>
00415 void addSurface (
00416         Array<T,3> const& lowerCorner,
00417         Array<T,3> const& delta1, plint n1, Array<T,3> const& delta2, plint n2,
00418         std::vector<typename TriangleSet<T>::Triangle>& triangles )
00419 {
00420     Array<T,3> pos1(lowerCorner);
00421     for(plint i1=0; i1<n1; ++i1, pos1+=delta1) {
00422         Array<T,3> pos2(pos1);
00423         for(plint i2=0; i2<n2; ++i2, pos2+=delta2) {
00424             typename TriangleSet<T>::Triangle triangle;
00425             triangle[0] = pos2;
00426             triangle[1] = pos2+delta1;
00427             triangle[2] = pos2+delta2;
00428             triangles.push_back(triangle);
00429             triangle[0] += delta1+delta2;
00430             std::swap(triangle[1], triangle[2]);
00431             triangles.push_back(triangle);
00432         }
00433     }
00434 }
00435 
00436 template<typename T>
00437 TriangleSet<T> constructCuboid (
00438         Array<T,3> const& lowerCorner, Array<T,3> const& upperCorner,
00439         Array<plint,3> const& nSegments )
00440 {
00441     std::vector<typename TriangleSet<T>::Triangle> triangles;
00442     T lx = upperCorner[0]-lowerCorner[0];
00443     T ly = upperCorner[1]-lowerCorner[1];
00444     T lz = upperCorner[2]-lowerCorner[2];
00445     Array<T,3> deltaX(lx/(T)nSegments[0], T(), T());
00446     Array<T,3> deltaY(T(), ly/(T)nSegments[1], T());
00447     Array<T,3> deltaZ(T(), T(), lz/(T)nSegments[2]);
00448 
00449     addSurface(lowerCorner,
00450                deltaZ, nSegments[2], deltaY, nSegments[1], triangles);
00451     addSurface(lowerCorner+Array<T,3>(lx,T(),T()),
00452                deltaY, nSegments[1], deltaZ, nSegments[2], triangles);
00453 
00454     addSurface(lowerCorner,
00455                deltaX, nSegments[0], deltaZ, nSegments[2], triangles);
00456     addSurface(lowerCorner+Array<T,3>(T(),ly,T()),
00457                deltaZ, nSegments[2], deltaX, nSegments[0], triangles);
00458 
00459     addSurface(lowerCorner,
00460                deltaY, nSegments[1], deltaX, nSegments[0], triangles);
00461     addSurface(lowerCorner+Array<T,3>(T(),T(),lz),
00462                deltaX, nSegments[0], deltaY, nSegments[1], triangles);
00463 
00464     return TriangleSet<T>(triangles);
00465 }
00466 
00467 
00468 template<typename T>
00469 TriangleSet<T> constructCylinder( Array<T,3> const& inletCenter, Array<T,3> const& axis,
00470                                   T inletRadius, T outletRadius,
00471                                   T length, plint nAxial, plint nCirc,
00472                                   std::vector<Array<T,3> >& inletPoints )
00473 {
00474     TriangleSet<T> cylinder (
00475             constructCylinder(Array<T,3>(T(),T(),T()), inletRadius, outletRadius,
00476             length, nAxial, nCirc, inletPoints) );
00477     Array<T,3> xAxis((T)1,T(),T());
00478     Array<T,3> rotAxis;
00479     T alignment = dot(xAxis, axis);
00480     T eps = (T)100.*std::numeric_limits<T>::epsilon();
00481     if (!util::fpequal(alignment, (T)1., eps)) {
00482         crossProduct<T>(xAxis, axis, rotAxis);
00483         rotAxis /= norm(rotAxis);
00484         T angle = angleBetweenVectors(xAxis, axis);
00485         cylinder.rotateAtOrigin(rotAxis, angle);
00486         for (pluint i=0; i<inletPoints.size(); ++i) {
00487             inletPoints[i] = rotateAtOrigin(inletPoints[i], rotAxis, angle);
00488         }
00489     }
00490     cylinder.translate(inletCenter);
00491     for (pluint i=0; i<inletPoints.size(); ++i) {
00492         inletPoints[i] += inletCenter;
00493     }
00494     return cylinder;
00495 }
00496 
00497 
00498 template<typename T>
00499 TriangleSet<T> patchTubes(TriangleSet<T> const& geometryWithOpenings, plint sortDirection, std::vector<T> patchLengths)
00500 {
00501     typedef typename TriangleSet<T>::Triangle Triangle;
00502     typedef typename TriangularSurfaceMesh<T>::Lid Lid;
00503 
00504     std::vector<Triangle> fullGeometry( geometryWithOpenings.getTriangles() );
00505 
00506     DEFscaledMesh<T>* defMesh = new DEFscaledMesh<T>(geometryWithOpenings);
00507     TriangularSurfaceMesh<T>& mesh = defMesh->getMesh();
00508 
00509     std::vector<Lid> holes = mesh.closeHoles();
00510     std::sort(holes.begin(), holes.end(), LidLessThan<T>(sortDirection, mesh));
00511 
00512     PLB_ASSERT( holes.size() == patchLengths.size() );
00513     
00514     for (pluint iHole=0; iHole<holes.size(); ++iHole) {
00515         Array<T,3> baryCenter = computeGeometricCenter(mesh,holes[iHole]);
00516         plint numHoleVertices = (plint) holes[iHole].boundaryVertices.size();
00517 
00518         Array<T,3> normal = computeNormal(mesh, holes[iHole]);
00519         T aveRadius = computeGeometricRadius(mesh,holes[iHole]);
00520 
00521         Array<T,3> nextCenter = baryCenter + normal*aveRadius*10./(T)numHoleVertices;
00522 
00523         plint numInletPoints = numHoleVertices;
00524         bool oddNumber = numHoleVertices%2==1;
00525         if (oddNumber) numInletPoints--; // Must be even for cylinder construction algorithm.
00526         std::vector<Array<T,3> > inletPoints;
00527         plint numPointsOnLength = numInletPoints*patchLengths[iHole]/aveRadius/8;
00528         if (numPointsOnLength<3) numPointsOnLength = 3;
00529         TriangleSet<T> piece = constructCylinder(nextCenter, normal, aveRadius, aveRadius, patchLengths[iHole],
00530                                                  numPointsOnLength, numInletPoints/2, inletPoints);
00531         std::vector<Triangle> pieceTriangles = piece.getTriangles();
00532 
00533         plint newId = 0;
00534         T minDistance = std::numeric_limits<T>::max();
00535         plint minDistanceId = -1;
00536         for (plint i=0; i<numHoleVertices; ++i) {
00537             plint iVertex = holes[iHole].boundaryVertices[i];
00538             Array<T,3> p1 = mesh.getVertex(iVertex);
00539             T nextDistance = norm(inletPoints[newId]-p1);
00540             if (nextDistance<minDistance) {
00541                 minDistance = nextDistance;
00542                 minDistanceId = i;
00543             }
00544         }
00545         plint oldId = minDistanceId;
00546         plint newId_p1 = 0;
00547         for (plint i=0; i<numInletPoints; ++i) {
00548             plint newId_p1 = (newId+1) % numInletPoints;
00549             plint oldId_p1 = oldId-1;
00550             if (oldId_p1<0) oldId_p1 = numHoleVertices-1;
00551 
00552             plint oldVertex1 = holes[iHole].boundaryVertices[oldId];
00553             plint oldVertex2 = holes[iHole].boundaryVertices[oldId_p1];
00554             Array<T,3> p1 = mesh.getVertex(oldVertex1);
00555             Array<T,3> p2 = inletPoints[newId];
00556             Array<T,3> p3 = mesh.getVertex(oldVertex2);
00557             Array<T,3> p4 = inletPoints[newId_p1];
00558 
00559             pieceTriangles.push_back(Triangle(p1,p3,p2));
00560             pieceTriangles.push_back(Triangle(p2,p3,p4));
00561 
00562             std::swap(newId, newId_p1);
00563             std::swap(oldId, oldId_p1);
00564         }
00565 
00566         if (oddNumber) {
00567             plint id_a = holes[iHole].boundaryVertices[oldId];
00568             plint oldId_p2 = oldId-1;
00569             if (oldId_p2<0) oldId_p2 = numHoleVertices-1;
00570             plint id_b = holes[iHole].boundaryVertices[oldId_p2];
00571             plint id_c = newId_p1;
00572             Array<T,3> a = mesh.getVertex(id_a);
00573             Array<T,3> b = mesh.getVertex(id_b);
00574             Array<T,3> c = inletPoints[id_c];
00575             pieceTriangles.push_back(Triangle(a,b,c));
00576         }
00577 
00578 
00579         fullGeometry.insert(fullGeometry.end(), pieceTriangles.begin(), pieceTriangles.end());
00580     }
00581 
00582     return TriangleSet<T>(fullGeometry, geometryWithOpenings.getPrecision());
00583 }
00584 
00585 } // namespace plb
00586 
00587 #endif  // TRIANGLE_SET_GENERATOR_HH