1 /****************************************************************************
2 * VCGLib                                                            o o     *
3 * Visual and Computer Graphics Library                            o     o   *
4 *                                                                _   O  _   *
5 * Copyright(C) 2004-2016                                           \/)\/    *
6 * Visual Computing Lab                                            /\/|      *
7 * ISTI - Italian National Research Council                           |      *
8 *                                                                    \      *
9 * All rights reserved.                                                      *
10 *                                                                           *
11 * This program is free software; you can redistribute it and/or modify      *
12 * it under the terms of the GNU General Public License as published by      *
13 * the Free Software Foundation; either version 2 of the License, or         *
14 * (at your option) any later version.                                       *
15 *                                                                           *
16 * This program is distributed in the hope that it will be useful,           *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of            *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
19 * GNU General Public License (http://www.gnu.org/licenses/gpl.txt)          *
20 * for more details.                                                         *
21 *                                                                           *
22 ****************************************************************************/
23 #ifndef __VCG_TRI_UPDATE_SELECTION
24 #define __VCG_TRI_UPDATE_SELECTION
25 
26 namespace vcg {
27 namespace tri {
28 /// \ingroup trimesh
29 /// \brief A stack for saving and restoring selection.
30 /**
31   This class is used to save the current selection onto a stack for later use.
32   \todo it should be generalized to other attributes with a templated approach.
33 */
34 template <class ComputeMeshType>
35 class SelectionStack
36 {
37   typedef typename ComputeMeshType::template PerVertexAttributeHandle< bool > vsHandle;
38   typedef typename ComputeMeshType::template PerEdgeAttributeHandle< bool >   esHandle;
39   typedef typename ComputeMeshType::template PerFaceAttributeHandle< bool >   fsHandle;
40   typedef typename ComputeMeshType::template PerTetraAttributeHandle< bool >  tsHandle;
41 
42 
43 public:
SelectionStack(ComputeMeshType & m)44   SelectionStack(ComputeMeshType &m)
45   {
46     _m=&m;
47   }
48 
push()49   bool push()
50   {
51     vsHandle vsH = Allocator<ComputeMeshType>::template AddPerVertexAttribute< bool >(*_m);
52     esHandle esH = Allocator<ComputeMeshType>::template AddPerEdgeAttribute< bool >  (*_m);
53     fsHandle fsH = Allocator<ComputeMeshType>::template AddPerFaceAttribute< bool >  (*_m);
54     tsHandle tsH = Allocator<ComputeMeshType>::template AddPerTetraAttribute< bool > (*_m);
55     typename ComputeMeshType::VertexIterator vi;
56     for(vi = _m->vert.begin(); vi != _m->vert.end(); ++vi)
57       if( !(*vi).IsD() ) vsH[*vi] = (*vi).IsS() ;
58 
59     typename ComputeMeshType::EdgeIterator ei;
60     for(ei = _m->edge.begin(); ei != _m->edge.end(); ++ei)
61       if( !(*ei).IsD() ) esH[*ei] = (*ei).IsS() ;
62 
63     typename ComputeMeshType::FaceIterator fi;
64     for(fi = _m->face.begin(); fi != _m->face.end(); ++fi)
65       if( !(*fi).IsD() ) fsH[*fi] = (*fi).IsS() ;
66 
67     typename ComputeMeshType::TetraIterator ti;
68     for(ti = _m->tetra.begin(); ti != _m->tetra.end(); ++ti)
69       if( !(*ti).IsD() ) tsH[*ti] = (*ti).IsS() ;
70 
71     vsV.push_back(vsH);
72     esV.push_back(esH);
73     fsV.push_back(fsH);
74     tsV.push_back(tsH);
75     return true;
76   }
77 
popOr()78   bool popOr()
79   {
80     return pop(true,false);
81   }
82 
popAnd()83   bool popAnd()
84   {
85     return pop(false,true);
86   }
87 
88   /// It restore a saved selection.
89   /// The process can be done or in a straightforward manner (e.g. selection values are substituted)
90   /// or preserving selected or unselected elements (e.g. the restoring is combined in OR/AND)
91   ///
92   bool pop(bool orFlag=false, bool andFlag=false)
93   {
94     if(vsV.empty()) return false;
95     if(orFlag && andFlag) return false;
96 
97     vsHandle vsH = vsV.back();
98     esHandle esH = esV.back();
99     fsHandle fsH = fsV.back();
100     tsHandle tsH = tsV.back();
101 
102     if(! (Allocator<ComputeMeshType>::template IsValidHandle(*_m, vsH))) return false;
103 
104     for(auto vi = _m->vert.begin(); vi != _m->vert.end(); ++vi)
105       if( !(*vi).IsD() )
106       {
107         if(vsH[*vi]) {
108            if(!andFlag) (*vi).SetS();
109         } else {
110           if(!orFlag)   (*vi).ClearS();
111         }
112       }
113 
114     for(auto ei = _m->edge.begin(); ei != _m->edge.end(); ++ei)
115       if( !(*ei).IsD() )
116       {
117         if(esH[*ei]) {
118            if(!andFlag) (*ei).SetS();
119         } else {
120           if(!orFlag)   (*ei).ClearS();
121         }
122       }
123 
124 
125     for(auto fi = _m->face.begin(); fi != _m->face.end(); ++fi)
126       if( !(*fi).IsD() )
127       {
128         if(fsH[*fi]) {
129            if(!andFlag) (*fi).SetS();
130         } else {
131           if(!orFlag)   (*fi).ClearS();
132         }
133      }
134 
135      for (auto ti = _m->tetra.begin(); ti != _m->tetra.end(); ++ti)
136       if (!(*ti).IsD())
137       {
138         if (tsH[*ti]) {
139           if (!andFlag) (*ti).SetS();
140         } else {
141           if (!orFlag)  (*ti).ClearS();
142         }
143       }
144 
145     Allocator<ComputeMeshType>::template DeletePerVertexAttribute<bool>(*_m,vsH);
146     Allocator<ComputeMeshType>::template DeletePerEdgeAttribute<bool>(*_m,esH);
147     Allocator<ComputeMeshType>::template DeletePerFaceAttribute<bool>(*_m,fsH);
148     Allocator<ComputeMeshType>::template DeletePerTetraAttribute<bool>(*_m,tsH);
149 
150     vsV.pop_back();
151     esV.pop_back();
152     fsV.pop_back();
153     tsV.pop_back();
154     return true;
155   }
156 
157 private:
158   ComputeMeshType *_m;
159   std::vector<vsHandle> vsV;
160   std::vector<esHandle> esV;
161   std::vector<fsHandle> fsV;
162   std::vector<tsHandle> tsV;
163 
164 };
165 
166 /// \ingroup trimesh
167 
168 /// \headerfile selection.h vcg/complex/algorithms/update/selection.h
169 
170 /// \brief Management, updating and conditional computation of selections  (per-vertex, per-edge, and per-face).
171 /**
172 This class is used to compute or update the selected bit flag that can be stored in the vertex, edge or face component of a mesh.
173 */
174 
175 template <class ComputeMeshType>
176 class UpdateSelection
177 {
178 
179 public:
180 typedef ComputeMeshType MeshType;
181 typedef	typename MeshType::ScalarType			ScalarType;
182 typedef typename MeshType::VertexType     VertexType;
183 typedef typename MeshType::VertexPointer  VertexPointer;
184 typedef typename MeshType::VertexIterator VertexIterator;
185 typedef typename MeshType::EdgeIterator   EdgeIterator;
186 typedef typename MeshType::FaceType       FaceType;
187 typedef typename MeshType::FacePointer    FacePointer;
188 typedef typename MeshType::FaceIterator   FaceIterator;
189 typedef typename MeshType::TetraType      TetraType;
190 typedef typename MeshType::TetraPointer   TetraPointer;
191 typedef typename MeshType::TetraIterator  TetraIterator;
192 
193 typedef typename vcg::Box3<ScalarType>  Box3Type;
194 
195 /// \brief This function select all the vertices.
VertexAll(MeshType & m)196 static size_t VertexAll(MeshType &m)
197 {
198   for(VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi)
199     if( !(*vi).IsD() )	(*vi).SetS();
200   return m.vn;
201 }
202 
203 /// \brief This function select all the edges.
EdgeAll(MeshType & m)204 static size_t EdgeAll(MeshType &m)
205 {
206   for(EdgeIterator ei = m.edge.begin(); ei != m.edge.end(); ++ei)
207     if( !(*ei).IsD() )	(*ei).SetS();
208   return m.fn;
209 }
210 /// \brief This function select all the faces.
FaceAll(MeshType & m)211 static size_t FaceAll(MeshType &m)
212 {
213   for(FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
214     if( !(*fi).IsD() )	(*fi).SetS();
215   return m.fn;
216 }
217 
218 /// \brief This function select all the tetras.
TetraAll(MeshType & m)219 static size_t TetraAll (MeshType & m)
220 {
221   ForEachTetra(m, [] (TetraType & t) {
222     t.SetS();
223   });
224 
225   return m.tn;
226 }
227 
228 /// \brief This function clear the selection flag for all the vertices.
VertexClear(MeshType & m)229 static size_t VertexClear(MeshType &m)
230 {
231   for(VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi)
232     if( !(*vi).IsD() )	(*vi).ClearS();
233   return 0;
234 }
235 
236 /// \brief This function clears the selection flag for all the edges.
EdgeClear(MeshType & m)237 static size_t EdgeClear(MeshType &m)
238 {
239   for(EdgeIterator ei = m.edge.begin(); ei != m.edge.end(); ++ei)
240     if( !(*ei).IsD() )	(*ei).ClearS();
241   return 0;
242 }
243 
244 /// \brief This function clears the selection flag for all the faces.
FaceClear(MeshType & m)245 static size_t FaceClear(MeshType &m)
246 {
247   for(FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
248     if( !(*fi).IsD() )	(*fi).ClearS();
249   return 0;
250 }
251 
252 /// \brief This function clears the selection flag for all the tetras.
TetraClear(MeshType & m)253 static size_t TetraClear (MeshType & m)
254 {
255   ForEachTetra(m, [] (TetraType & t) {
256     t.ClearS();
257   });
258 
259   return 0;
260 }
261 
262 /// \brief This function clears the selection flag for all the elements of a mesh (vertices, edges, and faces).
Clear(MeshType & m)263 static void Clear(MeshType &m)
264 {
265   VertexClear(m);
266   EdgeClear(m);
267   FaceClear(m);
268   TetraClear(m);
269 }
270 
271 /// \brief This function returns the number of selected faces.
FaceCount(MeshType & m)272 static size_t FaceCount(MeshType &m)
273 {
274   size_t selCnt=0;
275   for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi)
276     if(!(*fi).IsD() && (*fi).IsS()) ++selCnt;
277   return selCnt;
278 }
279 
280 /// \brief This function returns the number of selected edges.
EdgeCount(MeshType & m)281 static size_t EdgeCount(MeshType &m)
282 {
283   size_t selCnt=0;
284   for(EdgeIterator ei=m.edge.begin();ei!=m.edge.end();++ei)
285     if(!(*ei).IsD() && (*ei).IsS()) ++selCnt;
286   return selCnt;
287 }
288 
289 /// \brief This function returns the number of selected vertices.
VertexCount(MeshType & m)290 static size_t VertexCount(MeshType &m)
291 {
292   size_t selCnt=0;
293   for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
294     if(!(*vi).IsD() && (*vi).IsS()) ++selCnt;
295   return selCnt;
296 }
297 
298 /// \brief This function returns the number of selected tetras.
TetraCount(MeshType & m)299 static size_t TetraCount (MeshType & m)
300 {
301   size_t selCnt = 0;
302   ForEachTetra(m, [&selCnt] (TetraType & t) {
303     if (t.IsS())
304       ++selCnt;
305   });
306 
307   return selCnt;
308 }
309 /// \brief This function inverts the selection flag for all the faces.
FaceInvert(MeshType & m)310 static size_t FaceInvert(MeshType &m)
311 {
312   size_t selCnt=0;
313   for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi)
314       if(!(*fi).IsD())
315       {
316         if((*fi).IsS()) (*fi).ClearS();
317         else {
318           (*fi).SetS();
319           ++selCnt;
320         }
321       }
322   return selCnt;
323 }
324 
325 /// \brief This function inverts the selection flag for all the edges.
EdgeInvert(MeshType & m)326 static size_t EdgeInvert(MeshType &m)
327 {
328   size_t selCnt=0;
329   for(EdgeIterator ei=m.edge.begin();ei!=m.edge.end();++ei)
330       if(!(*ei).IsD())
331       {
332         if((*ei).IsS()) (*ei).ClearS();
333         else {
334           (*ei).SetS();
335           ++selCnt;
336         }
337       }
338   return selCnt;
339 }
340 
341 /// \brief This function inverts the selection flag for all the vertices.
VertexInvert(MeshType & m)342 static size_t VertexInvert(MeshType &m)
343 {
344   size_t selCnt=0;
345   for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
346       if(!(*vi).IsD())
347       {
348         if((*vi).IsS()) (*vi).ClearS();
349         else {
350           (*vi).SetS();
351           ++selCnt;
352         }
353       }
354   return selCnt;
355 }
356 
357 /// \brief This function inverts the selection flag for all the tetras.
TetraInvert(MeshType & m)358 static size_t TetraInvert (MeshType & m)
359 {
360   size_t selCnt = 0;
361   ForEachTetra(m, [&selCnt] (TetraType & t) {
362     if (t.IsS())
363       t.ClearS();
364     else
365     {
366       t.SetS();
367       ++selCnt;
368     }
369   });
370 
371   return selCnt;
372 }
373 
374 
375 /// \brief Select all the vertices that are touched by at least a single selected faces
376 static size_t VertexFromFaceLoose(MeshType &m, bool preserveSelection=false)
377 {
378   size_t selCnt=0;
379 
380   if(!preserveSelection) VertexClear(m);
381   for(FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
382     if( !(*fi).IsD() && (*fi).IsS())
383       for(int i = 0; i < (*fi).VN(); ++i)
384         if( !(*fi).V(i)->IsS()) { (*fi).V(i)->SetS(); ++selCnt; }
385   return selCnt;
386 }
387 
388 /// \brief Select all the vertices that are touched by at least a single selected edge
389 static size_t VertexFromEdgeLoose(MeshType &m, bool preserveSelection=false)
390 {
391   size_t selCnt=0;
392 
393   if(!preserveSelection) VertexClear(m);
394   for(EdgeIterator ei = m.edge.begin(); ei != m.edge.end(); ++ei)
395     if( !(*ei).IsD() && (*ei).IsS())
396     {
397       if( !(*ei).V(0)->IsS()) { (*ei).V(0)->SetS(); ++selCnt; }
398       if( !(*ei).V(1)->IsS()) { (*ei).V(1)->SetS(); ++selCnt; }
399     }
400   return selCnt;
401 }
402 
403 /// \brief Select ONLY the vertices that are touched ONLY by selected faces
404 /** In other words this function will select all the vertices having all the faces incident on them selected.
405 */
406 static size_t VertexFromFaceStrict(MeshType &m, bool preserveSelection=false)
407 {
408   SelectionStack<MeshType> ss(m);
409   if(preserveSelection) ss.push();
410   VertexFromFaceLoose(m);
411   for(FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
412     if( !(*fi).IsD() && !(*fi).IsS())
413       for(int i = 0; i < (*fi).VN(); ++i)
414         (*fi).V(i)->ClearS();
415 
416   if(preserveSelection) ss.popOr();
417   return VertexCount(m);
418 }
419 
420 /// \brief Select ONLY the faces with ALL the vertices selected
421 static size_t FaceFromVertexStrict(MeshType &m, bool preserveSelection=false)
422 {
423   SelectionStack<MeshType> ss(m);
424   if(preserveSelection) ss.push();
425   size_t selCnt=0;
426   FaceClear(m);
427   for(FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
428     if( !(*fi).IsD())
429     {
430       bool selFlag=true;
431       for(int i = 0; i < (*fi).VN(); ++i)
432         if(!(*fi).V(i)->IsS())
433           selFlag =false;
434       if(selFlag)
435       {
436         (*fi).SetS();
437         ++selCnt;
438       }
439     }
440 
441   if(preserveSelection) ss.popOr();
442   return selCnt;
443 }
444 
445 /// \brief Select all the faces with at least one selected vertex
446 static size_t FaceFromVertexLoose(MeshType &m, bool preserveSelection=false)
447 {
448   size_t selCnt=0;
449   if(!preserveSelection) FaceClear(m);
450   for(FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
451     if( !(*fi).IsD())
452     {
453       bool selVert=false;
454       for(int i = 0; i < (*fi).VN(); ++i)
455         if((*fi).V(i)->IsS())
456             selVert=true;
457 
458       if(selVert) {
459         (*fi).SetS();
460         ++selCnt;
461       }
462     }
463   return selCnt;
464 }
465 /// \brief This function dilate the face selection by simply first selecting all the vertices touched by the faces and then all the faces touched by these vertices
466 /// Note: it destroys the vertex selection.
FaceDilate(MeshType & m)467 static size_t FaceDilate(MeshType &m)
468 {
469   tri::UpdateSelection<MeshType>::VertexFromFaceLoose(m);
470   return tri::UpdateSelection<MeshType>::FaceFromVertexLoose(m);
471 }
472 
473 /// \brief This function erode the face selection by simply first selecting only the vertices completely surrounded by face and then the only faces with all the selected vertices
474 /// Note: it destroys the vertex selection.
FaceErode(MeshType & m)475 static size_t FaceErode(MeshType &m)
476 {
477   tri::UpdateSelection<MeshType>::VertexFromFaceStrict(m);
478   return tri::UpdateSelection<MeshType>::FaceFromVertexStrict(m);
479 }
480 
481 
482 /// \brief This function select the vertices with the border flag set
483 static size_t VertexFromBorderFlag(MeshType &m, bool preserveSelection=false)
484 {
485   size_t selCnt=0;
486   if(!preserveSelection) VertexClear(m);
487   for(VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi)
488     if( !(*vi).IsD() )
489     {
490       if((*vi).IsB() )
491       {
492         (*vi).SetS();
493         ++selCnt;
494       }
495     }
496   return selCnt;
497 }
498 
499 /// \brief This function select the faces that have an edge with the border flag set.
500 static size_t FaceFromBorderFlag(MeshType &m, bool preserveSelection=false)
501 {
502   tri::RequireTriangularMesh(m);
503   size_t selCnt=0;
504   if(!preserveSelection) FaceClear(m);
505   for(FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
506     if( !(*fi).IsD() )
507     {
508       bool bordFlag=false;
509       for(int i = 0; i < 3; ++i)
510         if((*fi).IsB(i)) bordFlag=true;
511       if(bordFlag)
512       {
513         (*fi).SetS();
514         ++selCnt;
515       }
516     }
517   return selCnt;
518 }
519 
520 /// \brief This function select the faces that have an edge outside the given range.
521 /// You can skip the second parameter to choose all the edges smaller than a given lenght
522 static size_t FaceOutOfRangeEdge(MeshType &m, ScalarType MinEdgeThr, ScalarType MaxEdgeThr=(std::numeric_limits<ScalarType>::max)(), bool preserveSelection=false)
523 {
524   if(!preserveSelection) FaceClear(m);
525   size_t selCnt = 0;
526   MinEdgeThr=MinEdgeThr*MinEdgeThr;
527   MaxEdgeThr=MaxEdgeThr*MaxEdgeThr;
528   for(FaceIterator fi=m.face.begin(); fi!=m.face.end();++fi)
529     if(!(*fi).IsD())
530       {
531         for(int i=0;i<(*fi).VN();++i)
532         {
533           const ScalarType squaredEdge=SquaredDistance((*fi).V0(i)->cP(),(*fi).V1(i)->cP());
534           if((squaredEdge<=MinEdgeThr) || (squaredEdge>=MaxEdgeThr) )
535           {
536             selCnt++;
537             (*fi).SetS();
538             break; // skip the rest of the edges of the tri
539           }
540         }
541       }
542       return selCnt;
543 }
544 
545 /// \brief This function expand current selection to cover the whole connected component.
FaceConnectedFF(MeshType & m)546 static size_t FaceConnectedFF(MeshType &m)
547 {
548   // it also assumes that the FF adjacency is well computed.
549   RequireFFAdjacency(m);
550   UpdateFlags<MeshType>::FaceClearV(m);
551 
552   std::deque<FacePointer> visitStack;
553   size_t selCnt=0;
554   for(FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
555     if( !(*fi).IsD() && (*fi).IsS() && !(*fi).IsV() )
556       visitStack.push_back(&*fi);
557 
558   while(!visitStack.empty())
559   {
560     FacePointer fp = visitStack.front();
561     visitStack.pop_front();
562     assert(!fp->IsV());
563     fp->SetV();
564     for(int i=0;i<fp->VN();++i) {
565       FacePointer ff = fp->FFp(i);
566       if(! ff->IsS())
567       {
568         ff->SetS();
569         ++selCnt;
570         visitStack.push_back(ff);
571         assert(!ff->IsV());
572       }
573     }
574   }
575   return selCnt;
576 }
577 /// \brief Select the faces whose quality is in the specified closed interval.
578 static size_t FaceFromQualityRange(MeshType &m,float minq, float maxq, bool preserveSelection=false)
579 {
580   size_t selCnt=0;
581   if(!preserveSelection) FaceClear(m);
582   RequirePerFaceQuality(m);
583   for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi)
584       if(!(*fi).IsD())
585       {
586         if( (*fi).Q()>=minq &&  (*fi).Q()<=maxq )
587           {
588             (*fi).SetS();
589             ++selCnt;
590           }
591       }
592   return selCnt;
593 }
594 
595 /// \brief Select the vertices whose quality is in the specified closed interval.
596 static size_t VertexFromQualityRange(MeshType &m,float minq, float maxq, bool preserveSelection=false)
597 {
598   size_t selCnt=0;
599   if(!preserveSelection) VertexClear(m);
600   RequirePerVertexQuality(m);
601   for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
602       if(!(*vi).IsD())
603       {
604         if( (*vi).Q()>=minq &&  (*vi).Q()<=maxq )
605                     {
606                         (*vi).SetS();
607                         ++selCnt;
608                     }
609       }
610   return selCnt;
611 }
612 
613 /// \brief Select the vertices contained in the specified Box
614 static size_t VertexInBox( MeshType & m, const Box3Type &bb, bool preserveSelection=false)
615 {
616   if(!preserveSelection) VertexClear(m);
617   int selCnt=0;
618   for (VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi) if(!(*vi).IsD())
619   {
620     if(bb.IsIn((*vi).cP()) ) {
621       (*vi).SetS();
622       ++selCnt;
623     }
624   }
625   return selCnt;
626 }
627 
628 /// \brief Select the border vertices that form a corner along the border
629 /// with an angle that is below a certain threshold (e.g. with 90 will select all the acute angles)
630 /// It assumes that the Per-Vertex border Flag has been set.
631 static size_t VertexCornerBorder(MeshType &m, ScalarType angleRad, bool preserveSelection=false)
632 {
633   if(!preserveSelection) VertexClear(m);
634   SimpleTempData<typename MeshType::VertContainer, ScalarType > angleSumH(m.vert,0);
635   int selCnt=0;
636   for(auto vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD())
637     angleSumH[vi]=0;
638 
639   for(auto fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD())
640   {
641     for(int i=0;i<(*fi).VN();++i)
642       angleSumH[fi->V(i)] += face::WedgeAngleRad(*fi,i);
643   }
644 
645   for(auto vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD())
646   {
647     if(angleSumH[vi]<angleRad && vi->IsB())
648     {
649       (*vi).SetS();
650       ++selCnt;
651     }
652   }
653   return selCnt;
654 }
655 
656 
657 void VertexNonManifoldEdges(MeshType &m, bool preserveSelection=false)
658 {
659   tri::RequireFFAdjacency(m);
660 
661   if(!preserveSelection) VertexClear(m);
662   for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)	if (!fi->IsD())
663     {
664       for(int i=0;i<fi->VN();++i)
665       if(!IsManifold(*fi,i)){
666         (*fi).V0(i)->SetS();
667         (*fi).V1(i)->SetS();
668         }
669     }
670 }
671 
672 }; // end class
673 
674 }	// End namespace
675 }	// End namespace
676 
677 
678 #endif
679