Salome HOME
PAL16834 (smesh Prism don't work with NETGEN_2D algorithm)
[modules/smesh.git] / src / StdMeshers / StdMeshers_Projection_2D.cxx
1 //  SMESH SMESH : implementaion of SMESH idl descriptions
2 //
3 //  Copyright (C) 2003  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. 
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 //
24 // File      : StdMeshers_Projection_2D.cxx
25 // Module    : SMESH
26 // Created   : Fri Oct 20 11:37:07 2006
27 // Author    : Edward AGAPOV (eap)
28
29
30 #include "StdMeshers_Projection_2D.hxx"
31
32 #include "StdMeshers_ProjectionSource2D.hxx"
33 #include "StdMeshers_ProjectionUtils.hxx"
34
35 #include "SMESHDS_Hypothesis.hxx"
36 #include "SMESHDS_SubMesh.hxx"
37 #include "SMESH_Block.hxx"
38 #include "SMESH_Gen.hxx"
39 #include "SMESH_Mesh.hxx"
40 #include "SMESH_MeshEditor.hxx"
41 #include "SMESH_Pattern.hxx"
42 #include "SMESH_subMesh.hxx"
43 #include "SMESH_subMeshEventListener.hxx"
44 #include "SMESH_Comment.hxx"
45 #include "SMDS_EdgePosition.hxx"
46
47 #include "utilities.h"
48
49 #include <TopExp.hxx>
50 #include <TopoDS.hxx>
51 #include <TopTools_ListIteratorOfListOfShape.hxx>
52 #include <BRep_Tool.hxx>
53
54
55
56 using namespace std;
57
58 #define RETURN_BAD_RESULT(msg) { MESSAGE(msg); return false; }
59
60 typedef StdMeshers_ProjectionUtils TAssocTool;
61
62 //=======================================================================
63 //function : StdMeshers_Projection_2D
64 //purpose  : 
65 //=======================================================================
66
67 StdMeshers_Projection_2D::StdMeshers_Projection_2D(int hypId, int studyId, SMESH_Gen* gen)
68   :SMESH_2D_Algo(hypId, studyId, gen)
69 {
70   _name = "Projection_2D";
71   _shapeType = (1 << TopAbs_FACE);      // 1 bit per shape type
72
73   _compatibleHypothesis.push_back("ProjectionSource2D");
74   _sourceHypo = 0;
75 }
76
77 //================================================================================
78 /*!
79  * \brief Destructor
80  */
81 //================================================================================
82
83 StdMeshers_Projection_2D::~StdMeshers_Projection_2D()
84 {}
85
86 //=======================================================================
87 //function : CheckHypothesis
88 //purpose  : 
89 //=======================================================================
90
91 bool StdMeshers_Projection_2D::CheckHypothesis(SMESH_Mesh&                          theMesh,
92                                                const TopoDS_Shape&                  theShape,
93                                                SMESH_Hypothesis::Hypothesis_Status& theStatus)
94 {
95   list <const SMESHDS_Hypothesis * >::const_iterator itl;
96
97   const list <const SMESHDS_Hypothesis * >&hyps = GetUsedHypothesis(theMesh, theShape);
98   if ( hyps.size() == 0 )
99   {
100     theStatus = HYP_MISSING;
101     return false;  // can't work with no hypothesis
102   }
103
104   if ( hyps.size() > 1 )
105   {
106     theStatus = HYP_ALREADY_EXIST;
107     return false;
108   }
109
110   const SMESHDS_Hypothesis *theHyp = hyps.front();
111
112   string hypName = theHyp->GetName();
113
114   theStatus = HYP_OK;
115
116   if (hypName == "ProjectionSource2D")
117   {
118     _sourceHypo = static_cast<const StdMeshers_ProjectionSource2D *>(theHyp);
119
120     // Check hypo parameters
121
122     SMESH_Mesh* srcMesh = _sourceHypo->GetSourceMesh();
123     SMESH_Mesh* tgtMesh = & theMesh;
124     if ( !srcMesh )
125       srcMesh = tgtMesh;
126
127     // check vertices
128     if ( _sourceHypo->HasVertexAssociation() )
129     {
130       // source vertices
131       TopoDS_Shape edge = TAssocTool::GetEdgeByVertices
132         ( srcMesh, _sourceHypo->GetSourceVertex(1), _sourceHypo->GetSourceVertex(2) );
133       if ( edge.IsNull() ||
134            !TAssocTool::IsSubShape( edge, srcMesh ) ||
135            !TAssocTool::IsSubShape( edge, _sourceHypo->GetSourceFace() ))
136       {
137         theStatus = HYP_BAD_PARAMETER;
138         SCRUTE((edge.IsNull()));
139         SCRUTE((TAssocTool::IsSubShape( edge, srcMesh )));
140         SCRUTE((TAssocTool::IsSubShape( edge, _sourceHypo->GetSourceFace() )));
141       }
142       else
143       {
144         // target vertices
145         edge = TAssocTool::GetEdgeByVertices
146           ( tgtMesh, _sourceHypo->GetTargetVertex(1), _sourceHypo->GetTargetVertex(2) );
147         if ( edge.IsNull() ||
148              !TAssocTool::IsSubShape( edge, tgtMesh ) ||
149              !TAssocTool::IsSubShape( edge, theShape ))
150         {
151           theStatus = HYP_BAD_PARAMETER;
152           SCRUTE((edge.IsNull()));
153           SCRUTE((TAssocTool::IsSubShape( edge, tgtMesh )));
154           SCRUTE((TAssocTool::IsSubShape( edge, theShape )));
155         }
156       }
157     }
158     // check a source face
159     if ( !TAssocTool::IsSubShape( _sourceHypo->GetSourceFace(), srcMesh ) ||
160          ( srcMesh == tgtMesh && theShape == _sourceHypo->GetSourceFace() ))
161     {
162       theStatus = HYP_BAD_PARAMETER;
163       SCRUTE((TAssocTool::IsSubShape( _sourceHypo->GetSourceFace(), srcMesh )));
164       SCRUTE((srcMesh == tgtMesh));
165       SCRUTE(( theShape == _sourceHypo->GetSourceFace() ));
166     }
167   }
168   else
169   {
170     theStatus = HYP_INCOMPATIBLE;
171   }
172   return ( theStatus == HYP_OK );
173 }
174
175 namespace {
176
177
178   //================================================================================
179   /*!
180    * \brief define if a node is new or old
181     * \param node - node to check
182     * \retval bool - true if the node existed before Compute() is called
183    */
184   //================================================================================
185
186   bool isOldNode( const SMDS_MeshNode* node )
187   {
188     // old nodes are shared by edges and new ones are shared
189     // only by faces created by mapper
190     bool isOld = false;
191     SMDS_ElemIteratorPtr invElem = node->GetInverseElementIterator();
192     while ( !isOld && invElem->more() )
193       isOld = ( invElem->next()->GetType() == SMDSAbs_Edge );
194     return isOld;
195   }
196
197   //================================================================================
198   /*!
199    * \brief Class to remove mesh built by pattern mapper on edges
200    * and vertices in the case of failure of projection algo.
201    * It does it's job at destruction
202    */
203   //================================================================================
204
205   class MeshCleaner {
206     SMESH_subMesh* sm;
207   public:
208     MeshCleaner( SMESH_subMesh* faceSubMesh ): sm(faceSubMesh) {}
209     ~MeshCleaner() { Clean(sm); }
210     void Release() { sm = 0; } // mesh will not be removed
211     static void Clean( SMESH_subMesh* sm )
212     {
213       if ( !sm ) return;
214       switch ( sm->GetSubShape().ShapeType() ) {
215       case TopAbs_VERTEX:
216       case TopAbs_EDGE: {
217         SMDS_NodeIteratorPtr nIt = sm->GetSubMeshDS()->GetNodes();
218         SMESHDS_Mesh* mesh = sm->GetFather()->GetMeshDS();
219         while ( nIt->more() ) {
220           const SMDS_MeshNode* node = nIt->next();
221           if ( !isOldNode( node ) )
222             mesh->RemoveNode( node );
223         }
224         // do not break but iterate over DependsOn()
225       }
226       default:
227         SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator(false,false);
228         while ( smIt->more() )
229           Clean( smIt->next() );
230       }
231     }
232   };
233
234   //================================================================================
235   /*!
236    * \brief find new nodes belonging to one free border of mesh on face
237     * \param sm - submesh on edge or vertex containg nodes to choose from
238     * \param face - the face bound the submesh
239     * \param u2nodes - map to fill with nodes
240     * \param seamNodes - set of found nodes
241     * \retval bool - is a success
242    */
243   //================================================================================
244
245   bool getBoundaryNodes ( SMESH_subMesh*                        sm,
246                           const TopoDS_Face&                    face,
247                           map< double, const SMDS_MeshNode* > & u2nodes,
248                           set< const SMDS_MeshNode* > &         seamNodes)
249   {
250     u2nodes.clear();
251     seamNodes.clear();
252     if ( !sm || !sm->GetSubMeshDS() )
253       RETURN_BAD_RESULT("Null submesh");
254
255     SMDS_NodeIteratorPtr nIt = sm->GetSubMeshDS()->GetNodes();
256     switch ( sm->GetSubShape().ShapeType() ) {
257
258     case TopAbs_VERTEX: {
259       while ( nIt->more() ) {
260         const SMDS_MeshNode* node = nIt->next();
261         if ( isOldNode( node ) ) continue;
262         u2nodes.insert( make_pair( 0., node ));
263         seamNodes.insert( node );
264         return true;
265       }
266       break;
267     }
268     case TopAbs_EDGE: {
269       
270       // Get submeshes of sub-vertices
271       const map< int, SMESH_subMesh * >& subSM = sm->DependsOn();
272       if ( subSM.size() != 2 )
273         RETURN_BAD_RESULT("there must be 2 submeshes of sub-vertices"
274                           " but we have " << subSM.size());
275       SMESH_subMesh* smV1 = subSM.begin()->second;
276       SMESH_subMesh* smV2 = subSM.rbegin()->second;
277       if ( !smV1->IsMeshComputed() || !smV2->IsMeshComputed() )
278         RETURN_BAD_RESULT("Empty vertex submeshes");
279
280       // Look for a new node on V1
281       nIt = smV1->GetSubMeshDS()->GetNodes();
282       const SMDS_MeshNode* nV1 = 0;
283       while ( nIt->more() && !nV1 ) {
284         const SMDS_MeshNode* node = nIt->next();
285         if ( !isOldNode( node ) ) nV1 = node;
286       }
287       if ( !nV1 )
288         RETURN_BAD_RESULT("No new node found on V1");
289
290       // Find a new node connected to nV1 and belonging to edge submesh;
291       const SMDS_MeshNode* nE = 0;
292       SMESHDS_SubMesh* smDS = sm->GetSubMeshDS();
293       SMDS_ElemIteratorPtr vElems = nV1->GetInverseElementIterator();
294       while ( vElems->more() && !nE ) {
295         const SMDS_MeshElement* elem = vElems->next();
296         if ( elem->GetType() != SMDSAbs_Face )
297           continue; // new nodes are shared by faces
298         int nbNodes = elem->NbNodes();
299         if ( elem->IsQuadratic() )
300           nbNodes /= 2;
301         int iV1 = elem->GetNodeIndex( nV1 );
302         // try next aftre nV1
303         int iE = SMESH_MesherHelper::WrapIndex( iV1 + 1, nbNodes );
304         if ( smDS->Contains( elem->GetNode( iE ) ))
305           nE = elem->GetNode( iE );
306         if ( !nE ) {
307           // try node before nV1
308           iE = SMESH_MesherHelper::WrapIndex( iV1 - 1, nbNodes );
309           if ( smDS->Contains( elem->GetNode( iE )))
310             nE = elem->GetNode( iE );
311         }
312         if ( nE && elem->IsQuadratic() ) { // find medium node between nV1 and nE
313           if ( Abs( iV1 - iE ) == 1 )
314             nE = elem->GetNode( Min ( iV1, iE ) + nbNodes );
315           else
316             nE = elem->GetNode( elem->NbNodes() - 1 );
317         }
318       }
319       if ( !nE )
320         RETURN_BAD_RESULT("new node on edge not found");
321
322       // Get the whole free border of a face
323       list< const SMDS_MeshNode* > bordNodes;
324       list< const SMDS_MeshElement* > bordFaces;
325       if ( !SMESH_MeshEditor::FindFreeBorder (nV1, nE, nV1, bordNodes, bordFaces ))
326         RETURN_BAD_RESULT("free border of a face not found by nodes " <<
327                           nV1->GetID() << " " << nE->GetID() );
328
329       // Insert nodes of the free border to the map until node on V2 encountered
330       SMESHDS_SubMesh* v2smDS = smV2->GetSubMeshDS();
331       list< const SMDS_MeshNode* >::iterator bordIt = bordNodes.begin();
332       bordIt++; // skip nV1
333       for ( ; bordIt != bordNodes.end(); ++bordIt ) {
334         const SMDS_MeshNode* node = *bordIt;
335         if ( v2smDS->Contains( node ))
336           break;
337         if ( node->GetPosition()->GetTypeOfPosition() != SMDS_TOP_EDGE )
338           RETURN_BAD_RESULT("Bad node position type: node " << node->GetID() <<
339                             " pos type " << node->GetPosition()->GetTypeOfPosition());
340         const SMDS_EdgePosition* pos =
341           static_cast<const SMDS_EdgePosition*>(node->GetPosition().get());
342         u2nodes.insert( make_pair( pos->GetUParameter(), node ));
343         seamNodes.insert( node );
344       }
345       if ( u2nodes.size() != seamNodes.size() )
346         RETURN_BAD_RESULT("Bad node params on edge " << sm->GetId() <<
347                           ", " << u2nodes.size() << " != " << seamNodes.size() );
348       return true;
349     }
350     default:;
351     }
352     RETURN_BAD_RESULT ("Unexpected submesh type");
353
354   } // bool getBoundaryNodes()
355
356 } // namespace
357
358 //=======================================================================
359 //function : Compute
360 //purpose  : 
361 //=======================================================================
362
363 bool StdMeshers_Projection_2D::Compute(SMESH_Mesh& theMesh, const TopoDS_Shape& theShape)
364 {
365   if ( !_sourceHypo )
366     return false;
367
368   TopoDS_Face tgtFace = TopoDS::Face( theShape.Oriented(TopAbs_FORWARD));
369   TopoDS_Face srcFace = TopoDS::Face( _sourceHypo->GetSourceFace().Oriented(TopAbs_FORWARD));
370
371   SMESH_Mesh * srcMesh = _sourceHypo->GetSourceMesh(); 
372   SMESH_Mesh * tgtMesh = & theMesh;
373   if ( !srcMesh )
374     srcMesh = tgtMesh;
375
376   SMESHDS_Mesh * meshDS = theMesh.GetMeshDS();
377
378   SMESH_MesherHelper helper( theMesh );
379   helper.SetSubShape( tgtFace );
380
381   // ---------------------------
382   // Make subshapes association
383   // ---------------------------
384
385   TAssocTool::TShapeShapeMap shape2ShapeMap;
386   TAssocTool::InitVertexAssociation( _sourceHypo, shape2ShapeMap );
387   if ( !TAssocTool::FindSubShapeAssociation( tgtFace, tgtMesh, srcFace, srcMesh,
388                                              shape2ShapeMap) )
389     return error(COMPERR_BAD_SHAPE,"Topology of source and target faces seems different" );
390
391   // ----------------------------------------------
392   // Assure that mesh on a source Face is computed
393   // ----------------------------------------------
394
395   SMESH_subMesh* srcSubMesh = srcMesh->GetSubMesh( srcFace );
396   SMESH_subMesh* tgtSubMesh = tgtMesh->GetSubMesh( tgtFace );
397
398   if ( tgtMesh == srcMesh ) {
399     if ( !TAssocTool::MakeComputed( srcSubMesh ))
400       return error(COMPERR_BAD_INPUT_MESH,"Source mesh not computed");
401   }
402   else {
403     if ( !srcSubMesh->IsMeshComputed() )
404       return error(COMPERR_BAD_INPUT_MESH,"Source mesh not computed");
405   }
406
407   // --------------------
408   // Prepare to mapping 
409   // --------------------
410
411   // Load pattern from the source face
412   SMESH_Pattern mapper;
413   mapper.Load( srcMesh, srcFace );
414   if ( mapper.GetErrorCode() != SMESH_Pattern::ERR_OK )
415     return error(COMPERR_BAD_INPUT_MESH,"Can't load mesh pattern from the source face");
416
417   // Find the first target vertex corresponding to first vertex of the <mapper>
418   // and <theReverse> flag needed to call mapper.Apply()
419
420   TopoDS_Vertex srcV1 = TopoDS::Vertex( mapper.GetSubShape( 1 ));
421   if ( srcV1.IsNull() )
422     RETURN_BAD_RESULT("Mesh is not bound to the face");
423   if ( !shape2ShapeMap.IsBound( srcV1 ))
424     RETURN_BAD_RESULT("Not associated vertices, srcV1 " << srcV1.TShape().operator->() );
425   TopoDS_Vertex tgtV1 = TopoDS::Vertex( shape2ShapeMap( srcV1 ));
426
427   if ( !TAssocTool::IsSubShape( srcV1, srcFace ))
428     RETURN_BAD_RESULT("Wrong srcV1 " << srcV1.TShape().operator->());
429   if ( !TAssocTool::IsSubShape( tgtV1, tgtFace ))
430     RETURN_BAD_RESULT("Wrong tgtV1 " << tgtV1.TShape().operator->());
431
432   // try to find out orientation by order of edges
433   bool reverse = false;
434   list< TopoDS_Edge > tgtEdges, srcEdges;
435   list< int > nbEdgesInWires;
436   SMESH_Block::GetOrderedEdges( tgtFace, tgtV1, tgtEdges, nbEdgesInWires);
437   SMESH_Block::GetOrderedEdges( srcFace, srcV1, srcEdges, nbEdgesInWires);
438   if ( nbEdgesInWires.front() > 1 ) // possible to find out
439   {
440     TopoDS_Edge srcE1 = srcEdges.front(), tgtE1 = tgtEdges.front();
441     reverse = ( ! srcE1.IsSame( shape2ShapeMap( tgtE1 )));
442   }
443   else if ( nbEdgesInWires.front() == 1 )
444   {
445     // TODO::Compare orientation of curves in a sole edge
446     //RETURN_BAD_RESULT("Not implemented case");
447   }
448   else
449   {
450     RETURN_BAD_RESULT("Bad result from SMESH_Block::GetOrderedEdges()");
451   }
452
453   // --------------------
454   // Perform 2D mapping 
455   // --------------------
456
457   // Compute mesh on a target face
458
459   mapper.Apply( tgtFace, tgtV1, reverse );
460   if ( mapper.GetErrorCode() != SMESH_Pattern::ERR_OK )
461     return error("Can't apply source mesh pattern to the face");
462
463   // Create the mesh
464
465   const bool toCreatePolygons = false, toCreatePolyedrs = false;
466   mapper.MakeMesh( tgtMesh, toCreatePolygons, toCreatePolyedrs );
467   if ( mapper.GetErrorCode() != SMESH_Pattern::ERR_OK )
468     return error("Can't make mesh by source mesh pattern");
469
470   // it will remove mesh built by pattern mapper on edges and vertices
471   // in failure case
472   //  MeshCleaner cleaner( tgtSubMesh );
473
474   // -------------------------------------------------------------------------
475   // mapper doesn't take care of nodes already existing on edges and vertices,
476   // so we must merge nodes created by it with existing ones 
477   // -------------------------------------------------------------------------
478
479   SMESH_MeshEditor editor( tgtMesh );
480   SMESH_MeshEditor::TListOfListOfNodes groupsOfNodes;
481
482   // Make groups of nodes to merge
483
484   // loop on edge and vertex submeshes of a target face
485   SMESH_subMeshIteratorPtr smIt = tgtSubMesh->getDependsOnIterator(false,false);
486   while ( smIt->more() )
487   {
488     SMESH_subMesh*     sm = smIt->next();
489     SMESHDS_SubMesh* smDS = sm->GetSubMeshDS();
490
491     // Sort new and old nodes of a submesh separately
492
493     bool isSeam = helper.IsSeamShape( sm->GetId() );
494
495     enum { NEW_NODES = 0, OLD_NODES };
496     map< double, const SMDS_MeshNode* > u2nodesMaps[2], u2nodesOnSeam;
497     map< double, const SMDS_MeshNode* >::iterator u_oldNode, u_newNode, u_newOnSeam, newEnd;
498     set< const SMDS_MeshNode* > seamNodes;
499
500     // mapper puts on a seam edge nodes from 2 edges
501     if ( isSeam && ! getBoundaryNodes ( sm, tgtFace, u2nodesOnSeam, seamNodes ))
502       RETURN_BAD_RESULT("getBoundaryNodes() failed");
503
504     SMDS_NodeIteratorPtr nIt = smDS->GetNodes();
505     while ( nIt->more() )
506     {
507       const SMDS_MeshNode* node = nIt->next();
508       bool isOld = isOldNode( node );
509
510       if ( !isOld && isSeam ) { // new node on a seam edge
511         if ( seamNodes.find( node ) != seamNodes.end())
512           continue; // node is already in the map
513       }
514
515       // sort nodes on edges by its position
516       map< double, const SMDS_MeshNode* > & pos2nodes = u2nodesMaps[isOld ? OLD_NODES : NEW_NODES];
517       switch ( node->GetPosition()->GetTypeOfPosition() )
518       {
519       case  SMDS_TOP_VERTEX: {
520         pos2nodes.insert( make_pair( 0, node ));
521         break;
522       }
523       case  SMDS_TOP_EDGE:   {
524         const SMDS_EdgePosition* pos =
525           static_cast<const SMDS_EdgePosition*>(node->GetPosition().get());
526         pos2nodes.insert( make_pair( pos->GetUParameter(), node ));
527         break;
528       }
529       default:
530         RETURN_BAD_RESULT("Wrong node position type: "<<
531                           node->GetPosition()->GetTypeOfPosition());
532       }
533     }
534     if ( u2nodesMaps[ NEW_NODES ].size() != u2nodesMaps[ OLD_NODES ].size() )
535     {
536       if ( u2nodesMaps[ NEW_NODES ].size() == 0                 &&
537            sm->GetSubShape().ShapeType() == TopAbs_EDGE         &&
538            BRep_Tool::Degenerated( TopoDS::Edge( sm->GetSubShape() )))
539         // NPAL15894 (tt88bis.py) - project mesh built by NETGEN_1d_2D that
540         // does not make segments/nodes on degenerated edges
541         continue;
542       RETURN_BAD_RESULT("Different nb of old and new nodes on shape #"<< sm->GetId() <<" "<<
543                         u2nodesMaps[ OLD_NODES ].size() << " != " <<
544                         u2nodesMaps[ NEW_NODES ].size());
545     }
546     if ( isSeam && u2nodesMaps[ OLD_NODES ].size() != u2nodesOnSeam.size() ) {
547       RETURN_BAD_RESULT("Different nb of old and seam nodes " <<
548                         u2nodesMaps[ OLD_NODES ].size() << " != " << u2nodesOnSeam.size());
549     }
550     // Make groups of nodes to merge
551     u_oldNode = u2nodesMaps[ OLD_NODES ].begin(); 
552     u_newNode = u2nodesMaps[ NEW_NODES ].begin();
553     newEnd    = u2nodesMaps[ NEW_NODES ].end();
554     u_newOnSeam = u2nodesOnSeam.begin();
555     for ( ; u_newNode != newEnd; ++u_newNode, ++u_oldNode ) {
556       groupsOfNodes.push_back( list< const SMDS_MeshNode* >() );
557       groupsOfNodes.back().push_back( u_oldNode->second );
558       groupsOfNodes.back().push_back( u_newNode->second );
559       if ( isSeam )
560         groupsOfNodes.back().push_back( (u_newOnSeam++)->second );
561     }
562   }
563
564   // Merge
565
566   editor.MergeNodes( groupsOfNodes );
567
568   // ---------------------------
569   // Check elements orientation
570   // ---------------------------
571
572   TopoDS_Face face = tgtFace;
573   if ( !theMesh.IsMainShape( tgtFace ))
574   {
575     // find the main shape
576     TopoDS_Shape mainShape = meshDS->ShapeToMesh();
577     switch ( mainShape.ShapeType() ) {
578     case TopAbs_SHELL:
579     case TopAbs_SOLID: break;
580     default:
581       TopTools_ListIteratorOfListOfShape ancestIt = theMesh.GetAncestors( face );
582       for ( ; ancestIt.More(); ancestIt.Next() ) {
583         TopAbs_ShapeEnum type = ancestIt.Value().ShapeType();
584         if ( type == TopAbs_SOLID ) {
585           mainShape = ancestIt.Value();
586           break;
587         } else if ( type == TopAbs_SHELL ) {
588           mainShape = ancestIt.Value();
589         }
590       }
591     }
592     // find tgtFace in the main solid or shell to know it's true orientation.
593     TopExp_Explorer exp( mainShape, TopAbs_FACE );
594     for ( ; exp.More(); exp.Next() ) {
595       if ( tgtFace.IsSame( exp.Current() )) {
596         face = TopoDS::Face( exp.Current() );
597         break;
598       }
599     }
600   }
601   // Fix orientation
602   if ( SMESH_Algo::IsReversedSubMesh( face, meshDS ))
603   {
604     SMDS_ElemIteratorPtr eIt = meshDS->MeshElements( face )->GetElements();
605     while ( eIt->more() ) {
606       const SMDS_MeshElement* e = eIt->next();
607       if ( e->GetType() == SMDSAbs_Face && !editor.Reorient( e ))
608         RETURN_BAD_RESULT("Pb of SMESH_MeshEditor::Reorient()");
609     }
610   }
611
612   //cleaner.Release(); // do not remove mesh
613
614   return true;
615 }
616
617 //=============================================================================
618 /*!
619  * \brief Sets a default event listener to submesh of the source face
620   * \param subMesh - submesh where algo is set
621  *
622  * This method is called when a submesh gets HYP_OK algo_state.
623  * After being set, event listener is notified on each event of a submesh.
624  * Arranges that CLEAN event is translated from source submesh to
625  * the submesh
626  */
627 //=============================================================================
628
629 void StdMeshers_Projection_2D::SetEventListener(SMESH_subMesh* subMesh)
630 {
631   TAssocTool::SetEventListener( subMesh,
632                                 _sourceHypo->GetSourceFace(),
633                                 _sourceHypo->GetSourceMesh() );
634 }