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