Developer Documentation
Loading...
Searching...
No Matches
HoleFillerT_impl.hh
1/* ========================================================================= *
2 * *
3 * OpenMesh *
4 * Copyright (c) 2001-2023, RWTH-Aachen University *
5 * Department of Computer Graphics and Multimedia *
6 * All rights reserved. *
7 * www.openmesh.org *
8 * *
9 *---------------------------------------------------------------------------*
10 * This file is part of OpenMesh. *
11 *---------------------------------------------------------------------------*
12 * *
13 * Redistribution and use in source and binary forms, with or without *
14 * modification, are permitted provided that the following conditions *
15 * are met: *
16 * *
17 * 1. Redistributions of source code must retain the above copyright notice, *
18 * this list of conditions and the following disclaimer. *
19 * *
20 * 2. Redistributions in binary form must reproduce the above copyright *
21 * notice, this list of conditions and the following disclaimer in the *
22 * documentation and/or other materials provided with the distribution. *
23 * *
24 * 3. Neither the name of the copyright holder nor the names of its *
25 * contributors may be used to endorse or promote products derived from *
26 * this software without specific prior written permission. *
27 * *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
33 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
34 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
35 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
36 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
37 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
38 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
39 * *
40 * ========================================================================= */
41
42
43
44//=============================================================================
45#include "HoleFillerT.hh"
47//=============================================================================
48
49//== NAMESPACES ===============================================================
50
51
52namespace OpenMesh {
53namespace HoleFiller {
54
55template< class MeshT >
56HoleFillerT< MeshT >::HoleFillerT(MeshT &_mesh )
57 : mesh_( _mesh )
58{
59 mesh_.request_vertex_status();
60 mesh_.request_edge_status();
61
62 if (! mesh_.get_property_handle(scale_,"scale") )
63 mesh_.add_property( scale_ , "scale" );
64}
65
66
67//=============================================================================
68
69
70
71template< class MeshT >
73{
74 mesh_.release_vertex_status();
75 mesh_.release_edge_status();
77 if ( mesh_.get_property_handle(scale_,"scale") )
78 mesh_.remove_property( scale_ );
79}
80
82//=============================================================================
83//
84// Identify and fill all holes of the mesh.
85//
86//=============================================================================
87
88
89template< class MeshT >
90void
92{
93
94
95 // Collect all boundary edges
96 std::vector< typename MeshT::EdgeHandle > bdry_edge;
97
98 for (auto ei : mesh_.edges())
99 if ( ei.is_boundary() )
100 bdry_edge.push_back( ei );
101
102
103 // Fill holes
104 int cnt = 0;
105 for (auto i : bdry_edge)
106 if ( mesh_.is_boundary( i ) )
107 {
108 ++cnt;
109 omlog() << "Filling hole " << cnt << "\n";
110 fill_hole( i, _stages );
111 }
112
113 // Smooth fillings
114 if ( _stages <= 2 )
115 return;
116
117 omlog() << "Stage 3 : Smoothing the hole fillings ... ";
118
120 smoother.initialize( OpenMesh::Smoother::SmootherT< MeshT >::
121 Tangential_and_Normal,
123
124 smoother.smooth( 500 );
125}
126
127
128//=============================================================================
129//
130// Fill a hole which is identified by one of its boundary edges.
131//
132//=============================================================================
133
134
135template< class MeshT >
136void
137HoleFillerT< MeshT >::fill_hole(typename MeshT::EdgeHandle _eh, int _stages )
138{
139
140 omlog() << " Stage 1 : Computing a minimal triangulation ... ";
141
142 // remember last vertex for selection of new ones
143 typename MeshT::VertexHandle old_last_handle = *(--mesh_.vertices_end());
144
145 // No boundary edge, no hole
146 if ( ! mesh_.is_boundary( _eh ) ) {
147 omerr() << "fill_hole: Given edge handle is not a boundary edge at a hole!" << std::endl;
148 return;
149 }
150
151 // Get boundary halfedge
152 OpenMesh::SmartHalfedgeHandle hh = make_smart(_eh, mesh_).h0();
153
154 if ( ! hh.is_boundary() )
155 hh = hh.opp();
156
157 // Collect boundary vertices
158 boundary_vertex_.clear();
159 opposite_vertex_.clear();
160
162
163 do {
164 boundary_vertex_.push_back( ch.from() );
165 opposite_vertex_.push_back( ch.opp().next().to() );
166 //check number of outgoing boundary HEH's at Vertex
167 int c = 0;
169
170 for (auto voh_it : vh.outgoing_halfedges())
171 if ( voh_it.is_boundary() )
172 c++;
173
174 if ( c >= 2){
176 typename MeshT::VertexOHalfedgeIter voh_it(mesh_,op);
177
178 ch = *(++voh_it);
179 }else
180 ch = ch.next();
181
182 } while ( ch != hh );
183
184
185 int nv = boundary_vertex_.size();
186
187 // Compute an initial triangulation
188 w_.clear();
189 w_.resize( nv, std::vector<Weight>( nv, Weight() ) );
190
191 l_.clear();
192 l_.resize( nv, std::vector<int>( nv, 0 ) );
193
194
195 for ( int i = 0; i < nv - 1; ++i )
196 w_[i][i+1] = Weight( 0, 0 );
197
198 for ( int j = 2; j < nv; ++j )
199 {
200 #pragma omp parallel for shared(j, nv)
201 for(int i = 0; i < nv-j; ++i)
202 {
203 Weight valmin;
204 int argmin = -1;
205 for ( int m = i + 1; m < i + j; ++m )
206 {
207 Weight newval = w_[i][m] + w_[m][i+j] + weight( i, m, i+j );
208 if ( newval < valmin )
209 {
210 valmin = newval;
211 argmin = m;
212 }
213 }
214
215 w_[i][i+j] = valmin;
216 l_[i][i+j] = argmin;
217 }
218 }
219
220 // Actually fill the hole. We collect all triangles and edges of
221 // this filling for further processing.
222 hole_edge_.clear();
223 hole_triangle_.clear();
224 if ( fill( 0, nv - 1 ) ){
225
226 if ( _stages <= 1 )
227 return;
228
229 omlog() << " Stage 2 : Fairing the filling ... " << std::endl;
230
231 std::vector< OpenMesh::SmartFaceHandle > handles = hole_triangle_;
232
233 fairing(handles);
234
235 //select all new vertices
236 typename MeshT::VertexIter old_end = ++typename MeshT::VertexIter(mesh_,old_last_handle);
237 typename MeshT::VertexIter v_end = mesh_.vertices_end();
238
239 for(; old_end != v_end; ++old_end)
240 if ( !mesh_.status(*old_end).deleted() )
241 mesh_.status(*old_end).set_selected( true );
242
243 }else
244 omerr() << "Could not create triangulation" << std::endl;
245}
246
247
249template< class MeshT >
250void
251HoleFillerT< MeshT >::fairing( std::vector< OpenMesh::SmartFaceHandle >& _faceHandles ){
252
253 //generate vector of all edges
254 hole_edge_.clear();
255
256 hole_triangle_ = _faceHandles;
257
262
263 if (! mesh_.get_property_handle(edgeProp,"edgeProp") )
264 mesh_.add_property( edgeProp, "edgeProp" );
265 if (! mesh_.get_property_handle(vertexProp,"vertexProp") )
266 mesh_.add_property( vertexProp, "vertexProp" );
267 if (! mesh_.get_property_handle(faceProp,"faceProp") )
268 mesh_.add_property( faceProp, "faceProp" );
269 if (! mesh_.get_property_handle(orderProp,"orderProp") )
270 mesh_.add_property( orderProp, "orderProp" );
271
272 //init properties by setting all of them to false
273 for (auto fIt : mesh_.faces()) {
274 mesh_.property( orderProp, fIt ) = false;
275 mesh_.property( faceProp, fIt ) = false;
276 }
277
278 for (auto eIt : mesh_.edges())
279 mesh_.property( edgeProp, eIt ) = false;
280
281 for (auto vIt : mesh_.vertices()) {
282 mesh_.property( vertexProp, vIt ) = false;
283 }
284
285 //set face property
286 for (uint i = 0; i < hole_triangle_.size(); i++){
287 mesh_.property( faceProp, hole_triangle_[i] ) = true;
288 }
289
290 //set properties
291 for (unsigned int i = 0; i < hole_triangle_.size(); i++){
292 for (auto fei : hole_triangle_[i].edges()) {
293 mesh_.status( fei ).set_locked(true);
294 //set edge property for all edges inside the hole (eg not on the hole boundary)
295 if (mesh_.property( faceProp, fei.h0().face() ) &&
296 mesh_.property( faceProp, fei.h1().face() ) ){
297
298 mesh_.property( edgeProp, fei ) = true;
299 hole_edge_.push_back( fei );
300 mesh_.status( fei ).set_locked(false);
301 }
302 }
303
305 for (auto fvi : hole_triangle_[i].vertices()){
306 //set vertex property for all vertices of the hole
307 for ( auto vfi : fvi.faces() )
308 mesh_.property( vertexProp, fvi ) = true;
309 }
310 }
311
312 //calculate scaling weights for vertices
313 for (auto vIt : mesh_.vertices())
314 if (mesh_.property(vertexProp, vIt)){
315
316 Scalar cnt = 0;
317 Scalar scale = 0;
318
319 for ( auto voh_it : vIt.outgoing_halfedges())
320 {
321
322 if (voh_it.face().is_valid() &&
323 voh_it.opp().face().is_valid() &&
324 mesh_.property(faceProp, voh_it.face() ) &&
325 mesh_.property(faceProp, voh_it.opp().face() ))
326 continue;
327
328 cnt += 1.0f;
329 Point p0 = mesh_.point( vIt );
330 Point p1 = mesh_.point( voh_it.to() );
331 scale += norm( p1 - p0 );
332
333 }
334
335 scale /= cnt;
336
337 mesh_.property( scale_, vIt ) = scale;
338 }
339
340 mesh_.remove_property(edgeProp);
341 mesh_.remove_property(vertexProp);
342 mesh_.remove_property(faceProp);
343 mesh_.remove_property(orderProp);
344
345 // Do the patch fairing
346 bool did_refine = true;
347
348 for ( int k = 0; k < 40 && did_refine; ++k )
349 {
350 uint end = hole_triangle_.size();
351
352 did_refine = false;
353 for ( unsigned int j = 0; j < end; ++j )
354 did_refine |= refine( hole_triangle_[j] );
355
356 for ( int i = 0; i < 10; ++i )
357 for ( unsigned int j = 0; j < hole_edge_.size(); ++j )
358 relax_edge( hole_edge_[j] );
359 }
360
361 // unlock everything
362 for ( auto ei : mesh_.edges())
363 mesh_.status( ei ).set_locked( false );
364}
365
366//=============================================================================
367//
368// Refine a face: Apply a 1-to-3 split if the edge lengths of the
369// face do not match the interpolated edge lengths of the hole
370// boundary.
371//
372//=============================================================================
373
374
375template< class MeshT >
376bool
377HoleFillerT< MeshT >::refine(typename MeshT::FaceHandle _fh )
378{
379
380 // Collect the three edges of the face into e0, e1, e2
381
382 typename MeshT::FEIter fei = mesh_.fe_iter( _fh );
383 OpenMesh::SmartEdgeHandle e0 = *fei; ++fei;
384 OpenMesh::SmartEdgeHandle e1 = *fei; ++fei;
385 OpenMesh::SmartEdgeHandle e2 = *fei; ++fei;
386
387
388 // Collect the vertices, vertex positions and scale factors of the face.
389
390 typename MeshT::FVIter fvi = mesh_.fv_iter( _fh );
391
392 typename MeshT::VertexHandle v0 = *fvi; ++fvi;
393 typename MeshT::VertexHandle v1 = *fvi; ++fvi;
394 typename MeshT::VertexHandle v2 = *fvi; ++fvi;
395
396 Point p0 = mesh_.point( v0 );
397 Point p1 = mesh_.point( v1 );
398 Point p2 = mesh_.point( v2 );
399
400 Scalar scale0 = mesh_.property( scale_, v0 );
401 Scalar scale1 = mesh_.property( scale_, v1 );
402 Scalar scale2 = mesh_.property( scale_, v2 );
403
404 // Interpolate the scale factor.
405
406 Scalar scale = ( scale0 + scale1 + scale2 ) / 3.0f;
407 Point center = typename MeshT::Scalar(1.0/3.0) * ( p0 + p1 + p2 );
408
409 Scalar d0 = 1.0f * norm( p0 - center );
410 Scalar d1 = 1.0f * norm( p1 - center );
411 Scalar d2 = 1.0f * norm( p2 - center );
412
413
414 //dont split triangles which tend to degenerate
415 if ( (d0 + d1 + d2) / 3.0f < scale) return false;
416
417
418 // If the edge lengths differ too much from the scale, perform a
419 // triangle split.
420
421 if ( ( d0 > scale && d0 > scale0 ) ||
422 ( d1 > scale && d1 > scale1 ) ||
423 ( d2 > scale && d2 > scale2 ) )
424 {
425 // Split the face ...
426 OpenMesh::SmartVertexHandle ch = mesh_.add_vertex( center );
427 mesh_.split( _fh, ch );
428
429 // ... put the new triangles into the global triangle list ...
430
431 for ( auto vfi : ch.faces() )
432 if ( vfi != _fh )
433 hole_triangle_.push_back( vfi );
434
435 // ... put the new edges into the global edge list ...
436
437 for ( auto vei : ch.edges() )
438 hole_edge_.push_back( vei );
439
440 // ... and set the appropriate scale factor for the new vertex.
441
442 mesh_.property( scale_, ch ) = scale;
443
444 relax_edge( e0 );
445 relax_edge( e1 );
446 relax_edge( e2 );
447
448 return true;
449 }
450
451 return false;
452}
453
454
455//=============================================================================
456//
457// Relax an edge: Flip it if one of its opposing vertices lies in
458// the circumsphere of the other three vertices.
459//
460//=============================================================================
461
462
463template< class MeshT >
464bool
465HoleFillerT< MeshT >::relax_edge( OpenMesh::SmartEdgeHandle _eh )
466{
467 if ( mesh_.status( _eh ).locked() )
468 return false;
469
470 // Abbreviations for the two halfedges of _eh
471
474
475 // Get the two end-vertices u and v of the edge
476
477 Point u( mesh_.point( h0.to() ) );
478 Point v( mesh_.point( h1.to() ) );
479
480 // Get the two opposing vertices a and b
481
482 Point a( mesh_.point( h0.next().to() ) );
483 Point b( mesh_.point( h1.next().to() ) );
484
485 // If the circumsphere criterion is fullfilled AND if the flip is
486 // topologically admissible, we do it.
487
488 if ( in_circumsphere( a, u, v, b ) || in_circumsphere( b, u, v, a ) ){
489 if ( mesh_.is_flip_ok( _eh ) )
490 {
491
492 mesh_.flip( _eh );
493
494 return true;
495 }else
496 mesh_.status(_eh).set_selected( true );
497}
498 return false;
499}
500
501
502//=============================================================================
503//
504// Test whether a point _x lies in the circumsphere of _a,_b,_c.
505//
506//=============================================================================
507
508
509template< class MeshT >
510bool
511HoleFillerT< MeshT >::in_circumsphere( const Point & _x,
512 const Point & _a,
513 const Point & _b,
514 const Point & _c ) const
515{
516 Point ab = _b - _a;
517 Point ac = _c - _a;
518
519 Scalar a00 = -2.0f * ( dot(ab , _a ) );
520 Scalar a01 = -2.0f * ( dot(ab , _b ) );
521 Scalar a02 = -2.0f * ( dot(ab , _c ) );
522 Scalar b0 = norm(_a)*norm(_a) - norm(_b)*norm(_b);
523
524 Scalar a10 = -2.0f * ( dot(ac , _a ) );
525 Scalar a11 = -2.0f * ( dot(ac , _b ) );
526 Scalar a12 = -2.0f * ( dot(ac , _c ) );
527 Scalar b1 = norm(_a)*norm(_a) - norm(_c)*norm(_c);
528
529 typename MeshT::Scalar alpha = -(-a11*a02+a01*a12-a12*b0+b1*a02+a11*b0-a01*b1)
530 / (-a11*a00+a11*a02-a10*a02+a00*a12+a01*a10-a01*a12);
531 typename MeshT::Scalar beta = (a10*b0-a10*a02-a12*b0+a00*a12+b1*a02-a00*b1)
532 / (-a11*a00+a11*a02-a10*a02+a00*a12+a01*a10-a01*a12);
533 typename MeshT::Scalar gamma = (-a11*a00-a10*b0+a00*b1+a11*b0+a01*a10-a01*b1)
534 / (-a11*a00+a11*a02-a10*a02+a00*a12+a01*a10-a01*a12);
535
536 Point center = alpha * _a + beta * _b + gamma * _c;
537
538 return norm( _x - center ) * norm( _x - center ) < norm( _a - center ) * norm( _a - center );
539}
540
541
542//=============================================================================
543//
544// Create the triangulation
545//
546// Recursively creates the triangulation for polygon (_i,...,_j).
547//
548//=============================================================================
549
550
551template< class MeshT >
552bool
553HoleFillerT< MeshT >::fill( int _i, int _j )
554{
555 // If the two vertices _i and _j are adjacent, there is nothing to do.
556
557 if ( _i + 1 == _j )
558 return true;
559
560 // If the middle vertex is invalid, this hole cannot be filled
561
562 if(l_[_i][_j] < 0)
563 return false;
564
565 // Create and store the middle triangle, store its edges.
566
567 OpenMesh::SmartFaceHandle fh = mesh_.add_face( boundary_vertex_[_i],
568 boundary_vertex_[ l_[_i][_j] ],
569 boundary_vertex_[_j] );
570 hole_triangle_.push_back( fh );
571
572 if (!fh.is_valid())
573 return false;
574
575 hole_edge_.push_back( mesh_.edge_handle
576 ( mesh_.find_halfedge( boundary_vertex_[_i],
577 boundary_vertex_[ l_[_i][_j] ] ) ) );
578 hole_edge_.push_back( mesh_.edge_handle
579 ( mesh_.find_halfedge( boundary_vertex_[ l_[_i][_j] ],
580 boundary_vertex_[_j] ) ) );
581
582
583 // Recursively create the left and right side of the
584 // triangulation.
585
586 if (!fill( _i, l_[_i][_j] ) || !fill( l_[_i][_j], _j ))
587 return false;
588 else
589 return true;
590}
591
592
593
594//=============================================================================
595//
596// Compute the weight of the triangle (_i,_j,_k).
597//
598//=============================================================================
599
600
601template< class MeshT >
602typename HoleFillerT< MeshT >::Weight
603HoleFillerT< MeshT >::weight( int _i, int _j, int _k )
604{
605 // Return an infinite weight if the insertion of this triangle
606 // would create complex edges.
607
608 if ( exists_edge( boundary_vertex_[_i], boundary_vertex_[_j] ) ||
609 exists_edge( boundary_vertex_[_j], boundary_vertex_[_k] ) ||
610 exists_edge( boundary_vertex_[_k], boundary_vertex_[_i] ) )
611 return Weight();
612
613
614 // Return an infinite weight, if one of the neighboring patches
615 // could not be created.
616
617
618 if ( l_[_i][_j] == -1 ) return Weight();
619 if ( l_[_j][_k] == -1 ) return Weight();
620
621
622 // Compute the maxmimum dihedral angles to the adjacent triangles
623 // (if they exist)
624
625 Scalar angle = 0.0f;
626
627 if ( _i + 1 == _j )
628 angle = std::max( angle, dihedral_angle( boundary_vertex_[_i],
629 boundary_vertex_[_j],
630 boundary_vertex_[_k],
631 opposite_vertex_[_i] ) );
632 else
633 angle = std::max( angle, dihedral_angle( boundary_vertex_[_i],
634 boundary_vertex_[_j],
635 boundary_vertex_[_k],
636 boundary_vertex_[l_[_i][_j]] ) );
637
638 if ( _j + 1 == _k )
639 angle = std::max( angle, dihedral_angle( boundary_vertex_[_j],
640 boundary_vertex_[_k],
641 boundary_vertex_[_i],
642 opposite_vertex_[_j] ) );
643 else
644 angle = std::max( angle, dihedral_angle( boundary_vertex_[_j],
645 boundary_vertex_[_k],
646 boundary_vertex_[_i],
647 boundary_vertex_[l_[_j][_k]] ) );
648
649 if ( _i == 0 && _k == (int) boundary_vertex_.size() - 1 )
650 angle = std::max( angle, dihedral_angle( boundary_vertex_[_k],
651 boundary_vertex_[_i],
652 boundary_vertex_[_j],
653 opposite_vertex_[_k] ) );
654
655
656 return Weight( angle, area( boundary_vertex_[_i],
657 boundary_vertex_[_j],
658 boundary_vertex_[_k] ) );
659}
660
661
662
663//=============================================================================
664//
665// Does an edge from vertex _u to _v exist?
666//
667//=============================================================================
668
669
670template< class MeshT >
671bool
672HoleFillerT< MeshT >::exists_edge( OpenMesh::SmartVertexHandle _u, typename MeshT::VertexHandle _w )
673{
674 for ( auto vohi : _u.outgoing_halfedges() )
675 if ( ! vohi.edge().is_boundary() )
676 if ( vohi.to() == _w )
677 return true;
678
679 return false;
680}
681
682
683
684//=============================================================================
685//
686// Compute the area of the triangle (_a,_b,_c).
687//
688//=============================================================================
689
690
691template< class MeshT >
692typename MeshT::Scalar
693HoleFillerT< MeshT >::area( typename MeshT::VertexHandle _a, typename MeshT::VertexHandle _b, typename MeshT::VertexHandle _c )
694{
695 Point a( mesh_.point( _a ) );
696 Point b( mesh_.point( _b ) );
697 Point c( mesh_.point( _c ) );
698
699 Point n( cross(( b - a ) , ( c - b )) );
700
701 return 0.5 * norm(n);
702}
703
704
705//=============================================================================
706//
707// Compute a dihedral angle
708//
709// Computes the dihedral angle (in degrees) between triangle
710// (_u,_v,_a) and triangle (_v,_u,_b), no matter whether these
711// triangles actually exist in the current mesh or not).
712//
713//=============================================================================
714
715
716template< class MeshT >
717typename MeshT::Scalar
718HoleFillerT< MeshT >::dihedral_angle( typename MeshT::VertexHandle _u, typename MeshT::VertexHandle _v, typename MeshT::VertexHandle _a, typename MeshT::VertexHandle _b )
719{
720 Point u( mesh_.point( _u ) );
721 Point v( mesh_.point( _v ) );
722 Point a( mesh_.point( _a ) );
723 Point b( mesh_.point( _b ) );
724
725 Point n0( cross(( v - u ) , ( a - v )) );
726 Point n1( cross(( u - v ) , ( b - u )) );
727
728 normalize(n0);
729 normalize(n1);
730
731 return acos( dot(n0,n1) ) * 180.0 / M_PI;
732}
733
734
736template< class MeshT >
737void
738HoleFillerT< MeshT >::removeDegeneratedFaces( std::vector< typename MeshT::FaceHandle >& _faceHandles ){
739
740 for (int i = _faceHandles.size()-1; i >= 0 ; i--){
741
742 if ( mesh_.status( _faceHandles[i] ).deleted() ){
743 // face might be deleted because of a previous edge collapse
744 // erase the face from the vector
745 _faceHandles.erase( _faceHandles.begin() + i );
746 continue;
747 }
748
749 //get the vertices (works only on triMeshes)
750 typename MeshT::FaceVertexIterator fvi = mesh_.fv_iter( _faceHandles[i] );
751 Point v0 = mesh_.point( *fvi);
752 ++fvi;
753 Point v1 = mesh_.point( *fvi );
754 ++fvi;
755 Point v2 = mesh_.point( *fvi );
756
757 //check if its degenerated
758 Point v0v1 = v1 - v0;
759 Point v0v2 = v2 - v0;
760 Point n = v0v1 % v0v2; // not normalized !
761 double d = n.sqrnorm();
762
763 if (d < FLT_MIN && d > -FLT_MIN) {
764 // degenerated face found
765 typename MeshT::FaceHalfedgeIterator hIt = mesh_.fh_iter( _faceHandles[i] );
766
767 //try to collapse one of the edges
768 while (hIt.is_valid()){
769 if ( mesh_.is_collapse_ok( *hIt ) ){
770 // collapse the edge to remove the triangle
771 mesh_.collapse( *hIt );
772 // and erase the corresponding face from the vector
773 _faceHandles.erase( _faceHandles.begin() + i );
774 break;
775 } else {
776 ++hIt;
777 }
778 }
779 }
780 }
781}
782
783//=============================================================================
784
785
786//=============================================================================
787} // namespace HoleFiller
788} // namespace OpenMesh
789//=============================================================================
bool is_valid() const
The handle is valid iff the index is not negative.
Definition Handles.hh:72
SmartVertexHandle add_vertex(const Point _p)
Definition PolyMeshT.hh:255
bool is_boundary() const
Returns true iff the handle is boundary.
void smooth(unsigned int _n)
Do _n smoothing iterations.
SmartVertexHandle split(EdgeHandle _eh, const Point &_p)
Edge split (= 2-to-4 split)
Definition TriMeshT.hh:275
SmartVertexHandle make_smart(VertexHandle _vh, const PolyConnectivity *_mesh)
Creats a SmartVertexHandle from a VertexHandle and a Mesh.
T angle(T _cos_angle, T _sin_angle)
Definition MathDefs.hh:140
SmartHalfedgeHandle h1() const
Shorthand for halfedge(1)
SmartHalfedgeHandle h0() const
Shorthand for halfedge(0)
SmartVertexHandle from() const
Returns vertex at start of halfedge.
SmartHalfedgeHandle next() const
Returns next halfedge handle.
SmartHalfedgeHandle opp() const
Returns opposite halfedge handle.
SmartVertexHandle to() const
Returns vertex pointed to by halfedge.
Smart version of VertexHandle contains a pointer to the corresponding mesh and allows easier access t...
PolyConnectivity::ConstVertexOHalfedgeRange outgoing_halfedges() const
Returns a range of incoming halfedges incident to the vertex (PolyConnectivity::voh_range())