Salome HOME
0022523: [CEA 1096] Add a colomn "biquadratic" in "Mesh Information"
[modules/smesh.git] / src / StdMeshers / StdMeshers_Propagation.cxx
1 // Copyright (C) 2007-2014  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 //  SMESH SMESH : implementaion of SMESH idl descriptions
24 //  File   : StdMeshers_Propagation.cxx
25 //  Module : SMESH
26 //
27 #include "StdMeshers_Propagation.hxx"
28
29 #include "utilities.h"
30
31 #include "SMDS_SetIterator.hxx"
32 #include "SMESH_Algo.hxx"
33 #include "SMESH_Gen.hxx"
34 #include "SMESH_HypoFilter.hxx"
35 #include "SMESH_Mesh.hxx"
36 #include "SMESH_subMesh.hxx"
37
38 #include <BRepTools_WireExplorer.hxx>
39 #include <TopTools_ListIteratorOfListOfShape.hxx>
40 #include <TopTools_MapOfShape.hxx>
41 #include <TopoDS.hxx>
42
43 #define DBGMSG(txt) \
44   //  cout << txt << endl;
45
46 using namespace std;
47
48 namespace {
49
50   // =======================================================================
51   /*!
52    * \brief Listener managing propagation of 1D hypotheses
53    */
54   // =======================================================================
55
56   class PropagationMgr: public SMESH_subMeshEventListener
57   {
58   public:
59     static PropagationMgr* GetListener();
60     /*!
61      * \brief Set listener on edge submesh
62      */
63     static void Set(SMESH_subMesh * submesh);
64     /*!
65      * \brief Return an edge from which hypotheses are propagated from
66      */
67     static TopoDS_Edge GetSource(SMESH_subMesh * submesh,
68                                  bool&           isPropagOfDistribution);
69     /*!
70      * \brief Does it's main job
71      */
72     void ProcessEvent(const int          event,
73                       const int          eventType,
74                       SMESH_subMesh*     subMesh,
75                       SMESH_subMeshEventListenerData* data,
76                       const SMESH_Hypothesis*         hyp = 0);
77   private:
78     PropagationMgr();
79   };
80 }
81
82 //=============================================================================
83 /*!
84  * StdMeshers_Propagation Implementation
85  */
86 //=============================================================================
87
88 StdMeshers_Propagation::StdMeshers_Propagation (int hypId, int studyId, SMESH_Gen * gen)
89   : SMESH_Hypothesis(hypId, studyId, gen)
90 {
91   _name = GetName();
92   _param_algo_dim = -1; // 1D auxiliary
93 }
94 StdMeshers_PropagOfDistribution::StdMeshers_PropagOfDistribution (int hypId,
95                                                                   int studyId,
96                                                                   SMESH_Gen * gen)
97   : StdMeshers_Propagation(hypId, studyId, gen)                        { _name = GetName(); }
98 StdMeshers_Propagation::~StdMeshers_Propagation()                      {}
99 string StdMeshers_Propagation::GetName ()                              { return "Propagation"; }
100 string StdMeshers_PropagOfDistribution::GetName ()                     { return "PropagOfDistribution"; }
101 ostream & StdMeshers_Propagation::SaveTo (ostream & save)              { return save; }
102 istream & StdMeshers_Propagation::LoadFrom (istream & load)            { return load; }
103 bool StdMeshers_Propagation::SetParametersByMesh(const SMESH_Mesh*,
104                                                  const TopoDS_Shape& ) { return false; }
105 bool StdMeshers_Propagation::SetParametersByDefaults(const TDefaults&,const SMESH_Mesh*) { return false; }
106 void StdMeshers_Propagation::SetPropagationMgr(SMESH_subMesh* subMesh) { PropagationMgr::Set( subMesh ); }
107 /*!
108  * \brief Return an edge from which hypotheses are propagated
109  */
110 TopoDS_Edge StdMeshers_Propagation::GetPropagationSource(SMESH_Mesh&         theMesh,
111                                                          const TopoDS_Shape& theEdge,
112                                                          bool&               isPropagOfDistribution)
113 {
114   return PropagationMgr::GetSource( theMesh.GetSubMeshContaining( theEdge ),
115                                     isPropagOfDistribution);
116 }
117
118 //=============================================================================
119 //=============================================================================
120 // PROPAGATION MANAGEMENT
121 //=============================================================================
122 //=============================================================================
123
124 namespace {
125
126   enum SubMeshState { WAIT_PROPAG_HYP, // propagation hyp or local 1D hyp is missing
127                       HAS_PROPAG_HYP,  // propag hyp on this submesh
128                       IN_CHAIN,        // submesh is in propagation chain
129                       LAST_IN_CHAIN,   // submesh with local 1D hyp breaking a chain
130                       MEANINGLESS_LAST };          // meaningless
131
132   struct PropagationMgrData : public EventListenerData
133   {
134     bool myForward; //!< true if a curve of edge in chain is codirected with one of source edge
135     bool myIsPropagOfDistribution; //!< type of Propagation hyp
136     PropagationMgrData( SubMeshState state=WAIT_PROPAG_HYP ): EventListenerData(true) {
137       myType = state; myForward = true; myIsPropagOfDistribution = false;
138     }
139     void Init() {
140       myType = WAIT_PROPAG_HYP;  mySubMeshes.clear(); myForward = true;
141       myIsPropagOfDistribution = false;
142     }
143     SubMeshState State() const {
144       return (SubMeshState) myType;
145     }
146     void SetState(SubMeshState state) {
147       myType = state;
148     }
149     void SetSource(SMESH_subMesh* sm ) {
150       mySubMeshes.clear(); if ( sm ) mySubMeshes.push_back( sm );
151     }
152     void AddSource(SMESH_subMesh* sm ) {
153       if ( sm ) mySubMeshes.push_back( sm );
154     }
155     void SetChain(list< SMESH_subMesh* >& chain ) {
156       mySubMeshes.clear(); mySubMeshes.splice( mySubMeshes.end(), chain );
157     }
158     SMESH_subMeshIteratorPtr GetChain() const;
159     SMESH_subMesh* GetSource() const;
160   };
161
162   //=============================================================================
163   /*!
164    * \brief return static PropagationMgr
165    */
166   PropagationMgr* PropagationMgr::GetListener()
167   {
168     static PropagationMgr theListener;
169     return &theListener;
170   }
171   PropagationMgr* getListener()
172   {
173     return PropagationMgr::GetListener();
174   }
175   //=============================================================================
176   /*!
177    * \brief return PropagationMgrData found on a submesh
178    */
179   PropagationMgrData* findData(SMESH_subMesh* sm)
180   {
181     if ( sm )
182       return static_cast< PropagationMgrData* >( sm->GetEventListenerData( getListener() ));
183     return 0;
184   }
185   //=============================================================================
186   /*!
187    * \brief return PropagationMgrData found on theEdge submesh
188    */
189   PropagationMgrData* findData(SMESH_Mesh& theMesh, const TopoDS_Shape& theEdge)
190   {
191     if ( theEdge.ShapeType() == TopAbs_EDGE )
192       return findData( theMesh.GetSubMeshContaining( theEdge ) );
193     return 0;
194   }
195   //=============================================================================
196   /*!
197    * \brief return existing or a new PropagationMgrData
198    */
199   PropagationMgrData* getData(SMESH_subMesh* sm)
200   {
201     PropagationMgrData* data = findData( sm );
202     if ( !data && sm ) {
203       data = new PropagationMgrData();
204       sm->SetEventListener( getListener(), data, sm );
205     }
206     return data;
207   }
208   //=============================================================================
209   /*!
210    * \brief Returns a local 1D hypothesis used for theEdge
211    */
212   const SMESH_Hypothesis* getLocal1DHyp (SMESH_Mesh&         theMesh,
213                                          const TopoDS_Shape& theEdge)
214   {
215     static SMESH_HypoFilter hypo;
216     hypo.Init( hypo.HasDim( 1 )).
217       AndNot ( hypo.IsAlgo() ).
218       AndNot ( hypo.IsAssignedTo( theMesh.GetMeshDS()->ShapeToMesh() ));
219     return theMesh.GetHypothesis( theEdge, hypo, true );
220   }
221   //=============================================================================
222   /*!
223    * \brief Returns a propagation hypothesis assigned to theEdge
224    */
225   const SMESH_Hypothesis* getProagationHyp (SMESH_Mesh&         theMesh,
226                                             const TopoDS_Shape& theEdge)
227   {
228     static SMESH_HypoFilter propagHypFilter;
229     if ( propagHypFilter.IsEmpty() )
230     {
231       propagHypFilter.
232         Init( SMESH_HypoFilter::HasName( StdMeshers_Propagation::GetName ())).
233         Or  ( SMESH_HypoFilter::HasName( StdMeshers_PropagOfDistribution::GetName ()));
234     }
235     return theMesh.GetHypothesis( theEdge, propagHypFilter, true );
236   }
237   //================================================================================
238   /*!
239    * \brief Return an iterator on a list of submeshes
240    */
241   SMESH_subMeshIteratorPtr iterate( list<SMESH_subMesh*>::const_iterator from,
242                                     list<SMESH_subMesh*>::const_iterator to)
243   {
244     typedef SMESH_subMesh* TsubMesh;
245     typedef SMDS_SetIterator< TsubMesh, list< TsubMesh >::const_iterator > TIterator;
246     return SMESH_subMeshIteratorPtr ( new TIterator( from, to ));
247   }
248   //================================================================================
249   /*!
250    * \brief Build propagation chain
251     * \param theMainSubMesh - the submesh with Propagation hypothesis
252    */
253   bool buildPropagationChain ( SMESH_subMesh* theMainSubMesh )
254   {
255     DBGMSG( "buildPropagationChain from " << theMainSubMesh->GetId() );
256     const TopoDS_Shape& theMainEdge = theMainSubMesh->GetSubShape();
257     if (theMainEdge.ShapeType() != TopAbs_EDGE) return true;
258
259     SMESH_Mesh* mesh = theMainSubMesh->GetFather();
260
261     PropagationMgrData* chainData = getData( theMainSubMesh );
262     chainData->SetState( HAS_PROPAG_HYP );
263
264     if ( const SMESH_Hypothesis * propagHyp = getProagationHyp( *mesh, theMainEdge ))
265       chainData->myIsPropagOfDistribution =
266         ( StdMeshers_PropagOfDistribution::GetName() == propagHyp->GetName() );
267
268     // Edge submeshes, to which the 1D hypothesis will be propagated from theMainEdge
269     list<SMESH_subMesh*> & chain = chainData->mySubMeshes;
270     chain.clear();
271     chain.push_back( theMainSubMesh );
272
273     TopTools_MapOfShape checkedShapes;
274     checkedShapes.Add( theMainEdge );
275
276     list<SMESH_subMesh*>::iterator smIt = chain.begin();
277     for ( ; smIt != chain.end(); ++smIt )
278     {
279       const TopoDS_Edge& anE = TopoDS::Edge( (*smIt)->GetSubShape() );
280       PropagationMgrData* data = findData( *smIt );
281       if ( !data ) continue;
282
283       // Iterate on faces, having edge <anE>
284       TopTools_ListIteratorOfListOfShape itA (mesh->GetAncestors(anE));
285       for (; itA.More(); itA.Next())
286       {
287         // there are objects of different type among the ancestors of edge
288         if ( itA.Value().ShapeType() != TopAbs_WIRE /*|| !checkedShapes.Add( itA.Value() )*/)
289           continue;
290
291         // Get ordered edges and find index of anE in a sequence
292         BRepTools_WireExplorer aWE (TopoDS::Wire(itA.Value()));
293         vector<TopoDS_Edge> edges;
294         edges.reserve(4);
295         int edgeIndex = 0;
296         for (; aWE.More(); aWE.Next()) {
297           TopoDS_Edge edge = aWE.Current();
298           edge.Orientation( aWE.Orientation() );
299           if ( edge.IsSame( anE ))
300             edgeIndex = edges.size();
301           edges.push_back( edge );
302         }
303
304         // Find an edge opposite to anE
305         TopoDS_Edge anOppE;
306         if ( edges.size() < 4 ) {
307           continue; // too few edges
308         }
309         else if ( edges.size() == 4 ) {
310           int oppIndex = ( edgeIndex + 2 ) % 4;
311           anOppE = edges[ oppIndex ];
312         }
313         else {
314           // count nb sides
315           TopoDS_Edge prevEdge = anE;
316           int nbSide = 0, eIndex = edgeIndex + 1;
317           for ( int i = 0; i < edges.size(); ++i, ++eIndex )
318           {
319             if ( eIndex == edges.size() )
320               eIndex = 0;
321             if ( !SMESH_Algo::IsContinuous( prevEdge, edges[ eIndex ])) {
322               nbSide++;
323             }
324             else {
325               // check that anE is not a part of a composite side
326               if ( anE.IsSame( prevEdge ) || anE.IsSame( edges[ eIndex ])) {
327                 anOppE.Nullify(); break;
328               }
329             }
330             if ( nbSide == 2 ) { // opposite side
331               if ( !anOppE.IsNull() ) {
332                 // composite opposite side -> stop propagation
333                 anOppE.Nullify(); break;
334               }
335               anOppE = edges[ eIndex ];
336             }
337             if ( nbSide == 5 ) {
338               anOppE.Nullify(); break; // too many sides
339             }
340             prevEdge = edges[ eIndex ];
341           }
342           if ( anOppE.IsNull() )
343             continue;
344           if ( nbSide != 4 ) {
345             DBGMSG( nbSide << " sides in wire #" << mesh->GetMeshDS()->ShapeToIndex( itA.Value() ) << " - SKIP" );
346             continue;
347           }
348         }
349         if ( anOppE.IsNull() || !checkedShapes.Add( anOppE ))
350           continue;
351         SMESH_subMesh* oppSM = mesh->GetSubMesh( anOppE );
352         PropagationMgrData* oppData = getData( oppSM );
353
354         // Add anOppE to aChain if ...
355         if ( oppData->State() == WAIT_PROPAG_HYP ) // ... anOppE is not in any chain
356         {
357           oppData->SetSource( theMainSubMesh );
358           if ( !getLocal1DHyp( *mesh, anOppE )) // ... no 1d hyp on anOppE
359           {
360             oppData->myForward = data->myForward;
361             if ( edges[ edgeIndex ].Orientation() == anOppE.Orientation() )
362               oppData->myForward = !oppData->myForward;
363             chain.push_back( oppSM );
364             oppSM->ComputeStateEngine( SMESH_subMesh::CLEAN );
365             oppData->SetState( IN_CHAIN );
366             DBGMSG( "set IN_CHAIN on " << oppSM->GetId() );
367             if ( oppSM->GetAlgoState() != SMESH_subMesh::HYP_OK )
368               // make oppSM check algo state
369               if ( SMESH_Algo* algo = mesh->GetGen()->GetAlgo( *mesh, anOppE ))
370                 oppSM->AlgoStateEngine(SMESH_subMesh::ADD_FATHER_ALGO,algo);
371           }
372           else {
373             oppData->SetState( LAST_IN_CHAIN );
374             DBGMSG( "set LAST_IN_CHAIN on " << oppSM->GetId() );
375           }
376         }
377         else if ( oppData->State() == LAST_IN_CHAIN ) // anOppE breaks other chain
378         {
379           DBGMSG( "encounters LAST_IN_CHAIN on " << oppSM->GetId() );
380           oppData->AddSource( theMainSubMesh );
381         }
382       } // loop on face ancestors
383     } // loop on the chain
384
385     // theMainSubMesh must not be in a chain
386     chain.pop_front();
387
388     return true;
389   }
390   //================================================================================
391   /*!
392    * \brief Clear propagation chain
393    */
394   bool clearPropagationChain( SMESH_subMesh* subMesh )
395   {
396     DBGMSG( "clearPropagationChain from " << subMesh->GetId() );
397     if ( PropagationMgrData* data = findData( subMesh ))
398     {
399       switch ( data->State() ) {
400       case IN_CHAIN:
401         return clearPropagationChain( data->GetSource() );
402
403       case HAS_PROPAG_HYP: {
404         SMESH_subMeshIteratorPtr smIt = data->GetChain();
405         while ( smIt->more() ) {
406           SMESH_subMesh* sm = smIt->next();
407           getData( sm )->Init();
408           sm->ComputeStateEngine( SMESH_subMesh::CLEAN );
409         }
410         data->Init();
411         break;
412       }
413       case LAST_IN_CHAIN: {
414         SMESH_subMeshIteratorPtr smIt = iterate( data->mySubMeshes.begin(),
415                                                  data->mySubMeshes.end());
416         while ( smIt->more() )
417           clearPropagationChain( smIt->next() );
418         data->Init();
419         break;
420       }
421       default:;
422       }
423       return true;
424     }
425     return false;
426   }
427
428
429   //================================================================================
430   /*!
431    * \brief Return an iterator on chain submeshes
432    */
433   //================================================================================
434
435   SMESH_subMeshIteratorPtr PropagationMgrData::GetChain() const
436   {
437     switch ( State() ) {
438     case HAS_PROPAG_HYP:
439       return iterate( mySubMeshes.begin(), mySubMeshes.end() );
440     case IN_CHAIN:
441       if ( mySubMeshes.empty() ) break;
442       return getData( mySubMeshes.front() )->GetChain();
443     default:;
444     }
445     return iterate( mySubMeshes.end(), mySubMeshes.end() );
446   }
447   //================================================================================
448   /*!
449    * \brief Return a propagation source submesh
450    */
451   //================================================================================
452
453   SMESH_subMesh* PropagationMgrData::GetSource() const
454   {
455     if ( myType == IN_CHAIN )
456       if ( !mySubMeshes.empty() ) 
457         return mySubMeshes.front();
458     return 0;
459   }
460
461
462   //================================================================================
463   /*!
464    * \brief Constructor
465    */
466   //================================================================================
467
468   PropagationMgr::PropagationMgr()
469     : SMESH_subMeshEventListener( false, // won't be deleted by submesh
470                                   "StdMeshers_Propagation::PropagationMgr")
471   {}
472   //================================================================================
473   /*!
474    * \brief Set PropagationMgr on a submesh
475    */
476   //================================================================================
477
478   void PropagationMgr::Set(SMESH_subMesh * submesh)
479   {
480     if ( findData( submesh )) return;
481     DBGMSG( "PropagationMgr::Set() on  " << submesh->GetId() );
482     PropagationMgrData* data = new PropagationMgrData();
483     submesh->SetEventListener( getListener(), data, submesh );
484
485     const SMESH_Hypothesis * propagHyp =
486       getProagationHyp( *submesh->GetFather(), submesh->GetSubShape() );
487     if ( propagHyp )
488     {
489       data->myIsPropagOfDistribution =
490         ( StdMeshers_PropagOfDistribution::GetName() == propagHyp->GetName() );
491       getListener()->ProcessEvent( SMESH_subMesh::ADD_HYP,
492                                    SMESH_subMesh::ALGO_EVENT,
493                                    submesh,
494                                    data,
495                                    propagHyp);
496     }
497   }
498   //================================================================================
499   /*!
500    * \brief Return an edge from which hypotheses are propagated
501    */
502   //================================================================================
503
504   TopoDS_Edge PropagationMgr::GetSource(SMESH_subMesh * submesh,
505                                         bool&           isPropagOfDistribution)
506   {
507     if ( PropagationMgrData* data = findData( submesh )) {
508       if ( data->State() == IN_CHAIN ) {
509         if ( SMESH_subMesh* sm = data->GetSource() )
510         {
511           TopoDS_Shape edge = sm->GetSubShape();
512           edge = edge.Oriented( data->myForward ? TopAbs_FORWARD : TopAbs_REVERSED );
513           DBGMSG( " GetSource() = edge " << sm->GetId() << " REV = " << (!data->myForward));
514           isPropagOfDistribution = false;
515           if ( PropagationMgrData* data = findData( sm ))
516             isPropagOfDistribution = data->myIsPropagOfDistribution;
517           if ( edge.ShapeType() == TopAbs_EDGE )
518             return TopoDS::Edge( edge );
519         }
520       }
521     }
522     return TopoDS_Edge();
523   }
524   //================================================================================
525   /*!
526    * \brief React on events on 1D submeshes
527    */
528   //================================================================================
529
530   void PropagationMgr::ProcessEvent(const int                       event,
531                                     const int                       eventType,
532                                     SMESH_subMesh*                  subMesh,
533                                     SMESH_subMeshEventListenerData* listenerData,
534                                     const SMESH_Hypothesis*         hyp)
535   {
536     if ( !listenerData )
537       return;
538     if ( !hyp || hyp->GetType() != SMESHDS_Hypothesis::PARAM_ALGO || hyp->GetDim() != 1 )
539       return;
540     if ( eventType != SMESH_subMesh::ALGO_EVENT )
541       return;
542     DBGMSG( "PropagationMgr::ProcessEvent() on  " << subMesh->GetId() );
543
544     bool isPropagHyp = ( StdMeshers_Propagation::GetName()          == hyp->GetName() ||
545                          StdMeshers_PropagOfDistribution::GetName() == hyp->GetName() );
546
547     PropagationMgrData* data = static_cast<PropagationMgrData*>( listenerData );
548     switch ( data->State() ) {
549
550     case WAIT_PROPAG_HYP: { // propagation hyp or local 1D hyp is missing
551       // --------------------------------------------------------
552       bool hasPropagHyp = ( isPropagHyp ||
553                             getProagationHyp( *subMesh->GetFather(), subMesh->GetSubShape()) );
554       if ( !hasPropagHyp )
555         return;
556       bool hasLocal1DHyp =  getLocal1DHyp( *subMesh->GetFather(), subMesh->GetSubShape());
557       if ( !hasLocal1DHyp )
558         return;
559       if ( event == SMESH_subMesh::ADD_HYP ||
560            event == SMESH_subMesh::ADD_FATHER_HYP ) // add local or propagation hyp
561       {
562         DBGMSG( "ADD_HYP propagation to WAIT_PROPAG_HYP " << subMesh->GetId() );
563         // build propagation chain
564         buildPropagationChain( subMesh );
565       }
566       return;
567     }
568     case HAS_PROPAG_HYP: {  // propag hyp on this submesh
569       // --------------------------------------------------------
570       switch ( event ) {
571       case SMESH_subMesh::REMOVE_HYP:
572       case SMESH_subMesh::REMOVE_FATHER_HYP: // remove propagation hyp
573         if ( isPropagHyp && !getProagationHyp( *subMesh->GetFather(), subMesh->GetSubShape()) )
574         {
575           DBGMSG( "REMOVE_HYP propagation from HAS_PROPAG_HYP " << subMesh->GetId() );
576           // clear propagation chain
577           clearPropagationChain( subMesh );
578         }
579         // return; -- hyp is modified any way
580       default:
581         //case SMESH_subMesh::MODIF_HYP: // hyp modif
582         // clear mesh in a chain
583         DBGMSG( "MODIF_HYP on HAS_PROPAG_HYP " << subMesh->GetId() );
584         SMESH_subMeshIteratorPtr smIt = data->GetChain();
585         while ( smIt->more() ) {
586           SMESH_subMesh* smInChain = smIt->next();
587           smInChain->AlgoStateEngine( SMESH_subMesh::MODIF_HYP,
588                                       (SMESH_Hypothesis*) hyp );
589         }
590         return;
591       }
592       return;
593     }
594     case IN_CHAIN: {       // submesh is in propagation chain
595       // --------------------------------------------------------
596       if ( event == SMESH_subMesh::ADD_HYP ) { // add local hypothesis
597         if ( isPropagHyp ) { // propagation hyp added
598           DBGMSG( "ADD_HYP propagation on IN_CHAIN " << subMesh->GetId() );
599           // collision - do nothing
600         }
601         else { // 1D hyp added
602           // rebuild propagation chain
603           DBGMSG( "ADD_HYP 1D on IN_CHAIN " << subMesh->GetId() );
604           SMESH_subMesh* sourceSM = data->GetSource();
605           clearPropagationChain( sourceSM );
606           buildPropagationChain( sourceSM );
607         }
608       }
609       return;
610     }
611     case LAST_IN_CHAIN: { // submesh with local 1D hyp, breaking a chain
612       // --------------------------------------------------------
613       if ( event == SMESH_subMesh::REMOVE_HYP ) { // remove local hyp
614         // rebuild propagation chain
615         DBGMSG( "REMOVE_HYP 1D from LAST_IN_CHAIN " << subMesh->GetId() );
616         list<SMESH_subMesh*> sourceSM = data->mySubMeshes;
617         clearPropagationChain( subMesh );
618         SMESH_subMeshIteratorPtr smIt = iterate( sourceSM.begin(), sourceSM.end());
619         while ( smIt->more() )
620           buildPropagationChain( smIt->next() );
621       }
622       return;
623     }
624     } // switch by SubMeshState
625   }
626
627 } // namespace