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