Salome HOME
b4f3e3b5546ab8c426b877f7f7a36970b36dee59
[modules/smesh.git] / src / StdMeshers / StdMeshers_Projection_2D.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_Projection_2D.cxx
25 // Module    : SMESH
26 // Created   : Fri Oct 20 11:37:07 2006
27 // Author    : Edward AGAPOV (eap)
28 //
29 #include "StdMeshers_Projection_2D.hxx"
30
31 #include "StdMeshers_ProjectionSource2D.hxx"
32 #include "StdMeshers_ProjectionUtils.hxx"
33 #include "StdMeshers_FaceSide.hxx"
34
35 #include "SMDS_EdgePosition.hxx"
36 #include "SMDS_FacePosition.hxx"
37 #include "SMESHDS_Hypothesis.hxx"
38 #include "SMESHDS_SubMesh.hxx"
39 #include "SMESH_Block.hxx"
40 #include "SMESH_Comment.hxx"
41 #include "SMESH_Gen.hxx"
42 #include "SMESH_Mesh.hxx"
43 #include "SMESH_MesherHelper.hxx"
44 #include "SMESH_Pattern.hxx"
45 #include "SMESH_subMesh.hxx"
46 #include "SMESH_subMeshEventListener.hxx"
47
48 #include "utilities.h"
49
50 #include <BRepAdaptor_Surface.hxx>
51 #include <BRep_Tool.hxx>
52 #include <Bnd_B2d.hxx>
53 #include <GeomAPI_ProjectPointOnSurf.hxx>
54 #include <TopExp.hxx>
55 #include <TopExp_Explorer.hxx>
56 #include <TopTools_DataMapIteratorOfDataMapOfShapeShape.hxx>
57 #include <TopTools_ListIteratorOfListOfShape.hxx>
58 #include <TopoDS.hxx>
59 #include <gp_Ax2.hxx>
60 #include <gp_Ax3.hxx>
61
62
63 using namespace std;
64
65 #define RETURN_BAD_RESULT(msg) { MESSAGE(")-: Error: " << msg); return false; }
66
67 namespace TAssocTool = StdMeshers_ProjectionUtils;
68 //typedef StdMeshers_ProjectionUtils TAssocTool;
69
70 //=======================================================================
71 //function : StdMeshers_Projection_2D
72 //purpose  : 
73 //=======================================================================
74
75 StdMeshers_Projection_2D::StdMeshers_Projection_2D(int hypId, int studyId, SMESH_Gen* gen)
76   :SMESH_2D_Algo(hypId, studyId, gen)
77 {
78   _name = "Projection_2D";
79   _compatibleHypothesis.push_back("ProjectionSource2D");
80   _sourceHypo = 0;
81 }
82
83 //================================================================================
84 /*!
85  * \brief Destructor
86  */
87 //================================================================================
88
89 StdMeshers_Projection_2D::~StdMeshers_Projection_2D()
90 {}
91
92 //=======================================================================
93 //function : CheckHypothesis
94 //purpose  : 
95 //=======================================================================
96
97 bool StdMeshers_Projection_2D::CheckHypothesis(SMESH_Mesh&                          theMesh,
98                                                const TopoDS_Shape&                  theShape,
99                                                SMESH_Hypothesis::Hypothesis_Status& theStatus)
100 {
101   list <const SMESHDS_Hypothesis * >::const_iterator itl;
102
103   const list <const SMESHDS_Hypothesis * >&hyps = GetUsedHypothesis(theMesh, theShape);
104   if ( hyps.size() == 0 )
105   {
106     theStatus = HYP_MISSING;
107     return false;  // can't work with no hypothesis
108   }
109
110   if ( hyps.size() > 1 )
111   {
112     theStatus = HYP_ALREADY_EXIST;
113     return false;
114   }
115
116   const SMESHDS_Hypothesis *theHyp = hyps.front();
117
118   string hypName = theHyp->GetName();
119
120   theStatus = HYP_OK;
121
122   if (hypName == "ProjectionSource2D")
123   {
124     _sourceHypo = static_cast<const StdMeshers_ProjectionSource2D *>(theHyp);
125
126     // Check hypo parameters
127
128     SMESH_Mesh* srcMesh = _sourceHypo->GetSourceMesh();
129     SMESH_Mesh* tgtMesh = & theMesh;
130     if ( !srcMesh )
131       srcMesh = tgtMesh;
132
133     // check vertices
134     if ( _sourceHypo->HasVertexAssociation() )
135     {
136       // source vertices
137       TopoDS_Shape edge = TAssocTool::GetEdgeByVertices
138         ( srcMesh, _sourceHypo->GetSourceVertex(1), _sourceHypo->GetSourceVertex(2) );
139       if ( edge.IsNull() ||
140            !SMESH_MesherHelper::IsSubShape( edge, srcMesh ) ||
141            !SMESH_MesherHelper::IsSubShape( edge, _sourceHypo->GetSourceFace() ))
142       {
143         theStatus = HYP_BAD_PARAMETER;
144         error("Invalid source vertices");
145         SCRUTE((edge.IsNull()));
146         SCRUTE((SMESH_MesherHelper::IsSubShape( edge, srcMesh )));
147         SCRUTE((SMESH_MesherHelper::IsSubShape( edge, _sourceHypo->GetSourceFace() )));
148       }
149       else
150       {
151         // target vertices
152         edge = TAssocTool::GetEdgeByVertices
153           ( tgtMesh, _sourceHypo->GetTargetVertex(1), _sourceHypo->GetTargetVertex(2) );
154         if ( edge.IsNull() || !SMESH_MesherHelper::IsSubShape( edge, tgtMesh ))
155         {
156           theStatus = HYP_BAD_PARAMETER;
157           error("Invalid target vertices");
158           SCRUTE((edge.IsNull()));
159           SCRUTE((SMESH_MesherHelper::IsSubShape( edge, tgtMesh )));
160         }
161         // PAL16203
162         else if ( !_sourceHypo->IsCompoundSource() &&
163                   !SMESH_MesherHelper::IsSubShape( edge, theShape ))
164         {
165           theStatus = HYP_BAD_PARAMETER;
166           error("Invalid target vertices");
167           SCRUTE((SMESH_MesherHelper::IsSubShape( edge, theShape )));
168         }
169       }
170     }
171     // check a source face
172     if ( !SMESH_MesherHelper::IsSubShape( _sourceHypo->GetSourceFace(), srcMesh ) ||
173          ( srcMesh == tgtMesh && theShape == _sourceHypo->GetSourceFace() ))
174     {
175       theStatus = HYP_BAD_PARAMETER;
176       error("Invalid source face");
177       SCRUTE((SMESH_MesherHelper::IsSubShape( _sourceHypo->GetSourceFace(), srcMesh )));
178       SCRUTE((srcMesh == tgtMesh));
179       SCRUTE(( theShape == _sourceHypo->GetSourceFace() ));
180     }
181   }
182   else
183   {
184     theStatus = HYP_INCOMPATIBLE;
185   }
186   return ( theStatus == HYP_OK );
187 }
188
189 namespace {
190
191   //================================================================================
192   /*!
193    * \brief define if a node is new or old
194    * \param node - node to check
195    * \retval bool - true if the node existed before Compute() is called
196    */
197   //================================================================================
198
199   bool isOldNode( const SMDS_MeshNode* node )
200   {
201     // old nodes are shared by edges and new ones are shared
202     // only by faces created by mapper
203     //if ( is1DComputed )
204     {
205       bool isOld = node->NbInverseElements(SMDSAbs_Edge) > 0;
206       return isOld;
207     }
208     // else
209     // {
210     //   SMDS_ElemIteratorPtr invFace = node->GetInverseElementIterator(SMDSAbs_Face);
211     //   bool isNew = invFace->more();
212     //   return !isNew;
213     // }
214   }
215
216   //================================================================================
217   /*!
218    * \brief Class to remove mesh built by pattern mapper on edges
219    * and vertices in the case of failure of projection algo.
220    * It does it's job at destruction
221    */
222   //================================================================================
223
224   class MeshCleaner {
225     SMESH_subMesh* sm;
226   public:
227     MeshCleaner( SMESH_subMesh* faceSubMesh ): sm(faceSubMesh) {}
228     ~MeshCleaner() { Clean(sm); }
229     void Release() { sm = 0; } // mesh will not be removed
230     static void Clean( SMESH_subMesh* sm, bool withSub=true )
231     {
232       if ( !sm || !sm->GetSubMeshDS() ) return;
233       // PAL16567, 18920. Remove face nodes as well
234 //       switch ( sm->GetSubShape().ShapeType() ) {
235 //       case TopAbs_VERTEX:
236 //       case TopAbs_EDGE: {
237         SMDS_NodeIteratorPtr nIt = sm->GetSubMeshDS()->GetNodes();
238         SMESHDS_Mesh* mesh = sm->GetFather()->GetMeshDS();
239         while ( nIt->more() ) {
240           const SMDS_MeshNode* node = nIt->next();
241           if ( !isOldNode( node ) )
242             mesh->RemoveNode( node );
243         }
244         // do not break but iterate over DependsOn()
245 //       }
246 //       default:
247         if ( !withSub ) return;
248         SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator(false,false);
249         while ( smIt->more() )
250           Clean( smIt->next(), false );
251 //       }
252     }
253   };
254
255   //================================================================================
256   /*!
257    * \brief find new nodes belonging to one free border of mesh on face
258     * \param sm - submesh on edge or vertex containg nodes to choose from
259     * \param face - the face bound by the submesh
260     * \param u2nodes - map to fill with nodes
261     * \param seamNodes - set of found nodes
262     * \retval bool - is a success
263    */
264   //================================================================================
265
266   bool getBoundaryNodes ( SMESH_subMesh*                        sm,
267                           const TopoDS_Face&                    face,
268                           map< double, const SMDS_MeshNode* > & u2nodes,
269                           set< const SMDS_MeshNode* > &         seamNodes)
270   {
271     u2nodes.clear();
272     seamNodes.clear();
273     if ( !sm || !sm->GetSubMeshDS() )
274       RETURN_BAD_RESULT("Null submesh");
275
276     SMDS_NodeIteratorPtr nIt = sm->GetSubMeshDS()->GetNodes();
277     switch ( sm->GetSubShape().ShapeType() ) {
278
279     case TopAbs_VERTEX: {
280       while ( nIt->more() ) {
281         const SMDS_MeshNode* node = nIt->next();
282         if ( isOldNode( node ) ) continue;
283         u2nodes.insert( make_pair( 0., node ));
284         seamNodes.insert( node );
285         return true;
286       }
287       break;
288     }
289     case TopAbs_EDGE: {
290       
291       // Get submeshes of sub-vertices
292       const map< int, SMESH_subMesh * >& subSM = sm->DependsOn();
293       if ( subSM.size() != 2 )
294         RETURN_BAD_RESULT("there must be 2 submeshes of sub-vertices"
295                           " but we have " << subSM.size());
296       SMESH_subMesh* smV1 = subSM.begin()->second;
297       SMESH_subMesh* smV2 = subSM.rbegin()->second;
298       if ( !smV1->IsMeshComputed() || !smV2->IsMeshComputed() )
299         RETURN_BAD_RESULT("Empty vertex submeshes");
300
301       const SMDS_MeshNode* nV1 = 0;
302       const SMDS_MeshNode* nE = 0;
303
304       // Look for nV1 - a new node on V1
305       nIt = smV1->GetSubMeshDS()->GetNodes();
306       while ( nIt->more() && !nE ) {
307         const SMDS_MeshNode* node = nIt->next();
308         if ( isOldNode( node ) ) continue;
309         nV1 = node;
310
311         // Find nE - a new node connected to nV1 and belonging to edge submesh;
312         SMESHDS_SubMesh* smDS = sm->GetSubMeshDS();
313         SMDS_ElemIteratorPtr vElems = nV1->GetInverseElementIterator(SMDSAbs_Face);
314         while ( vElems->more() && !nE ) {
315           const SMDS_MeshElement* elem = vElems->next();
316           int nbNodes = elem->NbNodes();
317           if ( elem->IsQuadratic() )
318             nbNodes /= 2;
319           int iV1 = elem->GetNodeIndex( nV1 );
320           // try next after nV1
321           int iE = SMESH_MesherHelper::WrapIndex( iV1 + 1, nbNodes );
322           if ( smDS->Contains( elem->GetNode( iE ) ))
323             nE = elem->GetNode( iE );
324           if ( !nE ) {
325             // try node before nV1
326             iE = SMESH_MesherHelper::WrapIndex( iV1 - 1, nbNodes );
327             if ( smDS->Contains( elem->GetNode( iE )))
328               nE = elem->GetNode( iE );
329           }
330           if ( nE && elem->IsQuadratic() ) { // find medium node between nV1 and nE
331             if ( Abs( iV1 - iE ) == 1 )
332               nE = elem->GetNode( Min ( iV1, iE ) + nbNodes );
333             else
334               nE = elem->GetNode( elem->NbNodes() - 1 );
335           }
336         }
337       }
338       if ( !nV1 )
339         RETURN_BAD_RESULT("No new node found on V1");
340       if ( !nE )
341         RETURN_BAD_RESULT("new node on edge not found");
342
343       // Get the whole free border of a face
344       list< const SMDS_MeshNode* > bordNodes;
345       list< const SMDS_MeshElement* > bordFaces;
346       if ( !SMESH_MeshEditor::FindFreeBorder (nV1, nE, nV1, bordNodes, bordFaces ))
347         RETURN_BAD_RESULT("free border of a face not found by nodes " <<
348                           nV1->GetID() << " " << nE->GetID() );
349
350       // Insert nodes of the free border to the map until node on V2 encountered
351       SMESHDS_SubMesh* v2smDS = smV2->GetSubMeshDS();
352       list< const SMDS_MeshNode* >::iterator bordIt = bordNodes.begin();
353       bordIt++; // skip nV1
354       for ( ; bordIt != bordNodes.end(); ++bordIt ) {
355         const SMDS_MeshNode* node = *bordIt;
356         if ( v2smDS->Contains( node ))
357           break;
358         if ( node->GetPosition()->GetTypeOfPosition() != SMDS_TOP_EDGE )
359           RETURN_BAD_RESULT("Bad node position type: node " << node->GetID() <<
360                             " pos type " << node->GetPosition()->GetTypeOfPosition());
361         const SMDS_EdgePosition* pos =
362           static_cast<const SMDS_EdgePosition*>(node->GetPosition());
363         u2nodes.insert( make_pair( pos->GetUParameter(), node ));
364         seamNodes.insert( node );
365       }
366       if ( u2nodes.size() != seamNodes.size() )
367         RETURN_BAD_RESULT("Bad node params on edge " << sm->GetId() <<
368                           ", " << u2nodes.size() << " != " << seamNodes.size() );
369       return true;
370     }
371     default:;
372     }
373     RETURN_BAD_RESULT ("Unexpected submesh type");
374
375   } // bool getBoundaryNodes()
376
377   //================================================================================
378   /*!
379    * \brief Check if two consecutive EDGEs are connected in 2D
380    *  \param [in] E1 - a well oriented non-seam EDGE
381    *  \param [in] E2 - a possibly well oriented seam EDGE
382    *  \param [in] F - a FACE
383    *  \return bool - result
384    */
385   //================================================================================
386
387   bool are2dConnected( const TopoDS_Edge & E1,
388                        const TopoDS_Edge & E2,
389                        const TopoDS_Face & F )
390   {
391     double f,l;
392     Handle(Geom2d_Curve) c1 = BRep_Tool::CurveOnSurface( E1, F, f, l );
393     gp_Pnt2d uvLast1 = c1->Value( E1.Orientation() == TopAbs_REVERSED ? f : l );
394
395     Handle(Geom2d_Curve) c2 = BRep_Tool::CurveOnSurface( E2, F, f, l );
396     gp_Pnt2d uvFirst2 = c2->Value( f );
397     gp_Pnt2d uvLast2  = c2->Value( l );
398     double tol2 = 1e-5 * uvLast2.SquareDistance( uvFirst2 );
399
400     return (( uvLast1.SquareDistance( uvFirst2 ) < tol2 ) ||
401             ( uvLast1.SquareDistance( uvLast2 ) < tol2 ));
402   }
403
404   //================================================================================
405   /*!
406    * \brief Compose TSideVector for both FACEs keeping matching order of EDGEs
407    *        and fill src2tgtNodes map
408    */
409   //================================================================================
410
411   TError getWires(const TopoDS_Face&                 tgtFace,
412                   const TopoDS_Face&                 srcFace,
413                   SMESH_Mesh *                       tgtMesh,
414                   SMESH_Mesh *                       srcMesh,
415                   const TAssocTool::TShapeShapeMap&  shape2ShapeMap,
416                   TSideVector&                       srcWires,
417                   TSideVector&                       tgtWires,
418                   TAssocTool::TNodeNodeMap&          src2tgtNodes,
419                   bool&                              is1DComputed)
420   {
421     SMESHDS_Mesh* tgtMeshDS = tgtMesh->GetMeshDS();
422     SMESHDS_Mesh* srcMeshDS = srcMesh->GetMeshDS();
423
424     src2tgtNodes.clear();
425
426     // get ordered src EDGEs
427     TError err;
428     srcWires = StdMeshers_FaceSide::GetFaceWires( srcFace, *srcMesh,/*skipMediumNodes=*/0, err);
429     if ( err && !err->IsOK() )
430       return err;
431
432     // make corresponding sequence of tgt EDGEs
433     tgtWires.resize( srcWires.size() );
434     for ( size_t iW = 0; iW < srcWires.size(); ++iW )
435     {
436       list< TopoDS_Edge > tgtEdges;
437       StdMeshers_FaceSidePtr srcWire = srcWires[iW];
438       TopTools_IndexedMapOfShape edgeMap; // to detect seam edges
439       for ( int iE = 0; iE < srcWire->NbEdges(); ++iE )
440       {
441         TopoDS_Edge     srcE = srcWire->Edge( iE );
442         TopoDS_Edge     tgtE = TopoDS::Edge( shape2ShapeMap( srcE, /*isSrc=*/true));
443         TopoDS_Shape srcEbis = shape2ShapeMap( tgtE, /*isSrc=*/false );
444         if ( srcE.Orientation() != srcEbis.Orientation() )
445           tgtE.Reverse();
446         // reverse a seam edge encountered for the second time
447         const int index = edgeMap.Add( tgtE );
448         if ( index < edgeMap.Extent() ) // E is a seam
449         {
450           // check which of edges to reverse, E or one already being in tgtEdges
451           if ( are2dConnected( tgtEdges.back(), tgtE, tgtFace ))
452           {
453             list< TopoDS_Edge >::iterator eIt = tgtEdges.begin();
454             std::advance( eIt, index-1 );
455             eIt->Reverse();
456           }
457           else
458           {
459             tgtE.Reverse();
460           }
461         }
462         if ( srcWire->NbEdges() == 1 && tgtMesh == srcMesh ) // circle
463         {
464           // try to verify ori by propagation
465           pair<int,TopoDS_Edge> nE =
466             StdMeshers_ProjectionUtils::GetPropagationEdge( srcMesh, tgtE, srcE );
467           if ( !nE.second.IsNull() )
468             tgtE = nE.second;
469         }
470         tgtEdges.push_back( tgtE );
471
472
473         // Fill map of src to tgt nodes with nodes on edges
474
475         if ( srcMesh->GetSubMesh( srcE )->IsEmpty() ||
476              tgtMesh->GetSubMesh( tgtE )->IsEmpty() )
477         {
478           // add nodes on VERTEXes for a case of not meshes EDGEs
479           const TopoDS_Shape&  srcV = SMESH_MesherHelper::IthVertex( 0, srcE );
480           const TopoDS_Shape&  tgtV = shape2ShapeMap( srcV, /*isSrc=*/true );
481           const SMDS_MeshNode* srcN = SMESH_Algo::VertexNode( TopoDS::Vertex( srcV ), srcMeshDS );
482           const SMDS_MeshNode* tgtN = SMESH_Algo::VertexNode( TopoDS::Vertex( tgtV ), tgtMeshDS );
483           if ( srcN && tgtN )
484             src2tgtNodes.insert( make_pair( srcN, tgtN ));
485         }
486         else
487         {
488           map< double, const SMDS_MeshNode* > srcNodes, tgtNodes;
489           if (( ! SMESH_Algo::GetSortedNodesOnEdge( srcMeshDS, TopoDS::Edge( srcE ),
490                                                     /*ignoreMediumNodes = */true,
491                                                     srcNodes ))
492               ||
493               ( ! SMESH_Algo::GetSortedNodesOnEdge( tgtMeshDS, TopoDS::Edge( tgtE ),
494                                                     /*ignoreMediumNodes = */true,
495                                                     tgtNodes ))
496               )
497             return SMESH_ComputeError::New( COMPERR_BAD_INPUT_MESH,
498                                             "Invalid node parameters on edges");
499
500           if (( srcNodes.size() != tgtNodes.size() ) && tgtNodes.size() > 0 )
501             return SMESH_ComputeError::New( COMPERR_BAD_INPUT_MESH,
502                                             "Different number of nodes on edges");
503           if ( !tgtNodes.empty() )
504           {
505             map< double, const SMDS_MeshNode* >::iterator u_tn = tgtNodes.begin();
506             map< double, const SMDS_MeshNode* >::iterator u_sn = srcNodes.begin();
507             for ( ; u_tn != tgtNodes.end(); ++u_tn, ++u_sn)
508               src2tgtNodes.insert( make_pair( u_sn->second, u_tn->second ));
509
510             is1DComputed = true;
511           }
512         }
513       } // loop on EDGEs of a WIRE
514
515       tgtWires[ iW ].reset( new StdMeshers_FaceSide( tgtFace, tgtEdges, tgtMesh,
516                                                      /*theIsForward = */ true,
517                                                      /*theIgnoreMediumNodes = */false));
518     } // loop on WIREs
519
520     return TError();
521   }
522
523   //================================================================================
524   /*!
525    * \brief Preform projection in case if tgtFace.IsPartner( srcFace ) and in case
526    * if projection by 3D transformation is possible
527    */
528   //================================================================================
529
530   bool projectPartner(const TopoDS_Face&                 tgtFace,
531                       const TopoDS_Face&                 srcFace,
532                       const TSideVector&                 tgtWires,
533                       const TSideVector&                 srcWires,
534                       const TAssocTool::TShapeShapeMap&  shape2ShapeMap,
535                       TAssocTool::TNodeNodeMap&          src2tgtNodes,
536                       const bool                         is1DComputed)
537   {
538     SMESH_Mesh *    tgtMesh = tgtWires[0]->GetMesh();
539     SMESH_Mesh *    srcMesh = srcWires[0]->GetMesh();
540     SMESHDS_Mesh* tgtMeshDS = tgtMesh->GetMeshDS();
541     SMESHDS_Mesh* srcMeshDS = srcMesh->GetMeshDS();
542     SMESH_MesherHelper helper( *tgtMesh );
543
544     const double tol = 1.e-7 * srcMeshDS->getMaxDim();
545
546     // transformation to get location of target nodes from source ones
547     StdMeshers_ProjectionUtils::TrsfFinder3D trsf;
548     if ( tgtFace.IsPartner( srcFace ))
549     {
550       gp_Trsf srcTrsf = srcFace.Location();
551       gp_Trsf tgtTrsf = tgtFace.Location();
552       trsf.Set( srcTrsf.Inverted() * tgtTrsf );
553     }
554     else
555     {
556       // Try to find the 3D transformation
557
558       const int totNbSeg = 50;
559       vector< gp_XYZ > srcPnts, tgtPnts;
560       srcPnts.reserve( totNbSeg );
561       tgtPnts.reserve( totNbSeg );
562       for ( size_t iW = 0; iW < srcWires.size(); ++iW )
563       {
564         const double minSegLen = srcWires[iW]->Length() / totNbSeg;
565         for ( int iE = 0; iE < srcWires[iW]->NbEdges(); ++iE )
566         {
567           int nbSeg    = Max( 1, int( srcWires[iW]->EdgeLength( iE ) / minSegLen ));
568           double srcU  = srcWires[iW]->FirstParameter( iE );
569           double tgtU  = tgtWires[iW]->FirstParameter( iE );
570           double srcDu = ( srcWires[iW]->LastParameter( iE )- srcU ) / nbSeg;
571           double tgtDu = ( tgtWires[iW]->LastParameter( iE )- tgtU ) / nbSeg;
572           for ( size_t i = 0; i < nbSeg; ++i  )
573           {
574             srcPnts.push_back( srcWires[iW]->Value3d( srcU ).XYZ() );
575             tgtPnts.push_back( tgtWires[iW]->Value3d( tgtU ).XYZ() );
576             srcU += srcDu;
577             tgtU += tgtDu;
578           }
579         }
580       }
581       if ( !trsf.Solve( srcPnts, tgtPnts ))
582         return false;
583
584       // check trsf
585
586       bool trsfIsOK = true;
587       const int nbTestPnt = 20;
588       const size_t  iStep = Max( 1, int( srcPnts.size() / nbTestPnt ));
589       // check boundary
590       for ( size_t i = 0; ( i < srcPnts.size() && trsfIsOK ); i += iStep )
591       {
592         gp_Pnt trsfTgt = trsf.Transform( srcPnts[i] );
593         trsfIsOK = ( trsfTgt.SquareDistance( tgtPnts[i] ) < tol*tol );
594       }
595       // check an in-FACE point
596       if ( trsfIsOK )
597       {
598         BRepAdaptor_Surface srcSurf( srcFace );
599         gp_Pnt srcP =
600           srcSurf.Value( 0.5 * ( srcSurf.FirstUParameter() + srcSurf.LastUParameter() ),
601                          0.5 * ( srcSurf.FirstVParameter() + srcSurf.LastVParameter() ));
602         gp_Pnt tgtTrsfP = trsf.Transform( srcP );
603         TopLoc_Location loc;
604         GeomAPI_ProjectPointOnSurf& proj = helper.GetProjector( tgtFace, loc, 0.1*tol );
605         if ( !loc.IsIdentity() )
606           tgtTrsfP.Transform( loc.Transformation().Inverted() );
607         proj.Perform( tgtTrsfP );
608         trsfIsOK = ( proj.IsDone() &&
609                      proj.NbPoints() > 0 &&
610                      proj.LowerDistance() < tol );
611       }
612       if ( !trsfIsOK )
613         return false;
614     }
615
616     // Make new faces
617
618     // prepare the helper to adding quadratic elements if necessary
619     helper.SetSubShape( tgtFace );
620     helper.IsQuadraticSubMesh( tgtFace );
621
622     SMESHDS_SubMesh* srcSubDS = srcMeshDS->MeshElements( srcFace );
623     if ( !is1DComputed && srcSubDS->NbElements() )
624       helper.SetIsQuadratic( srcSubDS->GetElements()->next()->IsQuadratic() );
625
626     SMESH_MesherHelper srcHelper( *srcMesh );
627     srcHelper.SetSubShape( srcFace );
628
629     const SMDS_MeshNode* nullNode = 0;
630     TAssocTool::TNodeNodeMap::iterator srcN_tgtN;
631
632     // indices of nodes to create properly oriented faces
633     bool isReverse = ( !trsf.IsIdentity() );
634     int tri1 = 1, tri2 = 2, quad1 = 1, quad3 = 3;
635     if ( isReverse )
636       std::swap( tri1, tri2 ), std::swap( quad1, quad3 );
637
638     SMDS_ElemIteratorPtr elemIt = srcSubDS->GetElements();
639     vector< const SMDS_MeshNode* > tgtNodes;
640     while ( elemIt->more() ) // loop on all mesh faces on srcFace
641     {
642       const SMDS_MeshElement* elem = elemIt->next();
643       const int nbN = elem->NbCornerNodes(); 
644       tgtNodes.resize( nbN );
645       helper.SetElementsOnShape( false );
646       for ( int i = 0; i < nbN; ++i ) // loop on nodes of the source element
647       {
648         const SMDS_MeshNode* srcNode = elem->GetNode(i);
649         srcN_tgtN = src2tgtNodes.insert( make_pair( srcNode, nullNode )).first;
650         if ( srcN_tgtN->second == nullNode )
651         {
652           // create a new node
653           gp_Pnt tgtP = trsf.Transform( SMESH_TNodeXYZ( srcNode ));
654           SMDS_MeshNode* n = helper.AddNode( tgtP.X(), tgtP.Y(), tgtP.Z() );
655           srcN_tgtN->second = n;
656           switch ( srcNode->GetPosition()->GetTypeOfPosition() )
657           {
658           case SMDS_TOP_FACE:
659           {
660             gp_Pnt2d srcUV = srcHelper.GetNodeUV( srcFace, srcNode );
661             tgtMeshDS->SetNodeOnFace( n, helper.GetSubShapeID(), srcUV.X(), srcUV.Y() );
662             break;
663           }
664           case SMDS_TOP_EDGE:
665           {
666             const TopoDS_Shape & srcE = srcMeshDS->IndexToShape( srcNode->getshapeId() );
667             const TopoDS_Shape & tgtE = shape2ShapeMap( srcE, /*isSrc=*/true );
668             double srcU = srcHelper.GetNodeU( TopoDS::Edge( srcE ), srcNode );
669             tgtMeshDS->SetNodeOnEdge( n, TopoDS::Edge( tgtE ), srcU );
670             break;
671           }
672           case SMDS_TOP_VERTEX:
673           {
674             const TopoDS_Shape & srcV = srcMeshDS->IndexToShape( srcNode->getshapeId() );
675             const TopoDS_Shape & tgtV = shape2ShapeMap( srcV, /*isSrc=*/true );
676             tgtMeshDS->SetNodeOnVertex( n, TopoDS::Vertex( tgtV ));
677             break;
678           }
679           default:;
680           }
681         }
682         tgtNodes[i] = srcN_tgtN->second;
683       }
684       // create a new face
685       helper.SetElementsOnShape( true );
686       switch ( nbN )
687       {
688       case 3: helper.AddFace(tgtNodes[0], tgtNodes[tri1], tgtNodes[tri2]); break;
689       case 4: helper.AddFace(tgtNodes[0], tgtNodes[quad1], tgtNodes[2], tgtNodes[quad3]); break;
690       default:
691         if ( isReverse ) std::reverse( tgtNodes.begin(), tgtNodes.end() );
692         helper.AddPolygonalFace( tgtNodes );
693       }
694     }
695
696     // check node positions
697
698     if ( !tgtFace.IsPartner( srcFace ) )
699     {
700       int nbOkPos = 0;
701       const double tol2d = 1e-12;
702       srcN_tgtN = src2tgtNodes.begin();
703       for ( ; srcN_tgtN != src2tgtNodes.end(); ++srcN_tgtN )
704       {
705         const SMDS_MeshNode* n = srcN_tgtN->second;
706         switch ( n->GetPosition()->GetTypeOfPosition() )
707         {
708         case SMDS_TOP_FACE:
709         {
710           gp_XY uv = helper.GetNodeUV( tgtFace, n ), uvBis = uv;
711           if (( helper.CheckNodeUV( tgtFace, n, uv, tol )) &&
712               (( uv - uvBis ).SquareModulus() < tol2d )    &&
713               ( ++nbOkPos > 10 ))
714             return true;
715           else
716             nbOkPos = 0;
717           break;
718         }
719         case SMDS_TOP_EDGE:
720         {
721           const TopoDS_Edge & tgtE = TopoDS::Edge( tgtMeshDS->IndexToShape( n->getshapeId() ));
722           double u = helper.GetNodeU( tgtE, n ), uBis = u;
723           if (( !helper.CheckNodeU( tgtE, n, u, tol )) ||
724               (( u - uBis ) < tol2d ))
725             nbOkPos = 0;
726           break;
727         }
728         default:;
729         }
730       }
731     }
732
733     return true;
734
735   } //   bool projectPartner()
736
737   //================================================================================
738   /*!
739    * \brief Preform projection in case if the faces are similar in 2D space
740    */
741   //================================================================================
742
743   bool projectBy2DSimilarity(const TopoDS_Face&                 tgtFace,
744                              const TopoDS_Face&                 srcFace,
745                              const TSideVector&                 tgtWires,
746                              const TSideVector&                 srcWires,
747                              const TAssocTool::TShapeShapeMap&  shape2ShapeMap,
748                              TAssocTool::TNodeNodeMap&          src2tgtNodes,
749                              const bool                         is1DComputed)
750   {
751     SMESH_Mesh * tgtMesh = tgtWires[0]->GetMesh();
752     SMESH_Mesh * srcMesh = srcWires[0]->GetMesh();
753
754     // WARNING: we can have problems if the FACE is symmetrical in 2D,
755     // then the projection can be mirrored relating to what is expected
756
757     // 1) Find 2D transformation
758
759     StdMeshers_ProjectionUtils::TrsfFinder2D trsf;
760     {
761       // get 2 pairs of corresponding UVs
762       gp_Pnt2d srcP0 = srcWires[0]->Value2d(0.0);
763       gp_Pnt2d srcP1 = srcWires[0]->Value2d(0.333);
764       gp_Pnt2d tgtP0 = tgtWires[0]->Value2d(0.0);
765       gp_Pnt2d tgtP1 = tgtWires[0]->Value2d(0.333);
766
767       // make transformation
768       gp_Trsf2d fromTgtCS, toSrcCS; // from/to global CS
769       gp_Ax2d srcCS( srcP0, gp_Vec2d( srcP0, srcP1 ));
770       gp_Ax2d tgtCS( tgtP0, gp_Vec2d( tgtP0, tgtP1 ));
771       toSrcCS  .SetTransformation( srcCS );
772       fromTgtCS.SetTransformation( tgtCS );
773       fromTgtCS.Invert();
774       trsf.Set( fromTgtCS * toSrcCS );
775
776       // check transformation
777       bool trsfIsOK = true;
778       const double tol = 1e-5 * gp_Vec2d( srcP0, srcP1 ).Magnitude();
779       for ( double u = 0.12; ( u < 1. && trsfIsOK ); u += 0.1 )
780       {
781         gp_Pnt2d srcUV  = srcWires[0]->Value2d( u );
782         gp_Pnt2d tgtUV  = tgtWires[0]->Value2d( u );
783         gp_Pnt2d tgtUV2 = trsf.Transform( srcUV );
784         trsfIsOK = ( tgtUV.Distance( tgtUV2 ) < tol );
785       }
786
787       // Find trsf using a least-square approximation
788       if ( !trsfIsOK )
789       {
790         // find trsf
791         const int totNbSeg = 50;
792         vector< gp_XY > srcPnts, tgtPnts;
793         srcPnts.resize( totNbSeg );
794         tgtPnts.resize( totNbSeg );
795         for ( size_t iW = 0; iW < srcWires.size(); ++iW )
796         {
797           const double minSegLen = srcWires[iW]->Length() / totNbSeg;
798           for ( int iE = 0; iE < srcWires[iW]->NbEdges(); ++iE )
799           {
800             int nbSeg    = Max( 1, int( srcWires[iW]->EdgeLength( iE ) / minSegLen ));
801             double srcU  = srcWires[iW]->FirstParameter( iE );
802             double tgtU  = tgtWires[iW]->FirstParameter( iE );
803             double srcDu = ( srcWires[iW]->LastParameter( iE )- srcU ) / nbSeg;
804             double tgtDu = ( tgtWires[iW]->LastParameter( iE )- tgtU ) / nbSeg;
805             for ( size_t i = 0; i < nbSeg; ++i, srcU += srcDu, tgtU += tgtDu  )
806             {
807               srcPnts.push_back( srcWires[iW]->Value2d( srcU ).XY() );
808               tgtPnts.push_back( tgtWires[iW]->Value2d( tgtU ).XY() );
809             }
810           }
811         }
812         if ( !trsf.Solve( srcPnts, tgtPnts ))
813           return false;
814
815         // check trsf
816
817         trsfIsOK = true;
818         const int nbTestPnt = 10;
819         const size_t  iStep = Max( 1, int( srcPnts.size() / nbTestPnt ));
820         for ( size_t i = 0; ( i < srcPnts.size() && trsfIsOK ); i += iStep )
821         {
822           gp_Pnt2d trsfTgt = trsf.Transform( srcPnts[i] );
823           trsfIsOK = ( trsfTgt.Distance( tgtPnts[i] ) < tol );
824         }
825         if ( !trsfIsOK )
826           return false;
827       }
828     } // "Find transformation" block
829
830     // 2) Projection
831
832     SMESHDS_SubMesh* srcSubDS = srcMesh->GetMeshDS()->MeshElements( srcFace );
833
834     SMESH_MesherHelper helper( *tgtMesh );
835     helper.SetSubShape( tgtFace );
836     if ( is1DComputed )
837       helper.IsQuadraticSubMesh( tgtFace );
838     else
839       helper.SetIsQuadratic( srcSubDS->GetElements()->next()->IsQuadratic() );
840     helper.SetElementsOnShape( true );
841     Handle(Geom_Surface) tgtSurface = BRep_Tool::Surface( tgtFace );
842     SMESHDS_Mesh* tgtMeshDS = tgtMesh->GetMeshDS();
843
844     SMESH_MesherHelper srcHelper( *srcMesh );
845     srcHelper.SetSubShape( srcFace );
846
847     const SMDS_MeshNode* nullNode = 0;
848     TAssocTool::TNodeNodeMap::iterator srcN_tgtN;
849
850     SMDS_ElemIteratorPtr elemIt = srcSubDS->GetElements();
851     vector< const SMDS_MeshNode* > tgtNodes;
852     bool uvOK;
853     while ( elemIt->more() ) // loop on all mesh faces on srcFace
854     {
855       const SMDS_MeshElement* elem = elemIt->next();
856       const int nbN = elem->NbCornerNodes(); 
857       tgtNodes.resize( nbN );
858       for ( int i = 0; i < nbN; ++i ) // loop on nodes of the source element
859       {
860         const SMDS_MeshNode* srcNode = elem->GetNode(i);
861         srcN_tgtN = src2tgtNodes.insert( make_pair( srcNode, nullNode )).first;
862         if ( srcN_tgtN->second == nullNode )
863         {
864           // create a new node
865           gp_Pnt2d srcUV = srcHelper.GetNodeUV( srcFace, srcNode,
866                                                 elem->GetNode( helper.WrapIndex(i+1,nbN)), &uvOK);
867           gp_Pnt2d   tgtUV = trsf.Transform( srcUV );
868           gp_Pnt      tgtP = tgtSurface->Value( tgtUV.X(), tgtUV.Y() );
869           SMDS_MeshNode* n = tgtMeshDS->AddNode( tgtP.X(), tgtP.Y(), tgtP.Z() );
870           switch ( srcNode->GetPosition()->GetTypeOfPosition() )
871           {
872           case SMDS_TOP_FACE: {
873             tgtMeshDS->SetNodeOnFace( n, helper.GetSubShapeID(), tgtUV.X(), tgtUV.Y() );
874             break;
875           }
876           case SMDS_TOP_EDGE: {
877             TopoDS_Shape srcEdge = srcHelper.GetSubShapeByNode( srcNode, srcHelper.GetMeshDS() );
878             TopoDS_Edge  tgtEdge = TopoDS::Edge( shape2ShapeMap( srcEdge, /*isSrc=*/true ));
879             tgtMeshDS->SetNodeOnEdge( n, TopoDS::Edge( tgtEdge ));
880             double U = srcHelper.GetNodeU( TopoDS::Edge( srcEdge ), srcNode );
881             helper.CheckNodeU( tgtEdge, n, U, Precision::PConfusion());
882             n->SetPosition(SMDS_PositionPtr(new SMDS_EdgePosition( U )));
883             break;
884           }
885           case SMDS_TOP_VERTEX: {
886             TopoDS_Shape srcV = srcHelper.GetSubShapeByNode( srcNode, srcHelper.GetMeshDS() );
887             TopoDS_Shape tgtV = shape2ShapeMap( srcV, /*isSrc=*/true );
888             tgtMeshDS->SetNodeOnVertex( n, TopoDS::Vertex( tgtV ));
889             break;
890           }
891           }
892           srcN_tgtN->second = n;
893         }
894         tgtNodes[i] = srcN_tgtN->second;
895       }
896       // create a new face (with reversed orientation)
897       switch ( nbN )
898       {
899       case 3: helper.AddFace(tgtNodes[0], tgtNodes[2], tgtNodes[1]); break;
900       case 4: helper.AddFace(tgtNodes[0], tgtNodes[3], tgtNodes[2], tgtNodes[1]); break;
901       }
902     }
903     return true;
904
905   } // bool projectBy2DSimilarity(...)
906
907   //================================================================================
908   /*!
909    * \brief Fix bad faces by smoothing
910    */
911   //================================================================================
912
913   void fixDistortedFaces( SMESH_MesherHelper& helper,
914                           TSideVector&        )
915   {
916     // Detect bad faces
917
918     bool haveBadFaces = false;
919
920     const TopoDS_Face&  F = TopoDS::Face( helper.GetSubShape() );
921     SMESHDS_SubMesh* smDS = helper.GetMeshDS()->MeshElements( F );
922     if ( !smDS || smDS->NbElements() == 0 ) return;
923
924     SMDS_ElemIteratorPtr faceIt = smDS->GetElements();
925     double prevArea2D = 0;
926     vector< const SMDS_MeshNode* > nodes;
927     vector< gp_XY >                uv;
928     while ( faceIt->more() && !haveBadFaces )
929     {
930       const SMDS_MeshElement* face = faceIt->next();
931
932       // get nodes
933       nodes.resize( face->NbCornerNodes() );
934       SMDS_MeshElement::iterator n = face->begin_nodes();
935       for ( size_t i = 0; i < nodes.size(); ++n, ++i )
936         nodes[ i ] = *n;
937
938       // get UVs
939       const SMDS_MeshNode* inFaceNode = 0;
940       if ( helper.HasSeam() )
941         for ( size_t i = 0; ( i < nodes.size() && !inFaceNode ); ++i )
942           if ( !helper.IsSeamShape( nodes[ i ]->getshapeId() ))
943             inFaceNode = nodes[ i ];
944
945       uv.resize( nodes.size() );
946       for ( size_t i = 0; i < nodes.size(); ++i )
947         uv[ i ] = helper.GetNodeUV( F, nodes[ i ], inFaceNode );
948
949       // compare orientation of triangles
950       for ( int iT = 0, nbT = nodes.size()-2; iT < nbT; ++iT )
951       {
952         gp_XY v1 = uv[ iT+1 ] - uv[ 0 ];
953         gp_XY v2 = uv[ iT+2 ] - uv[ 0 ];
954         double area2D = v2 ^ v1;
955         if (( haveBadFaces = ( area2D * prevArea2D < 0 )))
956           break;
957         prevArea2D = area2D;
958       }
959     }
960
961     // Fix faces
962
963     if ( haveBadFaces )
964     {
965       SMESH_MeshEditor editor( helper.GetMesh() );
966
967       TIDSortedElemSet faces;
968       for ( faceIt = smDS->GetElements(); faceIt->more(); )
969         faces.insert( faces.end(), faceIt->next() );
970
971       // choose smoothing algo
972       //SMESH_MeshEditor:: SmoothMethod algo = SMESH_MeshEditor::CENTROIDAL;
973       bool isConcaveBoundary = false;
974       TError err;
975       TSideVector tgtWires =
976         StdMeshers_FaceSide::GetFaceWires( F, *helper.GetMesh(),/*skipMediumNodes=*/0, err);
977       for ( size_t iW = 0; iW < tgtWires.size() && !isConcaveBoundary; ++iW )
978       {
979         TopoDS_Edge prevEdge = tgtWires[iW]->Edge( tgtWires[iW]->NbEdges() - 1 );
980         for ( int iE = 0; iE < tgtWires[iW]->NbEdges() && !isConcaveBoundary; ++iE )
981         {
982           double angle = helper.GetAngle( prevEdge, tgtWires[iW]->Edge( iE ),
983                                           F,        tgtWires[iW]->FirstVertex( iE ));
984           isConcaveBoundary = ( angle < -5. * M_PI / 180. );
985
986           prevEdge = tgtWires[iW]->Edge( iE );
987         }
988       }
989       SMESH_MeshEditor:: SmoothMethod algo =
990         isConcaveBoundary ? SMESH_MeshEditor::CENTROIDAL : SMESH_MeshEditor::LAPLACIAN;
991
992       // smoothing
993       set<const SMDS_MeshNode*> fixedNodes;
994       editor.Smooth( faces, fixedNodes, algo, /*nbIterations=*/ 10,
995                      /*theTgtAspectRatio=*/1.0, /*the2D=*/false);
996     }
997   }
998
999 } // namespace
1000
1001
1002 //=======================================================================
1003 //function : Compute
1004 //purpose  :
1005 //=======================================================================
1006
1007 bool StdMeshers_Projection_2D::Compute(SMESH_Mesh& theMesh, const TopoDS_Shape& theShape)
1008 {
1009   _src2tgtNodes.clear();
1010
1011   MESSAGE("Projection_2D Compute");
1012   if ( !_sourceHypo )
1013     return false;
1014
1015   SMESH_Mesh * srcMesh = _sourceHypo->GetSourceMesh();
1016   SMESH_Mesh * tgtMesh = & theMesh;
1017   if ( !srcMesh )
1018     srcMesh = tgtMesh;
1019
1020   SMESHDS_Mesh * meshDS = theMesh.GetMeshDS();
1021
1022   // ---------------------------
1023   // Make sub-shapes association
1024   // ---------------------------
1025
1026   TopoDS_Face   tgtFace = TopoDS::Face( theShape.Oriented(TopAbs_FORWARD));
1027   TopoDS_Shape srcShape = _sourceHypo->GetSourceFace().Oriented(TopAbs_FORWARD);
1028
1029   TAssocTool::TShapeShapeMap shape2ShapeMap;
1030   TAssocTool::InitVertexAssociation( _sourceHypo, shape2ShapeMap );
1031   if ( !TAssocTool::FindSubShapeAssociation( tgtFace, tgtMesh, srcShape, srcMesh,
1032                                              shape2ShapeMap)  ||
1033        !shape2ShapeMap.IsBound( tgtFace ))
1034   {
1035     if ( srcShape.ShapeType() == TopAbs_FACE )
1036     {
1037       int nbE1 = SMESH_MesherHelper::Count( tgtFace, TopAbs_EDGE, /*ignoreSame=*/true );
1038       int nbE2 = SMESH_MesherHelper::Count( srcShape, TopAbs_EDGE, /*ignoreSame=*/true );
1039       if ( nbE1 != nbE2 )
1040         return error(COMPERR_BAD_SHAPE,
1041                      SMESH_Comment("Different number of edges in source and target faces: ")
1042                      << nbE2 << " and " << nbE1 );
1043     }
1044     return error(COMPERR_BAD_SHAPE,"Topology of source and target faces seems different" );
1045   }
1046   TopoDS_Face srcFace = TopoDS::Face( shape2ShapeMap( tgtFace ).Oriented(TopAbs_FORWARD));
1047
1048   // ----------------------------------------------
1049   // Assure that mesh on a source Face is computed
1050   // ----------------------------------------------
1051
1052   SMESH_subMesh* srcSubMesh = srcMesh->GetSubMesh( srcFace );
1053   SMESH_subMesh* tgtSubMesh = tgtMesh->GetSubMesh( tgtFace );
1054
1055   string srcMeshError;
1056   if ( tgtMesh == srcMesh ) {
1057     if ( !TAssocTool::MakeComputed( srcSubMesh ))
1058       srcMeshError = TAssocTool::SourceNotComputedError( srcSubMesh, this );
1059   }
1060   else {
1061     if ( !srcSubMesh->IsMeshComputed() )
1062       srcMeshError = TAssocTool::SourceNotComputedError();
1063   }
1064   if ( !srcMeshError.empty() )
1065     return error(COMPERR_BAD_INPUT_MESH, srcMeshError );
1066
1067   // ===========
1068   // Projection
1069   // ===========
1070
1071   // get ordered src and tgt EDGEs
1072   TSideVector srcWires, tgtWires;
1073   bool is1DComputed = false; // if any tgt EDGE is meshed
1074   TError err = getWires( tgtFace, srcFace, tgtMesh, srcMesh,
1075                          shape2ShapeMap, srcWires, tgtWires, _src2tgtNodes, is1DComputed );
1076   if ( err && !err->IsOK() )
1077     return error( err );
1078
1079   bool done = false;
1080
1081  if ( !done )
1082   {
1083     // try to project from the same face with different location
1084     done = projectPartner( tgtFace, srcFace, tgtWires, srcWires,
1085                            shape2ShapeMap, _src2tgtNodes, is1DComputed );
1086   }
1087   if ( !done )
1088   {
1089     // projection in case if the faces are similar in 2D space
1090     done = projectBy2DSimilarity( tgtFace, srcFace, tgtWires, srcWires,
1091                                   shape2ShapeMap, _src2tgtNodes, is1DComputed);
1092   }
1093
1094   SMESH_MesherHelper helper( theMesh );
1095   helper.SetSubShape( tgtFace );
1096
1097   if ( !done )
1098   {
1099     _src2tgtNodes.clear();
1100     // --------------------
1101     // Prepare to mapping 
1102     // --------------------
1103
1104     // Check if node projection to a face is needed
1105     Bnd_B2d uvBox;
1106     SMDS_ElemIteratorPtr faceIt = srcSubMesh->GetSubMeshDS()->GetElements();
1107     int nbFaceNodes = 0;
1108     for ( ; nbFaceNodes < 3 && faceIt->more();  ) {
1109       const SMDS_MeshElement* face = faceIt->next();
1110       SMDS_ElemIteratorPtr nodeIt = face->nodesIterator();
1111       while ( nodeIt->more() ) {
1112         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
1113         if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE ) {
1114           nbFaceNodes++;
1115           uvBox.Add( helper.GetNodeUV( srcFace, node ));
1116         }
1117       }
1118     }
1119     const bool toProjectNodes =
1120       ( nbFaceNodes > 0 && ( uvBox.IsVoid() || uvBox.SquareExtent() < DBL_MIN ));
1121
1122     // Find the corresponding source and target vertex
1123     // and <theReverse> flag needed to call mapper.Apply()
1124
1125     TopoDS_Vertex srcV1, tgtV1;
1126     bool reverse = false;
1127
1128     if ( _sourceHypo->HasVertexAssociation() ) {
1129       srcV1 = _sourceHypo->GetSourceVertex(1);
1130       tgtV1 = _sourceHypo->GetTargetVertex(1);
1131     } else {
1132       srcV1 = TopoDS::Vertex( TopExp_Explorer( srcFace, TopAbs_VERTEX ).Current() );
1133       tgtV1 = TopoDS::Vertex( shape2ShapeMap( srcV1, /*isSrc=*/true ));
1134     }
1135     list< TopoDS_Edge > tgtEdges, srcEdges;
1136     list< int > nbEdgesInWires;
1137     SMESH_Block::GetOrderedEdges( tgtFace, tgtEdges, nbEdgesInWires, tgtV1 );
1138     SMESH_Block::GetOrderedEdges( srcFace, srcEdges, nbEdgesInWires, srcV1 );
1139
1140     if ( nbEdgesInWires.front() > 1 ) // possible to find out orientation
1141     {
1142       TopoDS_Edge srcE1 = srcEdges.front(), tgtE1 = tgtEdges.front();
1143       TopoDS_Shape srcE1bis = shape2ShapeMap( tgtE1 );
1144       reverse = ( ! srcE1.IsSame( srcE1bis ));
1145       if ( reverse &&
1146            _sourceHypo->HasVertexAssociation() &&
1147            nbEdgesInWires.front() > 2 &&
1148            helper.IsRealSeam( tgtEdges.front() ))
1149       {
1150         // projection to a face with seam EDGE; pb is that GetOrderedEdges()
1151         // always puts a seam EDGE first (if possible) and as a result
1152         // we can't use only theReverse flag to correctly associate source
1153         // and target faces in the mapper. Thus we select srcV1 so that
1154         // GetOrderedEdges() to return EDGEs in a needed order
1155         list< TopoDS_Edge >::iterator edge = srcEdges.begin();
1156         for ( ; edge != srcEdges.end(); ++edge ) {
1157           if ( srcE1bis.IsSame( *edge )) {
1158             srcV1 = helper.IthVertex( 0, *edge );
1159             break;
1160           }
1161         }
1162       }
1163     }
1164     else if ( nbEdgesInWires.front() == 1 )
1165     {
1166       // TODO::Compare orientation of curves in a sole edge
1167       //RETURN_BAD_RESULT("Not implemented case");
1168     }
1169     else
1170     {
1171       RETURN_BAD_RESULT("Bad result from SMESH_Block::GetOrderedEdges()");
1172     }
1173
1174     // Load pattern from the source face
1175     SMESH_Pattern mapper;
1176     mapper.Load( srcMesh, srcFace, toProjectNodes, srcV1 );
1177     if ( mapper.GetErrorCode() != SMESH_Pattern::ERR_OK )
1178       return error(COMPERR_BAD_INPUT_MESH,"Can't load mesh pattern from the source face");
1179
1180     // --------------------
1181     // Perform 2D mapping
1182     // --------------------
1183
1184     // Compute mesh on a target face
1185
1186     mapper.Apply( tgtFace, tgtV1, reverse );
1187     if ( mapper.GetErrorCode() != SMESH_Pattern::ERR_OK )
1188       return error("Can't apply source mesh pattern to the face");
1189
1190     // Create the mesh
1191
1192     const bool toCreatePolygons = false, toCreatePolyedrs = false;
1193     mapper.MakeMesh( tgtMesh, toCreatePolygons, toCreatePolyedrs );
1194     if ( mapper.GetErrorCode() != SMESH_Pattern::ERR_OK )
1195       return error("Can't make mesh by source mesh pattern");
1196
1197     // it will remove mesh built by pattern mapper on edges and vertices
1198     // in failure case
1199     MeshCleaner cleaner( tgtSubMesh );
1200
1201     // -------------------------------------------------------------------------
1202     // mapper doesn't take care of nodes already existing on edges and vertices,
1203     // so we must merge nodes created by it with existing ones
1204     // -------------------------------------------------------------------------
1205
1206     SMESH_MeshEditor::TListOfListOfNodes groupsOfNodes;
1207
1208     // Make groups of nodes to merge
1209
1210     // loop on EDGE and VERTEX sub-meshes of a target FACE
1211     SMESH_subMeshIteratorPtr smIt = tgtSubMesh->getDependsOnIterator(/*includeSelf=*/false,
1212                                                                      /*complexShapeFirst=*/false);
1213     while ( smIt->more() )
1214     {
1215       SMESH_subMesh*     sm = smIt->next();
1216       SMESHDS_SubMesh* smDS = sm->GetSubMeshDS();
1217       if ( !smDS || smDS->NbNodes() == 0 )
1218         continue;
1219       //if ( !is1DComputed && sm->GetSubShape().ShapeType() == TopAbs_EDGE )
1220       //  break;
1221
1222       if ( helper.IsDegenShape( sm->GetId() ) ) // to merge all nodes on degenerated
1223       {
1224         if ( sm->GetSubShape().ShapeType() == TopAbs_EDGE )
1225         {
1226           groupsOfNodes.push_back( list< const SMDS_MeshNode* >() );
1227           SMESH_subMeshIteratorPtr smDegenIt
1228             = sm->getDependsOnIterator(/*includeSelf=*/true,/*complexShapeFirst=*/false);
1229           while ( smDegenIt->more() )
1230             if (( smDS = smDegenIt->next()->GetSubMeshDS() ))
1231             {
1232               SMDS_NodeIteratorPtr nIt = smDS->GetNodes();
1233               while ( nIt->more() )
1234                 groupsOfNodes.back().push_back( nIt->next() );
1235             }
1236         }
1237         continue; // do not treat sm of degen VERTEX
1238       }
1239
1240       // Sort new and old nodes of a submesh separately
1241
1242       bool isSeam = helper.IsRealSeam( sm->GetId() );
1243
1244       enum { NEW_NODES = 0, OLD_NODES };
1245       map< double, const SMDS_MeshNode* > u2nodesMaps[2], u2nodesOnSeam;
1246       map< double, const SMDS_MeshNode* >::iterator u_oldNode, u_newNode, u_newOnSeam, newEnd;
1247       set< const SMDS_MeshNode* > seamNodes;
1248
1249       // mapper changed, no more "mapper puts on a seam edge nodes from 2 edges"
1250       if ( isSeam && ! getBoundaryNodes ( sm, tgtFace, u2nodesOnSeam, seamNodes ))
1251         ;//RETURN_BAD_RESULT("getBoundaryNodes() failed");
1252
1253       SMDS_NodeIteratorPtr nIt = smDS->GetNodes();
1254       while ( nIt->more() )
1255       {
1256         const SMDS_MeshNode* node = nIt->next();
1257         bool isOld = isOldNode( node );
1258
1259         if ( !isOld && isSeam ) { // new node on a seam edge
1260           if ( seamNodes.count( node ) )
1261             continue; // node is already in the map
1262         }
1263
1264         // sort nodes on edges by their position
1265         map< double, const SMDS_MeshNode* > & pos2nodes = u2nodesMaps[isOld ? OLD_NODES : NEW_NODES];
1266         switch ( node->GetPosition()->GetTypeOfPosition() )
1267         {
1268         case  SMDS_TOP_VERTEX: {
1269           if ( !is1DComputed && !pos2nodes.empty() )
1270             u2nodesMaps[isOld ? NEW_NODES : OLD_NODES].insert( make_pair( 0, node ));
1271           else
1272             pos2nodes.insert( make_pair( 0, node ));
1273           break;
1274         }
1275         case  SMDS_TOP_EDGE:   {
1276           const SMDS_EdgePosition* pos =
1277             static_cast<const SMDS_EdgePosition*>(node->GetPosition());
1278           pos2nodes.insert( make_pair( pos->GetUParameter(), node ));
1279           break;
1280         }
1281         default:
1282           RETURN_BAD_RESULT("Wrong node position type: "<<
1283                             node->GetPosition()->GetTypeOfPosition());
1284         }
1285       }
1286       const bool mergeNewToOld =
1287         ( u2nodesMaps[ NEW_NODES ].size() == u2nodesMaps[ OLD_NODES ].size() );
1288       const bool mergeSeamToNew =
1289         ( u2nodesMaps[ NEW_NODES ].size() == u2nodesOnSeam.size() );
1290
1291       if ( !mergeNewToOld )
1292         if ( u2nodesMaps[ NEW_NODES ].size() > 0 &&
1293              u2nodesMaps[ OLD_NODES ].size() > 0 )
1294         {
1295           u_oldNode = u2nodesMaps[ OLD_NODES ].begin(); 
1296           newEnd    = u2nodesMaps[ OLD_NODES ].end();
1297           for ( ; u_oldNode != newEnd; ++u_oldNode )
1298             SMESH_Algo::addBadInputElement( u_oldNode->second );
1299           return error( COMPERR_BAD_INPUT_MESH,
1300                         SMESH_Comment( "Existing mesh mismatches the projected 2D mesh on " )
1301                         << ( sm->GetSubShape().ShapeType() == TopAbs_EDGE ? "edge" : "vertex" )
1302                         << " #" << sm->GetId() );
1303         }
1304       if ( isSeam && !mergeSeamToNew ) {
1305         const TopoDS_Shape& seam = sm->GetSubShape();
1306         if ( u2nodesMaps[ NEW_NODES ].size() > 0 &&
1307              u2nodesOnSeam.size()            > 0 &&
1308              seam.ShapeType() == TopAbs_EDGE )
1309         {
1310           int nbE1 = SMESH_MesherHelper::Count( tgtFace, TopAbs_EDGE, /*ignoreSame=*/true );
1311           int nbE2 = SMESH_MesherHelper::Count( srcFace, TopAbs_EDGE, /*ignoreSame=*/true );
1312           if ( nbE1 != nbE2 ) // 2 EDGEs are mapped to a seam EDGE
1313           {
1314             // find the 2 EDGEs of srcFace
1315             TopTools_DataMapIteratorOfDataMapOfShapeShape src2tgtIt( shape2ShapeMap._map2to1 );
1316             for ( ; src2tgtIt.More(); src2tgtIt.Next() )
1317               if ( seam.IsSame( src2tgtIt.Value() ))
1318                 SMESH_Algo::addBadInputElements
1319                   ( srcMesh->GetMeshDS()->MeshElements( src2tgtIt.Key() ));
1320             return error( COMPERR_BAD_INPUT_MESH,
1321                           "Different number of nodes on two edges projected to a seam edge" );
1322           }
1323         }
1324       }
1325
1326       // Make groups of nodes to merge
1327
1328       u_oldNode = u2nodesMaps[ OLD_NODES ].begin(); 
1329       u_newNode = u2nodesMaps[ NEW_NODES ].begin();
1330       newEnd    = u2nodesMaps[ NEW_NODES ].end();
1331       u_newOnSeam = u2nodesOnSeam.begin();
1332       if ( mergeNewToOld )
1333         for ( ; u_newNode != newEnd; ++u_newNode, ++u_oldNode )
1334         {
1335           groupsOfNodes.push_back( list< const SMDS_MeshNode* >() );
1336           groupsOfNodes.back().push_back( u_oldNode->second );
1337           groupsOfNodes.back().push_back( u_newNode->second );
1338           if ( mergeSeamToNew )
1339             groupsOfNodes.back().push_back( (u_newOnSeam++)->second );
1340         }
1341       else if ( mergeSeamToNew )
1342         for ( ; u_newNode != newEnd; ++u_newNode, ++u_newOnSeam )
1343         {
1344           groupsOfNodes.push_back( list< const SMDS_MeshNode* >() );
1345           groupsOfNodes.back().push_back( u_newNode->second );
1346           groupsOfNodes.back().push_back( u_newOnSeam->second );
1347         }
1348
1349     } // loop on EDGE and VERTEX submeshes of a target FACE
1350
1351     // Merge
1352
1353     SMESH_MeshEditor editor( tgtMesh );
1354     int nbFaceBeforeMerge = tgtSubMesh->GetSubMeshDS()->NbElements();
1355     editor.MergeNodes( groupsOfNodes );
1356     int nbFaceAtferMerge = tgtSubMesh->GetSubMeshDS()->NbElements();
1357     if ( nbFaceBeforeMerge != nbFaceAtferMerge && !helper.HasDegeneratedEdges() )
1358       return error(COMPERR_BAD_INPUT_MESH, "Probably invalid node parameters on geom faces");
1359
1360
1361     // ----------------------------------------------------------------
1362     // The mapper can create distorted faces by placing nodes out of the FACE
1363     // boundary -- fix bad faces by smoothing
1364     // ----------------------------------------------------------------
1365
1366     fixDistortedFaces( helper, tgtWires );
1367
1368     // ----------------------------------------------------------------
1369     // The mapper can't create quadratic elements, so convert if needed
1370     // ----------------------------------------------------------------
1371
1372     faceIt         = srcSubMesh->GetSubMeshDS()->GetElements();
1373     bool srcIsQuad = faceIt->next()->IsQuadratic();
1374     faceIt         = tgtSubMesh->GetSubMeshDS()->GetElements();
1375     bool tgtIsQuad = faceIt->next()->IsQuadratic();
1376     if ( srcIsQuad && !tgtIsQuad )
1377     {
1378       TIDSortedElemSet tgtFaces;
1379       faceIt = tgtSubMesh->GetSubMeshDS()->GetElements();
1380       while ( faceIt->more() )
1381         tgtFaces.insert( tgtFaces.end(), faceIt->next() );
1382
1383       editor.ConvertToQuadratic(/*theForce3d=*/false, tgtFaces, false);
1384     }
1385
1386     cleaner.Release(); // not to remove mesh
1387
1388   } // end of projection using Pattern mapping
1389
1390
1391   // ---------------------------
1392   // Check elements orientation
1393   // ---------------------------
1394
1395   TopoDS_Face face = TopoDS::Face( theShape );
1396   if ( !theMesh.IsMainShape( tgtFace ))
1397   {
1398     // find the main shape
1399     TopoDS_Shape mainShape = meshDS->ShapeToMesh();
1400     switch ( mainShape.ShapeType() ) {
1401     case TopAbs_SHELL:
1402     case TopAbs_SOLID: break;
1403     default:
1404       TopTools_ListIteratorOfListOfShape ancestIt = theMesh.GetAncestors( face );
1405       for ( ; ancestIt.More(); ancestIt.Next() ) {
1406         TopAbs_ShapeEnum type = ancestIt.Value().ShapeType();
1407         if ( type == TopAbs_SOLID ) {
1408           mainShape = ancestIt.Value();
1409           break;
1410         } else if ( type == TopAbs_SHELL ) {
1411           mainShape = ancestIt.Value();
1412         }
1413       }
1414     }
1415     // find tgtFace in the main solid or shell to know it's true orientation.
1416     TopExp_Explorer exp( mainShape, TopAbs_FACE );
1417     for ( ; exp.More(); exp.Next() ) {
1418       if ( tgtFace.IsSame( exp.Current() )) {
1419         face = TopoDS::Face( exp.Current() );
1420         break;
1421       }
1422     }
1423   }
1424   // Fix orientation
1425   if ( helper.IsReversedSubMesh( face ))
1426   {
1427     SMESH_MeshEditor editor( tgtMesh );
1428     SMDS_ElemIteratorPtr eIt = meshDS->MeshElements( face )->GetElements();
1429     while ( eIt->more() ) {
1430       const SMDS_MeshElement* e = eIt->next();
1431       if ( e->GetType() == SMDSAbs_Face && !editor.Reorient( e ))
1432         RETURN_BAD_RESULT("Pb of SMESH_MeshEditor::Reorient()");
1433     }
1434   }
1435
1436   return true;
1437 }
1438
1439
1440 //=======================================================================
1441 //function : Evaluate
1442 //purpose  : 
1443 //=======================================================================
1444
1445 bool StdMeshers_Projection_2D::Evaluate(SMESH_Mesh&         theMesh,
1446                                         const TopoDS_Shape& theShape,
1447                                         MapShapeNbElems&    aResMap)
1448 {
1449   if ( !_sourceHypo )
1450     return false;
1451
1452   SMESH_Mesh * srcMesh = _sourceHypo->GetSourceMesh();
1453   SMESH_Mesh * tgtMesh = & theMesh;
1454   if ( !srcMesh )
1455     srcMesh = tgtMesh;
1456
1457   // ---------------------------
1458   // Make sub-shapes association
1459   // ---------------------------
1460
1461   TopoDS_Face tgtFace = TopoDS::Face( theShape.Oriented(TopAbs_FORWARD));
1462   TopoDS_Shape srcShape = _sourceHypo->GetSourceFace().Oriented(TopAbs_FORWARD);
1463
1464   TAssocTool::TShapeShapeMap shape2ShapeMap;
1465   TAssocTool::InitVertexAssociation( _sourceHypo, shape2ShapeMap );
1466   if ( !TAssocTool::FindSubShapeAssociation( tgtFace, tgtMesh, srcShape, srcMesh,
1467                                              shape2ShapeMap)  ||
1468        !shape2ShapeMap.IsBound( tgtFace ))
1469     return error(COMPERR_BAD_SHAPE,"Topology of source and target faces seems different" );
1470
1471   TopoDS_Face srcFace = TopoDS::Face( shape2ShapeMap( tgtFace ).Oriented(TopAbs_FORWARD));
1472
1473   // -------------------------------------------------------
1474   // Assure that mesh on a source Face is computed/evaluated
1475   // -------------------------------------------------------
1476
1477   std::vector<int> aVec;
1478
1479   SMESH_subMesh* srcSubMesh = srcMesh->GetSubMesh( srcFace );
1480   if ( srcSubMesh->IsMeshComputed() )
1481   {
1482     aVec.resize( SMDSEntity_Last, 0 );
1483     aVec[SMDSEntity_Node] = srcSubMesh->GetSubMeshDS()->NbNodes();
1484
1485     SMDS_ElemIteratorPtr elemIt = srcSubMesh->GetSubMeshDS()->GetElements();
1486     while ( elemIt->more() )
1487       aVec[ elemIt->next()->GetEntityType() ]++;
1488   }
1489   else
1490   {
1491     MapShapeNbElems  tmpResMap;
1492     MapShapeNbElems& srcResMap = (srcMesh == tgtMesh) ? aResMap : tmpResMap;
1493     if ( !_gen->Evaluate( *srcMesh, srcShape, srcResMap ))
1494       return error(COMPERR_BAD_INPUT_MESH,"Source mesh not evaluatable");
1495     aVec = srcResMap[ srcSubMesh ];
1496     if ( aVec.empty() )
1497       return error(COMPERR_BAD_INPUT_MESH,"Source mesh is wrongly evaluated");
1498   }
1499
1500   SMESH_subMesh * sm = theMesh.GetSubMesh(theShape);
1501   aResMap.insert(std::make_pair(sm,aVec));
1502
1503   return true;
1504 }
1505
1506
1507 //=============================================================================
1508 /*!
1509  * \brief Sets a default event listener to submesh of the source face
1510   * \param subMesh - submesh where algo is set
1511  *
1512  * This method is called when a submesh gets HYP_OK algo_state.
1513  * After being set, event listener is notified on each event of a submesh.
1514  * Arranges that CLEAN event is translated from source submesh to
1515  * the submesh
1516  */
1517 //=============================================================================
1518
1519 void StdMeshers_Projection_2D::SetEventListener(SMESH_subMesh* subMesh)
1520 {
1521   TAssocTool::SetEventListener( subMesh,
1522                                 _sourceHypo->GetSourceFace(),
1523                                 _sourceHypo->GetSourceMesh() );
1524 }