]> Creatis software - cpMesh.git/blob - lib/cpm/Algorithms/QuadEdge/EdgeDecimationFilter.hxx
First commit
[cpMesh.git] / lib / cpm / Algorithms / QuadEdge / EdgeDecimationFilter.hxx
1 #ifndef __CPM__ALGORITHMS__QUADEDGE__EDGEDECIMATIONFILTER__HXX__
2 #define __CPM__ALGORITHMS__QUADEDGE__EDGEDECIMATIONFILTER__HXX__
3
4 #include <itkTriangleHelper.h>
5
6 // -------------------------------------------------------------------------
7 template< class I, class O, class C >
8 cpm::Algorithms::QuadEdge::EdgeDecimationFilter< I, O, C >::
9 EdgeDecimationFilter( )
10   : Superclass( ),
11     m_Relocate( true ),
12     m_CheckOrientation( false ),
13     m_Element( NULL )
14 {
15   this->m_JoinVertexFunction = TOperator::New( );
16   this->m_PriorityQueue = TPriorityQueue::New( );
17 }
18
19 // -------------------------------------------------------------------------
20 template< class I, class O, class C >
21 cpm::Algorithms::QuadEdge::EdgeDecimationFilter< I, O, C >::
22 ~EdgeDecimationFilter( )
23 {
24   TOutPrimalEdge* edge;
25
26   while( !( this->m_PriorityQueue->Empty( ) ) )
27   {
28     edge = this->m_PriorityQueue->Peek( )->m_Element;
29     this->m_PriorityQueue->Pop( );
30
31     typename TQueueMap::iterator it = this->m_QueueMapper.find( edge );
32     delete it->second;
33     this->m_QueueMapper.erase( it );
34
35   } // elihw
36 }
37
38 // -------------------------------------------------------------------------
39 template< class I, class O, class C >
40 void cpm::Algorithms::QuadEdge::EdgeDecimationFilter< I, O, C >::
41 FillPriorityQueue( )
42 {
43   typename O::Pointer output = this->GetOutput( );
44
45   this->m_JoinVertexFunction->SetMesh( output );
46
47   /* TODO: fill queue with qedges
48      typename O::CellsContainerIterator it = output->GetEdgeCells( )->Begin( );
49      typename O::CellsContainerIterator end = output->GetEdgeCells( )->End( );
50
51      OutputEdgeCellType* edge;
52
53      // cache for use in MeasureEdge
54      this->GetOutput( ) = this->GetOutput( );
55
56      while( it != end )
57      {
58      edge = dynamic_cast< OutputEdgeCellType*  >( it.Value( ) );
59
60      if( edge )
61      {
62      PushElement( edge->GetQEGeom( ) );
63      }
64      ++it;
65      }
66  */
67 }
68
69 // -------------------------------------------------------------------------
70 template< class I, class O, class C >
71 void cpm::Algorithms::QuadEdge::EdgeDecimationFilter< I, O, C >::
72 PushElement( TOutPrimalEdge* iEdge )
73 {
74   TOutPointIdentifier id_org = iEdge->GetOrigin( );
75   TOutPointIdentifier id_dest = iEdge->GetDestination( );
76
77   TOutPrimalEdge* temp =( id_org < id_dest ) ? iEdge : iEdge->GetSym( );
78   TScalar   measure = MeasureEdge( temp );
79
80   TPriorityQueueItem* qi = new TPriorityQueueItem( temp,
81                                                          TPriority( false, measure ) );
82
83   this->m_QueueMapper[ temp ] = qi;
84   this->m_PriorityQueue->Push( qi );
85 }
86
87 // -------------------------------------------------------------------------
88 template< class I, class O, class C >
89 bool cpm::Algorithms::QuadEdge::EdgeDecimationFilter< I, O, C >::
90 IsEdgeOKToBeProcessed( TOutPrimalEdge* iEdge )
91 {
92   return( true );
93 }
94
95 // -------------------------------------------------------------------------
96 template< class I, class O, class C >
97 void cpm::Algorithms::QuadEdge::EdgeDecimationFilter< I, O, C >::
98 Extract( )
99 {
100   typename O::Pointer output = this->GetOutput( );
101
102   do
103   {
104     this->m_Element = this->m_PriorityQueue->Peek( )->m_Element;
105     this->m_Priority = this->m_PriorityQueue->Peek( )->m_Priority;
106
107     this->m_PriorityQueue->Pop( );
108     typename TQueueMap::iterator it = this->m_QueueMapper.find( this->m_Element );
109     delete it->second;
110     this->m_QueueMapper.erase( it );
111   }
112   while( !IsEdgeOKToBeProcessed( this->m_Element ) );
113 }
114
115 // -------------------------------------------------------------------------
116 template< class I, class O, class C >
117 void cpm::Algorithms::QuadEdge::EdgeDecimationFilter< I, O, C >::
118 DeleteElement( TOutPrimalEdge* iEdge )
119 {
120   if( iEdge ) // this test can be removed
121   {
122     TOutPrimalEdge* temp =( iEdge->GetOrigin( ) < iEdge->GetDestination( ) ) ?
123       iEdge : iEdge->GetSym( );
124
125     typename TQueueMap::iterator map_it = this->m_QueueMapper.find( temp );
126     if( map_it != this->m_QueueMapper.end( ) )
127     {
128       if( !map_it->second->m_Priority.first )
129       {
130         TPriorityQueueItem* e( map_it->second );
131         this->m_PriorityQueue->DeleteElement( e );
132         delete map_it->second;
133         this->m_QueueMapper.erase( map_it );
134       }
135     }
136   }
137 }
138
139 // -------------------------------------------------------------------------
140 template< class I, class O, class C >
141 void cpm::Algorithms::QuadEdge::EdgeDecimationFilter< I, O, C >::
142 DeletePoint(
143   const TOutPointIdentifier& iIdToBeDeleted,
144   const TOutPointIdentifier& iRemaining
145   )
146 {
147   /* TODO
148      ( void )iRemaining;
149      this->GetOutput( )->DeletePoint( iIdToBeDeleted );
150   */
151 }
152
153 // -------------------------------------------------------------------------
154 template< class I, class O, class C >
155 void cpm::Algorithms::QuadEdge::EdgeDecimationFilter< I, O, C >::
156 PushOrUpdateElement( TOutPrimalEdge* iEdge )
157 {
158   TOutPrimalEdge* temp = iEdge;
159
160   if( temp->GetOrigin( ) > temp->GetDestination( ) )
161   {
162     temp = temp->GetSym( );
163   }
164
165   typename TQueueMap::iterator map_it = this->m_QueueMapper.find( temp );
166
167   TScalar measure = MeasureEdge( temp );
168   if( map_it != this->m_QueueMapper.end( ) )
169   {
170     if( !map_it->second->m_Priority.first )
171     {
172       map_it->second->m_Priority.second = measure;
173       this->m_PriorityQueue->Update( map_it->second );
174     }
175   }
176   else
177   {
178     TPriorityQueueItem* qi = new TPriorityQueueItem( temp,
179                                                            TPriority( false, measure ) );
180     this->m_QueueMapper[ temp ] = qi;
181     this->m_PriorityQueue->Push( qi );
182   }
183 }
184
185 // -------------------------------------------------------------------------
186 template< class I, class O, class C >
187 void cpm::Algorithms::QuadEdge::EdgeDecimationFilter< I, O, C >::
188 JoinVertexFailed( )
189 {
190   typename TOperator::TEdgeStatus
191     status = this->m_JoinVertexFunction->GetEdgeStatus( );
192   switch( status )
193   {
194   default:
195   case TOperator::EDGE_NULL:
196   case TOperator::MESH_NULL:
197   case TOperator::FACE_ISOLATED:
198     break;
199   case TOperator::EDGE_ISOLATED:
200     itkDebugMacro( "EDGE_ISOLATED, at iteration: " << this->m_Iteration );
201     TagElementOut( this->m_Element );
202     break;
203     // more than 2 common vertices in 0-ring of org and dest respectively
204   case TOperator::TOO_MANY_COMMON_VERTICES:
205     itkDebugMacro( "TOO_MANY_COMMON_VERTICES, at iteration "
206                   << this->m_Iteration );
207     itkDebugMacro( << this->m_Element->GetOrigin( ) << " -> "
208                    << this->m_Element->GetDestination( ) );
209     this->TagElementOut( this->m_Element );
210     break;
211     // ******************************************************************
212     // Tetrahedron case
213   case TOperator::TETRAHEDRON_CONFIG:
214     itkDebugMacro( "TETRAHEDRON_CONFIG, at iteration " << this->m_Iteration );
215
216     this->TagElementOut( this->m_Element );
217     this->TagElementOut( this->m_Element->GetOnext( ) );
218     this->TagElementOut( this->m_Element->GetOprev( ) );
219     this->TagElementOut( this->m_Element->GetSym( ) );
220     this->TagElementOut( this->m_Element->GetSym( )->GetOnext( ) );
221     this->TagElementOut( this->m_Element->GetSym( )->GetOprev( ) );
222     this->TagElementOut( this->m_Element->GetOnext( )->GetLnext( ) );
223     break;
224     // ******************************************************************
225     // Samosa case
226   case TOperator::SAMOSA_CONFIG:
227     itkDebugMacro( "SAMOSA_CONFIG, at iteration " << this->m_Iteration );
228     this->RemoveSamosa( );
229     break;
230     // ******************************************************************
231     // Eye case
232   case TOperator::EYE_CONFIG:
233     itkDebugMacro( "EYE_CONFIG, at iteration " << this->m_Iteration );
234     this->RemoveEye( );
235     break;
236   case TOperator::EDGE_JOINING_DIFFERENT_BORDERS:
237     itkDebugMacro( "EDGE_JOINING_DIFFERENT_BORDERS, at iteration "
238                   << this->m_Iteration );
239     this->TagElementOut( this->m_Element );
240     break;
241   }
242 }
243
244 // -------------------------------------------------------------------------
245 template< class I, class O, class C >
246 bool cpm::Algorithms::QuadEdge::EdgeDecimationFilter< I, O, C >::
247 ProcessWithoutAnyTopologicalGuarantee( )
248 {
249   TOutPoint   pt;
250
251   TOutPointIdentifier id_org = this->m_Element->GetOrigin( );
252   TOutPointIdentifier id_dest = this->m_Element->GetDestination( );
253   TOutPointIdentifier idx =( id_org < id_dest ) ? id_org : id_dest;
254
255   bool to_be_processed( true );
256
257   if( this->m_Relocate )
258   {
259     pt = Relocate( this->m_Element );
260   }
261   else
262   {
263     pt = this->GetOutput( )->GetPoint( idx );
264   }
265
266 ///TODO use CheckOrientation!!!
267 //   if( this->m_CheckOrientation )
268 //     to_be_processed = CheckOrientation( this->m_Element, idx, pt );
269
270     if( !to_be_processed )
271     {
272       return( false );
273     }
274
275     std::list< TOutPrimalEdge*  > list_qe_to_be_deleted;
276     TOutPrimalEdge*               temp = this->m_Element->GetOnext( );
277
278     while( temp != this->m_Element )
279     {
280       list_qe_to_be_deleted.push_back( temp );
281       temp = temp->GetOnext( );
282     }
283
284     temp = this->m_Element->GetSym( )->GetOnext( );
285     while( temp != this->m_Element->GetSym( ) )
286     {
287       list_qe_to_be_deleted.push_back( temp );
288       temp = temp->GetOnext( );
289     }
290
291     typename std::list< TOutPrimalEdge*  >::iterator
292       it = list_qe_to_be_deleted.begin( );
293
294     while( it != list_qe_to_be_deleted.end( ) )
295     {
296       DeleteElement( *it );
297       ++it;
298     }
299
300     if( !this->m_JoinVertexFunction->Evaluate( this->m_Element ) )
301     {
302       it = list_qe_to_be_deleted.begin( );
303
304       while( it != list_qe_to_be_deleted.end( ) )
305       {
306         PushOrUpdateElement( *it );
307         ++it;
308       }
309
310       JoinVertexFailed( );
311     }
312     else
313     {
314       TOutPointIdentifier old_id = this->m_JoinVertexFunction->GetOldPointID( );
315
316       TOutPointIdentifier new_id =( old_id == id_dest ) ? id_org : id_dest;
317       DeletePoint( old_id, new_id );
318
319       TOutPrimalEdge* edge = this->GetOutput( )->FindEdge( new_id );
320       if( edge == ITK_NULLPTR )
321       {
322         itkDebugMacro( "edge == 0, at iteration " << this->m_Iteration );
323         return( false );
324       }
325
326       if( this->m_Relocate )
327       {
328         this->GetOutput( )->SetPoint( new_id, pt );
329       }
330
331       temp = edge;
332
333       do
334       {
335         PushOrUpdateElement( temp );
336         temp = temp->GetOnext( );
337       }
338       while( temp != edge );
339     }
340     return( false );
341 }
342
343 // -------------------------------------------------------------------------
344 template< class I, class O, class C >
345 unsigned int cpm::Algorithms::QuadEdge::EdgeDecimationFilter< I, O, C >::
346 CheckQEProcessingStatus( )
347 {
348   TOutPrimalEdge* qe = this->m_Element;
349   TOutPrimalEdge* qe_sym = qe->GetSym( );
350
351   /* TODO
352      bool LeftIsTriangle = qe->IsLnextOfTriangle( );
353      bool RightIsTriangle = qe->GetSym( )->IsLnextOfTriangle( );
354   */
355   bool LeftIsTriangle = false;
356   bool RightIsTriangle = false;
357
358   if( LeftIsTriangle || RightIsTriangle )
359   {
360     if( LeftIsTriangle && RightIsTriangle )
361     {
362       // two triangles
363       bool OriginOrderIsTwo;// TODO: =( qe->GetOrder( ) == 2 );
364       bool DestinationOrderIsTwo;// TODO: =( qe_sym->GetOrder( ) == 2 );
365
366       if( OriginOrderIsTwo || DestinationOrderIsTwo )
367       {
368         if( OriginOrderIsTwo && DestinationOrderIsTwo )
369         {
370           // isolated component made of two triangles
371           // sharing same points but with opposite orientation
372           // looks like a samosa
373           itkDebugMacro( "RemoveSamosa" );
374           return( 1 );
375         } // end if( OriginOrderIsTwo && DestinationOrderIsTwo )
376         else
377         {
378           // two triangles share three points and two edges
379           // the last edge is duplicated = two edge cells
380           // having the same points. It is a valid manifold case
381           // but you have to decimate it the right way.
382           // from the top the drawing of that case looks like an Eye
383           itkDebugMacro( "RemoveEye" );
384           return( 2 );
385         } // end else if( OriginOrderIsTwo && DestinationOrderIsTwo )
386       }   // end if( OriginOrderIsTwo || DestinationOrderIsTwo )
387       else  // if( OriginOrderIsTwo || DestinationOrderIsTwo )
388       {
389         if( NumberOfCommonVerticesIn0Ring( ) > 2 )
390         {
391           // both points have more than 2 edges on their O-ring
392           itkDebugMacro( "NumberOfCommonVerticesIn0Ring( ) > 2" );
393           return( 3 );
394         } //end if( NumberOfCommonVerticesIn0Ring( ) > 2 )
395         else
396         {
397           return( 0 );
398         }
399       } // end else if( OriginOrderIsTwo || DestinationOrderIsTwo )
400     }   // end if( LeftIsTriangle && RightIsTriangle )
401     else  // if( LeftIsTriangle && RightIsTriangle )
402     {
403       if( NumberOfCommonVerticesIn0Ring( ) > 1 )
404       {
405         itkDebugMacro( "NumberOfCommonVerticesIn0Ring( ) > 1" );
406         return( 4 );
407       }  // end if( NumberOfCommonVerticesIn0Ring( ) > 1 )
408       else // if( NumberOfCommonVerticesIn0Ring( ) > 1 )
409       {
410         if( RightIsTriangle )
411         {
412           return( 5 );
413         }
414         else
415         {
416           return( 6 );
417         }
418       } // end else if( NumberOfCommonVerticesIn0Ring( ) > 1 )
419     }   // end else if( LeftIsTriangle && RightIsTriangle )
420   }     // end if( LeftIsTriangle || RightIsTriangle )
421   else    // if( LeftIsTriangle || RightIsTriangle )
422   {
423     if( NumberOfCommonVerticesIn0Ring( ) > 0 )
424     {
425       return( 7 );
426     }
427     else
428     {
429       return( 0 );
430     }
431   } // end if( LeftIsTriangle || RightIsTriangle )
432
433   //   return( 0 );
434 }
435
436 // -------------------------------------------------------------------------
437 template< class I, class O, class C >
438 bool cpm::Algorithms::QuadEdge::EdgeDecimationFilter< I, O, C >::
439 ProcessWithTopologicalGuarantee( )
440 {
441   if( this->m_Priority.first )
442   {
443     return( true );
444   }
445
446   ProcessWithoutAnyTopologicalGuarantee( );
447   return( false );
448 }
449
450 // -------------------------------------------------------------------------
451 template< class I, class O, class C >
452 unsigned long cpm::Algorithms::QuadEdge::EdgeDecimationFilter< I, O, C >::
453 NumberOfCommonVerticesIn0Ring( ) const
454 {
455   TOutPrimalEdge* qe = this->m_Element;
456   TOutPrimalEdge* e_it  = qe->GetOnext( );
457
458   std::list< TOutPointIdentifier > dir_list, sym_list, intersection_list;
459   do
460   {
461     dir_list.push_back( e_it->GetDestination( ) );
462     e_it = e_it->GetOnext( );
463   }
464   while( e_it != qe );
465
466   qe = qe->GetSym( );
467   e_it = qe;
468
469   do
470   {
471     sym_list.push_back( e_it->GetDestination( ) );
472     e_it = e_it->GetOnext( );
473   }
474   while( e_it != qe );
475
476   dir_list.sort( );
477   sym_list.sort( );
478
479   std::set_intersection( dir_list.begin( ), dir_list.end( ),
480                          sym_list.begin( ), sym_list.end( ),
481                          std::back_inserter( intersection_list ) );
482
483   return( static_cast< unsigned long >( intersection_list.size( ) ) );
484 }
485
486 // -------------------------------------------------------------------------
487 template< class I, class O, class C >
488 void cpm::Algorithms::QuadEdge::EdgeDecimationFilter< I, O, C >::
489 RemoveSamosa( )
490 {
491   DeleteElement( this->m_Element->GetLnext( ) );
492   DeleteElement( this->m_Element->GetLprev( ) );
493   DeleteElement( this->m_Element->GetRnext( ) );
494   DeleteElement( this->m_Element->GetRprev( ) );
495 }
496
497 // -------------------------------------------------------------------------
498 template< class I, class O, class C >
499 void cpm::Algorithms::QuadEdge::EdgeDecimationFilter< I, O, C >::
500 TagElementOut( TOutPrimalEdge* iEdge )
501 {
502   typename TQueueMap::iterator map_it = this->m_QueueMapper.find( iEdge );
503
504   if( map_it != this->m_QueueMapper.end( ) )
505   {
506     map_it->second->m_Priority.first = true;
507     map_it->second->m_Priority.second = static_cast< TScalar >( 0. );
508     this->m_PriorityQueue->Update( map_it->second );
509   }
510   else
511   {
512     TPriorityQueueItem* qi = new TPriorityQueueItem( iEdge,
513                                                      TPriority( true, static_cast< TScalar >( 0. ) ) );
514
515     this->m_QueueMapper[ iEdge ] = qi;
516     this->m_PriorityQueue->Push( qi );
517   }
518 }
519
520 // -------------------------------------------------------------------------
521 template< class I, class O, class C >
522 void cpm::Algorithms::QuadEdge::EdgeDecimationFilter< I, O, C >::
523 RemoveEye( )
524 {
525   TOutPrimalEdge* qe = this->m_Element;
526   TOutPrimalEdge* qe_sym = this->m_Element->GetSym( );
527
528   /* TODO
529      if( qe->GetSym( )->GetOrder( ) == 2 )
530      {
531      qe = qe_sym;
532      }
533   */
534
535   TagElementOut( qe );
536   TagElementOut( qe->GetOnext( ) );
537   TagElementOut( qe->GetSym( )->GetOnext( ) );
538   TagElementOut( qe->GetSym( )->GetOprev( ) );
539 }
540
541 // -------------------------------------------------------------------------
542 template< class I, class O, class C >
543 bool cpm::Algorithms::QuadEdge::EdgeDecimationFilter< I, O, C >::
544 CheckOrientation( TOutPrimalEdge* iEdge,
545                  const TOutPointIdentifier& iId,
546                  const TOutPoint& iPt )
547 {
548   typename O::Pointer           output = this->GetOutput( );
549   typename O::CellsContainerPointer cells = output->GetCells( );
550
551   std::list< typename O::CellIdentifier > r1, r2, elements_to_be_tested;
552   TOutPrimalEdge*                     qe = iEdge;
553   TOutPrimalEdge*                     qe_it = qe->GetOnext( );
554
555   do
556   {
557     r1.push_back( qe_it->GetLeft( ) );
558     qe_it = qe_it->GetOnext( );
559   }
560   while( qe_it != qe );
561
562   qe = iEdge->GetSym( );
563   qe_it = qe->GetOnext( );
564
565   do
566   {
567     r2.push_back( qe_it->GetLeft( ) );
568     qe_it = qe_it->GetOnext( );
569   }
570   while( qe_it != qe );
571
572   r1.sort( );
573   r2.sort( );
574
575   std::set_symmetric_difference( r1.begin( ), r1.end( ),
576                                  r2.begin( ), r2.end( ),
577                                  std::back_inserter( elements_to_be_tested ) );
578
579   typename std::list< typename O::CellIdentifier >::iterator
580     it = elements_to_be_tested.begin( );
581
582   typedef itk::TriangleHelper< TOutPoint > TTriangle;
583
584   bool                  orientation_ok( true );
585   typename O::CellIdentifier  c_id( 0 );
586
587   /* TODO: iterate over polygons
588   OutputPolygonType*    poly;
589   TOutPointIdentifier p_id;
590
591   int             k( 0 ), replace_k( 0 );
592   TOutPoint pt[ 3 ];
593
594   OutputVectorType n_bef, n_aft;
595
596   while( ( it != elements_to_be_tested.end( ) ) && orientation_ok )
597   {
598     c_id =* it;
599     poly = dynamic_cast< OutputPolygonType*  >( cells->GetElement( c_id ) );
600     qe = poly->GetEdgeRingEntry( );
601     qe_it = qe;
602     k = 0;
603     do
604     {
605       p_id = qe_it->GetOrigin( );
606       if( p_id == iId )
607       {
608         replace_k = k;
609       }
610       pt[ k++ ] = output->GetPoint( p_id );
611       qe_it = qe_it->GetLnext( );
612     }
613     while( qe_it != qe );
614
615     n_bef = TTriangle::ComputeNormal( pt[ 0 ], pt[ 1 ], pt[ 2 ] );
616     switch( replace_k )
617     {
618     default:
619     case 0:
620       n_aft = TTriangle::ComputeNormal( iPt, pt[ 1 ], pt[ 2 ] );
621       break;
622     case 1:
623       n_aft = TTriangle::ComputeNormal( pt[ 0 ], iPt, pt[ 2 ] );
624       break;
625     case 2:
626       n_aft = TTriangle::ComputeNormal( pt[ 0 ], pt[ 1 ], iPt );
627       break;
628     }
629
630     orientation_ok =( n_bef * n_aft ) < 0.;
631     ++it;
632   }
633   */
634   return( orientation_ok );
635 }
636
637 // -------------------------------------------------------------------------
638 template< class I, class O, class C >
639 bool cpm::Algorithms::QuadEdge::EdgeDecimationFilter< I, O, C >::
640 IsCriterionSatisfied( )
641 {
642   if( this->m_PriorityQueue->Empty( ) )
643   {
644     return( true );
645   }
646   else
647   {
648     return( this->m_Criterion->is_satisfied( this->GetOutput( ), 0, this->m_Priority.second ) );
649   }
650 }
651
652 #endif // __CPM__ALGORITHMS__QUADEDGE__EDGEDECIMATIONFILTER__HXX__
653
654 // eof - $RCSfile$