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