1 /** @file gsSolid.hpp
2 
3     @brief Provides implementation of gsSolid class
4 
5     This file is part of the G+Smo library.
6 
7     This Source Code Form is subject to the terms of the Mozilla Public
8     License, v. 2.0. If a copy of the MPL was not distributed with this
9     file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 
11     Author(s): A. Mantzaflaris, D.-M. Nguyen, M. Pauley
12 */
13 
14 #pragma once
15 
16 #include <queue>
17 #include <set>
18 
19 #include <gsCore/gsMultiPatch.h>
20 #include <gsModeling/gsCurveLoop.h>
21 #include <gsModeling/gsTrimSurface.h>
22 #include <gsModeling/gsPlanarDomain.h>
23 
24 #include <gsNurbs/gsKnotVector.h>
25 //#include <gsNurbs/gsBSplineBasis.h>
26 #include <gsNurbs/gsTensorBSpline.h>
27 
28 #include <gsNurbs/gsBSpline.h>
29 
30 
31 
32 namespace gismo {
33 
34 template <class T>
print(std::ostream & os) const35 std::ostream &gsSolid<T>::print(std::ostream &os) const
36 {
37   os<<"\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n";
38   os<<"A free-form solid modeled as a gsSolid:\n";
39   os<<"Number of volumes   : "<< numVolumes <<"\n";
40   os<<"Number of half faces: "<< numHalfFaces <<"\n";
41   os<<"Number of half edges: "<< numHalfEdges <<"\n";
42   os<<"Number of vertices  : "<< numVertices <<"\n";
43   // print a face
44   os<< *face[0];
45   os<<"vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n";
46   return os;
47 }
48 
49 
50 template <class T>
addHeVertex(scalar_t const & x,scalar_t const & y,scalar_t const & z)51 void gsSolid<T>::addHeVertex(scalar_t const& x, scalar_t const& y, scalar_t const& z)
52 {
53     gsSolidHeVertexHandle v = this->makeHeVertex(x,y,z);
54     vertex.push_back(v );
55     v->setId(numVertices++);
56 
57     if(!initialized) {
58 	bb.pMax.x() = bb.pMin.x() = x;
59 	bb.pMax.y() = bb.pMin.y() = y;
60 	bb.pMax.z() = bb.pMin.z() = z;
61 	initialized = true;
62     }
63     else {
64 	if (bb.pMax.x() < x)
65 	    bb.pMax.x() = x;
66 	if (bb.pMin.x() > x)
67 	    bb.pMin.x() = x;
68 
69 	if (bb.pMax.y() < y)
70 	    bb.pMax.y() = y;
71 	if (bb.pMin.y() > y)
72 	    bb.pMin.y() = y;
73 
74 	if (bb.pMax.z() < z)
75 	    bb.pMax.z() = z;
76 	if (bb.pMin.z() > z)
77 	    bb.pMin.z() = z;
78     };
79 }
80 
81 template <class T>
addFace(std::vector<gsSolidHeVertexHandle> V)82 typename gsSolid<T>::gsSolidHalfFaceHandle gsSolid<T>::addFace(std::vector<gsSolidHeVertexHandle> V)
83 {
84     if (V.size() != 4) //{ gsDebug<<"\n The following initialization of trimmed surf does not work for less or more than 4 corners yet\n";};
85     {
86         return addFace_PlanarPolygon(V);
87     }
88 
89     gsMatrix<T> corner = gsMatrix<T>(4,3);
90     for (unsigned i=0; i<=3; i++)
91     {
92       for (unsigned xi=0;xi<=2;xi++)
93       {
94         corner(i,xi) = (V[i]->coords)[xi];
95       }
96     }
97     //gsTrimSurface<T>* tsurf = new  gsTrimSurface<T>(corner, 3, 3, 3);
98     gsTrimSurface<T>* tsurf = new  gsTrimSurface<T>(corner, 1, 1, 1);
99     return addFace(V,tsurf);
100 }
101 
102 template<class T>
~gsSolid()103 gsSolid<T>::~gsSolid()
104 {
105     //apaga os vertices
106     freeAll( vertex );
107 
108     //apaga as arestas
109     freeAll( edge );
110 
111     //apaga as faces
112     freeAll( face );
113 
114     //apaga as faces
115     freeAll( volume );
116 }
117 
118 
119 template<class T>
addFace(std::vector<gsSolidHeVertexHandle> V,gsTrimSurface<T> * tsurf)120 typename gsSolid<T>::gsSolidHalfFaceHandle gsSolid<T>::addFace(std::vector<gsSolidHeVertexHandle> V, gsTrimSurface<T> * tsurf)
121 {
122     gsSolidHalfFaceHandle f = new gsSolidHalfFace<T>();
123     f->surf = tsurf;
124     std::vector<gsSolidHalfEdgeHandle> H;
125     for ( typename std::vector<gsSolidHeVertexHandle>::iterator
126 	      it = V.begin(); it!= V.end(); ++it)
127     {
128 	// initialize the half-edge associated with the vertex *it and the face f
129 	gsSolidHalfEdgeHandle tempHe = this->makeSolidHalfEdge(*it,f);
130 	tempHe->setId(numHalfEdges++);
131 	H.push_back( tempHe );
132 	edge.push_back( tempHe );
133 	// assign the incident halfedge associated with the vertex *it to *it
134 	// fist check if hed is already assigned to avoid doubling assignments
135 	if (!(*it)->hed)
136 	(*it)->hed = tempHe;
137 
138     }
139     f->setBoundary( H );
140     face.push_back(f);
141     f->setId(numHalfFaces++);
142     return f;
143 }
144 
145 template<class T>
addFace(std::vector<std::vector<gsSolidHeVertexHandle>> loopV,gsTrimSurface<T> * tsurf)146 typename gsSolid<T>::gsSolidHalfFaceHandle gsSolid<T>::addFace(std::vector< std::vector<gsSolidHeVertexHandle> > loopV, gsTrimSurface<T> * tsurf)
147 {
148     gsSolidHalfFaceHandle f = new gsSolidHalfFace<T>();
149     f->surf = tsurf;
150     std::vector<gsSolidHalfEdgeHandle> H;
151 
152     std::vector<gsSolidHeVertexHandle>  V;
153     for ( size_t il=0; il<loopV.size(); il++)
154     {
155         V=loopV[il];
156         H.clear();
157         for ( typename std::vector<gsSolidHeVertexHandle>::iterator
158               it = V.begin(); it!= V.end(); ++it)
159         {
160             // initialize the half-edge associated with the vertex *it and the face f
161             gsSolidHalfEdgeHandle tempHe = this->makeSolidHalfEdge(*it,f);
162             tempHe->setId(numHalfEdges++);
163             H.push_back( tempHe );
164             edge.push_back( tempHe );
165             // assign the incident halfedge associated with the vertex *it to *it
166             // fist check if hed is already assigned to avoid doubling assignments
167             if (!(*it)->hed)
168                 (*it)->hed = tempHe;
169         }
170         f->setBoundary( H );
171     }
172 
173     face.push_back(f);
174     f->setId(numHalfFaces++);
175     return f;
176 }
177 
178 /// Add a planar face, specified by a list of vertices that should all be in the same plane.
179 template<class T>
addFace_PlanarPolygon(std::vector<gsSolidHeVertexHandle> V)180 typename gsSolid<T>::gsSolidHalfFaceHandle gsSolid<T>::addFace_PlanarPolygon(std::vector<gsSolidHeVertexHandle> V)
181 {
182   // make sure there are at least 3 vertices.
183   int faceVerts = V.size();
184   assert(faceVerts >= 3);
185 
186   // compute normal to plane and make sure all vertices are in the plane.
187   // (find a vertex with minimal x-coordinate, and minimal y in the case of a tie.
188   // this ensures that the vertex is convex.)
189   int minVert = faceVerts - 1;
190   for(int i = 0; i < faceVerts - 1; i++)
191   {
192     if(V[i]->coords(0) < V[minVert]->coords(0) ||
193       (V[i]->coords(0) <= V[minVert]->coords(0) && V[i]->coords(1) < V[minVert]->coords(1)))
194       {
195         //gsDebug << "\n" << V[i]->coords(0) << "," << V[i]->coords(1) << "," << V[i]->coords(2);
196         minVert = i;
197       }
198   }
199   gsVector3d<T> prevVector = V[(minVert + faceVerts - 1) % faceVerts]->coords - V[minVert]->coords;
200 
201   gsVector3d<T> normal(0, 0, 0);
202   int nextVertex = minVert;
203   while(normal.squaredNorm() == 0)
204   {
205     nextVertex = (nextVertex + 1) % faceVerts;
206     assert(nextVertex != minVert); // failed to find a vertex that is not collinear with minVert and the one before it.
207     normal = (V[nextVertex]->coords - V[minVert]->coords).cross(prevVector);
208   }
209   normal.normalize();
210   for(int i = 3; i < faceVerts; i++)
211   {
212     assert((V[i]->coords - V[0]->coords).dot(normal) == 0);
213   }
214   T normalCoord = normal.dot(V[0]->coords);
215   //gsDebug << "\nNormal coefs: " << normal << std::endl << normalCoord;
216 
217   // compute a good transformation that we can use to generate
218   // corners in the 2D domain. We make an
219   // arbitrary vector b1 orthogonal to normal, then cross product it with normal
220   // to produce a second vector b2. {b1, b2} form a basis for the plane.
221   // use the dot product of a vertex's coordinates with b1 and b2 to produce
222   // 2D coordinates.
223 
224   gsVector3d<T> a1(1, 0, 0), a2(0, 1, 0);
225   a1 = a1.cross(normal);
226   a2 = a2.cross(normal);
227   gsVector3d<T> b1, b2;
228   if(a1.squaredNorm() > a2.squaredNorm())
229   {
230     b1 = a1;
231   }
232   else
233   {
234     b1 = a2;
235   }
236   b1.normalize();
237   b2 = normal.cross(b1);
238 
239   //gsDebug << "\nbasis for plane. b1=" << b1 << ", b2="<< b2;
240 
241   // make a matrix containing a list of (transposed) corners in the domain.
242   // compute a good rectangle for the domain of the base surface. do this by
243   // finding the bounding box and enlarging it by a fixed amount (margin) on
244   // each side.
245   T margin(0.1);
246   T x, y;
247   gsMatrix<T> DomCor(faceVerts + 1, 2);
248   for(int i = 0; i < faceVerts; i++)
249   {
250     // compute x and y coords and insert them into the matrix DomCor
251     x = b1.dot(V[i]->coords);
252     y = b2.dot(V[i]->coords);
253     //gsDebug << "\nvertex " << i << " coords in domain: " << x << "," << y;
254     DomCor(i, 0) = x;
255     DomCor(i, 1) = y;
256   }
257   DomCor(faceVerts, 0) = DomCor(0, 0);
258   DomCor(faceVerts, 1) = DomCor(0, 1);
259 
260   // find min and max x and y values (before resizing the polygon)
261   T minx = DomCor.col(0).minCoeff();
262   T maxx = DomCor.col(0).maxCoeff();
263   T miny = DomCor.col(1).minCoeff();
264   T maxy = DomCor.col(1).maxCoeff();
265   T rangex = maxx - minx, rangey = maxy - miny;
266   T midx = (minx + maxx) / 2, midy = (miny + maxy) / 2;
267   T halfMaxRange = std::max(rangex, rangey) * (margin * 2 + 1) / 2;
268   minx -= rangex * margin;
269   maxx += rangex * margin;
270   miny -= rangey * margin;
271   maxy += rangey * margin;
272 
273   // resize and shift to be inside [0,1]x[0,1]
274   gsCurveLoop<T>::adjustPolygonToUnitSquare(DomCor, margin);
275 
276   //gsDebug << "\nDomain rectangular bounds: [" << minx << "," << maxx << "] x [" << miny << "," << maxy << "]";
277 
278   // create a trimming loop and construct a planar domain from it.
279   gsCurveLoop<T> * tloop = new gsCurveLoop<T>();
280   for(int i = 0; i < faceVerts; i++)
281   {
282     gsMatrix<T> tcp(2, 2);
283     tcp << DomCor(i, 0), DomCor(i, 1), DomCor(i + 1, 0), DomCor(i + 1, 1);
284     gsBSpline<T> * tcurve = new gsBSpline<T>( 0, 1, 0, 1, give(tcp) );
285     tloop->insertCurve(tcurve);
286   }
287   gsPlanarDomain<T> * domain1= new gsPlanarDomain<T>(tloop);
288 
289   // create the basis
290 
291   gsKnotVector<T> KV1 = gsKnotVector<T>(0, 1, 0, 2);//multiplicity at ends==1+1
292   gsKnotVector<T> KV2 = gsKnotVector<T>(0, 1, 0, 2);//multiplicity at ends==1+1
293 
294   // unused variables:
295   //gsBSplineBasis<T> * Bu= new gsBSplineBasis<T>(KV1);
296   //gsBSplineBasis<T> * Bv= new gsBSplineBasis<T>(KV2);
297 
298   // produce a 4x3 matrix with the control points
299   // to map the 2D domain linearly into 3D.
300   gsMatrix<T> pcp(4, 3);
301   for(int xi = 0; xi < 3; xi++) // loop over coordinates
302   {
303     pcp(0, xi) = normal[xi] * normalCoord + (midx - halfMaxRange) * b1[xi] + (midy - halfMaxRange) * b2[xi];
304     pcp(1, xi) = normal[xi] * normalCoord + (midx + halfMaxRange) * b1[xi] + (midy - halfMaxRange) * b2[xi];
305     pcp(2, xi) = normal[xi] * normalCoord + (midx - halfMaxRange) * b1[xi] + (midy + halfMaxRange) * b2[xi];
306     pcp(3, xi) = normal[xi] * normalCoord + (midx + halfMaxRange) * b1[xi] + (midy + halfMaxRange) * b2[xi];
307   }
308   //gsDebug << "\nControl point matrix: " << pcp << "\n";
309   //gsDebug << "\nPolygon points: " << DomCor << "\n";
310   typename gsTensorBSpline<2,T>::Ptr tp1(new gsTensorBSpline<2,T>(KV1, KV2, give(pcp)));
311 
312   // instantiate and add the half-face object.
313 
314   gsTrimSurface<T> * ts = new gsTrimSurface<T>(tp1, domain1);
315   return addFace(V, ts);
316 
317 }
318 
319 template<class T>
setHeMate()320 void gsSolid<T>::setHeMate()
321 {
322   // Method in the meantime: consider each pair of halfedges and see if they are mates
323   // Todo: consider each pair of faces
324   unsigned int noMate(0); // number of mates
325   for (typename std::vector<gsSolidHalfEdgeHandle>::iterator it1=edge.begin(); it1!=edge.end()-1; ++it1)
326   {
327     for (typename std::vector<gsSolidHalfEdgeHandle>::iterator it2=it1+1; it2!=edge.end(); ++it2)
328     {
329       // a pair of half edges are mates iff the source of one of them is the target of the orther
330       gsSolidHeVertexHandle source1, source2, target1, target2;
331       source1 = (*it1)->source;
332       source2 = (*it2)->source;
333       target1 = (*it1)->next->source;
334       target2 = (*it2)->next->source;
335       if (source1 == target2 && source2 == target1)
336       {
337 	noMate++;
338 	(*it1)->mate = *it2;
339 	(*it2)->mate = *it1;
340       }
341     }
342   }
343   // check if the number of mates is the same as the number of assignments
344   if (2*noMate!=edge.size())
345   {
346 //      gsDebug <<"\n"<<"The number of assignments of HE mates (="<< noMate <<") is NOT equal to number of edges (not halfedges) (="<< edge.size()/2 <<"), this is most likely because of the wrong order of the vertices of a face "<<"\n";
347 
348       gsWarn << "The number of assignments of HE mates (="<< noMate <<") is NOT equal to number of edges (not halfedges) (="<< edge.size()/2 <<"), this is most likely because of the wrong order of the vertices of a face, or the model is not a manifold\n";
349   }
350 }
351 
352 template<class T>
detectNonConvexEdges(std::vector<int> const & ncEdgeV1,std::vector<int> const & ncEdgeV2)353 std::vector< typename gsSolid<T>::gsSolidHalfEdgeHandle > gsSolid<T>::detectNonConvexEdges(std::vector<int> const & ncEdgeV1, std::vector<int> const & ncEdgeV2)
354 {
355   // Todo: detect nonconvex edges automatically
356   size_t nEdge = ncEdgeV1.size();
357   gsSolidHalfEdgeHandle he;
358   assert(nEdge==ncEdgeV2.size());
359   std::vector<gsSolidHalfEdgeHandle> nce;
360   for (size_t i=0;i<nEdge;i++)
361   {
362       he = this->getVertexFromID(ncEdgeV1[i])->getHalfEdge( this->getVertexFromID(ncEdgeV2[i]) );
363       he->is_convex = false;
364       he->mate->is_convex = false;
365       nce.push_back(he);
366   }
367   return nce;
368 }
369 
370 
371 
372 
373 template <class T>
splitFace(gsSolidHalfFaceHandle f,gsSolidHeVertexHandle startVertex,gsSolidHeVertexHandle endVertex,gsBSpline<T> * domainSpline)374 typename gsSolid<T>::gsSolidHalfFaceHandle gsSolid<T>::splitFace(
375   gsSolidHalfFaceHandle f,
376   gsSolidHeVertexHandle startVertex, gsSolidHeVertexHandle endVertex, gsBSpline<T> *domainSpline)
377 {
378   checkStructure();
379 
380   int numEdges = f->surf->domain().outer().size();
381   // search for a half-edge on this face whose source is endVertex
382   gsSolidHalfEdgeHandle nextEdge = endVertex->getHalfEdgeOnFace(f, false);
383   gsSolidHalfEdgeHandle matesPrev = nextEdge->prev;
384   int nextEdgeIdx = f->indexOfEdge(nextEdge);
385   // search for a half-edge whose end vertex (next->source) is startVertex
386   gsSolidHalfEdgeHandle prevEdge = startVertex->getHalfEdgeOnFace(f, true);
387   gsSolidHalfEdgeHandle matesNext = prevEdge->next;
388   int prevEdgeIdx = f->indexOfEdge(prevEdge);
389   // create a new half-edge
390   gsSolidHalfEdgeHandle newHE = this->makeSolidHalfEdge(startVertex, f);
391   newHE->face = f;
392   newHE->setId(numHalfEdges++);
393 
394   // close off the first part of this face off along the new half-edge
395   newHE->prev = prevEdge;
396   newHE->next = nextEdge;
397   prevEdge->next = newHE;
398   nextEdge->prev = newHE;
399   // create the half-edge's mate
400   gsSolidHalfEdgeHandle mate = this->makeSolidHalfEdge(endVertex, f);
401   newHE->mate = mate;
402   mate->mate = newHE;
403   mate->setId(numHalfEdges++);
404 
405   // close off the second part
406   mate->prev = matesPrev;
407   mate->next = matesNext;
408   matesPrev->next = mate;
409   matesNext->prev = mate;
410   // build a collection of edges for the new face
411   std::vector<gsSolidHalfEdgeHandle> newFaceBoundary;
412   newFaceBoundary.push_back(mate);
413   gsSolidHalfEdgeHandle tempEdge;
414   for(tempEdge = matesNext; tempEdge != mate; tempEdge = tempEdge->next)
415   {
416     newFaceBoundary.push_back(tempEdge);
417   }
418 
419   // create a reverse spline of domainSpline
420   std::vector<T> origKnots = domainSpline->knots();
421   std::vector<T> mateKnots;
422   mateKnots.reserve( origKnots.size() );
423   T flipPoint = (*origKnots.begin()) + (*origKnots.rbegin());
424   for(int i = origKnots.size() - 1; i >= 0; i--)
425   {
426     mateKnots.push_back(flipPoint - origKnots[i]);
427   }
428   gsKnotVector<T> mateKV(domainSpline->knots().degree(), mateKnots.begin(), mateKnots.end());
429   gsMatrix<T> mateCoefs = domainSpline->coefs().colwise().reverse();
430   gsBSpline<T> * reverseSpline = new gsBSpline<T>(mateKV, give(mateCoefs));
431 
432   // split the domain for the new face off from the old one
433   typename gsPlanarDomain<T>::uPtr newDomain =
434       f->surf->domain().split((prevEdgeIdx + 1) % numEdges, nextEdgeIdx, domainSpline, reverseSpline);
435   if(prevEdgeIdx > nextEdgeIdx)
436   {
437     // if startVertex is the source of the first edge, or is later in the cycle than endVertex,
438     // then the new face will contain loop[0]. The easiest thing to do is to reset loop[0] to
439     // the new start of f's cycle.
440     f->loop[0] = nextEdge;
441   }
442   typename gsSurface<T>::Ptr newBaseSurface = f->surf->getTP();
443 
444   // create the new face
445   gsTrimSurface<T> * newTS = new gsTrimSurface<T>(newBaseSurface , newDomain.release());
446 
447   // add the new face using existing half-edges. (calling addFace would re-add the edges)
448   // gsSolidHalfFace<T> * newFace = addFace(newFaceBoundary, newTS);
449   edge.push_back( newHE );
450   edge.push_back( mate );
451 
452   gsSolidHalfFaceHandle newFace = new gsSolidHalfFace<T>();
453   newFace->surf = newTS;
454   newFace->setBoundary( newFaceBoundary );
455   face.push_back(newFace);
456   newFace->setId(numHalfFaces++);
457   newFace->vol = f->vol;
458   newFace->vol->face.push_back(newFace);
459 
460   // update all the half-edges on the new face
461   tempEdge = mate;
462   do
463   {
464     tempEdge->face = newFace;
465     tempEdge = tempEdge->next;
466   } while(tempEdge != mate);
467 
468   checkStructure();
469 
470   return newFace;
471 }
472 
473 
474 template <class T>
plotEdgeGraph()475 gsMultiPatch<T> gsSolid<T>::plotEdgeGraph()
476 {
477     gsMultiPatch<T> mp;
478     std::vector<gsCurve<T>*>  loopv;
479     for (unsigned i=0;i!=numHalfFaces;i++)
480     {
481         loopv = face[i]->surf->domain().outer().curves();
482         for (unsigned j=0;j!=loopv.size();j++)
483         {
484             mp.addPatch(*loopv[j]);
485         }
486     }
487     return mp;
488 }
489 
490 template <class T>
newVolume(gsSolidHalfFaceHandle startingFace)491 gsVolumeBlock<T> *gsSolid<T>::newVolume(gsSolidHalfFaceHandle startingFace)
492 {
493   // set up the new volume
494   gsVolumeHandle newVol = new gsVolumeBlock<T>;
495   volume.push_back(newVol);
496   newVol->setId(numVolumes++);
497   // TODO: set the new volume's faces, and update the old volume's faces.
498   //newVol->face.push(startingFace);
499   // start the queue off with a single face
500   std::queue<gsSolidHalfFaceHandle> unprocessed;
501   unprocessed.push(startingFace);
502   std::set<gsSolidHalfFaceHandle> seen;
503   seen.insert(startingFace);
504   // process the queue
505   while(!unprocessed.empty())
506   {
507     // grab first face off the queue
508     gsSolidHalfFaceHandle nextFace = unprocessed.front();
509     unprocessed.pop();
510     // set the face's volume
511     nextFace->vol = newVol;
512     newVol->face.push_back(nextFace);
513     size_t numLoops = nextFace->loop.size();
514     // add all neighbours to the queue, if they haven't already been seen
515     for(size_t i = 0; i < numLoops; i++)
516     {
517       gsSolidHalfEdgeHandle firstEdge = nextFace->loop[i];
518       gsSolidHalfEdgeHandle nextEdge = firstEdge;
519       do
520       {
521         gsSolidHalfFaceHandle neighbFace = nextEdge->mate->face;
522         if(seen.find(neighbFace) == seen.end())
523         {
524           unprocessed.push(neighbFace);
525           seen.insert(neighbFace);
526         }
527         nextEdge = nextEdge->next;
528       } while(nextEdge != firstEdge);
529     }
530   }
531 
532   // process every volume's list of faces, keeping only faces with matching volume.
533   unsigned foundFaces = 0;
534   GISMO_ASSERT(numVolumes == volume.size(), "Number of volumes does not match");
535   for(size_t idxV = 0; idxV < numVolumes; idxV++)
536   {
537     size_t outF = 0;
538     size_t numF = volume[idxV]->face.size();
539     for(size_t inF = 0; inF < numF; inF++)
540     {
541       if(volume[idxV]->face[inF]->vol == volume[idxV])
542       {
543         volume[idxV]->face[outF] = volume[idxV]->face[inF];
544         outF++;
545         foundFaces++;
546       }
547     }
548     volume[idxV]->face.resize(outF);
549   }
550   GISMO_ASSERT(foundFaces == face.size() && foundFaces == numHalfFaces,
551                "Failed to assign a volume for all faces.");
552   return newVol;
553 }
554 
555 template <class T>
checkStructure(bool checkVerts) const556 void gsSolid<T>::checkStructure(bool checkVerts) const
557 {
558     GISMO_UNUSED(checkVerts);
559     size_t numEdges = edge.size();
560     for(size_t idxE = 0; idxE < numEdges; idxE++)
561     {
562         gsSolidHalfEdgeHandle e = edge[idxE];
563         if (e->mate->mate != e) gsWarn<< "Inconsistent solid graph.\n";
564         GISMO_ASSERT(e->prev->target() == e->source, "Inconsistent solid graph");
565         GISMO_ASSERT(e->mate->face->vol == e->face->vol, "Inconsistent solid graph");
566         GISMO_ASSERT(e->next->face == e->face, "Inconsistent solid graph");
567         GISMO_ASSERT(e->next->prev == e, "Inconsistent solid graph");
568         GISMO_ASSERT(e->prev->next == e, "Inconsistent solid graph");
569     }
570 
571     // check that the trimmed surface data matches the info we have about the face
572     for(size_t idxF = 0; idxF < numHalfFaces; idxF++)
573     {
574         gsSolidHalfFace<T> *f = face[idxF];
575         gsTrimSurface<T> *surf = f->surf;
576         size_t numLoops = surf->domain().numLoops();
577         GISMO_ASSERT(numLoops == f->loop.size(), "Number of holes in face does not match corresponding trimmed surface");
578         for(size_t loopNum = 0; loopNum < numLoops; loopNum++)
579         {
580             gsSolidHalfEdge<T> *e = f->loop[loopNum];
581             const gsCurveLoop<T> & curveLoop = surf->domain().loop(loopNum);
582             size_t numCurves = curveLoop.size();
583             for(size_t curveNum = 0; curveNum < numCurves; curveNum++)
584             {
585                 gsMatrix<T> surfCoord = surf->vertexCoord(loopNum, curveNum);
586                 GISMO_ASSERT(!checkVerts ||
587                              (surfCoord - e->source->coords).norm() < 0.0001, "Vertices in surface do not match vertex coordinates");
588 
589                 const gsMatrix<T>& thisCurveCoefs = curveLoop.curve(curveNum).coefs();
590                 gsMatrix<T> thisCurveEnd = thisCurveCoefs.row(thisCurveCoefs.rows() - 1);
591                 const gsMatrix<T>& nextCurveCoefs = curveLoop.curve((curveNum + 1) % numCurves).coefs();
592                 gsMatrix<T> nextCurveStart = nextCurveCoefs.row(0);
593                 GISMO_ASSERT((thisCurveEnd - nextCurveStart).norm() < 0.0001, "gsCurveLoop did not close up");
594                 e = e->next;
595             }
596             GISMO_ASSERT(e == f->loop[loopNum], "Face's loop did not close up properly");
597         }
598     }
599 }
600 
601 template <class T>
addFaceWithMate(const std::vector<gsSolidHeVertexHandle> & verts,gsTrimSurface<T> * surf)602 gsSolidHalfFace<T> *gsSolid<T>::addFaceWithMate(const std::vector<gsSolidHeVertexHandle> &verts, gsTrimSurface<T> *surf)
603 {
604   GISMO_ASSERT(verts.size() >= 2, "Unexpected number of vertices in new face");
605   checkStructure();
606 
607   std::vector<gsSolidHeVertexHandle> reorderedVerts;
608   reorderedVerts.push_back(verts[verts.size() - 1]);
609   for(size_t iV = 0; iV < verts.size() - 1; iV++)
610   {
611     reorderedVerts.push_back(verts[iV]);
612   }
613 
614   surf->cleanEndpoints(0.0000001);
615 
616   // special check to make sure the corners of the new trimmed surface actually
617   // match the coordinates of the vertex.
618   size_t numLoops = surf->domain().numLoops();
619   for(size_t loopNum = 0; loopNum < numLoops; loopNum++)
620   {
621     size_t numCurves = surf->domain().loop(loopNum).size();
622     for(size_t curveNum = 0; curveNum < numCurves; curveNum++)
623     {
624       gsMatrix<T> surfCoord = surf->vertexCoord(loopNum, curveNum);
625       GISMO_ASSERT((surfCoord - reorderedVerts[curveNum]->coords).norm() < 0.0001, "Vertices in surface do not match vertex coordinates");
626     }
627   }
628 
629   typedef typename gsTensorBSplineBasis<2,T>::GeometryType MasterSurface;
630   // Create a new master surface by taking this surface's one and flipping one coordinate.
631   gsTrimSurface<T> *surfReverse = new gsTrimSurface<T>(surf->getTP()->clone().release(), surf->domain().clone().release());
632   gsMatrix<T> &surfRevCoefs = surfReverse->getTP()->coefs();
633   MasterSurface *genGeom = dynamic_cast<MasterSurface *>(surfReverse->getTP().get());
634   GISMO_ASSERT(genGeom != NULL, "This procedure requires a gsGenericGeometry");
635   gsMatrix<T> revSupp = surfReverse->getTP()->support();
636   GISMO_ASSERT(revSupp.cols() == 2, "Unexpected support");
637 
638   gsMatrix<T> testPt(2,4);
639   testPt << 0.75 * revSupp(0, 0) + 0.25 * revSupp(0, 1), 0.75 * revSupp(0, 0) + 0.25 * revSupp(0, 1), 0.25 * revSupp(0, 0) + 0.75 * revSupp(0, 1), 0.25 * revSupp(0, 0) + 0.75 * revSupp(0, 1),
640             0.75 * revSupp(1, 0) + 0.25 * revSupp(1, 1), 0.25 * revSupp(1, 0) + 0.75 * revSupp(1, 1), 0.75 * revSupp(1, 0) + 0.25 * revSupp(1, 1), 0.25 * revSupp(1, 0) + 0.75 * revSupp(1, 1);
641   gsMatrix<T> testVal1;
642   genGeom->eval_into(testPt, testVal1);
643 
644   unsigned totalCPs = surfRevCoefs.rows();
645   unsigned dim = surfRevCoefs.cols();
646   unsigned blockSize = genGeom->basis().component(0).size();
647   for(unsigned blockStart = 0; blockStart < totalCPs; blockStart += blockSize) // note step size
648   {
649     surfRevCoefs.block(blockStart, 0, blockSize, dim) =
650       surfRevCoefs.block(blockStart, 0, blockSize, dim).colwise().reverse().eval();
651   }
652 
653   // test that the surface has been correctly reversed
654   gsMatrix<T> testVal2;
655   testPt << 0.25 * revSupp(0, 0) + 0.75 * revSupp(0, 1), 0.25 * revSupp(0, 0) + 0.75 * revSupp(0, 1), 0.75 * revSupp(0, 0) + 0.25 * revSupp(0, 1), 0.75 * revSupp(0, 0) + 0.25 * revSupp(0, 1),
656             0.75 * revSupp(1, 0) + 0.25 * revSupp(1, 1), 0.25 * revSupp(1, 0) + 0.75 * revSupp(1, 1), 0.75 * revSupp(1, 0) + 0.25 * revSupp(1, 1), 0.25 * revSupp(1, 0) + 0.75 * revSupp(1, 1);
657 
658   genGeom->eval_into(testPt, testVal2);
659   gsMatrix<T> origCoefs = surf->getTP()->coefs();
660   gsMatrix<T> testRevCoefs = surfReverse->getTP()->coefs();
661   GISMO_ASSERT((testVal2 - testVal1).norm() < 0.0001, "Surface was not correctly reversed");
662 
663   // Create new trimming loops by starting with this surfaces's one and
664   //  (i) reversing the whole loop
665   //  (ii) flipping one coordinate
666   numLoops = surfReverse->domain().numLoops();
667   for(size_t loopNum = 0; loopNum < numLoops; loopNum++)
668   {
669     gsCurveLoop<T> & revCurveLoop = surfReverse->domain().loop(loopNum);
670     size_t numCurves = revCurveLoop.size();
671     for(size_t curveNum = 0; curveNum < numCurves; curveNum++)
672     {
673       gsGeometry<T> & thisCurve = revCurveLoop.curve(curveNum);
674       gsMatrix<T> &curveCoefs = thisCurve.coefs();
675       curveCoefs = curveCoefs.colwise().reverse().eval();
676       size_t rows = curveCoefs.rows();
677       for(size_t cpIdx = 0; cpIdx < rows; cpIdx++)
678       {
679         curveCoefs(cpIdx, 0) = revSupp(0, 0) + revSupp(0, 1) - curveCoefs(cpIdx, 0);
680       }
681     }
682 
683     // reverse order of curves in the curve loop
684     std::reverse( revCurveLoop.curves().begin(), revCurveLoop.curves().end() );
685   }
686 
687   // the following loop is only for debugging purposes: check that curves are joined
688   for(size_t loopNum = 0; loopNum < numLoops; loopNum++)
689   {
690     const gsCurveLoop<T> & revCurveLoop = surfReverse->domain().loop(loopNum);
691     size_t numCurves = revCurveLoop.size();
692     for(size_t curveNum = 0; curveNum < numCurves; curveNum++)
693     {
694       // make sure each curve starts where the previous one ends
695       const gsGeometry<T> & thisCurve = revCurveLoop.curve(curveNum);
696       const gsGeometry<T> & nextCurve = revCurveLoop.curve((curveNum + 1) % numCurves);
697       gsMatrix<T> thisSupp = thisCurve.support();
698       gsMatrix<T> nextSupp = nextCurve.support();
699       gsMatrix<T> thisEndPt = nextCurve.eval(nextSupp.col(0));
700       gsMatrix<T> nextStartPt = thisCurve.eval(thisSupp.col(1));
701       GISMO_ASSERT((thisEndPt - nextStartPt).norm() < 0.0001, "Invalid curve loop");
702       // make sure corresponding points on the opposite faces have the same location in space
703       gsMatrix<T> thisCoord = surfReverse->vertexCoord(loopNum, curveNum);
704       gsMatrix<T> otherCoord = surf->vertexCoord(loopNum, (numCurves - curveNum) % numCurves);
705       GISMO_ASSERT((otherCoord - thisCoord).norm() < 0.0001, "Mate faces do not have matching vertices");
706       GISMO_ASSERT((otherCoord - reorderedVerts[(numCurves - curveNum) % numCurves]->coords).norm() < 0.0001, "Vertices in surface do not match vertex coordinates");
707     }
708   }
709 
710   // record the edge where we will start the updating process
711   gsSolidHalfEdgeHandle startEdge = reorderedVerts[0]->getHalfEdge(reorderedVerts[1]);
712   // duplicate all of the relevant vertices
713   std::vector<gsSolidHeVertexHandle> newVerts, newVertsReverse;
714   for(typename std::vector<gsSolidHeVertexHandle>::const_iterator iter = reorderedVerts.begin(); iter != reorderedVerts.end(); iter++)
715   {
716     addHeVertex((*iter)->coords(0), (*iter)->coords(1), (*iter)->coords(2));
717     newVerts.push_back(vertex[numVertices - 1]);
718   }
719   // reverse vertices start with vertex 0 and go back the other way
720   newVertsReverse.push_back(newVerts[0]);
721   for(int i = newVerts.size() - 1; i > 0; i--) newVertsReverse.push_back(newVerts[i]);
722   // create the first new face using the already existing vertices
723   gsSolidHalfFaceHandle frontFace = addFace(reorderedVerts, surf);
724   // create its mate using the duplicate vertices and the reverse trim surface
725   gsSolidHalfFaceHandle backFace = addFace(newVertsReverse, surfReverse);
726   backFace->vol = NULL;
727   // set faces as each others' mates
728   frontFace->mate = backFace;
729   backFace->mate = frontFace;
730   // make sure none of the vertices along the loop have their "hed" pointing
731   // into the new volume
732   size_t vertNum, numVerts = reorderedVerts.size();
733   for(vertNum = 0; vertNum < numVerts; vertNum++)
734   {
735     gsSolidHeVertex<T> *thisVert = reorderedVerts[vertNum];
736     gsSolidHeVertex<T> *nextVert = reorderedVerts[(vertNum + 1) % numVerts];
737     while(thisVert->hed->next->source != nextVert)
738     {
739       thisVert->hed = thisVert->hed->mate->next;
740       GISMO_ASSERT(thisVert->hed->source == thisVert, "Inconsistent solid graph");
741     }
742     thisVert->hed = thisVert->hed->mate->next;
743     GISMO_ASSERT(thisVert->hed->source == thisVert, "Inconsistent solid graph");
744   }
745   // loop over half-edges "he" in the loop
746   gsSolidHalfEdge<T> *he = startEdge;
747   vertNum = 0;
748   gsSolidHalfEdgeHandle newFrontFaceEdge = frontFace->loop[0];
749   while(newFrontFaceEdge->source != reorderedVerts[0]) newFrontFaceEdge = newFrontFaceEdge->next;
750   gsSolidHalfEdgeHandle newBackFaceEdge = backFace->loop[0];
751   while(newBackFaceEdge->source != newVerts[1]) newBackFaceEdge = newBackFaceEdge->next;
752   do
753   {
754     // set mates for the new front face
755     he->mate->mate = newFrontFaceEdge;
756     newFrontFaceEdge->mate = he->mate;
757     // set mates for the new back face
758     he->mate = newBackFaceEdge;
759     newBackFaceEdge->mate = he;
760     // set volume for the new back face (new front face's volume will
761     // get set later when we chase round the volume)
762     GISMO_ASSERT(backFace->vol == NULL || backFace->vol == he->face->vol, "Inconsistent solid graph");
763     backFace->vol = he->face->vol;
764     // set source
765     GISMO_ASSERT(newFrontFaceEdge->source == reorderedVerts[vertNum], "Inconsistent solid graph");
766     GISMO_ASSERT(newBackFaceEdge->source == newVerts[(vertNum + 1) % numVerts], "Inconsistent solid graph");
767     //if(he->source->hed == he) he->source->hed = newFrontFaceEdge;
768     //he->source = newVerts[vertNum];
769     // set up for the next edge
770     newFrontFaceEdge = newFrontFaceEdge->next;
771     newBackFaceEdge = newBackFaceEdge->prev;
772     vertNum++;
773     gsSolidHeVertexHandle nextVert = reorderedVerts[(vertNum + 1) % numVerts];
774     // loop round the edges coming out of this vertex, updating their source to the
775     // new vertex and looking for the next edge in the loop.
776     while(true) {
777       he = he->next;
778       GISMO_ASSERT(he->source->hed->source == he->source, "Inconsistent solid graph");
779       if(he->source->hed == he) he->source->hed = newFrontFaceEdge;
780       he->source = newVerts[vertNum % numVerts];
781       if(he->next->source == nextVert || he == startEdge) break;
782       GISMO_ASSERT(he->source->hed->source == he->source, "Inconsistent solid graph");
783       GISMO_ASSERT(he->prev->target() == he->source, "Inconsistent solid graph");
784       he = he->mate;
785     }
786   } while(he != startEdge);
787   // add the newly created back face to its volume's list (the front face will
788   // be assigned to a new volume when we call newVolume)
789   GISMO_ASSERT(backFace->vol != NULL, "Could not find the volume for mate face");
790   backFace->vol->face.push_back(backFace);
791 
792   // chase all the faces in the new volume, setting their volume member correctly
793   newVolume(frontFace);
794 
795   checkStructure();
796 
797 
798   // TODO: the following not always true, only do this temporarily
799   gsSolidHalfEdge<T>* he0;
800   he0 = frontFace->loop[0];
801   he = he0;
802   for (size_t i=0;i<this->nHalfEdges();i++)
803   {
804       he->is_convex = true;
805       he->mate->is_convex = true;
806       if (he->target()==he0->source) break;
807       he = he->next;
808   }
809 
810   he0 = frontFace->mate->loop[0];
811   he = he0;
812   for (size_t i=0;i<this->nHalfEdges();i++)
813   {
814       he->is_convex = true;
815       he->mate->is_convex = true;
816       if (he->target()==he0->source) break;
817       he = he->next;
818   }
819 
820   return frontFace;
821 }
822 
823 template <class T>
impedingEdges(gsSolidHalfEdgeHandle he) const824 std::vector<typename gsSolid<T>::gsSolidHalfEdgeHandle > gsSolid<T>::impedingEdges(gsSolidHalfEdgeHandle he) const
825 {
826     //int vol = he->face->vol->getId();
827     // collect vertices of the two faces incident to the HE
828     std::vector<gsSolidHeVertexHandle> v0,v1;
829     gsSolidHalfFaceHandle face0 = he->face;
830     gsSolidHalfFaceHandle face1 = he->mate->face;
831     v0 = face0->getVertices();
832     v1 = face1->getVertices();
833     // remove two vertices of the HE from the two vertex sets
834     typename std::vector<gsSolidHeVertexHandle>::iterator it,it0,it1;
835     for (it = v0.begin();it!=v0.end();++it)
836     {
837         if ( ((*it)==he->source) || ((*it)==he->target()) )
838         {it0 = it;break;}
839     }
840     v0.erase(it0, it0 + 2);
841     for (it = v1.begin();it!=v1.end();++it)
842     {
843         if ( ((*it)==he->source) || ((*it)==he->target()) )
844         {it0 = it;break;}
845     }
846     v1.erase(it0, it0 + 2);
847     // check if each vertex in v0 is connected by a HE to another in v1
848     std::vector<gsSolidHalfEdgeHandle> iHE,vHE;
849     typename std::vector<gsSolidHalfEdgeHandle>::iterator ite;
850     for (it0 = v0.begin();it0!=v0.end();++it0)
851     {
852         vHE = (*it0)->halfEdges();
853         for (ite = vHE.begin();ite!=vHE.end();++ite)
854         {
855             for (it1 = v1.begin();it1!=v1.end();++it1)
856             {
857                 if ( (*ite)->target()== *it1) iHE.push_back(*ite);
858             }
859 
860         }
861     }
862     return iHE;
863 }
864 
865 template <class T>
insertNewVertex(gsSolidHalfEdgeHandle he)866 void gsSolid<T>::insertNewVertex(gsSolidHalfEdgeHandle he)
867 {
868     checkStructure();
869     gsSolidHalfEdgeHandle hem = he->mate;
870     gsSolidHalfFaceHandle face0,face1;
871     face0 = he->face;
872     face1 = he->mate->face;
873     size_t nFace = this->nHalfFaces();
874     bool convex = he->is_convex;
875     int heLoopN = he->loopN();
876     int hemLoopN = hem->loopN();
877     // adapting trimming structure of the two incident faces: first, face0:
878     size_t he_tCurveID = face0->indexOfEdge(he);
879     size_t hem_tCurveID = face1->indexOfEdge(hem);
880     gsMatrix<T> mid0 = face0->surf->splitCurve(he->loopN(), he_tCurveID);
881     gsMatrix<T> space0;
882     face0->surf->getTP()->eval_into(mid0.transpose(), space0);
883     // TODO: deal with possibility that the new vertex is on an inner loop
884     T nearestParam = face1->surf->nearestPoint(0, hem_tCurveID, 10, 10, space0);
885     gsMatrix<T> mid1 = face1->surf->splitCurve(hem->loopN(), hem_tCurveID, nearestParam);
886     gsMatrix<T> space1;
887     face1->surf->getTP()->eval_into(mid1.transpose(), space1);
888     //GISMO_ASSERT((space0 - space1).norm() < 0.0001, "Midpoints of curves do not match");
889     //denote:
890     // he->source----------newV-----------he->target()
891     //          s-----------n-------------t
892     gsSolidHeVertexHandle s,n,t;
893     // create new HeVertex located at the midpoint between the two new trimmed surface corners
894     n = new gsSolidHeVertex<T>(
895                 0.5 * (space0(0, 0) + space1(0, 0)),
896                 0.5 * (space0(1, 0) + space1(1, 0)),
897                 0.5 * (space0(2, 0) + space1(2, 0)),
898                 this->nVertices());
899     // add n to the vertex list
900     (this->vertex).push_back(n);
901 
902     // add four new HEs: sn,ns,nt,tn
903     s = he->source;
904     t = he->target();
905     gsSolidHalfEdgeHandle sn = new gsSolidHalfEdge<T>(s,face0,nFace,convex,heLoopN);
906     gsSolidHalfEdgeHandle nt = new gsSolidHalfEdge<T>(n,face0,nFace+1,convex,heLoopN);
907     gsSolidHalfEdgeHandle tn = new gsSolidHalfEdge<T>(t,face1,nFace+2,convex,hemLoopN);
908     gsSolidHalfEdgeHandle ns = new gsSolidHalfEdge<T>(n,face1,nFace+3,convex,hemLoopN);
909     //
910     he->prev->next = sn;
911     sn->next = nt;
912     nt->next = he->next;
913     he->next->prev = nt;
914     nt->prev = sn;
915     sn->prev = he->prev;
916     //
917     hem->prev->next = tn;
918     tn->next = ns;
919     ns->next = hem->next;
920     hem->next->prev = ns;
921     ns->prev = tn;
922     tn->prev = hem->prev;
923     //
924     sn->mate = ns;
925     ns->mate = sn;
926     nt->mate = tn;
927     tn->mate = nt;
928     //
929     n->hed = nt;
930     this->edge.push_back(sn);
931     this->edge.push_back(nt);
932     this->edge.push_back(tn);
933     this->edge.push_back(ns);
934 
935     // reassign pointers to each loop of a face
936     if ((face0->loop).at(he->loopN())==he) (face0->loop).at(he->loopN()) = sn;
937     if ((face1->loop).at(he->mate->loopN())==he->mate) (face1->loop).at(he->mate->loopN()) = tn;
938 
939     // reassign HE pointers of s and t
940     if (s->hed == he) s->hed = he->prev->mate;
941     if (t->hed == hem) t->hed = hem->prev->mate;
942 
943     // remove the HE with larger ID
944     int idmax = he->getId();
945     if (he->mate->getId()>idmax) idmax=he->mate->getId();
946     int idmin = he->getId();
947     if (he->mate->getId()<idmin) idmin=he->mate->getId();
948     // recalculate ID for HEs
949     for (size_t i = idmax+1; i < this->nHalfEdges(); i++)
950     {
951         this->edge[i]->setId(i-1);
952     }
953     this->edge.erase(this->edge.begin() + idmax);
954     // remove the HE with smaller ID
955     for (size_t i = idmin+1; i < this->nHalfEdges(); i++)
956     {
957         this->edge[i]->setId(i-1);
958     }
959     this->edge.erase(this->edge.begin() + idmin);
960     //
961     this->numVertices = this->nVertices();
962     this->numHalfEdges = this->nHalfEdges();
963     this->numHalfFaces = this->nHalfFaces();
964     this->numVolumes = this->nVolumes();
965     gsDebug<<"A new vertex is added to the halfedge with source: "<<*he->source<<
966                " and target: "<<*he->target()<<std::endl;
967     delete he;
968     delete hem;
969     checkStructure();
970 }
971 
972 template <class T>
handleImpedingEdges(gsSolidHalfEdgeHandle he)973 void gsSolid<T>::handleImpedingEdges(gsSolidHalfEdgeHandle he)
974 {
975     typename std::vector<gsSolidHalfEdgeHandle> iHe;
976     iHe=this->impedingEdges(he);
977     typename std::vector<gsSolidHalfEdgeHandle>::const_iterator it;
978     if (iHe.empty()==false)
979     {
980         for (it=iHe.begin();it!=iHe.end();++it)
981         {
982             this->insertNewVertex(*it);
983         }
984     }
985     checkStructure();
986 }
987 
988 } // namespace
989