Salome HOME
c7eb286003290d5f27e0455a7ca55c30a893c7ff
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2012  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.
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 // File      : SMESH_MeshEditor.cxx
24 // Created   : Mon Apr 12 16:10:22 2004
25 // Author    : Edward AGAPOV (eap)
26
27 #include "SMESH_MeshEditor.hxx"
28
29 #include "SMDS_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
38
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
41
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
48
49 #include <Basics_OCCTVersion.hxx>
50
51 #include "utilities.h"
52
53 #include <BRepAdaptor_Surface.hxx>
54 #include <BRepBuilderAPI_MakeEdge.hxx>
55 #include <BRepClass3d_SolidClassifier.hxx>
56 #include <BRep_Tool.hxx>
57 #include <ElCLib.hxx>
58 #include <Extrema_GenExtPS.hxx>
59 #include <Extrema_POnCurv.hxx>
60 #include <Extrema_POnSurf.hxx>
61 #include <GC_MakeSegment.hxx>
62 #include <Geom2d_Curve.hxx>
63 #include <GeomAPI_ExtremaCurveCurve.hxx>
64 #include <GeomAdaptor_Surface.hxx>
65 #include <Geom_Curve.hxx>
66 #include <Geom_Line.hxx>
67 #include <Geom_Surface.hxx>
68 #include <IntAna_IntConicQuad.hxx>
69 #include <IntAna_Quadric.hxx>
70 #include <Precision.hxx>
71 #include <TColStd_ListOfInteger.hxx>
72 #include <TopAbs_State.hxx>
73 #include <TopExp.hxx>
74 #include <TopExp_Explorer.hxx>
75 #include <TopTools_ListIteratorOfListOfShape.hxx>
76 #include <TopTools_ListOfShape.hxx>
77 #include <TopTools_SequenceOfShape.hxx>
78 #include <TopoDS.hxx>
79 #include <TopoDS_Face.hxx>
80 #include <TopoDS_Solid.hxx>
81 #include <gp.hxx>
82 #include <gp_Ax1.hxx>
83 #include <gp_Dir.hxx>
84 #include <gp_Lin.hxx>
85 #include <gp_Pln.hxx>
86 #include <gp_Trsf.hxx>
87 #include <gp_Vec.hxx>
88 #include <gp_XY.hxx>
89 #include <gp_XYZ.hxx>
90
91 #include <cmath>
92
93 #include <map>
94 #include <set>
95 #include <numeric>
96 #include <limits>
97 #include <algorithm>
98 #include <sstream>
99
100 #include <Standard_Failure.hxx>
101 #include <Standard_ErrorHandler.hxx>
102
103 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
104
105 using namespace std;
106 using namespace SMESH::Controls;
107
108 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshNode*> >    TElemOfNodeListMap;
109 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshElement*> > TElemOfElemListMap;
110
111 typedef SMDS_SetIterator< SMDS_pElement, TIDSortedElemSet::const_iterator> TSetIterator;
112
113 //=======================================================================
114 //function : SMESH_MeshEditor
115 //purpose  :
116 //=======================================================================
117
118 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
119   :myMesh( theMesh ) // theMesh may be NULL
120 {
121 }
122
123 //================================================================================
124 /*!
125  * \brief Clears myLastCreatedNodes and myLastCreatedElems
126  */
127 //================================================================================
128
129 void SMESH_MeshEditor::CrearLastCreated()
130 {
131   myLastCreatedNodes.Clear();
132   myLastCreatedElems.Clear();
133 }
134
135
136 //=======================================================================
137 /*!
138  * \brief Add element
139  */
140 //=======================================================================
141
142 SMDS_MeshElement*
143 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
144                              const SMDSAbs_ElementType            type,
145                              const bool                           isPoly,
146                              const int                            ID,
147                              const double                         ballDiameter)
148 {
149   //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
150   SMDS_MeshElement* e = 0;
151   int nbnode = node.size();
152   SMESHDS_Mesh* mesh = GetMeshDS();
153   switch ( type ) {
154   case SMDSAbs_Face:
155     if ( !isPoly ) {
156       if      (nbnode == 3) {
157         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
158         else           e = mesh->AddFace      (node[0], node[1], node[2] );
159       }
160       else if (nbnode == 4) {
161         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
162         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
163       }
164       else if (nbnode == 6) {
165         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
166                                                node[4], node[5], ID);
167         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
168                                                node[4], node[5] );
169       }
170       else if (nbnode == 8) {
171         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
172                                                node[4], node[5], node[6], node[7], ID);
173         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
174                                                node[4], node[5], node[6], node[7] );
175       }
176       else if (nbnode == 9) {
177         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
178                                                node[4], node[5], node[6], node[7], node[8], ID);
179         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
180                                                node[4], node[5], node[6], node[7], node[8] );
181       }
182     } else {
183       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
184       else           e = mesh->AddPolygonalFace      (node    );
185     }
186     break;
187
188   case SMDSAbs_Volume:
189     if ( !isPoly ) {
190       if      (nbnode == 4) {
191         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
192         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
193       }
194       else if (nbnode == 5) {
195         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
196                                                  node[4], ID);
197         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
198                                                  node[4] );
199       }
200       else if (nbnode == 6) {
201         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
202                                                  node[4], node[5], ID);
203         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
204                                                  node[4], node[5] );
205       }
206       else if (nbnode == 8) {
207         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
208                                                  node[4], node[5], node[6], node[7], ID);
209         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
210                                                  node[4], node[5], node[6], node[7] );
211       }
212       else if (nbnode == 10) {
213         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
214                                                  node[4], node[5], node[6], node[7],
215                                                  node[8], node[9], ID);
216         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
217                                                  node[4], node[5], node[6], node[7],
218                                                  node[8], node[9] );
219       }
220       else if (nbnode == 12) {
221         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
222                                                  node[4], node[5], node[6], node[7],
223                                                  node[8], node[9], node[10], node[11], ID);
224         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
225                                                  node[4], node[5], node[6], node[7],
226                                                  node[8], node[9], node[10], node[11] );
227       }
228       else if (nbnode == 13) {
229         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
230                                                  node[4], node[5], node[6], node[7],
231                                                  node[8], node[9], node[10],node[11],
232                                                  node[12],ID);
233         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
234                                                  node[4], node[5], node[6], node[7],
235                                                  node[8], node[9], node[10],node[11],
236                                                  node[12] );
237       }
238       else if (nbnode == 15) {
239         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
240                                                  node[4], node[5], node[6], node[7],
241                                                  node[8], node[9], node[10],node[11],
242                                                  node[12],node[13],node[14],ID);
243         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
244                                                  node[4], node[5], node[6], node[7],
245                                                  node[8], node[9], node[10],node[11],
246                                                  node[12],node[13],node[14] );
247       }
248       else if (nbnode == 20) {
249         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
250                                                  node[4], node[5], node[6], node[7],
251                                                  node[8], node[9], node[10],node[11],
252                                                  node[12],node[13],node[14],node[15],
253                                                  node[16],node[17],node[18],node[19],ID);
254         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
255                                                  node[4], node[5], node[6], node[7],
256                                                  node[8], node[9], node[10],node[11],
257                                                  node[12],node[13],node[14],node[15],
258                                                  node[16],node[17],node[18],node[19] );
259       }
260       else if (nbnode == 27) {
261         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
262                                                  node[4], node[5], node[6], node[7],
263                                                  node[8], node[9], node[10],node[11],
264                                                  node[12],node[13],node[14],node[15],
265                                                  node[16],node[17],node[18],node[19],
266                                                  node[20],node[21],node[22],node[23],
267                                                  node[24],node[25],node[26], ID);
268         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
269                                                  node[4], node[5], node[6], node[7],
270                                                  node[8], node[9], node[10],node[11],
271                                                  node[12],node[13],node[14],node[15],
272                                                  node[16],node[17],node[18],node[19],
273                                                  node[20],node[21],node[22],node[23],
274                                                  node[24],node[25],node[26] );
275       }
276     }
277     break;
278
279   case SMDSAbs_Edge:
280     if ( nbnode == 2 ) {
281       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
282       else           e = mesh->AddEdge      (node[0], node[1] );
283     }
284     else if ( nbnode == 3 ) {
285       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
286       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
287     }
288     break;
289
290   case SMDSAbs_0DElement:
291     if ( nbnode == 1 ) {
292       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
293       else           e = mesh->Add0DElement      (node[0] );
294     }
295     break;
296
297   case SMDSAbs_Node:
298     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
299     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z());
300     break;
301
302   case SMDSAbs_Ball:
303     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
304     else           e = mesh->AddBall      (node[0], ballDiameter);
305     break;
306
307   default:;
308   }
309   if ( e ) myLastCreatedElems.Append( e );
310   return e;
311 }
312
313 //=======================================================================
314 /*!
315  * \brief Add element
316  */
317 //=======================================================================
318
319 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> &       nodeIDs,
320                                                const SMDSAbs_ElementType type,
321                                                const bool                isPoly,
322                                                const int                 ID)
323 {
324   vector<const SMDS_MeshNode*> nodes;
325   nodes.reserve( nodeIDs.size() );
326   vector<int>::const_iterator id = nodeIDs.begin();
327   while ( id != nodeIDs.end() ) {
328     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
329       nodes.push_back( node );
330     else
331       return 0;
332   }
333   return AddElement( nodes, type, isPoly, ID );
334 }
335
336 //=======================================================================
337 //function : Remove
338 //purpose  : Remove a node or an element.
339 //           Modify a compute state of sub-meshes which become empty
340 //=======================================================================
341
342 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
343                               const bool         isNodes )
344 {
345   myLastCreatedElems.Clear();
346   myLastCreatedNodes.Clear();
347
348   SMESHDS_Mesh* aMesh = GetMeshDS();
349   set< SMESH_subMesh *> smmap;
350
351   int removed = 0;
352   list<int>::const_iterator it = theIDs.begin();
353   for ( ; it != theIDs.end(); it++ ) {
354     const SMDS_MeshElement * elem;
355     if ( isNodes )
356       elem = aMesh->FindNode( *it );
357     else
358       elem = aMesh->FindElement( *it );
359     if ( !elem )
360       continue;
361
362     // Notify VERTEX sub-meshes about modification
363     if ( isNodes ) {
364       const SMDS_MeshNode* node = cast2Node( elem );
365       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
366         if ( int aShapeID = node->getshapeId() )
367           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
368             smmap.insert( sm );
369     }
370     // Find sub-meshes to notify about modification
371     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
372     //     while ( nodeIt->more() ) {
373     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
374     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
375     //       if ( aPosition.get() ) {
376     //         if ( int aShapeID = aPosition->GetShapeId() ) {
377     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
378     //             smmap.insert( sm );
379     //         }
380     //       }
381     //     }
382
383     // Do remove
384     if ( isNodes )
385       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
386     else
387       aMesh->RemoveElement( elem );
388     removed++;
389   }
390
391   // Notify sub-meshes about modification
392   if ( !smmap.empty() ) {
393     set< SMESH_subMesh *>::iterator smIt;
394     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
395       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
396   }
397
398   //   // Check if the whole mesh becomes empty
399   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
400   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
401
402   return removed;
403 }
404
405 //================================================================================
406 /*!
407  * \brief Create 0D elements on all nodes of the given object except those
408  *        nodes on which a 0D element already exists.
409  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
410  *                    the all mesh is treated
411  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
412  */
413 //================================================================================
414
415 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
416                                                    TIDSortedElemSet&       all0DElems )
417 {
418   typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::const_iterator> TSetIterator;
419   SMDS_ElemIteratorPtr elemIt;
420   if ( elements.empty() )
421     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
422   else
423     elemIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
424
425   while ( elemIt->more() )
426   {
427     const SMDS_MeshElement* e = elemIt->next();
428     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
429     while ( nodeIt->more() )
430     {
431       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
432       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
433       if ( it0D->more() )
434         all0DElems.insert( it0D->next() );
435       else {
436         myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
437         all0DElems.insert( myLastCreatedElems.Last() );
438       }
439     }
440   }
441 }
442
443 //=======================================================================
444 //function : FindShape
445 //purpose  : Return an index of the shape theElem is on
446 //           or zero if a shape not found
447 //=======================================================================
448
449 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
450 {
451   myLastCreatedElems.Clear();
452   myLastCreatedNodes.Clear();
453
454   SMESHDS_Mesh * aMesh = GetMeshDS();
455   if ( aMesh->ShapeToMesh().IsNull() )
456     return 0;
457
458   int aShapeID = theElem->getshapeId();
459   if ( aShapeID < 1 )
460     return 0;
461
462   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
463     if ( sm->Contains( theElem ))
464       return aShapeID;
465
466   if ( theElem->GetType() == SMDSAbs_Node ) {
467     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
468   }
469   else {
470     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
471   }
472
473   TopoDS_Shape aShape; // the shape a node of theElem is on
474   if ( theElem->GetType() != SMDSAbs_Node )
475   {
476     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
477     while ( nodeIt->more() ) {
478       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
479       if ((aShapeID = node->getshapeId()) > 0) {
480         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
481           if ( sm->Contains( theElem ))
482             return aShapeID;
483           if ( aShape.IsNull() )
484             aShape = aMesh->IndexToShape( aShapeID );
485         }
486       }
487     }
488   }
489
490   // None of nodes is on a proper shape,
491   // find the shape among ancestors of aShape on which a node is
492   if ( !aShape.IsNull() ) {
493     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
494     for ( ; ancIt.More(); ancIt.Next() ) {
495       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
496       if ( sm && sm->Contains( theElem ))
497         return aMesh->ShapeToIndex( ancIt.Value() );
498     }
499   }
500   else
501   {
502     const map<int,SMESHDS_SubMesh*>& id2sm = GetMeshDS()->SubMeshes();
503     map<int,SMESHDS_SubMesh*>::const_iterator id_sm = id2sm.begin();
504     for ( ; id_sm != id2sm.end(); ++id_sm )
505       if ( id_sm->second->Contains( theElem ))
506         return id_sm->first;
507   }
508
509   //MESSAGE ("::FindShape() - SHAPE NOT FOUND")
510   return 0;
511 }
512
513 //=======================================================================
514 //function : IsMedium
515 //purpose  :
516 //=======================================================================
517
518 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
519                                 const SMDSAbs_ElementType typeToCheck)
520 {
521   bool isMedium = false;
522   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
523   while (it->more() && !isMedium ) {
524     const SMDS_MeshElement* elem = it->next();
525     isMedium = elem->IsMediumNode(node);
526   }
527   return isMedium;
528 }
529
530 //=======================================================================
531 //function : ShiftNodesQuadTria
532 //purpose  : auxilary
533 //           Shift nodes in the array corresponded to quadratic triangle
534 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
535 //=======================================================================
536 static void ShiftNodesQuadTria(const SMDS_MeshNode* aNodes[])
537 {
538   const SMDS_MeshNode* nd1 = aNodes[0];
539   aNodes[0] = aNodes[1];
540   aNodes[1] = aNodes[2];
541   aNodes[2] = nd1;
542   const SMDS_MeshNode* nd2 = aNodes[3];
543   aNodes[3] = aNodes[4];
544   aNodes[4] = aNodes[5];
545   aNodes[5] = nd2;
546 }
547
548 //=======================================================================
549 //function : edgeConnectivity
550 //purpose  : auxilary
551 //           return number of the edges connected with the theNode.
552 //           if theEdges has connections with the other type of the
553 //           elements, return -1
554 //=======================================================================
555 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
556 {
557   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
558   int nb=0;
559   while(elemIt->more()) {
560     elemIt->next();
561     nb++;
562   }
563   return nb;
564 }
565
566
567 //=======================================================================
568 //function : GetNodesFromTwoTria
569 //purpose  : auxilary
570 //           Shift nodes in the array corresponded to quadratic triangle
571 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
572 //=======================================================================
573 static bool GetNodesFromTwoTria(const SMDS_MeshElement * theTria1,
574                                 const SMDS_MeshElement * theTria2,
575                                 const SMDS_MeshNode* N1[],
576                                 const SMDS_MeshNode* N2[])
577 {
578   SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
579   int i=0;
580   while(i<6) {
581     N1[i] = static_cast<const SMDS_MeshNode*>( it->next() );
582     i++;
583   }
584   if(it->more()) return false;
585   it = theTria2->nodesIterator();
586   i=0;
587   while(i<6) {
588     N2[i] = static_cast<const SMDS_MeshNode*>( it->next() );
589     i++;
590   }
591   if(it->more()) return false;
592
593   int sames[3] = {-1,-1,-1};
594   int nbsames = 0;
595   int j;
596   for(i=0; i<3; i++) {
597     for(j=0; j<3; j++) {
598       if(N1[i]==N2[j]) {
599         sames[i] = j;
600         nbsames++;
601         break;
602       }
603     }
604   }
605   if(nbsames!=2) return false;
606   if(sames[0]>-1) {
607     ShiftNodesQuadTria(N1);
608     if(sames[1]>-1) {
609       ShiftNodesQuadTria(N1);
610     }
611   }
612   i = sames[0] + sames[1] + sames[2];
613   for(; i<2; i++) {
614     ShiftNodesQuadTria(N2);
615   }
616   // now we receive following N1 and N2 (using numeration as above image)
617   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
618   // i.e. first nodes from both arrays determ new diagonal
619   return true;
620 }
621
622 //=======================================================================
623 //function : InverseDiag
624 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
625 //           but having other common link.
626 //           Return False if args are improper
627 //=======================================================================
628
629 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
630                                     const SMDS_MeshElement * theTria2 )
631 {
632   MESSAGE("InverseDiag");
633   myLastCreatedElems.Clear();
634   myLastCreatedNodes.Clear();
635
636   if (!theTria1 || !theTria2)
637     return false;
638
639   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
640   if (!F1) return false;
641   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
642   if (!F2) return false;
643   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
644       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
645
646     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
647     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
648     //    |/ |                                         | \|
649     //  B +--+ 2                                     B +--+ 2
650
651     // put nodes in array and find out indices of the same ones
652     const SMDS_MeshNode* aNodes [6];
653     int sameInd [] = { 0, 0, 0, 0, 0, 0 };
654     int i = 0;
655     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
656     while ( it->more() ) {
657       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
658
659       if ( i > 2 ) // theTria2
660         // find same node of theTria1
661         for ( int j = 0; j < 3; j++ )
662           if ( aNodes[ i ] == aNodes[ j ]) {
663             sameInd[ j ] = i;
664             sameInd[ i ] = j;
665             break;
666           }
667       // next
668       i++;
669       if ( i == 3 ) {
670         if ( it->more() )
671           return false; // theTria1 is not a triangle
672         it = theTria2->nodesIterator();
673       }
674       if ( i == 6 && it->more() )
675         return false; // theTria2 is not a triangle
676     }
677
678     // find indices of 1,2 and of A,B in theTria1
679     int iA = 0, iB = 0, i1 = 0, i2 = 0;
680     for ( i = 0; i < 6; i++ ) {
681       if ( sameInd [ i ] == 0 ) {
682         if ( i < 3 ) i1 = i;
683         else         i2 = i;
684       }
685       else if (i < 3) {
686         if ( iA ) iB = i;
687         else      iA = i;
688       }
689     }
690     // nodes 1 and 2 should not be the same
691     if ( aNodes[ i1 ] == aNodes[ i2 ] )
692       return false;
693
694     // theTria1: A->2
695     aNodes[ iA ] = aNodes[ i2 ];
696     // theTria2: B->1
697     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
698
699     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
700     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
701
702     return true;
703
704   } // end if(F1 && F2)
705
706   // check case of quadratic faces
707   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle)
708     return false;
709   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle)
710     return false;
711
712   //       5
713   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
714   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
715   //    |   / |
716   //  7 +  +  + 6
717   //    | /9  |
718   //    |/    |
719   //  4 +--+--+ 3
720   //       8
721
722   const SMDS_MeshNode* N1 [6];
723   const SMDS_MeshNode* N2 [6];
724   if(!GetNodesFromTwoTria(theTria1,theTria2,N1,N2))
725     return false;
726   // now we receive following N1 and N2 (using numeration as above image)
727   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
728   // i.e. first nodes from both arrays determ new diagonal
729
730   const SMDS_MeshNode* N1new [6];
731   const SMDS_MeshNode* N2new [6];
732   N1new[0] = N1[0];
733   N1new[1] = N2[0];
734   N1new[2] = N2[1];
735   N1new[3] = N1[4];
736   N1new[4] = N2[3];
737   N1new[5] = N1[5];
738   N2new[0] = N1[0];
739   N2new[1] = N1[1];
740   N2new[2] = N2[0];
741   N2new[3] = N1[3];
742   N2new[4] = N2[5];
743   N2new[5] = N1[4];
744   // replaces nodes in faces
745   GetMeshDS()->ChangeElementNodes( theTria1, N1new, 6 );
746   GetMeshDS()->ChangeElementNodes( theTria2, N2new, 6 );
747
748   return true;
749 }
750
751 //=======================================================================
752 //function : findTriangles
753 //purpose  : find triangles sharing theNode1-theNode2 link
754 //=======================================================================
755
756 static bool findTriangles(const SMDS_MeshNode *    theNode1,
757                           const SMDS_MeshNode *    theNode2,
758                           const SMDS_MeshElement*& theTria1,
759                           const SMDS_MeshElement*& theTria2)
760 {
761   if ( !theNode1 || !theNode2 ) return false;
762
763   theTria1 = theTria2 = 0;
764
765   set< const SMDS_MeshElement* > emap;
766   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
767   while (it->more()) {
768     const SMDS_MeshElement* elem = it->next();
769     if ( elem->NbNodes() == 3 )
770       emap.insert( elem );
771   }
772   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
773   while (it->more()) {
774     const SMDS_MeshElement* elem = it->next();
775     if ( emap.find( elem ) != emap.end() ) {
776       if ( theTria1 ) {
777         // theTria1 must be element with minimum ID
778         if( theTria1->GetID() < elem->GetID() ) {
779           theTria2 = elem;
780         }
781         else {
782           theTria2 = theTria1;
783           theTria1 = elem;
784         }
785         break;
786       }
787       else {
788         theTria1 = elem;
789       }
790     }
791   }
792   return ( theTria1 && theTria2 );
793 }
794
795 //=======================================================================
796 //function : InverseDiag
797 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
798 //           with ones built on the same 4 nodes but having other common link.
799 //           Return false if proper faces not found
800 //=======================================================================
801
802 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
803                                     const SMDS_MeshNode * theNode2)
804 {
805   myLastCreatedElems.Clear();
806   myLastCreatedNodes.Clear();
807
808   MESSAGE( "::InverseDiag()" );
809
810   const SMDS_MeshElement *tr1, *tr2;
811   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
812     return false;
813
814   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
815   if (!F1) return false;
816   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
817   if (!F2) return false;
818   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
819       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
820
821     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
822     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
823     //    |/ |                                    | \|
824     //  B +--+ 2                                B +--+ 2
825
826     // put nodes in array
827     // and find indices of 1,2 and of A in tr1 and of B in tr2
828     int i, iA1 = 0, i1 = 0;
829     const SMDS_MeshNode* aNodes1 [3];
830     SMDS_ElemIteratorPtr it;
831     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
832       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
833       if ( aNodes1[ i ] == theNode1 )
834         iA1 = i; // node A in tr1
835       else if ( aNodes1[ i ] != theNode2 )
836         i1 = i;  // node 1
837     }
838     int iB2 = 0, i2 = 0;
839     const SMDS_MeshNode* aNodes2 [3];
840     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
841       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
842       if ( aNodes2[ i ] == theNode2 )
843         iB2 = i; // node B in tr2
844       else if ( aNodes2[ i ] != theNode1 )
845         i2 = i;  // node 2
846     }
847
848     // nodes 1 and 2 should not be the same
849     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
850       return false;
851
852     // tr1: A->2
853     aNodes1[ iA1 ] = aNodes2[ i2 ];
854     // tr2: B->1
855     aNodes2[ iB2 ] = aNodes1[ i1 ];
856
857     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
858     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
859
860     return true;
861   }
862
863   // check case of quadratic faces
864   return InverseDiag(tr1,tr2);
865 }
866
867 //=======================================================================
868 //function : getQuadrangleNodes
869 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
870 //           fusion of triangles tr1 and tr2 having shared link on
871 //           theNode1 and theNode2
872 //=======================================================================
873
874 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
875                         const SMDS_MeshNode *    theNode1,
876                         const SMDS_MeshNode *    theNode2,
877                         const SMDS_MeshElement * tr1,
878                         const SMDS_MeshElement * tr2 )
879 {
880   if( tr1->NbNodes() != tr2->NbNodes() )
881     return false;
882   // find the 4-th node to insert into tr1
883   const SMDS_MeshNode* n4 = 0;
884   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
885   int i=0;
886   while ( !n4 && i<3 ) {
887     const SMDS_MeshNode * n = cast2Node( it->next() );
888     i++;
889     bool isDiag = ( n == theNode1 || n == theNode2 );
890     if ( !isDiag )
891       n4 = n;
892   }
893   // Make an array of nodes to be in a quadrangle
894   int iNode = 0, iFirstDiag = -1;
895   it = tr1->nodesIterator();
896   i=0;
897   while ( i<3 ) {
898     const SMDS_MeshNode * n = cast2Node( it->next() );
899     i++;
900     bool isDiag = ( n == theNode1 || n == theNode2 );
901     if ( isDiag ) {
902       if ( iFirstDiag < 0 )
903         iFirstDiag = iNode;
904       else if ( iNode - iFirstDiag == 1 )
905         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
906     }
907     else if ( n == n4 ) {
908       return false; // tr1 and tr2 should not have all the same nodes
909     }
910     theQuadNodes[ iNode++ ] = n;
911   }
912   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
913     theQuadNodes[ iNode ] = n4;
914
915   return true;
916 }
917
918 //=======================================================================
919 //function : DeleteDiag
920 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
921 //           with a quadrangle built on the same 4 nodes.
922 //           Return false if proper faces not found
923 //=======================================================================
924
925 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
926                                    const SMDS_MeshNode * theNode2)
927 {
928   myLastCreatedElems.Clear();
929   myLastCreatedNodes.Clear();
930
931   MESSAGE( "::DeleteDiag()" );
932
933   const SMDS_MeshElement *tr1, *tr2;
934   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
935     return false;
936
937   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
938   if (!F1) return false;
939   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
940   if (!F2) return false;
941   SMESHDS_Mesh * aMesh = GetMeshDS();
942
943   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
944       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
945
946     const SMDS_MeshNode* aNodes [ 4 ];
947     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
948       return false;
949
950     const SMDS_MeshElement* newElem = 0;
951     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
952     myLastCreatedElems.Append(newElem);
953     AddToSameGroups( newElem, tr1, aMesh );
954     int aShapeId = tr1->getshapeId();
955     if ( aShapeId )
956       {
957         aMesh->SetMeshElementOnShape( newElem, aShapeId );
958       }
959     aMesh->RemoveElement( tr1 );
960     aMesh->RemoveElement( tr2 );
961
962     return true;
963   }
964
965   // check case of quadratic faces
966   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
967     return false;
968   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
969     return false;
970
971   //       5
972   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
973   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
974   //    |   / |
975   //  7 +  +  + 6
976   //    | /9  |
977   //    |/    |
978   //  4 +--+--+ 3
979   //       8
980
981   const SMDS_MeshNode* N1 [6];
982   const SMDS_MeshNode* N2 [6];
983   if(!GetNodesFromTwoTria(tr1,tr2,N1,N2))
984     return false;
985   // now we receive following N1 and N2 (using numeration as above image)
986   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
987   // i.e. first nodes from both arrays determ new diagonal
988
989   const SMDS_MeshNode* aNodes[8];
990   aNodes[0] = N1[0];
991   aNodes[1] = N1[1];
992   aNodes[2] = N2[0];
993   aNodes[3] = N2[1];
994   aNodes[4] = N1[3];
995   aNodes[5] = N2[5];
996   aNodes[6] = N2[3];
997   aNodes[7] = N1[5];
998
999   const SMDS_MeshElement* newElem = 0;
1000   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1001                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1002   myLastCreatedElems.Append(newElem);
1003   AddToSameGroups( newElem, tr1, aMesh );
1004   int aShapeId = tr1->getshapeId();
1005   if ( aShapeId )
1006     {
1007       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1008     }
1009   aMesh->RemoveElement( tr1 );
1010   aMesh->RemoveElement( tr2 );
1011
1012   // remove middle node (9)
1013   GetMeshDS()->RemoveNode( N1[4] );
1014
1015   return true;
1016 }
1017
1018 //=======================================================================
1019 //function : Reorient
1020 //purpose  : Reverse theElement orientation
1021 //=======================================================================
1022
1023 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1024 {
1025   MESSAGE("Reorient");
1026   myLastCreatedElems.Clear();
1027   myLastCreatedNodes.Clear();
1028
1029   if (!theElem)
1030     return false;
1031   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1032   if ( !it || !it->more() )
1033     return false;
1034
1035   switch ( theElem->GetType() ) {
1036
1037   case SMDSAbs_Edge:
1038   case SMDSAbs_Face: {
1039     if(!theElem->IsQuadratic()) {
1040       int i = theElem->NbNodes();
1041       vector<const SMDS_MeshNode*> aNodes( i );
1042       while ( it->more() )
1043         aNodes[ --i ]= static_cast<const SMDS_MeshNode*>( it->next() );
1044       return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], theElem->NbNodes() );
1045     }
1046     else {
1047       // quadratic elements
1048       if(theElem->GetType()==SMDSAbs_Edge) {
1049         vector<const SMDS_MeshNode*> aNodes(3);
1050         aNodes[1]= static_cast<const SMDS_MeshNode*>( it->next() );
1051         aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1052         aNodes[2]= static_cast<const SMDS_MeshNode*>( it->next() );
1053         return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], 3 );
1054       }
1055       else {
1056         int nbn = theElem->NbNodes();
1057         vector<const SMDS_MeshNode*> aNodes(nbn);
1058         aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1059         int i=1;
1060         for(; i<nbn/2; i++) {
1061           aNodes[nbn/2-i]= static_cast<const SMDS_MeshNode*>( it->next() );
1062         }
1063         for(i=0; i<nbn/2; i++) {
1064           aNodes[nbn-i-1]= static_cast<const SMDS_MeshNode*>( it->next() );
1065         }
1066         return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], nbn );
1067       }
1068     }
1069   }
1070   case SMDSAbs_Volume: {
1071     if (theElem->IsPoly()) {
1072       // TODO reorient vtk polyhedron
1073       MESSAGE("reorient vtk polyhedron ?");
1074       const SMDS_VtkVolume* aPolyedre =
1075         dynamic_cast<const SMDS_VtkVolume*>( theElem );
1076       if (!aPolyedre) {
1077         MESSAGE("Warning: bad volumic element");
1078         return false;
1079       }
1080
1081       int nbFaces = aPolyedre->NbFaces();
1082       vector<const SMDS_MeshNode *> poly_nodes;
1083       vector<int> quantities (nbFaces);
1084
1085       // reverse each face of the polyedre
1086       for (int iface = 1; iface <= nbFaces; iface++) {
1087         int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1088         quantities[iface - 1] = nbFaceNodes;
1089
1090         for (inode = nbFaceNodes; inode >= 1; inode--) {
1091           const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1092           poly_nodes.push_back(curNode);
1093         }
1094       }
1095
1096       return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1097
1098     }
1099     else {
1100       SMDS_VolumeTool vTool;
1101       if ( !vTool.Set( theElem ))
1102         return false;
1103       vTool.Inverse();
1104       MESSAGE("ChangeElementNodes reorient: check vTool.Inverse");
1105       return GetMeshDS()->ChangeElementNodes( theElem, vTool.GetNodes(), vTool.NbNodes() );
1106     }
1107   }
1108   default:;
1109   }
1110
1111   return false;
1112 }
1113
1114 //================================================================================
1115 /*!
1116  * \brief Reorient faces.
1117  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1118  * \param theDirection - desired direction of normal of \a theFace
1119  * \param theFace - one of \a theFaces that sould be orientated according to
1120  *        \a theDirection and whose orientation defines orientation of other faces
1121  * \return number of reoriented faces.
1122  */
1123 //================================================================================
1124
1125 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1126                                   const gp_Dir&            theDirection,
1127                                   const SMDS_MeshElement * theFace)
1128 {
1129   int nbReori = 0;
1130   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1131
1132   if ( theFaces.empty() )
1133   {
1134     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1135     while ( fIt->more() )
1136       theFaces.insert( theFaces.end(), fIt->next() );
1137   }
1138
1139   // orient theFace according to theDirection
1140   gp_XYZ normal;
1141   SMESH_Algo::FaceNormal( theFace, normal, /*normalized=*/false );
1142   if ( normal * theDirection.XYZ() < 0 )
1143     nbReori += Reorient( theFace );
1144
1145   // Orient other faces
1146
1147   set< const SMDS_MeshElement* > startFaces;
1148   TIDSortedElemSet avoidSet;
1149   set< SMESH_TLink > checkedLinks;
1150   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1151
1152   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1153     theFaces.erase( theFace );
1154   startFaces.insert( theFace );
1155
1156   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1157   while ( startFace != startFaces.end() )
1158   {
1159     theFace = *startFace;
1160     const int nbNodes = theFace->NbCornerNodes();
1161
1162     avoidSet.clear();
1163     avoidSet.insert(theFace);
1164
1165     NLink link( theFace->GetNode( 0 ), 0 );
1166     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1167     {
1168       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1169       linkIt_isNew = checkedLinks.insert( link );
1170       if ( !linkIt_isNew.second )
1171       {
1172         // link has already been checked and won't be encountered more
1173         // if the group (theFaces) is manifold
1174         checkedLinks.erase( linkIt_isNew.first );
1175       }
1176       else
1177       {
1178         int nodeInd1, nodeInd2;
1179         const SMDS_MeshElement* otherFace = FindFaceInSet( link.first, link.second,
1180                                                            theFaces, avoidSet,
1181                                                            & nodeInd1, & nodeInd2);
1182         if ( otherFace && otherFace != theFace)
1183         {
1184           // link must be reversed in otherFace if orientation ot otherFace
1185           // is same as that of theFace
1186           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1187           {
1188             // cout << "Reorient " << otherFace->GetID() << " near theFace=" <<theFace->GetID()
1189             //      << " \tlink( " << link.first->GetID() << " " << link.second->GetID() << endl;
1190             nbReori += Reorient( otherFace );
1191           }
1192           startFaces.insert( otherFace );
1193           if ( theFaces.size() > 1 ) // leave 1 face to prevent finding not selected faces
1194             theFaces.erase( otherFace );
1195         }
1196       }
1197       std::swap( link.first, link.second );
1198     }
1199     startFaces.erase( startFace );
1200     startFace = startFaces.begin();
1201   }
1202   return nbReori;
1203 }
1204
1205 //=======================================================================
1206 //function : getBadRate
1207 //purpose  :
1208 //=======================================================================
1209
1210 static double getBadRate (const SMDS_MeshElement*               theElem,
1211                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1212 {
1213   SMESH::Controls::TSequenceOfXYZ P;
1214   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1215     return 1e100;
1216   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1217   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1218 }
1219
1220 //=======================================================================
1221 //function : QuadToTri
1222 //purpose  : Cut quadrangles into triangles.
1223 //           theCrit is used to select a diagonal to cut
1224 //=======================================================================
1225
1226 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1227                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1228 {
1229   myLastCreatedElems.Clear();
1230   myLastCreatedNodes.Clear();
1231
1232   MESSAGE( "::QuadToTri()" );
1233
1234   if ( !theCrit.get() )
1235     return false;
1236
1237   SMESHDS_Mesh * aMesh = GetMeshDS();
1238
1239   Handle(Geom_Surface) surface;
1240   SMESH_MesherHelper   helper( *GetMesh() );
1241
1242   TIDSortedElemSet::iterator itElem;
1243   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1244     const SMDS_MeshElement* elem = *itElem;
1245     if ( !elem || elem->GetType() != SMDSAbs_Face )
1246       continue;
1247     if ( elem->NbCornerNodes() != 4 )
1248       continue;
1249
1250     // retrieve element nodes
1251     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1252
1253     // compare two sets of possible triangles
1254     double aBadRate1, aBadRate2; // to what extent a set is bad
1255     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1256     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1257     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1258
1259     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1260     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1261     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1262
1263     int aShapeId = FindShape( elem );
1264     const SMDS_MeshElement* newElem1 = 0;
1265     const SMDS_MeshElement* newElem2 = 0;
1266
1267     if( !elem->IsQuadratic() ) {
1268
1269       // split liner quadrangle
1270
1271       if ( aBadRate1 <= aBadRate2 ) {
1272         // tr1 + tr2 is better
1273         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1274         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1275       }
1276       else {
1277         // tr3 + tr4 is better
1278         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1279         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1280       }
1281     }
1282     else {
1283
1284       // split quadratic quadrangle
1285
1286       // get surface elem is on
1287       if ( aShapeId != helper.GetSubShapeID() ) {
1288         surface.Nullify();
1289         TopoDS_Shape shape;
1290         if ( aShapeId > 0 )
1291           shape = aMesh->IndexToShape( aShapeId );
1292         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1293           TopoDS_Face face = TopoDS::Face( shape );
1294           surface = BRep_Tool::Surface( face );
1295           if ( !surface.IsNull() )
1296             helper.SetSubShape( shape );
1297         }
1298       }
1299       // find middle point for (0,1,2,3)
1300       // and create a node in this point;
1301       const SMDS_MeshNode* newN = 0;
1302       if ( aNodes.size() == 9 )
1303       {
1304         // SMDSEntity_BiQuad_Quadrangle
1305         newN = aNodes.back();
1306       }
1307       else
1308       {
1309         gp_XYZ p( 0,0,0 );
1310         if ( surface.IsNull() )
1311         {
1312           for ( int i = 0; i < 4; i++ )
1313             p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1314           p /= 4;
1315         }
1316         else
1317         {
1318           const SMDS_MeshNode* inFaceNode = 0;
1319           if ( helper.GetNodeUVneedInFaceNode() )
1320             for ( size_t i = 0; i < aNodes.size() && !inFaceNode; ++i )
1321               if ( aNodes[ i ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1322                 inFaceNode = aNodes[ i ];
1323
1324           TopoDS_Face face = TopoDS::Face( helper.GetSubShape() );
1325           gp_XY uv( 0,0 );
1326           for ( int i = 0; i < 4; i++ )
1327             uv += helper.GetNodeUV( face, aNodes[i], inFaceNode );
1328           uv /= 4.;
1329           p = surface->Value( uv.X(), uv.Y() ).XYZ();
1330         }
1331         newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1332         myLastCreatedNodes.Append(newN);
1333       }
1334       // create a new element
1335       if ( aBadRate1 <= aBadRate2 ) {
1336         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1337                                   aNodes[6], aNodes[7], newN );
1338         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
1339                                   newN,      aNodes[4], aNodes[5] );
1340       }
1341       else {
1342         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1343                                   aNodes[7], aNodes[4], newN );
1344         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
1345                                   newN,      aNodes[5], aNodes[6] );
1346       }
1347     } // quadratic case
1348
1349     // care of a new element
1350
1351     myLastCreatedElems.Append(newElem1);
1352     myLastCreatedElems.Append(newElem2);
1353     AddToSameGroups( newElem1, elem, aMesh );
1354     AddToSameGroups( newElem2, elem, aMesh );
1355
1356     // put a new triangle on the same shape
1357     if ( aShapeId )
1358       {
1359         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1360         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1361       }
1362     aMesh->RemoveElement( elem );
1363   }
1364   return true;
1365 }
1366
1367 //=======================================================================
1368 //function : BestSplit
1369 //purpose  : Find better diagonal for cutting.
1370 //=======================================================================
1371
1372 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1373                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1374 {
1375   myLastCreatedElems.Clear();
1376   myLastCreatedNodes.Clear();
1377
1378   if (!theCrit.get())
1379     return -1;
1380
1381   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1382     return -1;
1383
1384   if( theQuad->NbNodes()==4 ||
1385       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1386
1387     // retrieve element nodes
1388     const SMDS_MeshNode* aNodes [4];
1389     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1390     int i = 0;
1391     //while (itN->more())
1392     while (i<4) {
1393       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1394     }
1395     // compare two sets of possible triangles
1396     double aBadRate1, aBadRate2; // to what extent a set is bad
1397     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1398     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1399     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1400
1401     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1402     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1403     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1404
1405     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1406       return 1; // diagonal 1-3
1407
1408     return 2; // diagonal 2-4
1409   }
1410   return -1;
1411 }
1412
1413 namespace
1414 {
1415   // Methods of splitting volumes into tetra
1416
1417   const int theHexTo5_1[5*4+1] =
1418     {
1419       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1420     };
1421   const int theHexTo5_2[5*4+1] =
1422     {
1423       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1424     };
1425   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1426
1427   const int theHexTo6_1[6*4+1] =
1428     {
1429       1, 5, 6, 0,    0, 1, 2, 6,     0, 4, 5, 6,    0, 4, 6, 7,     0, 2, 3, 6,   0, 3, 7, 6,  -1
1430     };
1431   const int theHexTo6_2[6*4+1] =
1432     {
1433       2, 6, 7, 1,    1, 2, 3, 7,     1, 5, 6, 7,    1, 5, 7, 4,     1, 3, 0, 7,   1, 0, 4, 7,  -1
1434     };
1435   const int theHexTo6_3[6*4+1] =
1436     {
1437       3, 7, 4, 2,    2, 3, 0, 4,     2, 6, 7, 4,    2, 6, 4, 5,     2, 0, 1, 4,   2, 1, 5, 4,  -1
1438     };
1439   const int theHexTo6_4[6*4+1] =
1440     {
1441       0, 4, 5, 3,    3, 0, 1, 5,     3, 7, 4, 5,    3, 7, 5, 6,     3, 1, 2, 5,   3, 2, 6, 5,  -1
1442     };
1443   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1444
1445   const int thePyraTo2_1[2*4+1] =
1446     {
1447       0, 1, 2, 4,    0, 2, 3, 4,   -1
1448     };
1449   const int thePyraTo2_2[2*4+1] =
1450     {
1451       1, 2, 3, 4,    1, 3, 0, 4,   -1
1452     };
1453   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1454
1455   const int thePentaTo3_1[3*4+1] =
1456     {
1457       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1458     };
1459   const int thePentaTo3_2[3*4+1] =
1460     {
1461       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1462     };
1463   const int thePentaTo3_3[3*4+1] =
1464     {
1465       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1466     };
1467   const int thePentaTo3_4[3*4+1] =
1468     {
1469       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1470     };
1471   const int thePentaTo3_5[3*4+1] =
1472     {
1473       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1474     };
1475   const int thePentaTo3_6[3*4+1] =
1476     {
1477       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1478     };
1479   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1480                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1481
1482   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1483   {
1484     int _n1, _n2, _n3;
1485     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1486     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1487     bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1488   };
1489   struct TSplitMethod
1490   {
1491     int        _nbTetra;
1492     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1493     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1494     bool       _ownConn;      //!< to delete _connectivity in destructor
1495     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1496
1497     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1498       : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1499     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1500     bool hasFacet( const TTriangleFacet& facet ) const
1501     {
1502       const int* tetConn = _connectivity;
1503       for ( ; tetConn[0] >= 0; tetConn += 4 )
1504         if (( facet.contains( tetConn[0] ) +
1505               facet.contains( tetConn[1] ) +
1506               facet.contains( tetConn[2] ) +
1507               facet.contains( tetConn[3] )) == 3 )
1508           return true;
1509       return false;
1510     }
1511   };
1512
1513   //=======================================================================
1514   /*!
1515    * \brief return TSplitMethod for the given element
1516    */
1517   //=======================================================================
1518
1519   TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1520   {
1521     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1522
1523     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1524     // an edge and a face barycenter; tertaherdons are based on triangles and
1525     // a volume barycenter
1526     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1527
1528     // Find out how adjacent volumes are split
1529
1530     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1531     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1532     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1533     {
1534       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1535       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1536       if ( nbNodes < 4 ) continue;
1537
1538       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1539       const int* nInd = vol.GetFaceNodesIndices( iF );
1540       if ( nbNodes == 4 )
1541       {
1542         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1543         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1544         if      ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1545         else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1546       }
1547       else
1548       {
1549         int iCom = 0; // common node of triangle faces to split into
1550         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1551         {
1552           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1553                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1554                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1555           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1556                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1557                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1558           if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1559           {
1560             triaSplits.push_back( t012 );
1561             triaSplits.push_back( t023 );
1562             break;
1563           }
1564         }
1565       }
1566       if ( !triaSplits.empty() )
1567         hasAdjacentSplits = true;
1568     }
1569
1570     // Among variants of split method select one compliant with adjacent volumes
1571
1572     TSplitMethod method;
1573     if ( !vol.Element()->IsPoly() && !is24TetMode )
1574     {
1575       int nbVariants = 2, nbTet = 0;
1576       const int** connVariants = 0;
1577       switch ( vol.Element()->GetEntityType() )
1578       {
1579       case SMDSEntity_Hexa:
1580       case SMDSEntity_Quad_Hexa:
1581       case SMDSEntity_TriQuad_Hexa:
1582         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1583           connVariants = theHexTo5, nbTet = 5;
1584         else
1585           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1586         break;
1587       case SMDSEntity_Pyramid:
1588       case SMDSEntity_Quad_Pyramid:
1589         connVariants = thePyraTo2;  nbTet = 2;
1590         break;
1591       case SMDSEntity_Penta:
1592       case SMDSEntity_Quad_Penta:
1593         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1594         break;
1595       default:
1596         nbVariants = 0;
1597       }
1598       for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1599       {
1600         // check method compliancy with adjacent tetras,
1601         // all found splits must be among facets of tetras described by this method
1602         method = TSplitMethod( nbTet, connVariants[variant] );
1603         if ( hasAdjacentSplits && method._nbTetra > 0 )
1604         {
1605           bool facetCreated = true;
1606           for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1607           {
1608             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1609             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1610               facetCreated = method.hasFacet( *facet );
1611           }
1612           if ( !facetCreated )
1613             method = TSplitMethod(0); // incompatible method
1614         }
1615       }
1616     }
1617     if ( method._nbTetra < 1 )
1618     {
1619       // No standard method is applicable, use a generic solution:
1620       // each facet of a volume is split into triangles and
1621       // each of triangles and a volume barycenter form a tetrahedron.
1622
1623       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1624
1625       int* connectivity = new int[ maxTetConnSize + 1 ];
1626       method._connectivity = connectivity;
1627       method._ownConn = true;
1628       method._baryNode = !isHex27; // to create central node or not
1629
1630       int connSize = 0;
1631       int baryCenInd = vol.NbNodes() - int( isHex27 );
1632       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1633       {
1634         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1635         const int*   nInd = vol.GetFaceNodesIndices( iF );
1636         // find common node of triangle facets of tetra to create
1637         int iCommon = 0; // index in linear numeration
1638         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1639         if ( !triaSplits.empty() )
1640         {
1641           // by found facets
1642           const TTriangleFacet* facet = &triaSplits.front();
1643           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1644             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1645                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1646               break;
1647         }
1648         else if ( nbNodes > 3 && !is24TetMode )
1649         {
1650           // find the best method of splitting into triangles by aspect ratio
1651           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1652           map< double, int > badness2iCommon;
1653           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1654           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1655           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1656           {
1657             double badness = 0;
1658             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1659             {
1660               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1661                                       nodes[ iQ*((iLast-1)%nbNodes)],
1662                                       nodes[ iQ*((iLast  )%nbNodes)]);
1663               badness += getBadRate( &tria, aspectRatio );
1664             }
1665             badness2iCommon.insert( make_pair( badness, iCommon ));
1666           }
1667           // use iCommon with lowest badness
1668           iCommon = badness2iCommon.begin()->second;
1669         }
1670         if ( iCommon >= nbNodes )
1671           iCommon = 0; // something wrong
1672
1673         // fill connectivity of tetrahedra based on a current face
1674         int nbTet = nbNodes - 2;
1675         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1676         {
1677           int faceBaryCenInd;
1678           if ( isHex27 )
1679           {
1680             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1681             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1682           }
1683           else
1684           {
1685             method._faceBaryNode[ iF ] = 0;
1686             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1687           }
1688           nbTet = nbNodes;
1689           for ( int i = 0; i < nbTet; ++i )
1690           {
1691             int i1 = i, i2 = (i+1) % nbNodes;
1692             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1693             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1694             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1695             connectivity[ connSize++ ] = faceBaryCenInd;
1696             connectivity[ connSize++ ] = baryCenInd;
1697           }
1698         }
1699         else
1700         {
1701           for ( int i = 0; i < nbTet; ++i )
1702           {
1703             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1704             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1705             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1706             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1707             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1708             connectivity[ connSize++ ] = baryCenInd;
1709           }
1710         }
1711         method._nbTetra += nbTet;
1712
1713       } // loop on volume faces
1714
1715       connectivity[ connSize++ ] = -1;
1716
1717     } // end of generic solution
1718
1719     return method;
1720   }
1721   //================================================================================
1722   /*!
1723    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1724    */
1725   //================================================================================
1726
1727   bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1728   {
1729     // find the tetrahedron including the three nodes of facet
1730     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1731     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1732     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1733     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1734     while ( volIt1->more() )
1735     {
1736       const SMDS_MeshElement* v = volIt1->next();
1737       SMDSAbs_EntityType type = v->GetEntityType();
1738       if ( type != SMDSEntity_Tetra && type != SMDSEntity_Quad_Tetra )
1739         continue;
1740       if ( type == SMDSEntity_Quad_Tetra && v->GetNodeIndex( n1 ) > 3 )
1741         continue; // medium node not allowed
1742       const int ind2 = v->GetNodeIndex( n2 );
1743       if ( ind2 < 0 || 3 < ind2 )
1744         continue;
1745       const int ind3 = v->GetNodeIndex( n3 );
1746       if ( ind3 < 0 || 3 < ind3 )
1747         continue;
1748       return true;
1749     }
1750     return false;
1751   }
1752
1753   //=======================================================================
1754   /*!
1755    * \brief A key of a face of volume
1756    */
1757   //=======================================================================
1758
1759   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
1760   {
1761     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1762     {
1763       TIDSortedNodeSet sortedNodes;
1764       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1765       int nbNodes = vol.NbFaceNodes( iF );
1766       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1767       for ( int i = 0; i < nbNodes; i += iQ )
1768         sortedNodes.insert( fNodes[i] );
1769       TIDSortedNodeSet::iterator n = sortedNodes.begin();
1770       first.first   = (*(n++))->GetID();
1771       first.second  = (*(n++))->GetID();
1772       second.first  = (*(n++))->GetID();
1773       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
1774     }
1775   };
1776 } // namespace
1777
1778 //=======================================================================
1779 //function : SplitVolumesIntoTetra
1780 //purpose  : Split volume elements into tetrahedra.
1781 //=======================================================================
1782
1783 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1784                                               const int                theMethodFlags)
1785 {
1786   // std-like iterator on coordinates of nodes of mesh element
1787   typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1788   NXyzIterator xyzEnd;
1789
1790   SMDS_VolumeTool    volTool;
1791   SMESH_MesherHelper helper( *GetMesh());
1792
1793   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1794   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1795
1796   SMESH_SequenceOfElemPtr newNodes, newElems;
1797
1798   // map face of volume to it's baricenrtic node
1799   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1800   double bc[3];
1801
1802   TIDSortedElemSet::const_iterator elem = theElems.begin();
1803   for ( ; elem != theElems.end(); ++elem )
1804   {
1805     if ( (*elem)->GetType() != SMDSAbs_Volume )
1806       continue;
1807     SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1808     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
1809       continue;
1810
1811     if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange...
1812
1813     TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1814     if ( splitMethod._nbTetra < 1 ) continue;
1815
1816     // find submesh to add new tetras to
1817     if ( !subMesh || !subMesh->Contains( *elem ))
1818     {
1819       int shapeID = FindShape( *elem );
1820       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1821       subMesh = GetMeshDS()->MeshElements( shapeID );
1822     }
1823     int iQ;
1824     if ( (*elem)->IsQuadratic() )
1825     {
1826       iQ = 2;
1827       // add quadratic links to the helper
1828       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1829       {
1830         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1831         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
1832         for ( int iN = 0; iN < nbN; iN += iQ )
1833           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
1834       }
1835       helper.SetIsQuadratic( true );
1836     }
1837     else
1838     {
1839       iQ = 1;
1840       helper.SetIsQuadratic( false );
1841     }
1842     vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1843     helper.SetElementsOnShape( true );
1844     if ( splitMethod._baryNode )
1845     {
1846       // make a node at barycenter
1847       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1848       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1849       nodes.push_back( gcNode );
1850       newNodes.Append( gcNode );
1851     }
1852     if ( !splitMethod._faceBaryNode.empty() )
1853     {
1854       // make or find baricentric nodes of faces
1855       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1856       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1857       {
1858         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1859           volFace2BaryNode.insert
1860           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
1861         if ( !f_n->second )
1862         {
1863           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1864           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1865         }
1866         nodes.push_back( iF_n->second = f_n->second );
1867       }
1868     }
1869
1870     // make tetras
1871     vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1872     const int* tetConn = splitMethod._connectivity;
1873     for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1874       newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1875                                                        nodes[ tetConn[1] ],
1876                                                        nodes[ tetConn[2] ],
1877                                                        nodes[ tetConn[3] ]));
1878
1879     ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
1880
1881     // Split faces on sides of the split volume
1882
1883     const SMDS_MeshNode** volNodes = volTool.GetNodes();
1884     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1885     {
1886       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
1887       if ( nbNodes < 4 ) continue;
1888
1889       // find an existing face
1890       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
1891                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
1892       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
1893                                                                        /*noMedium=*/false))
1894       {
1895         // make triangles
1896         helper.SetElementsOnShape( false );
1897         vector< const SMDS_MeshElement* > triangles;
1898
1899         // find submesh to add new triangles in
1900         if ( !fSubMesh || !fSubMesh->Contains( face ))
1901         {
1902           int shapeID = FindShape( face );
1903           fSubMesh = GetMeshDS()->MeshElements( shapeID );
1904         }
1905         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
1906         if ( iF_n != splitMethod._faceBaryNode.end() )
1907         {
1908           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
1909           {
1910             const SMDS_MeshNode* n1 = fNodes[iN];
1911             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
1912             const SMDS_MeshNode *n3 = iF_n->second;
1913             if ( !volTool.IsFaceExternal( iF ))
1914               swap( n2, n3 );
1915             triangles.push_back( helper.AddFace( n1,n2,n3 ));
1916
1917             if ( fSubMesh && n3->getshapeId() < 1 )
1918               fSubMesh->AddNode( n3 );
1919           }
1920         }
1921         else
1922         {
1923           // among possible triangles create ones discribed by split method
1924           const int* nInd = volTool.GetFaceNodesIndices( iF );
1925           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1926           int iCom = 0; // common node of triangle faces to split into
1927           list< TTriangleFacet > facets;
1928           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
1929           {
1930             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
1931                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
1932                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
1933             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
1934                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
1935                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
1936             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
1937             {
1938               facets.push_back( t012 );
1939               facets.push_back( t023 );
1940               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
1941                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
1942                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
1943                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
1944               break;
1945             }
1946           }
1947           list< TTriangleFacet >::iterator facet = facets.begin();
1948           for ( ; facet != facets.end(); ++facet )
1949           {
1950             if ( !volTool.IsFaceExternal( iF ))
1951               swap( facet->_n2, facet->_n3 );
1952             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
1953                                                  volNodes[ facet->_n2 ],
1954                                                  volNodes[ facet->_n3 ]));
1955           }
1956         }
1957         for ( int i = 0; i < triangles.size(); ++i )
1958         {
1959           if ( !triangles[i] ) continue;
1960           if ( fSubMesh )
1961             fSubMesh->AddElement( triangles[i]);
1962           newElems.Append( triangles[i] );
1963         }
1964         ReplaceElemInGroups( face, triangles, GetMeshDS() );
1965         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
1966       }
1967
1968     } // loop on volume faces to split them into triangles
1969
1970     GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
1971
1972     if ( geomType == SMDSEntity_TriQuad_Hexa )
1973     {
1974       // remove medium nodes that could become free
1975       for ( int i = 20; i < volTool.NbNodes(); ++i )
1976         if ( volNodes[i]->NbInverseElements() == 0 )
1977           GetMeshDS()->RemoveNode( volNodes[i] );
1978     }
1979   } // loop on volumes to split
1980
1981   myLastCreatedNodes = newNodes;
1982   myLastCreatedElems = newElems;
1983 }
1984
1985 //=======================================================================
1986 //function : AddToSameGroups
1987 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
1988 //=======================================================================
1989
1990 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
1991                                         const SMDS_MeshElement* elemInGroups,
1992                                         SMESHDS_Mesh *          aMesh)
1993 {
1994   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1995   if (!groups.empty()) {
1996     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1997     for ( ; grIt != groups.end(); grIt++ ) {
1998       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
1999       if ( group && group->Contains( elemInGroups ))
2000         group->SMDSGroup().Add( elemToAdd );
2001     }
2002   }
2003 }
2004
2005
2006 //=======================================================================
2007 //function : RemoveElemFromGroups
2008 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2009 //=======================================================================
2010 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2011                                              SMESHDS_Mesh *          aMesh)
2012 {
2013   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2014   if (!groups.empty())
2015   {
2016     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2017     for (; GrIt != groups.end(); GrIt++)
2018     {
2019       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2020       if (!grp || grp->IsEmpty()) continue;
2021       grp->SMDSGroup().Remove(removeelem);
2022     }
2023   }
2024 }
2025
2026 //================================================================================
2027 /*!
2028  * \brief Replace elemToRm by elemToAdd in the all groups
2029  */
2030 //================================================================================
2031
2032 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2033                                             const SMDS_MeshElement* elemToAdd,
2034                                             SMESHDS_Mesh *          aMesh)
2035 {
2036   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2037   if (!groups.empty()) {
2038     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2039     for ( ; grIt != groups.end(); grIt++ ) {
2040       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2041       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2042         group->SMDSGroup().Add( elemToAdd );
2043     }
2044   }
2045 }
2046
2047 //================================================================================
2048 /*!
2049  * \brief Replace elemToRm by elemToAdd in the all groups
2050  */
2051 //================================================================================
2052
2053 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2054                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2055                                             SMESHDS_Mesh *                         aMesh)
2056 {
2057   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2058   if (!groups.empty())
2059   {
2060     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2061     for ( ; grIt != groups.end(); grIt++ ) {
2062       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2063       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2064         for ( int i = 0; i < elemToAdd.size(); ++i )
2065           group->SMDSGroup().Add( elemToAdd[ i ] );
2066     }
2067   }
2068 }
2069
2070 //=======================================================================
2071 //function : QuadToTri
2072 //purpose  : Cut quadrangles into triangles.
2073 //           theCrit is used to select a diagonal to cut
2074 //=======================================================================
2075
2076 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2077                                   const bool         the13Diag)
2078 {
2079   myLastCreatedElems.Clear();
2080   myLastCreatedNodes.Clear();
2081
2082   MESSAGE( "::QuadToTri()" );
2083
2084   SMESHDS_Mesh * aMesh = GetMeshDS();
2085
2086   Handle(Geom_Surface) surface;
2087   SMESH_MesherHelper   helper( *GetMesh() );
2088
2089   TIDSortedElemSet::iterator itElem;
2090   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2091     const SMDS_MeshElement* elem = *itElem;
2092     if ( !elem || elem->GetType() != SMDSAbs_Face )
2093       continue;
2094     bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2095     if(!isquad) continue;
2096
2097     if(elem->NbNodes()==4) {
2098       // retrieve element nodes
2099       const SMDS_MeshNode* aNodes [4];
2100       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2101       int i = 0;
2102       while ( itN->more() )
2103         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2104
2105       int aShapeId = FindShape( elem );
2106       const SMDS_MeshElement* newElem1 = 0;
2107       const SMDS_MeshElement* newElem2 = 0;
2108       if ( the13Diag ) {
2109         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2110         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2111       }
2112       else {
2113         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2114         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2115       }
2116       myLastCreatedElems.Append(newElem1);
2117       myLastCreatedElems.Append(newElem2);
2118       // put a new triangle on the same shape and add to the same groups
2119       if ( aShapeId )
2120         {
2121           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2122           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2123         }
2124       AddToSameGroups( newElem1, elem, aMesh );
2125       AddToSameGroups( newElem2, elem, aMesh );
2126       //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2127       aMesh->RemoveElement( elem );
2128     }
2129
2130     // Quadratic quadrangle
2131
2132     if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2133
2134       // get surface elem is on
2135       int aShapeId = FindShape( elem );
2136       if ( aShapeId != helper.GetSubShapeID() ) {
2137         surface.Nullify();
2138         TopoDS_Shape shape;
2139         if ( aShapeId > 0 )
2140           shape = aMesh->IndexToShape( aShapeId );
2141         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2142           TopoDS_Face face = TopoDS::Face( shape );
2143           surface = BRep_Tool::Surface( face );
2144           if ( !surface.IsNull() )
2145             helper.SetSubShape( shape );
2146         }
2147       }
2148
2149       const SMDS_MeshNode* aNodes [8];
2150       const SMDS_MeshNode* inFaceNode = 0;
2151       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2152       int i = 0;
2153       while ( itN->more() ) {
2154         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2155         if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2156              aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2157         {
2158           inFaceNode = aNodes[ i-1 ];
2159         }
2160       }
2161
2162       // find middle point for (0,1,2,3)
2163       // and create a node in this point;
2164       gp_XYZ p( 0,0,0 );
2165       if ( surface.IsNull() ) {
2166         for(i=0; i<4; i++)
2167           p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2168         p /= 4;
2169       }
2170       else {
2171         TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2172         gp_XY uv( 0,0 );
2173         for(i=0; i<4; i++)
2174           uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2175         uv /= 4.;
2176         p = surface->Value( uv.X(), uv.Y() ).XYZ();
2177       }
2178       const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2179       myLastCreatedNodes.Append(newN);
2180
2181       // create a new element
2182       const SMDS_MeshElement* newElem1 = 0;
2183       const SMDS_MeshElement* newElem2 = 0;
2184       if ( the13Diag ) {
2185         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2186                                   aNodes[6], aNodes[7], newN );
2187         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2188                                   newN,      aNodes[4], aNodes[5] );
2189       }
2190       else {
2191         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2192                                   aNodes[7], aNodes[4], newN );
2193         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2194                                   newN,      aNodes[5], aNodes[6] );
2195       }
2196       myLastCreatedElems.Append(newElem1);
2197       myLastCreatedElems.Append(newElem2);
2198       // put a new triangle on the same shape and add to the same groups
2199       if ( aShapeId )
2200         {
2201           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2202           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2203         }
2204       AddToSameGroups( newElem1, elem, aMesh );
2205       AddToSameGroups( newElem2, elem, aMesh );
2206       aMesh->RemoveElement( elem );
2207     }
2208   }
2209
2210   return true;
2211 }
2212
2213 //=======================================================================
2214 //function : getAngle
2215 //purpose  :
2216 //=======================================================================
2217
2218 double getAngle(const SMDS_MeshElement * tr1,
2219                 const SMDS_MeshElement * tr2,
2220                 const SMDS_MeshNode *    n1,
2221                 const SMDS_MeshNode *    n2)
2222 {
2223   double angle = 2. * M_PI; // bad angle
2224
2225   // get normals
2226   SMESH::Controls::TSequenceOfXYZ P1, P2;
2227   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2228        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2229     return angle;
2230   gp_Vec N1,N2;
2231   if(!tr1->IsQuadratic())
2232     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2233   else
2234     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2235   if ( N1.SquareMagnitude() <= gp::Resolution() )
2236     return angle;
2237   if(!tr2->IsQuadratic())
2238     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2239   else
2240     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2241   if ( N2.SquareMagnitude() <= gp::Resolution() )
2242     return angle;
2243
2244   // find the first diagonal node n1 in the triangles:
2245   // take in account a diagonal link orientation
2246   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2247   for ( int t = 0; t < 2; t++ ) {
2248     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2249     int i = 0, iDiag = -1;
2250     while ( it->more()) {
2251       const SMDS_MeshElement *n = it->next();
2252       if ( n == n1 || n == n2 ) {
2253         if ( iDiag < 0)
2254           iDiag = i;
2255         else {
2256           if ( i - iDiag == 1 )
2257             nFirst[ t ] = ( n == n1 ? n2 : n1 );
2258           else
2259             nFirst[ t ] = n;
2260           break;
2261         }
2262       }
2263       i++;
2264     }
2265   }
2266   if ( nFirst[ 0 ] == nFirst[ 1 ] )
2267     N2.Reverse();
2268
2269   angle = N1.Angle( N2 );
2270   //SCRUTE( angle );
2271   return angle;
2272 }
2273
2274 // =================================================
2275 // class generating a unique ID for a pair of nodes
2276 // and able to return nodes by that ID
2277 // =================================================
2278 class LinkID_Gen {
2279 public:
2280
2281   LinkID_Gen( const SMESHDS_Mesh* theMesh )
2282     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2283   {}
2284
2285   long GetLinkID (const SMDS_MeshNode * n1,
2286                   const SMDS_MeshNode * n2) const
2287   {
2288     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2289   }
2290
2291   bool GetNodes (const long             theLinkID,
2292                  const SMDS_MeshNode* & theNode1,
2293                  const SMDS_MeshNode* & theNode2) const
2294   {
2295     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2296     if ( !theNode1 ) return false;
2297     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2298     if ( !theNode2 ) return false;
2299     return true;
2300   }
2301
2302 private:
2303   LinkID_Gen();
2304   const SMESHDS_Mesh* myMesh;
2305   long                myMaxID;
2306 };
2307
2308
2309 //=======================================================================
2310 //function : TriToQuad
2311 //purpose  : Fuse neighbour triangles into quadrangles.
2312 //           theCrit is used to select a neighbour to fuse with.
2313 //           theMaxAngle is a max angle between element normals at which
2314 //           fusion is still performed.
2315 //=======================================================================
2316
2317 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
2318                                   SMESH::Controls::NumericalFunctorPtr theCrit,
2319                                   const double                         theMaxAngle)
2320 {
2321   myLastCreatedElems.Clear();
2322   myLastCreatedNodes.Clear();
2323
2324   MESSAGE( "::TriToQuad()" );
2325
2326   if ( !theCrit.get() )
2327     return false;
2328
2329   SMESHDS_Mesh * aMesh = GetMeshDS();
2330
2331   // Prepare data for algo: build
2332   // 1. map of elements with their linkIDs
2333   // 2. map of linkIDs with their elements
2334
2335   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2336   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2337   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
2338   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2339
2340   TIDSortedElemSet::iterator itElem;
2341   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2342     const SMDS_MeshElement* elem = *itElem;
2343     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2344     bool IsTria = elem->NbNodes()==3 || (elem->NbNodes()==6 && elem->IsQuadratic());
2345     if(!IsTria) continue;
2346
2347     // retrieve element nodes
2348     const SMDS_MeshNode* aNodes [4];
2349     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2350     int i = 0;
2351     while ( i<3 )
2352       aNodes[ i++ ] = cast2Node( itN->next() );
2353     aNodes[ 3 ] = aNodes[ 0 ];
2354
2355     // fill maps
2356     for ( i = 0; i < 3; i++ ) {
2357       SMESH_TLink link( aNodes[i], aNodes[i+1] );
2358       // check if elements sharing a link can be fused
2359       itLE = mapLi_listEl.find( link );
2360       if ( itLE != mapLi_listEl.end() ) {
2361         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2362           continue;
2363         const SMDS_MeshElement* elem2 = (*itLE).second.front();
2364         //if ( FindShape( elem ) != FindShape( elem2 ))
2365         //  continue; // do not fuse triangles laying on different shapes
2366         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2367           continue; // avoid making badly shaped quads
2368         (*itLE).second.push_back( elem );
2369       }
2370       else {
2371         mapLi_listEl[ link ].push_back( elem );
2372       }
2373       mapEl_setLi [ elem ].insert( link );
2374     }
2375   }
2376   // Clean the maps from the links shared by a sole element, ie
2377   // links to which only one element is bound in mapLi_listEl
2378
2379   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2380     int nbElems = (*itLE).second.size();
2381     if ( nbElems < 2  ) {
2382       const SMDS_MeshElement* elem = (*itLE).second.front();
2383       SMESH_TLink link = (*itLE).first;
2384       mapEl_setLi[ elem ].erase( link );
2385       if ( mapEl_setLi[ elem ].empty() )
2386         mapEl_setLi.erase( elem );
2387     }
2388   }
2389
2390   // Algo: fuse triangles into quadrangles
2391
2392   while ( ! mapEl_setLi.empty() ) {
2393     // Look for the start element:
2394     // the element having the least nb of shared links
2395     const SMDS_MeshElement* startElem = 0;
2396     int minNbLinks = 4;
2397     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2398       int nbLinks = (*itEL).second.size();
2399       if ( nbLinks < minNbLinks ) {
2400         startElem = (*itEL).first;
2401         minNbLinks = nbLinks;
2402         if ( minNbLinks == 1 )
2403           break;
2404       }
2405     }
2406
2407     // search elements to fuse starting from startElem or links of elements
2408     // fused earlyer - startLinks
2409     list< SMESH_TLink > startLinks;
2410     while ( startElem || !startLinks.empty() ) {
2411       while ( !startElem && !startLinks.empty() ) {
2412         // Get an element to start, by a link
2413         SMESH_TLink linkId = startLinks.front();
2414         startLinks.pop_front();
2415         itLE = mapLi_listEl.find( linkId );
2416         if ( itLE != mapLi_listEl.end() ) {
2417           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2418           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2419           for ( ; itE != listElem.end() ; itE++ )
2420             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2421               startElem = (*itE);
2422           mapLi_listEl.erase( itLE );
2423         }
2424       }
2425
2426       if ( startElem ) {
2427         // Get candidates to be fused
2428         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2429         const SMESH_TLink *link12, *link13;
2430         startElem = 0;
2431         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2432         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2433         ASSERT( !setLi.empty() );
2434         set< SMESH_TLink >::iterator itLi;
2435         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2436         {
2437           const SMESH_TLink & link = (*itLi);
2438           itLE = mapLi_listEl.find( link );
2439           if ( itLE == mapLi_listEl.end() )
2440             continue;
2441
2442           const SMDS_MeshElement* elem = (*itLE).second.front();
2443           if ( elem == tr1 )
2444             elem = (*itLE).second.back();
2445           mapLi_listEl.erase( itLE );
2446           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2447             continue;
2448           if ( tr2 ) {
2449             tr3 = elem;
2450             link13 = &link;
2451           }
2452           else {
2453             tr2 = elem;
2454             link12 = &link;
2455           }
2456
2457           // add other links of elem to list of links to re-start from
2458           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2459           set< SMESH_TLink >::iterator it;
2460           for ( it = links.begin(); it != links.end(); it++ ) {
2461             const SMESH_TLink& link2 = (*it);
2462             if ( link2 != link )
2463               startLinks.push_back( link2 );
2464           }
2465         }
2466
2467         // Get nodes of possible quadrangles
2468         const SMDS_MeshNode *n12 [4], *n13 [4];
2469         bool Ok12 = false, Ok13 = false;
2470         const SMDS_MeshNode *linkNode1, *linkNode2;
2471         if(tr2) {
2472           linkNode1 = link12->first;
2473           linkNode2 = link12->second;
2474           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2475             Ok12 = true;
2476         }
2477         if(tr3) {
2478           linkNode1 = link13->first;
2479           linkNode2 = link13->second;
2480           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2481             Ok13 = true;
2482         }
2483
2484         // Choose a pair to fuse
2485         if ( Ok12 && Ok13 ) {
2486           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2487           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2488           double aBadRate12 = getBadRate( &quad12, theCrit );
2489           double aBadRate13 = getBadRate( &quad13, theCrit );
2490           if (  aBadRate13 < aBadRate12 )
2491             Ok12 = false;
2492           else
2493             Ok13 = false;
2494         }
2495
2496         // Make quadrangles
2497         // and remove fused elems and removed links from the maps
2498         mapEl_setLi.erase( tr1 );
2499         if ( Ok12 ) {
2500           mapEl_setLi.erase( tr2 );
2501           mapLi_listEl.erase( *link12 );
2502           if(tr1->NbNodes()==3) {
2503             const SMDS_MeshElement* newElem = 0;
2504             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2505             myLastCreatedElems.Append(newElem);
2506             AddToSameGroups( newElem, tr1, aMesh );
2507             int aShapeId = tr1->getshapeId();
2508             if ( aShapeId )
2509               {
2510                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2511               }
2512             aMesh->RemoveElement( tr1 );
2513             aMesh->RemoveElement( tr2 );
2514           }
2515           else {
2516             const SMDS_MeshNode* N1 [6];
2517             const SMDS_MeshNode* N2 [6];
2518             GetNodesFromTwoTria(tr1,tr2,N1,N2);
2519             // now we receive following N1 and N2 (using numeration as above image)
2520             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
2521             // i.e. first nodes from both arrays determ new diagonal
2522             const SMDS_MeshNode* aNodes[8];
2523             aNodes[0] = N1[0];
2524             aNodes[1] = N1[1];
2525             aNodes[2] = N2[0];
2526             aNodes[3] = N2[1];
2527             aNodes[4] = N1[3];
2528             aNodes[5] = N2[5];
2529             aNodes[6] = N2[3];
2530             aNodes[7] = N1[5];
2531             const SMDS_MeshElement* newElem = 0;
2532             newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2533                                      aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2534             myLastCreatedElems.Append(newElem);
2535             AddToSameGroups( newElem, tr1, aMesh );
2536             int aShapeId = tr1->getshapeId();
2537             if ( aShapeId )
2538               {
2539                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2540               }
2541             aMesh->RemoveElement( tr1 );
2542             aMesh->RemoveElement( tr2 );
2543             // remove middle node (9)
2544             GetMeshDS()->RemoveNode( N1[4] );
2545           }
2546         }
2547         else if ( Ok13 ) {
2548           mapEl_setLi.erase( tr3 );
2549           mapLi_listEl.erase( *link13 );
2550           if(tr1->NbNodes()==3) {
2551             const SMDS_MeshElement* newElem = 0;
2552             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2553             myLastCreatedElems.Append(newElem);
2554             AddToSameGroups( newElem, tr1, aMesh );
2555             int aShapeId = tr1->getshapeId();
2556             if ( aShapeId )
2557               {
2558                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2559               }
2560             aMesh->RemoveElement( tr1 );
2561             aMesh->RemoveElement( tr3 );
2562           }
2563           else {
2564             const SMDS_MeshNode* N1 [6];
2565             const SMDS_MeshNode* N2 [6];
2566             GetNodesFromTwoTria(tr1,tr3,N1,N2);
2567             // now we receive following N1 and N2 (using numeration as above image)
2568             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
2569             // i.e. first nodes from both arrays determ new diagonal
2570             const SMDS_MeshNode* aNodes[8];
2571             aNodes[0] = N1[0];
2572             aNodes[1] = N1[1];
2573             aNodes[2] = N2[0];
2574             aNodes[3] = N2[1];
2575             aNodes[4] = N1[3];
2576             aNodes[5] = N2[5];
2577             aNodes[6] = N2[3];
2578             aNodes[7] = N1[5];
2579             const SMDS_MeshElement* newElem = 0;
2580             newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2581                                      aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2582             myLastCreatedElems.Append(newElem);
2583             AddToSameGroups( newElem, tr1, aMesh );
2584             int aShapeId = tr1->getshapeId();
2585             if ( aShapeId )
2586               {
2587                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2588               }
2589             aMesh->RemoveElement( tr1 );
2590             aMesh->RemoveElement( tr3 );
2591             // remove middle node (9)
2592             GetMeshDS()->RemoveNode( N1[4] );
2593           }
2594         }
2595
2596         // Next element to fuse: the rejected one
2597         if ( tr3 )
2598           startElem = Ok12 ? tr3 : tr2;
2599
2600       } // if ( startElem )
2601     } // while ( startElem || !startLinks.empty() )
2602   } // while ( ! mapEl_setLi.empty() )
2603
2604   return true;
2605 }
2606
2607
2608 /*#define DUMPSO(txt) \
2609 //  cout << txt << endl;
2610 //=============================================================================
2611 //
2612 //
2613 //
2614 //=============================================================================
2615 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2616 {
2617 if ( i1 == i2 )
2618 return;
2619 int tmp = idNodes[ i1 ];
2620 idNodes[ i1 ] = idNodes[ i2 ];
2621 idNodes[ i2 ] = tmp;
2622 gp_Pnt Ptmp = P[ i1 ];
2623 P[ i1 ] = P[ i2 ];
2624 P[ i2 ] = Ptmp;
2625 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2626 }
2627
2628 //=======================================================================
2629 //function : SortQuadNodes
2630 //purpose  : Set 4 nodes of a quadrangle face in a good order.
2631 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
2632 //           1 or 2 else 0.
2633 //=======================================================================
2634
2635 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2636 int               idNodes[] )
2637 {
2638   gp_Pnt P[4];
2639   int i;
2640   for ( i = 0; i < 4; i++ ) {
2641     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2642     if ( !n ) return 0;
2643     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2644   }
2645
2646   gp_Vec V1(P[0], P[1]);
2647   gp_Vec V2(P[0], P[2]);
2648   gp_Vec V3(P[0], P[3]);
2649
2650   gp_Vec Cross1 = V1 ^ V2;
2651   gp_Vec Cross2 = V2 ^ V3;
2652
2653   i = 0;
2654   if (Cross1.Dot(Cross2) < 0)
2655   {
2656     Cross1 = V2 ^ V1;
2657     Cross2 = V1 ^ V3;
2658
2659     if (Cross1.Dot(Cross2) < 0)
2660       i = 2;
2661     else
2662       i = 1;
2663     swap ( i, i + 1, idNodes, P );
2664
2665     //     for ( int ii = 0; ii < 4; ii++ ) {
2666     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2667     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2668     //     }
2669   }
2670   return i;
2671 }
2672
2673 //=======================================================================
2674 //function : SortHexaNodes
2675 //purpose  : Set 8 nodes of a hexahedron in a good order.
2676 //           Return success status
2677 //=======================================================================
2678
2679 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2680                                       int               idNodes[] )
2681 {
2682   gp_Pnt P[8];
2683   int i;
2684   DUMPSO( "INPUT: ========================================");
2685   for ( i = 0; i < 8; i++ ) {
2686     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2687     if ( !n ) return false;
2688     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2689     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2690   }
2691   DUMPSO( "========================================");
2692
2693
2694   set<int> faceNodes;  // ids of bottom face nodes, to be found
2695   set<int> checkedId1; // ids of tried 2-nd nodes
2696   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2697   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
2698   int iMin, iLoop1 = 0;
2699
2700   // Loop to try the 2-nd nodes
2701
2702   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2703   {
2704     // Find not checked 2-nd node
2705     for ( i = 1; i < 8; i++ )
2706       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2707         int id1 = idNodes[i];
2708         swap ( 1, i, idNodes, P );
2709         checkedId1.insert ( id1 );
2710         break;
2711       }
2712
2713     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2714     // ie that all but meybe one (id3 which is on the same face) nodes
2715     // lay on the same side from the triangle plane.
2716
2717     bool manyInPlane = false; // more than 4 nodes lay in plane
2718     int iLoop2 = 0;
2719     while ( ++iLoop2 < 6 ) {
2720
2721       // get 1-2-3 plane coeffs
2722       Standard_Real A, B, C, D;
2723       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2724       if ( N.SquareMagnitude() > gp::Resolution() )
2725       {
2726         gp_Pln pln ( P[0], N );
2727         pln.Coefficients( A, B, C, D );
2728
2729         // find the node (iMin) closest to pln
2730         Standard_Real dist[ 8 ], minDist = DBL_MAX;
2731         set<int> idInPln;
2732         for ( i = 3; i < 8; i++ ) {
2733           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2734           if ( fabs( dist[i] ) < minDist ) {
2735             minDist = fabs( dist[i] );
2736             iMin = i;
2737           }
2738           if ( fabs( dist[i] ) <= tol )
2739             idInPln.insert( idNodes[i] );
2740         }
2741
2742         // there should not be more than 4 nodes in bottom plane
2743         if ( idInPln.size() > 1 )
2744         {
2745           DUMPSO( "### idInPln.size() = " << idInPln.size());
2746           // idInPlane does not contain the first 3 nodes
2747           if ( manyInPlane || idInPln.size() == 5)
2748             return false; // all nodes in one plane
2749           manyInPlane = true;
2750
2751           // set the 1-st node to be not in plane
2752           for ( i = 3; i < 8; i++ ) {
2753             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2754               DUMPSO( "### Reset 0-th node");
2755               swap( 0, i, idNodes, P );
2756               break;
2757             }
2758           }
2759
2760           // reset to re-check second nodes
2761           leastDist = DBL_MAX;
2762           faceNodes.clear();
2763           checkedId1.clear();
2764           iLoop1 = 0;
2765           break; // from iLoop2;
2766         }
2767
2768         // check that the other 4 nodes are on the same side
2769         bool sameSide = true;
2770         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2771         for ( i = 3; sameSide && i < 8; i++ ) {
2772           if ( i != iMin )
2773             sameSide = ( isNeg == dist[i] <= 0.);
2774         }
2775
2776         // keep best solution
2777         if ( sameSide && minDist < leastDist ) {
2778           leastDist = minDist;
2779           faceNodes.clear();
2780           faceNodes.insert( idNodes[ 1 ] );
2781           faceNodes.insert( idNodes[ 2 ] );
2782           faceNodes.insert( idNodes[ iMin ] );
2783           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2784                   << " leastDist = " << leastDist);
2785           if ( leastDist <= DBL_MIN )
2786             break;
2787         }
2788       }
2789
2790       // set next 3-d node to check
2791       int iNext = 2 + iLoop2;
2792       if ( iNext < 8 ) {
2793         DUMPSO( "Try 2-nd");
2794         swap ( 2, iNext, idNodes, P );
2795       }
2796     } // while ( iLoop2 < 6 )
2797   } // iLoop1
2798
2799   if ( faceNodes.empty() ) return false;
2800
2801   // Put the faceNodes in proper places
2802   for ( i = 4; i < 8; i++ ) {
2803     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2804       // find a place to put
2805       int iTo = 1;
2806       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2807         iTo++;
2808       DUMPSO( "Set faceNodes");
2809       swap ( iTo, i, idNodes, P );
2810     }
2811   }
2812
2813
2814   // Set nodes of the found bottom face in good order
2815   DUMPSO( " Found bottom face: ");
2816   i = SortQuadNodes( theMesh, idNodes );
2817   if ( i ) {
2818     gp_Pnt Ptmp = P[ i ];
2819     P[ i ] = P[ i+1 ];
2820     P[ i+1 ] = Ptmp;
2821   }
2822   //   else
2823   //     for ( int ii = 0; ii < 4; ii++ ) {
2824   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2825   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2826   //    }
2827
2828   // Gravity center of the top and bottom faces
2829   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2830   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2831
2832   // Get direction from the bottom to the top face
2833   gp_Vec upDir ( aGCb, aGCt );
2834   Standard_Real upDirSize = upDir.Magnitude();
2835   if ( upDirSize <= gp::Resolution() ) return false;
2836   upDir / upDirSize;
2837
2838   // Assure that the bottom face normal points up
2839   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2840   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2841   if ( Nb.Dot( upDir ) < 0 ) {
2842     DUMPSO( "Reverse bottom face");
2843     swap( 1, 3, idNodes, P );
2844   }
2845
2846   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2847   Standard_Real minDist = DBL_MAX;
2848   for ( i = 4; i < 8; i++ ) {
2849     // projection of P[i] to the plane defined by P[0] and upDir
2850     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2851     Standard_Real sqDist = P[0].SquareDistance( Pp );
2852     if ( sqDist < minDist ) {
2853       minDist = sqDist;
2854       iMin = i;
2855     }
2856   }
2857   DUMPSO( "Set 4-th");
2858   swap ( 4, iMin, idNodes, P );
2859
2860   // Set nodes of the top face in good order
2861   DUMPSO( "Sort top face");
2862   i = SortQuadNodes( theMesh, &idNodes[4] );
2863   if ( i ) {
2864     i += 4;
2865     gp_Pnt Ptmp = P[ i ];
2866     P[ i ] = P[ i+1 ];
2867     P[ i+1 ] = Ptmp;
2868   }
2869
2870   // Assure that direction of the top face normal is from the bottom face
2871   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
2872   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
2873   if ( Nt.Dot( upDir ) < 0 ) {
2874     DUMPSO( "Reverse top face");
2875     swap( 5, 7, idNodes, P );
2876   }
2877
2878   //   DUMPSO( "OUTPUT: ========================================");
2879   //   for ( i = 0; i < 8; i++ ) {
2880   //     float *p = ugrid->GetPoint(idNodes[i]);
2881   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
2882   //   }
2883
2884   return true;
2885 }*/
2886
2887 //================================================================================
2888 /*!
2889  * \brief Return nodes linked to the given one
2890  * \param theNode - the node
2891  * \param linkedNodes - the found nodes
2892  * \param type - the type of elements to check
2893  *
2894  * Medium nodes are ignored
2895  */
2896 //================================================================================
2897
2898 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
2899                                        TIDSortedElemSet &   linkedNodes,
2900                                        SMDSAbs_ElementType  type )
2901 {
2902   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
2903   while ( elemIt->more() )
2904   {
2905     const SMDS_MeshElement* elem = elemIt->next();
2906     if(elem->GetType() == SMDSAbs_0DElement)
2907       continue;
2908
2909     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
2910     if ( elem->GetType() == SMDSAbs_Volume )
2911     {
2912       SMDS_VolumeTool vol( elem );
2913       while ( nodeIt->more() ) {
2914         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2915         if ( theNode != n && vol.IsLinked( theNode, n ))
2916           linkedNodes.insert( n );
2917       }
2918     }
2919     else
2920     {
2921       for ( int i = 0; nodeIt->more(); ++i ) {
2922         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2923         if ( n == theNode ) {
2924           int iBefore = i - 1;
2925           int iAfter  = i + 1;
2926           if ( elem->IsQuadratic() ) {
2927             int nb = elem->NbNodes() / 2;
2928             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
2929             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
2930           }
2931           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
2932           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
2933         }
2934       }
2935     }
2936   }
2937 }
2938
2939 //=======================================================================
2940 //function : laplacianSmooth
2941 //purpose  : pulls theNode toward the center of surrounding nodes directly
2942 //           connected to that node along an element edge
2943 //=======================================================================
2944
2945 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
2946                      const Handle(Geom_Surface)&          theSurface,
2947                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2948 {
2949   // find surrounding nodes
2950
2951   TIDSortedElemSet nodeSet;
2952   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
2953
2954   // compute new coodrs
2955
2956   double coord[] = { 0., 0., 0. };
2957   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
2958   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
2959     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
2960     if ( theSurface.IsNull() ) { // smooth in 3D
2961       coord[0] += node->X();
2962       coord[1] += node->Y();
2963       coord[2] += node->Z();
2964     }
2965     else { // smooth in 2D
2966       ASSERT( theUVMap.find( node ) != theUVMap.end() );
2967       gp_XY* uv = theUVMap[ node ];
2968       coord[0] += uv->X();
2969       coord[1] += uv->Y();
2970     }
2971   }
2972   int nbNodes = nodeSet.size();
2973   if ( !nbNodes )
2974     return;
2975   coord[0] /= nbNodes;
2976   coord[1] /= nbNodes;
2977
2978   if ( !theSurface.IsNull() ) {
2979     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
2980     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
2981     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
2982     coord[0] = p3d.X();
2983     coord[1] = p3d.Y();
2984     coord[2] = p3d.Z();
2985   }
2986   else
2987     coord[2] /= nbNodes;
2988
2989   // move node
2990
2991   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
2992 }
2993
2994 //=======================================================================
2995 //function : centroidalSmooth
2996 //purpose  : pulls theNode toward the element-area-weighted centroid of the
2997 //           surrounding elements
2998 //=======================================================================
2999
3000 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3001                       const Handle(Geom_Surface)&          theSurface,
3002                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3003 {
3004   gp_XYZ aNewXYZ(0.,0.,0.);
3005   SMESH::Controls::Area anAreaFunc;
3006   double totalArea = 0.;
3007   int nbElems = 0;
3008
3009   // compute new XYZ
3010
3011   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3012   while ( elemIt->more() )
3013   {
3014     const SMDS_MeshElement* elem = elemIt->next();
3015     nbElems++;
3016
3017     gp_XYZ elemCenter(0.,0.,0.);
3018     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3019     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3020     int nn = elem->NbNodes();
3021     if(elem->IsQuadratic()) nn = nn/2;
3022     int i=0;
3023     //while ( itN->more() ) {
3024     while ( i<nn ) {
3025       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3026       i++;
3027       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3028       aNodePoints.push_back( aP );
3029       if ( !theSurface.IsNull() ) { // smooth in 2D
3030         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3031         gp_XY* uv = theUVMap[ aNode ];
3032         aP.SetCoord( uv->X(), uv->Y(), 0. );
3033       }
3034       elemCenter += aP;
3035     }
3036     double elemArea = anAreaFunc.GetValue( aNodePoints );
3037     totalArea += elemArea;
3038     elemCenter /= nn;
3039     aNewXYZ += elemCenter * elemArea;
3040   }
3041   aNewXYZ /= totalArea;
3042   if ( !theSurface.IsNull() ) {
3043     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3044     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3045   }
3046
3047   // move node
3048
3049   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3050 }
3051
3052 //=======================================================================
3053 //function : getClosestUV
3054 //purpose  : return UV of closest projection
3055 //=======================================================================
3056
3057 static bool getClosestUV (Extrema_GenExtPS& projector,
3058                           const gp_Pnt&     point,
3059                           gp_XY &           result)
3060 {
3061   projector.Perform( point );
3062   if ( projector.IsDone() ) {
3063     double u, v, minVal = DBL_MAX;
3064     for ( int i = projector.NbExt(); i > 0; i-- )
3065 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3066       if ( projector.SquareDistance( i ) < minVal ) {
3067         minVal = projector.SquareDistance( i );
3068 #else
3069       if ( projector.Value( i ) < minVal ) {
3070         minVal = projector.Value( i );
3071 #endif
3072         projector.Point( i ).Parameter( u, v );
3073       }
3074     result.SetCoord( u, v );
3075     return true;
3076   }
3077   return false;
3078 }
3079
3080 //=======================================================================
3081 //function : Smooth
3082 //purpose  : Smooth theElements during theNbIterations or until a worst
3083 //           element has aspect ratio <= theTgtAspectRatio.
3084 //           Aspect Ratio varies in range [1.0, inf].
3085 //           If theElements is empty, the whole mesh is smoothed.
3086 //           theFixedNodes contains additionally fixed nodes. Nodes built
3087 //           on edges and boundary nodes are always fixed.
3088 //=======================================================================
3089
3090 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3091                                set<const SMDS_MeshNode*> & theFixedNodes,
3092                                const SmoothMethod          theSmoothMethod,
3093                                const int                   theNbIterations,
3094                                double                      theTgtAspectRatio,
3095                                const bool                  the2D)
3096 {
3097   myLastCreatedElems.Clear();
3098   myLastCreatedNodes.Clear();
3099
3100   MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3101
3102   if ( theTgtAspectRatio < 1.0 )
3103     theTgtAspectRatio = 1.0;
3104
3105   const double disttol = 1.e-16;
3106
3107   SMESH::Controls::AspectRatio aQualityFunc;
3108
3109   SMESHDS_Mesh* aMesh = GetMeshDS();
3110
3111   if ( theElems.empty() ) {
3112     // add all faces to theElems
3113     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3114     while ( fIt->more() ) {
3115       const SMDS_MeshElement* face = fIt->next();
3116       theElems.insert( face );
3117     }
3118   }
3119   // get all face ids theElems are on
3120   set< int > faceIdSet;
3121   TIDSortedElemSet::iterator itElem;
3122   if ( the2D )
3123     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3124       int fId = FindShape( *itElem );
3125       // check that corresponding submesh exists and a shape is face
3126       if (fId &&
3127           faceIdSet.find( fId ) == faceIdSet.end() &&
3128           aMesh->MeshElements( fId )) {
3129         TopoDS_Shape F = aMesh->IndexToShape( fId );
3130         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3131           faceIdSet.insert( fId );
3132       }
3133     }
3134   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3135
3136   // ===============================================
3137   // smooth elements on each TopoDS_Face separately
3138   // ===============================================
3139
3140   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3141   for ( ; fId != faceIdSet.rend(); ++fId ) {
3142     // get face surface and submesh
3143     Handle(Geom_Surface) surface;
3144     SMESHDS_SubMesh* faceSubMesh = 0;
3145     TopoDS_Face face;
3146     double fToler2 = 0, f,l;
3147     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3148     bool isUPeriodic = false, isVPeriodic = false;
3149     if ( *fId ) {
3150       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3151       surface = BRep_Tool::Surface( face );
3152       faceSubMesh = aMesh->MeshElements( *fId );
3153       fToler2 = BRep_Tool::Tolerance( face );
3154       fToler2 *= fToler2 * 10.;
3155       isUPeriodic = surface->IsUPeriodic();
3156       if ( isUPeriodic )
3157         surface->UPeriod();
3158       isVPeriodic = surface->IsVPeriodic();
3159       if ( isVPeriodic )
3160         surface->VPeriod();
3161       surface->Bounds( u1, u2, v1, v2 );
3162     }
3163     // ---------------------------------------------------------
3164     // for elements on a face, find movable and fixed nodes and
3165     // compute UV for them
3166     // ---------------------------------------------------------
3167     bool checkBoundaryNodes = false;
3168     bool isQuadratic = false;
3169     set<const SMDS_MeshNode*> setMovableNodes;
3170     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3171     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3172     list< const SMDS_MeshElement* > elemsOnFace;
3173
3174     Extrema_GenExtPS projector;
3175     GeomAdaptor_Surface surfAdaptor;
3176     if ( !surface.IsNull() ) {
3177       surfAdaptor.Load( surface );
3178       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3179     }
3180     int nbElemOnFace = 0;
3181     itElem = theElems.begin();
3182     // loop on not yet smoothed elements: look for elems on a face
3183     while ( itElem != theElems.end() ) {
3184       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3185         break; // all elements found
3186
3187       const SMDS_MeshElement* elem = *itElem;
3188       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3189            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3190         ++itElem;
3191         continue;
3192       }
3193       elemsOnFace.push_back( elem );
3194       theElems.erase( itElem++ );
3195       nbElemOnFace++;
3196
3197       if ( !isQuadratic )
3198         isQuadratic = elem->IsQuadratic();
3199
3200       // get movable nodes of elem
3201       const SMDS_MeshNode* node;
3202       SMDS_TypeOfPosition posType;
3203       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3204       int nn = 0, nbn =  elem->NbNodes();
3205       if(elem->IsQuadratic())
3206         nbn = nbn/2;
3207       while ( nn++ < nbn ) {
3208         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3209         const SMDS_PositionPtr& pos = node->GetPosition();
3210         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3211         if (posType != SMDS_TOP_EDGE &&
3212             posType != SMDS_TOP_VERTEX &&
3213             theFixedNodes.find( node ) == theFixedNodes.end())
3214         {
3215           // check if all faces around the node are on faceSubMesh
3216           // because a node on edge may be bound to face
3217           SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3218           bool all = true;
3219           if ( faceSubMesh ) {
3220             while ( eIt->more() && all ) {
3221               const SMDS_MeshElement* e = eIt->next();
3222               all = faceSubMesh->Contains( e );
3223             }
3224           }
3225           if ( all )
3226             setMovableNodes.insert( node );
3227           else
3228             checkBoundaryNodes = true;
3229         }
3230         if ( posType == SMDS_TOP_3DSPACE )
3231           checkBoundaryNodes = true;
3232       }
3233
3234       if ( surface.IsNull() )
3235         continue;
3236
3237       // get nodes to check UV
3238       list< const SMDS_MeshNode* > uvCheckNodes;
3239       itN = elem->nodesIterator();
3240       nn = 0; nbn =  elem->NbNodes();
3241       if(elem->IsQuadratic())
3242         nbn = nbn/2;
3243       while ( nn++ < nbn ) {
3244         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3245         if ( uvMap.find( node ) == uvMap.end() )
3246           uvCheckNodes.push_back( node );
3247         // add nodes of elems sharing node
3248         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3249         //         while ( eIt->more() ) {
3250         //           const SMDS_MeshElement* e = eIt->next();
3251         //           if ( e != elem ) {
3252         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3253         //             while ( nIt->more() ) {
3254         //               const SMDS_MeshNode* n =
3255         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3256         //               if ( uvMap.find( n ) == uvMap.end() )
3257         //                 uvCheckNodes.push_back( n );
3258         //             }
3259         //           }
3260         //         }
3261       }
3262       // check UV on face
3263       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3264       for ( ; n != uvCheckNodes.end(); ++n ) {
3265         node = *n;
3266         gp_XY uv( 0, 0 );
3267         const SMDS_PositionPtr& pos = node->GetPosition();
3268         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3269         // get existing UV
3270         switch ( posType ) {
3271         case SMDS_TOP_FACE: {
3272           SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3273           uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3274           break;
3275         }
3276         case SMDS_TOP_EDGE: {
3277           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3278           Handle(Geom2d_Curve) pcurve;
3279           if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3280             pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3281           if ( !pcurve.IsNull() ) {
3282             double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3283             uv = pcurve->Value( u ).XY();
3284           }
3285           break;
3286         }
3287         case SMDS_TOP_VERTEX: {
3288           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3289           if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3290             uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3291           break;
3292         }
3293         default:;
3294         }
3295         // check existing UV
3296         bool project = true;
3297         gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3298         double dist1 = DBL_MAX, dist2 = 0;
3299         if ( posType != SMDS_TOP_3DSPACE ) {
3300           dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3301           project = dist1 > fToler2;
3302         }
3303         if ( project ) { // compute new UV
3304           gp_XY newUV;
3305           if ( !getClosestUV( projector, pNode, newUV )) {
3306             MESSAGE("Node Projection Failed " << node);
3307           }
3308           else {
3309             if ( isUPeriodic )
3310               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3311             if ( isVPeriodic )
3312               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3313             // check new UV
3314             if ( posType != SMDS_TOP_3DSPACE )
3315               dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3316             if ( dist2 < dist1 )
3317               uv = newUV;
3318           }
3319         }
3320         // store UV in the map
3321         listUV.push_back( uv );
3322         uvMap.insert( make_pair( node, &listUV.back() ));
3323       }
3324     } // loop on not yet smoothed elements
3325
3326     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3327       checkBoundaryNodes = true;
3328
3329     // fix nodes on mesh boundary
3330
3331     if ( checkBoundaryNodes ) {
3332       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3333       map< SMESH_TLink, int >::iterator link_nb;
3334       // put all elements links to linkNbMap
3335       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3336       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3337         const SMDS_MeshElement* elem = (*elemIt);
3338         int nbn =  elem->NbCornerNodes();
3339         // loop on elem links: insert them in linkNbMap
3340         for ( int iN = 0; iN < nbn; ++iN ) {
3341           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3342           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3343           SMESH_TLink link( n1, n2 );
3344           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3345           link_nb->second++;
3346         }
3347       }
3348       // remove nodes that are in links encountered only once from setMovableNodes
3349       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3350         if ( link_nb->second == 1 ) {
3351           setMovableNodes.erase( link_nb->first.node1() );
3352           setMovableNodes.erase( link_nb->first.node2() );
3353         }
3354       }
3355     }
3356
3357     // -----------------------------------------------------
3358     // for nodes on seam edge, compute one more UV ( uvMap2 );
3359     // find movable nodes linked to nodes on seam and which
3360     // are to be smoothed using the second UV ( uvMap2 )
3361     // -----------------------------------------------------
3362
3363     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3364     if ( !surface.IsNull() ) {
3365       TopExp_Explorer eExp( face, TopAbs_EDGE );
3366       for ( ; eExp.More(); eExp.Next() ) {
3367         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3368         if ( !BRep_Tool::IsClosed( edge, face ))
3369           continue;
3370         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3371         if ( !sm ) continue;
3372         // find out which parameter varies for a node on seam
3373         double f,l;
3374         gp_Pnt2d uv1, uv2;
3375         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3376         if ( pcurve.IsNull() ) continue;
3377         uv1 = pcurve->Value( f );
3378         edge.Reverse();
3379         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3380         if ( pcurve.IsNull() ) continue;
3381         uv2 = pcurve->Value( f );
3382         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3383         // assure uv1 < uv2
3384         if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3385           gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3386         }
3387         // get nodes on seam and its vertices
3388         list< const SMDS_MeshNode* > seamNodes;
3389         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3390         while ( nSeamIt->more() ) {
3391           const SMDS_MeshNode* node = nSeamIt->next();
3392           if ( !isQuadratic || !IsMedium( node ))
3393             seamNodes.push_back( node );
3394         }
3395         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3396         for ( ; vExp.More(); vExp.Next() ) {
3397           sm = aMesh->MeshElements( vExp.Current() );
3398           if ( sm ) {
3399             nSeamIt = sm->GetNodes();
3400             while ( nSeamIt->more() )
3401               seamNodes.push_back( nSeamIt->next() );
3402           }
3403         }
3404         // loop on nodes on seam
3405         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3406         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3407           const SMDS_MeshNode* nSeam = *noSeIt;
3408           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3409           if ( n_uv == uvMap.end() )
3410             continue;
3411           // set the first UV
3412           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3413           // set the second UV
3414           listUV.push_back( *n_uv->second );
3415           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3416           if ( uvMap2.empty() )
3417             uvMap2 = uvMap; // copy the uvMap contents
3418           uvMap2[ nSeam ] = &listUV.back();
3419
3420           // collect movable nodes linked to ones on seam in nodesNearSeam
3421           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3422           while ( eIt->more() ) {
3423             const SMDS_MeshElement* e = eIt->next();
3424             int nbUseMap1 = 0, nbUseMap2 = 0;
3425             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3426             int nn = 0, nbn =  e->NbNodes();
3427             if(e->IsQuadratic()) nbn = nbn/2;
3428             while ( nn++ < nbn )
3429             {
3430               const SMDS_MeshNode* n =
3431                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3432               if (n == nSeam ||
3433                   setMovableNodes.find( n ) == setMovableNodes.end() )
3434                 continue;
3435               // add only nodes being closer to uv2 than to uv1
3436               gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3437                            0.5 * ( n->Y() + nSeam->Y() ),
3438                            0.5 * ( n->Z() + nSeam->Z() ));
3439               gp_XY uv;
3440               getClosestUV( projector, pMid, uv );
3441               if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3442                 nodesNearSeam.insert( n );
3443                 nbUseMap2++;
3444               }
3445               else
3446                 nbUseMap1++;
3447             }
3448             // for centroidalSmooth all element nodes must
3449             // be on one side of a seam
3450             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3451               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3452               nn = 0;
3453               while ( nn++ < nbn ) {
3454                 const SMDS_MeshNode* n =
3455                   static_cast<const SMDS_MeshNode*>( nIt->next() );
3456                 setMovableNodes.erase( n );
3457               }
3458             }
3459           }
3460         } // loop on nodes on seam
3461       } // loop on edge of a face
3462     } // if ( !face.IsNull() )
3463
3464     if ( setMovableNodes.empty() ) {
3465       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3466       continue; // goto next face
3467     }
3468
3469     // -------------
3470     // SMOOTHING //
3471     // -------------
3472
3473     int it = -1;
3474     double maxRatio = -1., maxDisplacement = -1.;
3475     set<const SMDS_MeshNode*>::iterator nodeToMove;
3476     for ( it = 0; it < theNbIterations; it++ ) {
3477       maxDisplacement = 0.;
3478       nodeToMove = setMovableNodes.begin();
3479       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3480         const SMDS_MeshNode* node = (*nodeToMove);
3481         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3482
3483         // smooth
3484         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3485         if ( theSmoothMethod == LAPLACIAN )
3486           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3487         else
3488           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3489
3490         // node displacement
3491         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3492         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3493         if ( aDispl > maxDisplacement )
3494           maxDisplacement = aDispl;
3495       }
3496       // no node movement => exit
3497       //if ( maxDisplacement < 1.e-16 ) {
3498       if ( maxDisplacement < disttol ) {
3499         MESSAGE("-- no node movement --");
3500         break;
3501       }
3502
3503       // check elements quality
3504       maxRatio  = 0;
3505       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3506       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3507         const SMDS_MeshElement* elem = (*elemIt);
3508         if ( !elem || elem->GetType() != SMDSAbs_Face )
3509           continue;
3510         SMESH::Controls::TSequenceOfXYZ aPoints;
3511         if ( aQualityFunc.GetPoints( elem, aPoints )) {
3512           double aValue = aQualityFunc.GetValue( aPoints );
3513           if ( aValue > maxRatio )
3514             maxRatio = aValue;
3515         }
3516       }
3517       if ( maxRatio <= theTgtAspectRatio ) {
3518         MESSAGE("-- quality achived --");
3519         break;
3520       }
3521       if (it+1 == theNbIterations) {
3522         MESSAGE("-- Iteration limit exceeded --");
3523       }
3524     } // smoothing iterations
3525
3526     MESSAGE(" Face id: " << *fId <<
3527             " Nb iterstions: " << it <<
3528             " Displacement: " << maxDisplacement <<
3529             " Aspect Ratio " << maxRatio);
3530
3531     // ---------------------------------------
3532     // new nodes positions are computed,
3533     // record movement in DS and set new UV
3534     // ---------------------------------------
3535     nodeToMove = setMovableNodes.begin();
3536     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3537       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3538       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3539       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3540       if ( node_uv != uvMap.end() ) {
3541         gp_XY* uv = node_uv->second;
3542         node->SetPosition
3543           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3544       }
3545     }
3546
3547     // move medium nodes of quadratic elements
3548     if ( isQuadratic )
3549     {
3550       SMESH_MesherHelper helper( *GetMesh() );
3551       if ( !face.IsNull() )
3552         helper.SetSubShape( face );
3553       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3554       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3555         const SMDS_VtkFace* QF =
3556           dynamic_cast<const SMDS_VtkFace*> (*elemIt);
3557         if(QF && QF->IsQuadratic()) {
3558           vector<const SMDS_MeshNode*> Ns;
3559           Ns.reserve(QF->NbNodes()+1);
3560           SMDS_ElemIteratorPtr anIter = QF->interlacedNodesElemIterator();
3561           while ( anIter->more() )
3562             Ns.push_back( cast2Node(anIter->next()) );
3563           Ns.push_back( Ns[0] );
3564           double x, y, z;
3565           for(int i=0; i<QF->NbNodes(); i=i+2) {
3566             if ( !surface.IsNull() ) {
3567               gp_XY uv1 = helper.GetNodeUV( face, Ns[i], Ns[i+2] );
3568               gp_XY uv2 = helper.GetNodeUV( face, Ns[i+2], Ns[i] );
3569               gp_XY uv = ( uv1 + uv2 ) / 2.;
3570               gp_Pnt xyz = surface->Value( uv.X(), uv.Y() );
3571               x = xyz.X(); y = xyz.Y(); z = xyz.Z();
3572             }
3573             else {
3574               x = (Ns[i]->X() + Ns[i+2]->X())/2;
3575               y = (Ns[i]->Y() + Ns[i+2]->Y())/2;
3576               z = (Ns[i]->Z() + Ns[i+2]->Z())/2;
3577             }
3578             if( fabs( Ns[i+1]->X() - x ) > disttol ||
3579                 fabs( Ns[i+1]->Y() - y ) > disttol ||
3580                 fabs( Ns[i+1]->Z() - z ) > disttol ) {
3581               // we have to move i+1 node
3582               aMesh->MoveNode( Ns[i+1], x, y, z );
3583             }
3584           }
3585         }
3586       }
3587     }
3588
3589   } // loop on face ids
3590
3591 }
3592
3593 //=======================================================================
3594 //function : isReverse
3595 //purpose  : Return true if normal of prevNodes is not co-directied with
3596 //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3597 //           iNotSame is where prevNodes and nextNodes are different.
3598 //           If result is true then future volume orientation is OK
3599 //=======================================================================
3600
3601 static bool isReverse(const SMDS_MeshElement*             face,
3602                       const vector<const SMDS_MeshNode*>& prevNodes,
3603                       const vector<const SMDS_MeshNode*>& nextNodes,
3604                       const int                           iNotSame)
3605 {
3606
3607   SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
3608   SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
3609   gp_XYZ extrDir( pN - pP ), faceNorm;
3610   SMESH_Algo::FaceNormal( face, faceNorm, /*normalized=*/false );
3611
3612   return faceNorm * extrDir < 0.0;
3613 }
3614
3615 //=======================================================================
3616 /*!
3617  * \brief Create elements by sweeping an element
3618  * \param elem - element to sweep
3619  * \param newNodesItVec - nodes generated from each node of the element
3620  * \param newElems - generated elements
3621  * \param nbSteps - number of sweeping steps
3622  * \param srcElements - to append elem for each generated element
3623  */
3624 //=======================================================================
3625
3626 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
3627                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3628                                     list<const SMDS_MeshElement*>&        newElems,
3629                                     const int                             nbSteps,
3630                                     SMESH_SequenceOfElemPtr&              srcElements)
3631 {
3632   //MESSAGE("sweepElement " << nbSteps);
3633   SMESHDS_Mesh* aMesh = GetMeshDS();
3634
3635   const int           nbNodes = elem->NbNodes();
3636   const int         nbCorners = elem->NbCornerNodes();
3637   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
3638                                                           polyhedron creation !!! */
3639   // Loop on elem nodes:
3640   // find new nodes and detect same nodes indices
3641   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3642   vector<const SMDS_MeshNode*> prevNod( nbNodes );
3643   vector<const SMDS_MeshNode*> nextNod( nbNodes );
3644   vector<const SMDS_MeshNode*> midlNod( nbNodes );
3645
3646   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
3647   vector<int> sames(nbNodes);
3648   vector<bool> isSingleNode(nbNodes);
3649
3650   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3651     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
3652     const SMDS_MeshNode*                         node = nnIt->first;
3653     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3654     if ( listNewNodes.empty() )
3655       return;
3656
3657     itNN   [ iNode ] = listNewNodes.begin();
3658     prevNod[ iNode ] = node;
3659     nextNod[ iNode ] = listNewNodes.front();
3660
3661     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
3662                                                              corner node of linear */
3663     if ( prevNod[ iNode ] != nextNod [ iNode ])
3664       nbDouble += !isSingleNode[iNode];
3665
3666     if( iNode < nbCorners ) { // check corners only
3667       if ( prevNod[ iNode ] == nextNod [ iNode ])
3668         sames[nbSame++] = iNode;
3669       else
3670         iNotSameNode = iNode;
3671     }
3672   }
3673
3674   if ( nbSame == nbNodes || nbSame > 2) {
3675     MESSAGE( " Too many same nodes of element " << elem->GetID() );
3676     return;
3677   }
3678
3679   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
3680   {
3681     // fix nodes order to have bottom normal external
3682     if ( baseType == SMDSEntity_Polygon )
3683     {
3684       std::reverse( itNN.begin(), itNN.end() );
3685       std::reverse( prevNod.begin(), prevNod.end() );
3686       std::reverse( midlNod.begin(), midlNod.end() );
3687       std::reverse( nextNod.begin(), nextNod.end() );
3688       std::reverse( isSingleNode.begin(), isSingleNode.end() );
3689     }
3690     else
3691     {
3692       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
3693       SMDS_MeshCell::applyInterlace( ind, itNN );
3694       SMDS_MeshCell::applyInterlace( ind, prevNod );
3695       SMDS_MeshCell::applyInterlace( ind, nextNod );
3696       SMDS_MeshCell::applyInterlace( ind, midlNod );
3697       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3698       if ( nbSame > 0 )
3699       {
3700         sames[nbSame] = iNotSameNode;
3701         for ( int j = 0; j <= nbSame; ++j )
3702           for ( size_t i = 0; i < ind.size(); ++i )
3703             if ( ind[i] == sames[j] )
3704             {
3705               sames[j] = i;
3706               break;
3707             }
3708         iNotSameNode = sames[nbSame];
3709       }
3710     }
3711   }
3712
3713   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3714   if ( nbSame > 0 ) {
3715     iSameNode    = sames[ nbSame-1 ];
3716     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
3717     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
3718     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
3719   }
3720
3721   // make new elements
3722   for (int iStep = 0; iStep < nbSteps; iStep++ )
3723   {
3724     // get next nodes
3725     for ( iNode = 0; iNode < nbNodes; iNode++ )
3726     {
3727       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
3728       nextNod[ iNode ] = *itNN[ iNode ]++;
3729     }
3730
3731     SMDS_MeshElement* aNewElem = 0;
3732     /*if(!elem->IsPoly())*/ {
3733       switch ( baseType ) {
3734       case SMDSEntity_0D:
3735       case SMDSEntity_Node: { // sweep NODE
3736         if ( nbSame == 0 ) {
3737           if ( isSingleNode[0] )
3738             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3739           else
3740             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3741         }
3742         else
3743           return;
3744         break;
3745       }
3746       case SMDSEntity_Edge: { // sweep EDGE
3747         if ( nbDouble == 0 )
3748         {
3749           if ( nbSame == 0 ) // ---> quadrangle
3750             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3751                                       nextNod[ 1 ], nextNod[ 0 ] );
3752           else               // ---> triangle
3753             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3754                                       nextNod[ iNotSameNode ] );
3755         }
3756         else                 // ---> polygon
3757         {
3758           vector<const SMDS_MeshNode*> poly_nodes;
3759           poly_nodes.push_back( prevNod[0] );
3760           poly_nodes.push_back( prevNod[1] );
3761           if ( prevNod[1] != nextNod[1] )
3762           {
3763             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
3764             poly_nodes.push_back( nextNod[1] );
3765           }
3766           if ( prevNod[0] != nextNod[0] )
3767           {
3768             poly_nodes.push_back( nextNod[0] );
3769             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
3770           }
3771           switch ( poly_nodes.size() ) {
3772           case 3:
3773             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
3774             break;
3775           case 4:
3776             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
3777                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
3778             break;
3779           default:
3780             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
3781           }
3782         }
3783         break;
3784       }
3785       case SMDSEntity_Triangle: // TRIANGLE --->
3786         {
3787           if ( nbDouble > 0 ) break;
3788           if ( nbSame == 0 )       // ---> pentahedron
3789             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3790                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
3791
3792           else if ( nbSame == 1 )  // ---> pyramid
3793             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3794                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
3795                                          nextNod[ iSameNode ]);
3796
3797           else // 2 same nodes:       ---> tetrahedron
3798             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3799                                          nextNod[ iNotSameNode ]);
3800           break;
3801         }
3802       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
3803         {
3804           if ( nbSame == 2 )
3805             return;
3806           if ( nbDouble+nbSame == 2 )
3807           {
3808             if(nbSame==0) {      // ---> quadratic quadrangle
3809               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3810                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
3811             }
3812             else { //(nbSame==1) // ---> quadratic triangle
3813               if(sames[0]==2) {
3814                 return; // medium node on axis
3815               }
3816               else if(sames[0]==0)
3817                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3818                                           nextNod[2], midlNod[1], prevNod[2]);
3819               else // sames[0]==1
3820                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3821                                           midlNod[0], nextNod[2], prevNod[2]);
3822             }
3823           }
3824           else if ( nbDouble == 3 )
3825           {
3826             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
3827               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3828                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
3829             }
3830           }
3831           else
3832             return;
3833           break;
3834         }
3835       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
3836         if ( nbDouble > 0 ) break;
3837
3838         if ( nbSame == 0 )       // ---> hexahedron
3839           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
3840                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
3841
3842         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
3843           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3844                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
3845                                        nextNod[ iSameNode ]);
3846           newElems.push_back( aNewElem );
3847           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
3848                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3849                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
3850         }
3851         else if ( nbSame == 2 ) { // ---> pentahedron
3852           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3853             // iBeforeSame is same too
3854             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3855                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
3856                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
3857           else
3858             // iAfterSame is same too
3859             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
3860                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3861                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
3862         }
3863         break;
3864       }
3865       case SMDSEntity_Quad_Triangle: { // sweep Quadratic TRIANGLE --->
3866         if ( nbDouble+nbSame != 3 ) break;
3867         if(nbSame==0) {
3868           // --->  pentahedron with 15 nodes
3869           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3870                                        nextNod[0], nextNod[1], nextNod[2],
3871                                        prevNod[3], prevNod[4], prevNod[5],
3872                                        nextNod[3], nextNod[4], nextNod[5],
3873                                        midlNod[0], midlNod[1], midlNod[2]);
3874         }
3875         else if(nbSame==1) {
3876           // --->  2d order pyramid of 13 nodes
3877           int apex = iSameNode;
3878           int i0 = ( apex + 1 ) % nbCorners;
3879           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
3880           int i0a = apex + 3;
3881           int i1a = i1 + 3;
3882           int i01 = i0 + 3;
3883           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
3884                                       nextNod[i0], nextNod[i1], prevNod[apex],
3885                                       prevNod[i01], midlNod[i0],
3886                                       nextNod[i01], midlNod[i1],
3887                                       prevNod[i1a], prevNod[i0a],
3888                                       nextNod[i0a], nextNod[i1a]);
3889         }
3890         else if(nbSame==2) {
3891           // --->  2d order tetrahedron of 10 nodes
3892           int n1 = iNotSameNode;
3893           int n2 = ( n1 + 1             ) % nbCorners;
3894           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
3895           int n12 = n1 + 3;
3896           int n23 = n2 + 3;
3897           int n31 = n3 + 3;
3898           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
3899                                        prevNod[n12], prevNod[n23], prevNod[n31],
3900                                        midlNod[n1], nextNod[n12], nextNod[n31]);
3901         }
3902         break;
3903       }
3904       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
3905         if( nbSame == 0 ) {
3906           if ( nbDouble != 4 ) break;
3907           // --->  hexahedron with 20 nodes
3908           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3909                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3910                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3911                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3912                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
3913         }
3914         else if(nbSame==1) {
3915           // ---> pyramid + pentahedron - can not be created since it is needed
3916           // additional middle node at the center of face
3917           INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
3918           return;
3919         }
3920         else if( nbSame == 2 ) {
3921           if ( nbDouble != 2 ) break;
3922           // --->  2d order Pentahedron with 15 nodes
3923           int n1,n2,n4,n5;
3924           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
3925             // iBeforeSame is same too
3926             n1 = iBeforeSame;
3927             n2 = iOpposSame;
3928             n4 = iSameNode;
3929             n5 = iAfterSame;
3930           }
3931           else {
3932             // iAfterSame is same too
3933             n1 = iSameNode;
3934             n2 = iBeforeSame;
3935             n4 = iAfterSame;
3936             n5 = iOpposSame;
3937           }
3938           int n12 = n2 + 4;
3939           int n45 = n4 + 4;
3940           int n14 = n1 + 4;
3941           int n25 = n5 + 4;
3942           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
3943                                        prevNod[n4], prevNod[n5], nextNod[n5],
3944                                        prevNod[n12], midlNod[n2], nextNod[n12],
3945                                        prevNod[n45], midlNod[n5], nextNod[n45],
3946                                        prevNod[n14], prevNod[n25], nextNod[n25]);
3947         }
3948         break;
3949       }
3950       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
3951
3952         if( nbSame == 0 && nbDouble == 9 ) {
3953           // --->  tri-quadratic hexahedron with 27 nodes
3954           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3955                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3956                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3957                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3958                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
3959                                        prevNod[8], // bottom center
3960                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
3961                                        nextNod[8], // top center
3962                                        midlNod[8]);// elem center
3963         }
3964         else
3965         {
3966           return;
3967         }
3968         break;
3969       }
3970       case SMDSEntity_Polygon: { // sweep POLYGON
3971
3972         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
3973           // --->  hexagonal prism
3974           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3975                                        prevNod[3], prevNod[4], prevNod[5],
3976                                        nextNod[0], nextNod[1], nextNod[2],
3977                                        nextNod[3], nextNod[4], nextNod[5]);
3978         }
3979         break;
3980       }
3981       case SMDSEntity_Ball:
3982         return;
3983
3984       default:
3985         break;
3986       }
3987     }
3988
3989     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
3990     {
3991       if ( baseType != SMDSEntity_Polygon )
3992       {
3993         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
3994         SMDS_MeshCell::applyInterlace( ind, prevNod );
3995         SMDS_MeshCell::applyInterlace( ind, nextNod );
3996         SMDS_MeshCell::applyInterlace( ind, midlNod );
3997         SMDS_MeshCell::applyInterlace( ind, itNN );
3998         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3999         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4000       }
4001       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4002       vector<int> quantities (nbNodes + 2);
4003       polyedre_nodes.clear();
4004       quantities.clear();
4005
4006       // bottom of prism
4007       for (int inode = 0; inode < nbNodes; inode++)
4008         polyedre_nodes.push_back( prevNod[inode] );
4009       quantities.push_back( nbNodes );
4010
4011       // top of prism
4012       polyedre_nodes.push_back( nextNod[0] );
4013       for (int inode = nbNodes; inode-1; --inode )
4014         polyedre_nodes.push_back( nextNod[inode-1] );
4015       quantities.push_back( nbNodes );
4016
4017       // side faces
4018       for (int iface = 0; iface < nbNodes; iface++)
4019       {
4020         const int prevNbNodes = polyedre_nodes.size();
4021         int inextface = (iface+1) % nbNodes;
4022         polyedre_nodes.push_back( prevNod[inextface] );
4023         polyedre_nodes.push_back( prevNod[iface] );
4024         if ( prevNod[iface] != nextNod[iface] )
4025         {
4026           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4027           polyedre_nodes.push_back( nextNod[iface] );
4028         }
4029         if ( prevNod[inextface] != nextNod[inextface] )
4030         {
4031           polyedre_nodes.push_back( nextNod[inextface] );
4032           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4033         }
4034         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4035         if ( nbFaceNodes > 2 )
4036           quantities.push_back( nbFaceNodes );
4037         else // degenerated face
4038           polyedre_nodes.resize( prevNbNodes );
4039       }
4040       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4041     }
4042
4043     if ( aNewElem ) {
4044       newElems.push_back( aNewElem );
4045       myLastCreatedElems.Append(aNewElem);
4046       srcElements.Append( elem );
4047     }
4048
4049     // set new prev nodes
4050     for ( iNode = 0; iNode < nbNodes; iNode++ )
4051       prevNod[ iNode ] = nextNod[ iNode ];
4052
4053   } // for steps
4054 }
4055
4056 //=======================================================================
4057 /*!
4058  * \brief Create 1D and 2D elements around swept elements
4059  * \param mapNewNodes - source nodes and ones generated from them
4060  * \param newElemsMap - source elements and ones generated from them
4061  * \param elemNewNodesMap - nodes generated from each node of each element
4062  * \param elemSet - all swept elements
4063  * \param nbSteps - number of sweeping steps
4064  * \param srcElements - to append elem for each generated element
4065  */
4066 //=======================================================================
4067
4068 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4069                                   TElemOfElemListMap &     newElemsMap,
4070                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4071                                   TIDSortedElemSet&        elemSet,
4072                                   const int                nbSteps,
4073                                   SMESH_SequenceOfElemPtr& srcElements)
4074 {
4075   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4076   SMESHDS_Mesh* aMesh = GetMeshDS();
4077
4078   // Find nodes belonging to only one initial element - sweep them to get edges.
4079
4080   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4081   for ( ; nList != mapNewNodes.end(); nList++ )
4082   {
4083     const SMDS_MeshNode* node =
4084       static_cast<const SMDS_MeshNode*>( nList->first );
4085     if ( newElemsMap.count( node ))
4086       continue; // node was extruded into edge
4087     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4088     int nbInitElems = 0;
4089     const SMDS_MeshElement* el = 0;
4090     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4091     while ( eIt->more() && nbInitElems < 2 ) {
4092       el = eIt->next();
4093       SMDSAbs_ElementType type = el->GetType();
4094       if ( type == SMDSAbs_Volume || type < highType ) continue;
4095       if ( type > highType ) {
4096         nbInitElems = 0;
4097         highType = type;
4098       }
4099       nbInitElems += elemSet.count(el);
4100     }
4101     if ( nbInitElems < 2 ) {
4102       bool NotCreateEdge = el && el->IsMediumNode(node);
4103       if(!NotCreateEdge) {
4104         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4105         list<const SMDS_MeshElement*> newEdges;
4106         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4107       }
4108     }
4109   }
4110
4111   // Make a ceiling for each element ie an equal element of last new nodes.
4112   // Find free links of faces - make edges and sweep them into faces.
4113
4114   TElemOfElemListMap::iterator   itElem      = newElemsMap.begin();
4115   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4116   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4117   {
4118     const SMDS_MeshElement* elem = itElem->first;
4119     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4120
4121     if(itElem->second.size()==0) continue;
4122
4123     const bool isQuadratic = elem->IsQuadratic();
4124
4125     if ( elem->GetType() == SMDSAbs_Edge ) {
4126       // create a ceiling edge
4127       if ( !isQuadratic ) {
4128         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4129                                vecNewNodes[ 1 ]->second.back())) {
4130           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4131                                                    vecNewNodes[ 1 ]->second.back()));
4132           srcElements.Append( myLastCreatedElems.Last() );
4133         }
4134       }
4135       else {
4136         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4137                                vecNewNodes[ 1 ]->second.back(),
4138                                vecNewNodes[ 2 ]->second.back())) {
4139           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4140                                                    vecNewNodes[ 1 ]->second.back(),
4141                                                    vecNewNodes[ 2 ]->second.back()));
4142           srcElements.Append( myLastCreatedElems.Last() );
4143         }
4144       }
4145     }
4146     if ( elem->GetType() != SMDSAbs_Face )
4147       continue;
4148
4149     bool hasFreeLinks = false;
4150
4151     TIDSortedElemSet avoidSet;
4152     avoidSet.insert( elem );
4153
4154     set<const SMDS_MeshNode*> aFaceLastNodes;
4155     int iNode, nbNodes = vecNewNodes.size();
4156     if ( !isQuadratic ) {
4157       // loop on the face nodes
4158       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4159         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4160         // look for free links of the face
4161         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4162         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4163         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4164         // check if a link is free
4165         if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4166           hasFreeLinks = true;
4167           // make an edge and a ceiling for a new edge
4168           if ( !aMesh->FindEdge( n1, n2 )) {
4169             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // free link edge
4170             srcElements.Append( myLastCreatedElems.Last() );
4171           }
4172           n1 = vecNewNodes[ iNode ]->second.back();
4173           n2 = vecNewNodes[ iNext ]->second.back();
4174           if ( !aMesh->FindEdge( n1, n2 )) {
4175             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // ceiling edge
4176             srcElements.Append( myLastCreatedElems.Last() );
4177           }
4178         }
4179       }
4180     }
4181     else { // elem is quadratic face
4182       int nbn = nbNodes/2;
4183       for ( iNode = 0; iNode < nbn; iNode++ ) {
4184         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4185         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4186         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4187         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4188         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4189         // check if a link is free
4190         if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4191              ! SMESH_MeshEditor::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4192              ! SMESH_MeshEditor::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4193           hasFreeLinks = true;
4194           // make an edge and a ceiling for a new edge
4195           // find medium node
4196           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4197             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4198             srcElements.Append( myLastCreatedElems.Last() );
4199           }
4200           n1 = vecNewNodes[ iNode ]->second.back();
4201           n2 = vecNewNodes[ iNext ]->second.back();
4202           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4203           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4204             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4205             srcElements.Append( myLastCreatedElems.Last() );
4206           }
4207         }
4208       }
4209       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4210         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4211       }
4212     }
4213
4214     // sweep free links into faces
4215
4216     if ( hasFreeLinks )  {
4217       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4218       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4219
4220       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4221       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4222         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4223         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4224       }
4225       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4226         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4227         std::advance( v, volNb );
4228         // find indices of free faces of a volume and their source edges
4229         list< int > freeInd;
4230         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4231         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4232         int iF, nbF = vTool.NbFaces();
4233         for ( iF = 0; iF < nbF; iF ++ ) {
4234           if (vTool.IsFreeFace( iF ) &&
4235               vTool.GetFaceNodes( iF, faceNodeSet ) &&
4236               initNodeSet != faceNodeSet) // except an initial face
4237           {
4238             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4239               continue;
4240             freeInd.push_back( iF );
4241             // find source edge of a free face iF
4242             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4243             commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4244             std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4245                                    initNodeSet.begin(), initNodeSet.end(),
4246                                    commonNodes.begin());
4247             if ( (*v)->IsQuadratic() )
4248               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4249             else
4250               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4251 #ifdef _DEBUG_
4252             if ( !srcEdges.back() )
4253             {
4254               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4255                    << iF << " of volume #" << vTool.ID() << endl;
4256             }
4257 #endif
4258           }
4259         }
4260         if ( freeInd.empty() )
4261           continue;
4262
4263         // create faces for all steps;
4264         // if such a face has been already created by sweep of edge,
4265         // assure that its orientation is OK
4266         for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4267           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4268           vTool.SetExternalNormal();
4269           const int nextShift = vTool.IsForward() ? +1 : -1;
4270           list< int >::iterator ind = freeInd.begin();
4271           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4272           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4273           {
4274             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4275             int nbn = vTool.NbFaceNodes( *ind );
4276             const SMDS_MeshElement * f = 0;
4277             if ( nbn == 3 )              ///// triangle
4278             {
4279               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4280               if ( !f ||
4281                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4282               {
4283                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4284                                                      nodes[ 1 ],
4285                                                      nodes[ 1 + nextShift ] };
4286                 if ( f )
4287                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4288                 else
4289                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4290                                                             newOrder[ 2 ] ));
4291               }
4292             }
4293             else if ( nbn == 4 )       ///// quadrangle
4294             {
4295               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4296               if ( !f ||
4297                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4298               {
4299                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4300                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4301                 if ( f )
4302                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4303                 else
4304                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4305                                                             newOrder[ 2 ], newOrder[ 3 ]));
4306               }
4307             }
4308             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4309             {
4310               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4311               if ( !f ||
4312                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4313               {
4314                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4315                                                      nodes[2],
4316                                                      nodes[2 + 2*nextShift],
4317                                                      nodes[3 - 2*nextShift],
4318                                                      nodes[3],
4319                                                      nodes[3 + 2*nextShift]};
4320                 if ( f )
4321                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4322                 else
4323                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4324                                                             newOrder[ 1 ],
4325                                                             newOrder[ 2 ],
4326                                                             newOrder[ 3 ],
4327                                                             newOrder[ 4 ],
4328                                                             newOrder[ 5 ] ));
4329               }
4330             }
4331             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4332             {
4333               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4334                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4335               if ( !f ||
4336                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4337               {
4338                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4339                                                      nodes[4 - 2*nextShift],
4340                                                      nodes[4],
4341                                                      nodes[4 + 2*nextShift],
4342                                                      nodes[1],
4343                                                      nodes[5 - 2*nextShift],
4344                                                      nodes[5],
4345                                                      nodes[5 + 2*nextShift] };
4346                 if ( f )
4347                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4348                 else
4349                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4350                                                            newOrder[ 2 ], newOrder[ 3 ],
4351                                                            newOrder[ 4 ], newOrder[ 5 ],
4352                                                            newOrder[ 6 ], newOrder[ 7 ]));
4353               }
4354             }
4355             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4356             {
4357               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4358                                       SMDSAbs_Face, /*noMedium=*/false);
4359               if ( !f ||
4360                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4361               {
4362                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4363                                                      nodes[4 - 2*nextShift],
4364                                                      nodes[4],
4365                                                      nodes[4 + 2*nextShift],
4366                                                      nodes[1],
4367                                                      nodes[5 - 2*nextShift],
4368                                                      nodes[5],
4369                                                      nodes[5 + 2*nextShift],
4370                                                      nodes[8] };
4371                 if ( f )
4372                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4373                 else
4374                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4375                                                            newOrder[ 2 ], newOrder[ 3 ],
4376                                                            newOrder[ 4 ], newOrder[ 5 ],
4377                                                            newOrder[ 6 ], newOrder[ 7 ],
4378                                                            newOrder[ 8 ]));
4379               }
4380             }
4381             else  //////// polygon
4382             {
4383               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4384               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4385               if ( !f ||
4386                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4387               {
4388                 if ( !vTool.IsForward() )
4389                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4390                 if ( f )
4391                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4392                 else
4393                   AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4394               }
4395             }
4396
4397             while ( srcElements.Length() < myLastCreatedElems.Length() )
4398               srcElements.Append( *srcEdge );
4399
4400           }  // loop on free faces
4401
4402           // go to the next volume
4403           iVol = 0;
4404           while ( iVol++ < nbVolumesByStep ) v++;
4405
4406         } // loop on steps
4407       } // loop on volumes of one step
4408     } // sweep free links into faces
4409
4410     // Make a ceiling face with a normal external to a volume
4411
4412     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4413
4414     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4415     if ( iF >= 0 ) {
4416       lastVol.SetExternalNormal();
4417       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4418       int nbn = lastVol.NbFaceNodes( iF );
4419       if ( nbn == 3 ) {
4420         if (!hasFreeLinks ||
4421             !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
4422           myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
4423       }
4424       else if ( nbn == 4 )
4425       {
4426         if (!hasFreeLinks ||
4427             !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
4428           myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]));
4429       }
4430       else if ( nbn == 6 && isQuadratic )
4431       {
4432         if (!hasFreeLinks ||
4433             !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5]) )
4434           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
4435                                                    nodes[1], nodes[3], nodes[5]));
4436       }
4437       else if ( nbn == 8 && isQuadratic )
4438       {
4439         if (!hasFreeLinks ||
4440             !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
4441                              nodes[1], nodes[3], nodes[5], nodes[7]) )
4442           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4443                                                    nodes[1], nodes[3], nodes[5], nodes[7]));
4444       }
4445       else if ( nbn == 9 && isQuadratic )
4446       {
4447         if (!hasFreeLinks ||
4448             !aMesh->FindElement(vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4449                                 SMDSAbs_Face, /*noMedium=*/false) )
4450           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4451                                                    nodes[1], nodes[3], nodes[5], nodes[7],
4452                                                    nodes[8]));
4453       }
4454       else {
4455         vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes + nbn );
4456         if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
4457           myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4458       }
4459
4460       while ( srcElements.Length() < myLastCreatedElems.Length() )
4461         srcElements.Append( myLastCreatedElems.Last() );
4462     }
4463   } // loop on swept elements
4464 }
4465
4466 //=======================================================================
4467 //function : RotationSweep
4468 //purpose  :
4469 //=======================================================================
4470
4471 SMESH_MeshEditor::PGroupIDs
4472 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4473                                 const gp_Ax1&      theAxis,
4474                                 const double       theAngle,
4475                                 const int          theNbSteps,
4476                                 const double       theTol,
4477                                 const bool         theMakeGroups,
4478                                 const bool         theMakeWalls)
4479 {
4480   myLastCreatedElems.Clear();
4481   myLastCreatedNodes.Clear();
4482
4483   // source elements for each generated one
4484   SMESH_SequenceOfElemPtr srcElems, srcNodes;
4485
4486   MESSAGE( "RotationSweep()");
4487   gp_Trsf aTrsf;
4488   aTrsf.SetRotation( theAxis, theAngle );
4489   gp_Trsf aTrsf2;
4490   aTrsf2.SetRotation( theAxis, theAngle/2. );
4491
4492   gp_Lin aLine( theAxis );
4493   double aSqTol = theTol * theTol;
4494
4495   SMESHDS_Mesh* aMesh = GetMeshDS();
4496
4497   TNodeOfNodeListMap mapNewNodes;
4498   TElemOfVecOfNnlmiMap mapElemNewNodes;
4499   TElemOfElemListMap newElemsMap;
4500
4501   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4502                                      myMesh->NbFaces(ORDER_QUADRATIC) +
4503                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
4504   // loop on theElems
4505   TIDSortedElemSet::iterator itElem;
4506   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4507     const SMDS_MeshElement* elem = *itElem;
4508     if ( !elem || elem->GetType() == SMDSAbs_Volume )
4509       continue;
4510     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4511     newNodesItVec.reserve( elem->NbNodes() );
4512
4513     // loop on elem nodes
4514     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4515     while ( itN->more() )
4516     {
4517       // check if a node has been already sweeped
4518       const SMDS_MeshNode* node = cast2Node( itN->next() );
4519
4520       gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4521       double coord[3];
4522       aXYZ.Coord( coord[0], coord[1], coord[2] );
4523       bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4524
4525       TNodeOfNodeListMapItr nIt =
4526         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4527       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4528       if ( listNewNodes.empty() )
4529       {
4530         // check if we are to create medium nodes between corner ones
4531         bool needMediumNodes = false;
4532         if ( isQuadraticMesh )
4533         {
4534           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4535           while (it->more() && !needMediumNodes )
4536           {
4537             const SMDS_MeshElement* invElem = it->next();
4538             if ( invElem != elem && !theElems.count( invElem )) continue;
4539             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4540             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4541               needMediumNodes = true;
4542           }
4543         }
4544
4545         // make new nodes
4546         const SMDS_MeshNode * newNode = node;
4547         for ( int i = 0; i < theNbSteps; i++ ) {
4548           if ( !isOnAxis ) {
4549             if ( needMediumNodes )  // create a medium node
4550             {
4551               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4552               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4553               myLastCreatedNodes.Append(newNode);
4554               srcNodes.Append( node );
4555               listNewNodes.push_back( newNode );
4556               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4557             }
4558             else {
4559               aTrsf.Transforms( coord[0], coord[1], coord[2] );
4560             }
4561             // create a corner node
4562             newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4563             myLastCreatedNodes.Append(newNode);
4564             srcNodes.Append( node );
4565             listNewNodes.push_back( newNode );
4566           }
4567           else {
4568             listNewNodes.push_back( newNode );
4569             // if ( needMediumNodes )
4570             //   listNewNodes.push_back( newNode );
4571           }
4572         }
4573       }
4574       newNodesItVec.push_back( nIt );
4575     }
4576     // make new elements
4577     sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4578   }
4579
4580   if ( theMakeWalls )
4581     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4582
4583   PGroupIDs newGroupIDs;
4584   if ( theMakeGroups )
4585     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4586
4587   return newGroupIDs;
4588 }
4589
4590
4591 //=======================================================================
4592 //function : CreateNode
4593 //purpose  :
4594 //=======================================================================
4595 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4596                                                   const double y,
4597                                                   const double z,
4598                                                   const double tolnode,
4599                                                   SMESH_SequenceOfNode& aNodes)
4600 {
4601   // myLastCreatedElems.Clear();
4602   // myLastCreatedNodes.Clear();
4603
4604   gp_Pnt P1(x,y,z);
4605   SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4606
4607   // try to search in sequence of existing nodes
4608   // if aNodes.Length()>0 we 'nave to use given sequence
4609   // else - use all nodes of mesh
4610   if(aNodes.Length()>0) {
4611     int i;
4612     for(i=1; i<=aNodes.Length(); i++) {
4613       gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4614       if(P1.Distance(P2)<tolnode)
4615         return aNodes.Value(i);
4616     }
4617   }
4618   else {
4619     SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4620     while(itn->more()) {
4621       const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4622       gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4623       if(P1.Distance(P2)<tolnode)
4624         return aN;
4625     }
4626   }
4627
4628   // create new node and return it
4629   const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4630   //myLastCreatedNodes.Append(NewNode);
4631   return NewNode;
4632 }
4633
4634
4635 //=======================================================================
4636 //function : ExtrusionSweep
4637 //purpose  :
4638 //=======================================================================
4639
4640 SMESH_MeshEditor::PGroupIDs
4641 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &  theElems,
4642                                   const gp_Vec&       theStep,
4643                                   const int           theNbSteps,
4644                                   TElemOfElemListMap& newElemsMap,
4645                                   const bool          theMakeGroups,
4646                                   const int           theFlags,
4647                                   const double        theTolerance)
4648 {
4649   ExtrusParam aParams;
4650   aParams.myDir = gp_Dir(theStep);
4651   aParams.myNodes.Clear();
4652   aParams.mySteps = new TColStd_HSequenceOfReal;
4653   int i;
4654   for(i=1; i<=theNbSteps; i++)
4655     aParams.mySteps->Append(theStep.Magnitude());
4656
4657   return
4658     ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4659 }
4660
4661
4662 //=======================================================================
4663 //function : ExtrusionSweep
4664 //purpose  :
4665 //=======================================================================
4666
4667 SMESH_MeshEditor::PGroupIDs
4668 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &  theElems,
4669                                   ExtrusParam&        theParams,
4670                                   TElemOfElemListMap& newElemsMap,
4671                                   const bool          theMakeGroups,
4672                                   const int           theFlags,
4673                                   const double        theTolerance)
4674 {
4675   myLastCreatedElems.Clear();
4676   myLastCreatedNodes.Clear();
4677
4678   // source elements for each generated one
4679   SMESH_SequenceOfElemPtr srcElems, srcNodes;
4680
4681   SMESHDS_Mesh* aMesh = GetMeshDS();
4682
4683   int nbsteps = theParams.mySteps->Length();
4684
4685   TNodeOfNodeListMap mapNewNodes;
4686   //TNodeOfNodeVecMap mapNewNodes;
4687   TElemOfVecOfNnlmiMap mapElemNewNodes;
4688   //TElemOfVecOfMapNodesMap mapElemNewNodes;
4689
4690   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4691                                      myMesh->NbFaces(ORDER_QUADRATIC) +
4692                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
4693   // loop on theElems
4694   TIDSortedElemSet::iterator itElem;
4695   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4696     // check element type
4697     const SMDS_MeshElement* elem = *itElem;
4698     if ( !elem  || elem->GetType() == SMDSAbs_Volume )
4699       continue;
4700
4701     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4702     newNodesItVec.reserve( elem->NbNodes() );
4703
4704     // loop on elem nodes
4705     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4706     while ( itN->more() )
4707     {
4708       // check if a node has been already sweeped
4709       const SMDS_MeshNode* node = cast2Node( itN->next() );
4710       TNodeOfNodeListMap::iterator nIt =
4711         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4712       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4713       if ( listNewNodes.empty() )
4714       {
4715         // make new nodes
4716
4717         // check if we are to create medium nodes between corner ones
4718         bool needMediumNodes = false;
4719         if ( isQuadraticMesh )
4720         {
4721           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4722           while (it->more() && !needMediumNodes )
4723           {
4724             const SMDS_MeshElement* invElem = it->next();
4725             if ( invElem != elem && !theElems.count( invElem )) continue;
4726             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4727             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4728               needMediumNodes = true;
4729           }
4730         }
4731
4732         double coord[] = { node->X(), node->Y(), node->Z() };
4733         for ( int i = 0; i < nbsteps; i++ )
4734         {
4735           if ( needMediumNodes ) // create a medium node
4736           {
4737             double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4738             double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4739             double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4740             if( theFlags & EXTRUSION_FLAG_SEW ) {
4741               const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4742                                                          theTolerance, theParams.myNodes);
4743               listNewNodes.push_back( newNode );
4744             }
4745             else {
4746               const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4747               myLastCreatedNodes.Append(newNode);
4748               srcNodes.Append( node );
4749               listNewNodes.push_back( newNode );
4750             }
4751           }
4752           // create a corner node
4753           coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4754           coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4755           coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4756           if( theFlags & EXTRUSION_FLAG_SEW ) {
4757             const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4758                                                        theTolerance, theParams.myNodes);
4759             listNewNodes.push_back( newNode );
4760           }
4761           else {
4762             const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4763             myLastCreatedNodes.Append(newNode);
4764             srcNodes.Append( node );
4765             listNewNodes.push_back( newNode );
4766           }
4767         }
4768       }
4769       newNodesItVec.push_back( nIt );
4770     }
4771     // make new elements
4772     sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4773   }
4774
4775   if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4776     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4777   }
4778   PGroupIDs newGroupIDs;
4779   if ( theMakeGroups )
4780     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4781
4782   return newGroupIDs;
4783 }
4784
4785 //=======================================================================
4786 //function : ExtrusionAlongTrack
4787 //purpose  :
4788 //=======================================================================
4789 SMESH_MeshEditor::Extrusion_Error
4790 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
4791                                        SMESH_subMesh*       theTrack,
4792                                        const SMDS_MeshNode* theN1,
4793                                        const bool           theHasAngles,
4794                                        list<double>&        theAngles,
4795                                        const bool           theLinearVariation,
4796                                        const bool           theHasRefPoint,
4797                                        const gp_Pnt&        theRefPoint,
4798                                        const bool           theMakeGroups)
4799 {
4800   MESSAGE("ExtrusionAlongTrack");
4801   myLastCreatedElems.Clear();
4802   myLastCreatedNodes.Clear();
4803
4804   int aNbE;
4805   std::list<double> aPrms;
4806   TIDSortedElemSet::iterator itElem;
4807
4808   gp_XYZ aGC;
4809   TopoDS_Edge aTrackEdge;
4810   TopoDS_Vertex aV1, aV2;
4811
4812   SMDS_ElemIteratorPtr aItE;
4813   SMDS_NodeIteratorPtr aItN;
4814   SMDSAbs_ElementType aTypeE;
4815
4816   TNodeOfNodeListMap mapNewNodes;
4817
4818   // 1. Check data
4819   aNbE = theElements.size();
4820   // nothing to do
4821   if ( !aNbE )
4822     return EXTR_NO_ELEMENTS;
4823
4824   // 1.1 Track Pattern
4825   ASSERT( theTrack );
4826
4827   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4828
4829   aItE = pSubMeshDS->GetElements();
4830   while ( aItE->more() ) {
4831     const SMDS_MeshElement* pE = aItE->next();
4832     aTypeE = pE->GetType();
4833     // Pattern must contain links only
4834     if ( aTypeE != SMDSAbs_Edge )
4835       return EXTR_PATH_NOT_EDGE;
4836   }
4837
4838   list<SMESH_MeshEditor_PathPoint> fullList;
4839
4840   const TopoDS_Shape& aS = theTrack->GetSubShape();
4841   // Sub-shape for the Pattern must be an Edge or Wire
4842   if( aS.ShapeType() == TopAbs_EDGE ) {
4843     aTrackEdge = TopoDS::Edge( aS );
4844     // the Edge must not be degenerated
4845     if ( BRep_Tool::Degenerated( aTrackEdge ) )
4846       return EXTR_BAD_PATH_SHAPE;
4847     TopExp::Vertices( aTrackEdge, aV1, aV2 );
4848     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4849     const SMDS_MeshNode* aN1 = aItN->next();
4850     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4851     const SMDS_MeshNode* aN2 = aItN->next();
4852     // starting node must be aN1 or aN2
4853     if ( !( aN1 == theN1 || aN2 == theN1 ) )
4854       return EXTR_BAD_STARTING_NODE;
4855     aItN = pSubMeshDS->GetNodes();
4856     while ( aItN->more() ) {
4857       const SMDS_MeshNode* pNode = aItN->next();
4858       const SMDS_EdgePosition* pEPos =
4859         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4860       double aT = pEPos->GetUParameter();
4861       aPrms.push_back( aT );
4862     }
4863     //Extrusion_Error err =
4864     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4865   } else if( aS.ShapeType() == TopAbs_WIRE ) {
4866     list< SMESH_subMesh* > LSM;
4867     TopTools_SequenceOfShape Edges;
4868     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4869     while(itSM->more()) {
4870       SMESH_subMesh* SM = itSM->next();
4871       LSM.push_back(SM);
4872       const TopoDS_Shape& aS = SM->GetSubShape();
4873       Edges.Append(aS);
4874     }
4875     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4876     int startNid = theN1->GetID();
4877     TColStd_MapOfInteger UsedNums;
4878
4879     int NbEdges = Edges.Length();
4880     int i = 1;
4881     for(; i<=NbEdges; i++) {
4882       int k = 0;
4883       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4884       for(; itLSM!=LSM.end(); itLSM++) {
4885         k++;
4886         if(UsedNums.Contains(k)) continue;
4887         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4888         SMESH_subMesh* locTrack = *itLSM;
4889         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4890         TopExp::Vertices( aTrackEdge, aV1, aV2 );
4891         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4892         const SMDS_MeshNode* aN1 = aItN->next();
4893         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4894         const SMDS_MeshNode* aN2 = aItN->next();
4895         // starting node must be aN1 or aN2
4896         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4897         // 2. Collect parameters on the track edge
4898         aPrms.clear();
4899         aItN = locMeshDS->GetNodes();
4900         while ( aItN->more() ) {
4901           const SMDS_MeshNode* pNode = aItN->next();
4902           const SMDS_EdgePosition* pEPos =
4903             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4904           double aT = pEPos->GetUParameter();
4905           aPrms.push_back( aT );
4906         }
4907         list<SMESH_MeshEditor_PathPoint> LPP;
4908         //Extrusion_Error err =
4909         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4910         LLPPs.push_back(LPP);
4911         UsedNums.Add(k);
4912         // update startN for search following egde
4913         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4914         else startNid = aN1->GetID();
4915         break;
4916       }
4917     }
4918     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4919     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4920     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4921     for(; itPP!=firstList.end(); itPP++) {
4922       fullList.push_back( *itPP );
4923     }
4924     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4925     fullList.pop_back();
4926     itLLPP++;
4927     for(; itLLPP!=LLPPs.end(); itLLPP++) {
4928       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4929       itPP = currList.begin();
4930       SMESH_MeshEditor_PathPoint PP2 = currList.front();
4931       gp_Dir D1 = PP1.Tangent();
4932       gp_Dir D2 = PP2.Tangent();
4933       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4934                            (D1.Z()+D2.Z())/2 ) );
4935       PP1.SetTangent(Dnew);
4936       fullList.push_back(PP1);
4937       itPP++;
4938       for(; itPP!=firstList.end(); itPP++) {
4939         fullList.push_back( *itPP );
4940       }
4941       PP1 = fullList.back();
4942       fullList.pop_back();
4943     }
4944     // if wire not closed
4945     fullList.push_back(PP1);
4946     // else ???
4947   }
4948   else {
4949     return EXTR_BAD_PATH_SHAPE;
4950   }
4951
4952   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4953                           theHasRefPoint, theRefPoint, theMakeGroups);
4954 }
4955
4956
4957 //=======================================================================
4958 //function : ExtrusionAlongTrack
4959 //purpose  :
4960 //=======================================================================
4961 SMESH_MeshEditor::Extrusion_Error
4962 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
4963                                        SMESH_Mesh*          theTrack,
4964                                        const SMDS_MeshNode* theN1,
4965                                        const bool           theHasAngles,
4966                                        list<double>&        theAngles,
4967                                        const bool           theLinearVariation,
4968                                        const bool           theHasRefPoint,
4969                                        const gp_Pnt&        theRefPoint,
4970                                        const bool           theMakeGroups)
4971 {
4972   myLastCreatedElems.Clear();
4973   myLastCreatedNodes.Clear();
4974
4975   int aNbE;
4976   std::list<double> aPrms;
4977   TIDSortedElemSet::iterator itElem;
4978
4979   gp_XYZ aGC;
4980   TopoDS_Edge aTrackEdge;
4981   TopoDS_Vertex aV1, aV2;
4982
4983   SMDS_ElemIteratorPtr aItE;
4984   SMDS_NodeIteratorPtr aItN;
4985   SMDSAbs_ElementType aTypeE;
4986
4987   TNodeOfNodeListMap mapNewNodes;
4988
4989   // 1. Check data
4990   aNbE = theElements.size();
4991   // nothing to do
4992   if ( !aNbE )
4993     return EXTR_NO_ELEMENTS;
4994
4995   // 1.1 Track Pattern
4996   ASSERT( theTrack );
4997
4998   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
4999
5000   aItE = pMeshDS->elementsIterator();
5001   while ( aItE->more() ) {
5002     const SMDS_MeshElement* pE = aItE->next();
5003     aTypeE = pE->GetType();
5004     // Pattern must contain links only
5005     if ( aTypeE != SMDSAbs_Edge )
5006       return EXTR_PATH_NOT_EDGE;
5007   }
5008
5009   list<SMESH_MeshEditor_PathPoint> fullList;
5010
5011   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5012
5013   if( aS == SMESH_Mesh::PseudoShape() ) {
5014     //Mesh without shape
5015     const SMDS_MeshNode* currentNode = NULL;
5016     const SMDS_MeshNode* prevNode = theN1;
5017     std::vector<const SMDS_MeshNode*> aNodesList;
5018     aNodesList.push_back(theN1);
5019     int nbEdges = 0, conn=0;
5020     const SMDS_MeshElement* prevElem = NULL;
5021     const SMDS_MeshElement* currentElem = NULL;
5022     int totalNbEdges = theTrack->NbEdges();
5023     SMDS_ElemIteratorPtr nIt;
5024
5025     //check start node
5026     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5027       return EXTR_BAD_STARTING_NODE;
5028     }
5029
5030     conn = nbEdgeConnectivity(theN1);
5031     if(conn > 2)
5032       return EXTR_PATH_NOT_EDGE;
5033
5034     aItE = theN1->GetInverseElementIterator();
5035     prevElem = aItE->next();
5036     currentElem = prevElem;
5037     //Get all nodes
5038     if(totalNbEdges == 1 ) {
5039       nIt = currentElem->nodesIterator();
5040       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5041       if(currentNode == prevNode)
5042         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5043       aNodesList.push_back(currentNode);
5044     } else {
5045       nIt = currentElem->nodesIterator();
5046       while( nIt->more() ) {
5047         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5048         if(currentNode == prevNode)
5049           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5050         aNodesList.push_back(currentNode);
5051
5052         //case of the closed mesh
5053         if(currentNode == theN1) {
5054           nbEdges++;
5055           break;
5056         }
5057
5058         conn = nbEdgeConnectivity(currentNode);
5059         if(conn > 2) {
5060           return EXTR_PATH_NOT_EDGE;
5061         }else if( conn == 1 && nbEdges > 0 ) {
5062           //End of the path
5063           nbEdges++;
5064           break;
5065         }else {
5066           prevNode = currentNode;
5067           aItE = currentNode->GetInverseElementIterator();
5068           currentElem = aItE->next();
5069           if( currentElem  == prevElem)
5070             currentElem = aItE->next();
5071           nIt = currentElem->nodesIterator();
5072           prevElem = currentElem;
5073           nbEdges++;
5074         }
5075       }
5076     }
5077
5078     if(nbEdges != totalNbEdges)
5079       return EXTR_PATH_NOT_EDGE;
5080
5081     TopTools_SequenceOfShape Edges;
5082     double x1,x2,y1,y2,z1,z2;
5083     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5084     int startNid = theN1->GetID();
5085     for(int i = 1; i < aNodesList.size(); i++) {
5086       x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5087       y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5088       z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5089       TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5090       list<SMESH_MeshEditor_PathPoint> LPP;
5091       aPrms.clear();
5092       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5093       LLPPs.push_back(LPP);
5094       if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5095       else startNid = aNodesList[i-1]->GetID();
5096
5097     }
5098
5099     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5100     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5101     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5102     for(; itPP!=firstList.end(); itPP++) {
5103       fullList.push_back( *itPP );
5104     }
5105
5106     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5107     SMESH_MeshEditor_PathPoint PP2;
5108     fullList.pop_back();
5109     itLLPP++;
5110     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5111       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5112       itPP = currList.begin();
5113       PP2 = currList.front();
5114       gp_Dir D1 = PP1.Tangent();
5115       gp_Dir D2 = PP2.Tangent();
5116       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5117                            (D1.Z()+D2.Z())/2 ) );
5118       PP1.SetTangent(Dnew);
5119       fullList.push_back(PP1);
5120       itPP++;
5121       for(; itPP!=currList.end(); itPP++) {
5122         fullList.push_back( *itPP );
5123       }
5124       PP1 = fullList.back();
5125       fullList.pop_back();
5126     }
5127     fullList.push_back(PP1);
5128
5129   } // Sub-shape for the Pattern must be an Edge or Wire
5130   else if( aS.ShapeType() == TopAbs_EDGE ) {
5131     aTrackEdge = TopoDS::Edge( aS );
5132     // the Edge must not be degenerated
5133     if ( BRep_Tool::Degenerated( aTrackEdge ) )
5134       return EXTR_BAD_PATH_SHAPE;
5135     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5136     aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5137     const SMDS_MeshNode* aN1 = aItN->next();
5138     aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5139     const SMDS_MeshNode* aN2 = aItN->next();
5140     // starting node must be aN1 or aN2
5141     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5142       return EXTR_BAD_STARTING_NODE;
5143     aItN = pMeshDS->nodesIterator();
5144     while ( aItN->more() ) {
5145       const SMDS_MeshNode* pNode = aItN->next();
5146       if( pNode==aN1 || pNode==aN2 ) continue;
5147       const SMDS_EdgePosition* pEPos =
5148         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5149       double aT = pEPos->GetUParameter();
5150       aPrms.push_back( aT );
5151     }
5152     //Extrusion_Error err =
5153     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5154   }
5155   else if( aS.ShapeType() == TopAbs_WIRE ) {
5156     list< SMESH_subMesh* > LSM;
5157     TopTools_SequenceOfShape Edges;
5158     TopExp_Explorer eExp(aS, TopAbs_EDGE);
5159     for(; eExp.More(); eExp.Next()) {
5160       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5161       if( BRep_Tool::Degenerated(E) ) continue;
5162       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5163       if(SM) {
5164         LSM.push_back(SM);
5165         Edges.Append(E);
5166       }
5167     }
5168     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5169     int startNid = theN1->GetID();
5170     TColStd_MapOfInteger UsedNums;
5171     int NbEdges = Edges.Length();
5172     int i = 1;
5173     for(; i<=NbEdges; i++) {
5174       int k = 0;
5175       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5176       for(; itLSM!=LSM.end(); itLSM++) {
5177         k++;
5178         if(UsedNums.Contains(k)) continue;
5179         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5180         SMESH_subMesh* locTrack = *itLSM;
5181         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5182         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5183         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5184         const SMDS_MeshNode* aN1 = aItN->next();
5185         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5186         const SMDS_MeshNode* aN2 = aItN->next();
5187         // starting node must be aN1 or aN2
5188         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5189         // 2. Collect parameters on the track edge
5190         aPrms.clear();
5191         aItN = locMeshDS->GetNodes();
5192         while ( aItN->more() ) {
5193           const SMDS_MeshNode* pNode = aItN->next();
5194           const SMDS_EdgePosition* pEPos =
5195             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5196           double aT = pEPos->GetUParameter();
5197           aPrms.push_back( aT );
5198         }
5199         list<SMESH_MeshEditor_PathPoint> LPP;
5200         //Extrusion_Error err =
5201         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5202         LLPPs.push_back(LPP);
5203         UsedNums.Add(k);
5204         // update startN for search following egde
5205         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5206         else startNid = aN1->GetID();
5207         break;
5208       }
5209     }
5210     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5211     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5212     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5213     for(; itPP!=firstList.end(); itPP++) {
5214       fullList.push_back( *itPP );
5215     }
5216     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5217     fullList.pop_back();
5218     itLLPP++;
5219     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5220       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5221       itPP = currList.begin();
5222       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5223       gp_Dir D1 = PP1.Tangent();
5224       gp_Dir D2 = PP2.Tangent();
5225       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5226                            (D1.Z()+D2.Z())/2 ) );
5227       PP1.SetTangent(Dnew);
5228       fullList.push_back(PP1);
5229       itPP++;
5230       for(; itPP!=currList.end(); itPP++) {
5231         fullList.push_back( *itPP );
5232       }
5233       PP1 = fullList.back();
5234       fullList.pop_back();
5235     }
5236     // if wire not closed
5237     fullList.push_back(PP1);
5238     // else ???
5239   }
5240   else {
5241     return EXTR_BAD_PATH_SHAPE;
5242   }
5243
5244   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5245                           theHasRefPoint, theRefPoint, theMakeGroups);
5246 }
5247
5248
5249 //=======================================================================
5250 //function : MakeEdgePathPoints
5251 //purpose  : auxilary for ExtrusionAlongTrack
5252 //=======================================================================
5253 SMESH_MeshEditor::Extrusion_Error
5254 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5255                                      const TopoDS_Edge& aTrackEdge,
5256                                      bool FirstIsStart,
5257                                      list<SMESH_MeshEditor_PathPoint>& LPP)
5258 {
5259   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5260   aTolVec=1.e-7;
5261   aTolVec2=aTolVec*aTolVec;
5262   double aT1, aT2;
5263   TopoDS_Vertex aV1, aV2;
5264   TopExp::Vertices( aTrackEdge, aV1, aV2 );
5265   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5266   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5267   // 2. Collect parameters on the track edge
5268   aPrms.push_front( aT1 );
5269   aPrms.push_back( aT2 );
5270   // sort parameters
5271   aPrms.sort();
5272   if( FirstIsStart ) {
5273     if ( aT1 > aT2 ) {
5274       aPrms.reverse();
5275     }
5276   }
5277   else {
5278     if ( aT2 > aT1 ) {
5279       aPrms.reverse();
5280     }
5281   }
5282   // 3. Path Points
5283   SMESH_MeshEditor_PathPoint aPP;
5284   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5285   std::list<double>::iterator aItD = aPrms.begin();
5286   for(; aItD != aPrms.end(); ++aItD) {
5287     double aT = *aItD;
5288     gp_Pnt aP3D;
5289     gp_Vec aVec;
5290     aC3D->D1( aT, aP3D, aVec );
5291     aL2 = aVec.SquareMagnitude();
5292     if ( aL2 < aTolVec2 )
5293       return EXTR_CANT_GET_TANGENT;
5294     gp_Dir aTgt( aVec );
5295     aPP.SetPnt( aP3D );
5296     aPP.SetTangent( aTgt );
5297     aPP.SetParameter( aT );
5298     LPP.push_back(aPP);
5299   }
5300   return EXTR_OK;
5301 }
5302
5303
5304 //=======================================================================
5305 //function : MakeExtrElements
5306 //purpose  : auxilary for ExtrusionAlongTrack
5307 //=======================================================================
5308 SMESH_MeshEditor::Extrusion_Error
5309 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet&  theElements,
5310                                    list<SMESH_MeshEditor_PathPoint>& fullList,
5311                                    const bool theHasAngles,
5312                                    list<double>& theAngles,
5313                                    const bool theLinearVariation,
5314                                    const bool theHasRefPoint,
5315                                    const gp_Pnt& theRefPoint,
5316                                    const bool theMakeGroups)
5317 {
5318   MESSAGE("MakeExtrElements");
5319   //cout<<"MakeExtrElements  fullList.size() = "<<fullList.size()<<endl;
5320   int aNbTP = fullList.size();
5321   vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5322   // Angles
5323   if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5324     LinearAngleVariation(aNbTP-1, theAngles);
5325   }
5326   vector<double> aAngles( aNbTP );
5327   int j = 0;
5328   for(; j<aNbTP; ++j) {
5329     aAngles[j] = 0.;
5330   }
5331   if ( theHasAngles ) {
5332     double anAngle;;
5333     std::list<double>::iterator aItD = theAngles.begin();
5334     for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5335       anAngle = *aItD;
5336       aAngles[j] = anAngle;
5337     }
5338   }
5339   // fill vector of path points with angles
5340   //aPPs.resize(fullList.size());
5341   j = -1;
5342   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5343   for(; itPP!=fullList.end(); itPP++) {
5344     j++;
5345     SMESH_MeshEditor_PathPoint PP = *itPP;
5346     PP.SetAngle(aAngles[j]);
5347     aPPs[j] = PP;
5348   }
5349
5350   TNodeOfNodeListMap mapNewNodes;
5351   TElemOfVecOfNnlmiMap mapElemNewNodes;
5352   TElemOfElemListMap newElemsMap;
5353   TIDSortedElemSet::iterator itElem;
5354   double aX, aY, aZ;
5355   int aNb;
5356   SMDSAbs_ElementType aTypeE;
5357   // source elements for each generated one
5358   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5359
5360   // 3. Center of rotation aV0
5361   gp_Pnt aV0 = theRefPoint;
5362   gp_XYZ aGC;
5363   if ( !theHasRefPoint ) {
5364     aNb = 0;
5365     aGC.SetCoord( 0.,0.,0. );
5366
5367     itElem = theElements.begin();
5368     for ( ; itElem != theElements.end(); itElem++ ) {
5369       const SMDS_MeshElement* elem = *itElem;
5370
5371       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5372       while ( itN->more() ) {
5373         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5374         aX = node->X();
5375         aY = node->Y();
5376         aZ = node->Z();
5377
5378         if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5379           list<const SMDS_MeshNode*> aLNx;
5380           mapNewNodes[node] = aLNx;
5381           //
5382           gp_XYZ aXYZ( aX, aY, aZ );
5383           aGC += aXYZ;
5384           ++aNb;
5385         }
5386       }
5387     }
5388     aGC /= aNb;
5389     aV0.SetXYZ( aGC );
5390   } // if (!theHasRefPoint) {
5391   mapNewNodes.clear();
5392
5393   // 4. Processing the elements
5394   SMESHDS_Mesh* aMesh = GetMeshDS();
5395
5396   for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5397     // check element type
5398     const SMDS_MeshElement* elem = *itElem;
5399     aTypeE = elem->GetType();
5400     if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5401       continue;
5402
5403     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5404     newNodesItVec.reserve( elem->NbNodes() );
5405
5406     // loop on elem nodes
5407     int nodeIndex = -1;
5408     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5409     while ( itN->more() )
5410     {
5411       ++nodeIndex;
5412       // check if a node has been already processed
5413       const SMDS_MeshNode* node =
5414         static_cast<const SMDS_MeshNode*>( itN->next() );
5415       TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5416       if ( nIt == mapNewNodes.end() ) {
5417         nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5418         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5419
5420         // make new nodes
5421         aX = node->X();  aY = node->Y(); aZ = node->Z();
5422
5423         Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5424         gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5425         gp_Ax1 anAx1, anAxT1T0;
5426         gp_Dir aDT1x, aDT0x, aDT1T0;
5427
5428         aTolAng=1.e-4;
5429
5430         aV0x = aV0;
5431         aPN0.SetCoord(aX, aY, aZ);
5432
5433         const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5434         aP0x = aPP0.Pnt();
5435         aDT0x= aPP0.Tangent();
5436         //cout<<"j = 0   PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5437
5438         for ( j = 1; j < aNbTP; ++j ) {
5439           const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5440           aP1x = aPP1.Pnt();
5441           aDT1x = aPP1.Tangent();
5442           aAngle1x = aPP1.Angle();
5443
5444           gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5445           // Translation
5446           gp_Vec aV01x( aP0x, aP1x );
5447           aTrsf.SetTranslation( aV01x );
5448
5449           // traslated point
5450           aV1x = aV0x.Transformed( aTrsf );
5451           aPN1 = aPN0.Transformed( aTrsf );
5452
5453           // rotation 1 [ T1,T0 ]
5454           aAngleT1T0=-aDT1x.Angle( aDT0x );
5455           if (fabs(aAngleT1T0) > aTolAng) {
5456             aDT1T0=aDT1x^aDT0x;
5457             anAxT1T0.SetLocation( aV1x );
5458             anAxT1T0.SetDirection( aDT1T0 );
5459             aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5460
5461             aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5462           }
5463
5464           // rotation 2
5465           if ( theHasAngles ) {
5466             anAx1.SetLocation( aV1x );
5467             anAx1.SetDirection( aDT1x );
5468             aTrsfRot.SetRotation( anAx1, aAngle1x );
5469
5470             aPN1 = aPN1.Transformed( aTrsfRot );
5471           }
5472
5473           // make new node
5474           //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5475           if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5476             // create additional node
5477             double x = ( aPN1.X() + aPN0.X() )/2.;
5478             double y = ( aPN1.Y() + aPN0.Y() )/2.;
5479             double z = ( aPN1.Z() + aPN0.Z() )/2.;
5480             const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5481             myLastCreatedNodes.Append(newNode);
5482             srcNodes.Append( node );
5483             listNewNodes.push_back( newNode );
5484           }
5485           aX = aPN1.X();
5486           aY = aPN1.Y();
5487           aZ = aPN1.Z();
5488           const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5489           myLastCreatedNodes.Append(newNode);
5490           srcNodes.Append( node );
5491           listNewNodes.push_back( newNode );
5492
5493           aPN0 = aPN1;
5494           aP0x = aP1x;
5495           aV0x = aV1x;
5496           aDT0x = aDT1x;
5497         }
5498       }
5499
5500       else {
5501         // if current elem is quadratic and current node is not medium
5502         // we have to check - may be it is needed to insert additional nodes
5503         if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5504           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5505           if(listNewNodes.size()==aNbTP-1) {
5506             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5507             gp_XYZ P(node->X(), node->Y(), node->Z());
5508             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5509             int i;
5510             for(i=0; i<aNbTP-1; i++) {
5511               const SMDS_MeshNode* N = *it;
5512               double x = ( N->X() + P.X() )/2.;
5513               double y = ( N->Y() + P.Y() )/2.;
5514               double z = ( N->Z() + P.Z() )/2.;
5515               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5516               srcNodes.Append( node );
5517               myLastCreatedNodes.Append(newN);
5518               aNodes[2*i] = newN;
5519               aNodes[2*i+1] = N;
5520               P = gp_XYZ(N->X(),N->Y(),N->Z());
5521             }
5522             listNewNodes.clear();
5523             for(i=0; i<2*(aNbTP-1); i++) {
5524               listNewNodes.push_back(aNodes[i]);
5525             }
5526           }
5527         }
5528       }
5529
5530       newNodesItVec.push_back( nIt );
5531     }
5532     // make new elements
5533     //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5534     //              newNodesItVec[0]->second.size(), myLastCreatedElems );
5535     sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5536   }
5537
5538   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5539
5540   if ( theMakeGroups )
5541     generateGroups( srcNodes, srcElems, "extruded");
5542
5543   return EXTR_OK;
5544 }
5545
5546
5547 //=======================================================================
5548 //function : LinearAngleVariation
5549 //purpose  : auxilary for ExtrusionAlongTrack
5550 //=======================================================================
5551 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5552                                             list<double>& Angles)
5553 {
5554   int nbAngles = Angles.size();
5555   if( nbSteps > nbAngles ) {
5556     vector<double> theAngles(nbAngles);
5557     list<double>::iterator it = Angles.begin();
5558     int i = -1;
5559     for(; it!=Angles.end(); it++) {
5560       i++;
5561       theAngles[i] = (*it);
5562     }
5563     list<double> res;
5564     double rAn2St = double( nbAngles ) / double( nbSteps );
5565     double angPrev = 0, angle;
5566     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5567       double angCur = rAn2St * ( iSt+1 );
5568       double angCurFloor  = floor( angCur );
5569       double angPrevFloor = floor( angPrev );
5570       if ( angPrevFloor == angCurFloor )
5571         angle = rAn2St * theAngles[ int( angCurFloor ) ];
5572       else {
5573         int iP = int( angPrevFloor );
5574         double angPrevCeil = ceil(angPrev);
5575         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5576
5577         int iC = int( angCurFloor );
5578         if ( iC < nbAngles )
5579           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5580
5581         iP = int( angPrevCeil );
5582         while ( iC-- > iP )
5583           angle += theAngles[ iC ];
5584       }
5585       res.push_back(angle);
5586       angPrev = angCur;
5587     }
5588     Angles.clear();
5589     it = res.begin();
5590     for(; it!=res.end(); it++)
5591       Angles.push_back( *it );
5592   }
5593 }
5594
5595
5596 //================================================================================
5597 /*!
5598  * \brief Move or copy theElements applying theTrsf to their nodes
5599  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5600  *  \param theTrsf - transformation to apply
5601  *  \param theCopy - if true, create translated copies of theElems
5602  *  \param theMakeGroups - if true and theCopy, create translated groups
5603  *  \param theTargetMesh - mesh to copy translated elements into
5604  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5605  */
5606 //================================================================================
5607
5608 SMESH_MeshEditor::PGroupIDs
5609 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5610                              const gp_Trsf&     theTrsf,
5611                              const bool         theCopy,
5612                              const bool         theMakeGroups,
5613                              SMESH_Mesh*        theTargetMesh)
5614 {
5615   myLastCreatedElems.Clear();
5616   myLastCreatedNodes.Clear();
5617
5618   bool needReverse = false;
5619   string groupPostfix;
5620   switch ( theTrsf.Form() ) {
5621   case gp_PntMirror:
5622     MESSAGE("gp_PntMirror");
5623     needReverse = true;
5624     groupPostfix = "mirrored";
5625     break;
5626   case gp_Ax1Mirror:
5627     MESSAGE("gp_Ax1Mirror");
5628     groupPostfix = "mirrored";
5629     break;
5630   case gp_Ax2Mirror:
5631     MESSAGE("gp_Ax2Mirror");
5632     needReverse = true;
5633     groupPostfix = "mirrored";
5634     break;
5635   case gp_Rotation:
5636     MESSAGE("gp_Rotation");
5637     groupPostfix = "rotated";
5638     break;
5639   case gp_Translation:
5640     MESSAGE("gp_Translation");
5641     groupPostfix = "translated";
5642     break;
5643   case gp_Scale:
5644     MESSAGE("gp_Scale");
5645     groupPostfix = "scaled";
5646     break;
5647   case gp_CompoundTrsf: // different scale by axis
5648     MESSAGE("gp_CompoundTrsf");
5649     groupPostfix = "scaled";
5650     break;
5651   default:
5652     MESSAGE("default");
5653     needReverse = false;
5654     groupPostfix = "transformed";
5655   }
5656
5657   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5658   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5659   SMESHDS_Mesh* aMesh    = GetMeshDS();
5660
5661
5662   // map old node to new one
5663   TNodeNodeMap nodeMap;
5664
5665   // elements sharing moved nodes; those of them which have all
5666   // nodes mirrored but are not in theElems are to be reversed
5667   TIDSortedElemSet inverseElemSet;
5668
5669   // source elements for each generated one
5670   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5671
5672   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5673   TIDSortedElemSet orphanNode;
5674
5675   if ( theElems.empty() ) // transform the whole mesh
5676   {
5677     // add all elements
5678     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5679     while ( eIt->more() ) theElems.insert( eIt->next() );
5680     // add orphan nodes
5681     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5682     while ( nIt->more() )
5683     {
5684       const SMDS_MeshNode* node = nIt->next();
5685       if ( node->NbInverseElements() == 0)
5686         orphanNode.insert( node );
5687     }
5688   }
5689
5690   // loop on elements to transform nodes : first orphan nodes then elems
5691   TIDSortedElemSet::iterator itElem;
5692   TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5693   for (int i=0; i<2; i++)
5694   for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5695     const SMDS_MeshElement* elem = *itElem;
5696     if ( !elem )
5697       continue;
5698
5699     // loop on elem nodes
5700     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5701     while ( itN->more() ) {
5702
5703       const SMDS_MeshNode* node = cast2Node( itN->next() );
5704       // check if a node has been already transformed
5705       pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5706         nodeMap.insert( make_pair ( node, node ));
5707       if ( !n2n_isnew.second )
5708         continue;
5709
5710       double coord[3];
5711       coord[0] = node->X();
5712       coord[1] = node->Y();
5713       coord[2] = node->Z();
5714       theTrsf.Transforms( coord[0], coord[1], coord[2] );
5715       if ( theTargetMesh ) {
5716         const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5717         n2n_isnew.first->second = newNode;
5718         myLastCreatedNodes.Append(newNode);
5719         srcNodes.Append( node );
5720       }
5721       else if ( theCopy ) {
5722         const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5723         n2n_isnew.first->second = newNode;
5724         myLastCreatedNodes.Append(newNode);
5725         srcNodes.Append( node );
5726       }
5727       else {
5728         aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5729         // node position on shape becomes invalid
5730         const_cast< SMDS_MeshNode* > ( node )->SetPosition
5731           ( SMDS_SpacePosition::originSpacePosition() );
5732       }
5733
5734       // keep inverse elements
5735       if ( !theCopy && !theTargetMesh && needReverse ) {
5736         SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5737         while ( invElemIt->more() ) {
5738           const SMDS_MeshElement* iel = invElemIt->next();
5739           inverseElemSet.insert( iel );
5740         }
5741       }
5742     }
5743   }
5744
5745   // either create new elements or reverse mirrored ones
5746   if ( !theCopy && !needReverse && !theTargetMesh )
5747     return PGroupIDs();
5748
5749   TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5750   for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5751     theElems.insert( *invElemIt );
5752
5753   // Replicate or reverse elements
5754
5755   std::vector<int> iForw;
5756   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5757   {
5758     const SMDS_MeshElement* elem = *itElem;
5759     if ( !elem ) continue;
5760
5761     SMDSAbs_GeometryType geomType = elem->GetGeomType();
5762     int                  nbNodes  = elem->NbNodes();
5763     if ( geomType == SMDSGeom_NONE ) continue; // node
5764
5765     switch ( geomType ) {
5766
5767     case SMDSGeom_POLYGON:  // ---------------------- polygon
5768       {
5769         vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5770         int iNode = 0;
5771         SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5772         while (itN->more()) {
5773           const SMDS_MeshNode* node =
5774             static_cast<const SMDS_MeshNode*>(itN->next());
5775           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5776           if (nodeMapIt == nodeMap.end())
5777             break; // not all nodes transformed
5778           if (needReverse) {
5779             // reverse mirrored faces and volumes
5780             poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5781           } else {
5782             poly_nodes[iNode] = (*nodeMapIt).second;
5783           }
5784           iNode++;
5785         }
5786         if ( iNode != nbNodes )
5787           continue; // not all nodes transformed
5788
5789         if ( theTargetMesh ) {
5790           myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5791           srcElems.Append( elem );
5792         }
5793         else if ( theCopy ) {
5794           myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5795           srcElems.Append( elem );
5796         }
5797         else {
5798           aMesh->ChangePolygonNodes(elem, poly_nodes);
5799         }
5800       }
5801       break;
5802
5803     case SMDSGeom_POLYHEDRA:  // ------------------ polyhedral volume
5804       {
5805         const SMDS_VtkVolume* aPolyedre =
5806           dynamic_cast<const SMDS_VtkVolume*>( elem );
5807         if (!aPolyedre) {
5808           MESSAGE("Warning: bad volumic element");
5809           continue;
5810         }
5811
5812         vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5813         vector<int> quantities; quantities.reserve( nbNodes );
5814
5815         bool allTransformed = true;
5816         int nbFaces = aPolyedre->NbFaces();
5817         for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5818           int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5819           for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5820             const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5821             TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5822             if (nodeMapIt == nodeMap.end()) {
5823               allTransformed = false; // not all nodes transformed
5824             } else {
5825               poly_nodes.push_back((*nodeMapIt).second);
5826             }
5827             if ( needReverse && allTransformed )
5828               std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5829           }
5830           quantities.push_back(nbFaceNodes);
5831         }
5832         if ( !allTransformed )
5833           continue; // not all nodes transformed
5834
5835         if ( theTargetMesh ) {
5836           myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5837           srcElems.Append( elem );
5838         }
5839         else if ( theCopy ) {
5840           myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5841           srcElems.Append( elem );
5842         }
5843         else {
5844           aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5845         }
5846       }
5847       break;
5848
5849     case SMDSGeom_BALL: // -------------------- Ball
5850       {
5851         if ( !theCopy && !theTargetMesh ) continue;
5852
5853         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
5854         if (nodeMapIt == nodeMap.end())
5855           continue; // not all nodes transformed
5856
5857         double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
5858         if ( theTargetMesh ) {
5859           myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
5860           srcElems.Append( elem );
5861         }
5862         else {
5863           myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
5864           srcElems.Append( elem );
5865         }
5866       }
5867       break;
5868
5869     default: // ----------------------- Regular elements
5870
5871       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
5872       const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
5873       const std::vector<int>& i = needReverse ? iRev : iForw;
5874
5875       // find transformed nodes
5876       vector<const SMDS_MeshNode*> nodes(nbNodes);
5877       int iNode = 0;
5878       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5879       while ( itN->more() ) {
5880         const SMDS_MeshNode* node =
5881           static_cast<const SMDS_MeshNode*>( itN->next() );
5882         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5883         if ( nodeMapIt == nodeMap.end() )
5884           break; // not all nodes transformed
5885         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5886       }
5887       if ( iNode != nbNodes )
5888         continue; // not all nodes transformed
5889
5890       if ( theTargetMesh ) {
5891         if ( SMDS_MeshElement* copy =
5892              targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5893           myLastCreatedElems.Append( copy );
5894           srcElems.Append( elem );
5895         }
5896       }
5897       else if ( theCopy ) {
5898         if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5899           srcElems.Append( elem );
5900       }
5901       else {
5902         // reverse element as it was reversed by transformation
5903         if ( nbNodes > 2 )
5904           aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5905       }
5906     } // switch ( geomType )
5907
5908   } // loop on elements
5909
5910   PGroupIDs newGroupIDs;
5911
5912   if ( ( theMakeGroups && theCopy ) ||
5913        ( theMakeGroups && theTargetMesh ) )
5914     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5915
5916   return newGroupIDs;
5917 }
5918
5919 //=======================================================================
5920 /*!
5921  * \brief Create groups of elements made during transformation
5922  * \param nodeGens - nodes making corresponding myLastCreatedNodes
5923  * \param elemGens - elements making corresponding myLastCreatedElems
5924  * \param postfix - to append to names of new groups
5925  */
5926 //=======================================================================
5927
5928 SMESH_MeshEditor::PGroupIDs
5929 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5930                                  const SMESH_SequenceOfElemPtr& elemGens,
5931                                  const std::string&             postfix,
5932                                  SMESH_Mesh*                    targetMesh)
5933 {
5934   PGroupIDs newGroupIDs( new list<int> );
5935   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
5936
5937   // Sort existing groups by types and collect their names
5938
5939   // to store an old group and a generated new one
5940   typedef pair< SMESHDS_GroupBase*, SMESHDS_Group* > TOldNewGroup;
5941   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
5942   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
5943   // group names
5944   set< string > groupNames;
5945
5946   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
5947   if ( !groupIt->more() ) return newGroupIDs;
5948
5949   int newGroupID = mesh->GetGroupIds().back()+1;
5950   while ( groupIt->more() )
5951   {
5952     SMESH_Group * group = groupIt->next();
5953     if ( !group ) continue;
5954     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
5955     if ( !groupDS || groupDS->IsEmpty() ) continue;
5956     groupNames.insert( group->GetName() );
5957     groupDS->SetStoreName( group->GetName() );
5958     SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(),
5959                                                  groupDS->GetType() );
5960     groupsByType[ groupDS->GetType() ].push_back( make_pair( groupDS, newGroup ));
5961     orderedOldNewGroups.push_back( & groupsByType[ groupDS->GetType() ].back() );
5962   }
5963
5964   // Loop on nodes and elements to add them in new groups
5965
5966   for ( int isNodes = 0; isNodes < 2; ++isNodes )
5967   {
5968     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
5969     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
5970     if ( gens.Length() != elems.Length() )
5971       throw SALOME_Exception(LOCALIZED("invalid args"));
5972
5973     // loop on created elements
5974     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
5975     {
5976       const SMDS_MeshElement* sourceElem = gens( iElem );
5977       if ( !sourceElem ) {
5978         MESSAGE("generateGroups(): NULL source element");
5979         continue;
5980       }
5981       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
5982       if ( groupsOldNew.empty() ) { // no groups of this type at all
5983         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
5984           ++iElem; // skip all elements made by sourceElem
5985         continue;
5986       }
5987       // collect all elements made by sourceElem
5988       list< const SMDS_MeshElement* > resultElems;
5989       if ( const SMDS_MeshElement* resElem = elems( iElem ))
5990         if ( resElem != sourceElem )
5991           resultElems.push_back( resElem );
5992       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
5993         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
5994           if ( resElem != sourceElem )
5995             resultElems.push_back( resElem );
5996
5997       // add resultElems to groups made by ones the sourceElem belongs to
5998       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
5999       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6000       {
6001         SMESHDS_GroupBase* oldGroup = gOldNew->first;
6002         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6003         {
6004           // fill in a new group
6005           SMDS_MeshGroup & newGroup = gOldNew->second->SMDSGroup();
6006           list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6007           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6008             newGroup.Add( *resElemIt );
6009         }
6010       }
6011     } // loop on created elements
6012   }// loop on nodes and elements
6013
6014   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6015
6016   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6017   {
6018     SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->first;
6019     SMESHDS_Group*     newGroupDS = orderedOldNewGroups[i]->second;
6020     if ( newGroupDS->IsEmpty() )
6021     {
6022       mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6023     }
6024     else
6025     {
6026       // make a name
6027       string name = oldGroupDS->GetStoreName();
6028       if ( !targetMesh ) {
6029         name += "_";
6030         name += postfix;
6031         int nb = 1;
6032         while ( !groupNames.insert( name ).second ) // name exists
6033           name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << postfix << "_" << nb++;
6034       }
6035       newGroupDS->SetStoreName( name.c_str() );
6036
6037       // make a SMESH_Groups
6038       mesh->AddGroup( newGroupDS );
6039       newGroupIDs->push_back( newGroupDS->GetID() );
6040
6041       // set group type
6042       newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6043     }
6044   }
6045
6046   return newGroupIDs;
6047 }
6048
6049 //================================================================================
6050 /*!
6051  * \brief Return list of group of nodes close to each other within theTolerance
6052  *        Search among theNodes or in the whole mesh if theNodes is empty using
6053  *        an Octree algorithm
6054  */
6055 //================================================================================
6056
6057 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6058                                             const double         theTolerance,
6059                                             TListOfListOfNodes & theGroupsOfNodes)
6060 {
6061   myLastCreatedElems.Clear();
6062   myLastCreatedNodes.Clear();
6063
6064   if ( theNodes.empty() )
6065   { // get all nodes in the mesh
6066     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6067     while ( nIt->more() )
6068       theNodes.insert( theNodes.end(),nIt->next());
6069   }
6070
6071   SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6072 }
6073
6074
6075 //=======================================================================
6076 /*!
6077  * \brief Implementation of search for the node closest to point
6078  */
6079 //=======================================================================
6080
6081 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
6082 {
6083   //---------------------------------------------------------------------
6084   /*!
6085    * \brief Constructor
6086    */
6087   SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
6088   {
6089     myMesh = ( SMESHDS_Mesh* ) theMesh;
6090
6091     TIDSortedNodeSet nodes;
6092     if ( theMesh ) {
6093       SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
6094       while ( nIt->more() )
6095         nodes.insert( nodes.end(), nIt->next() );
6096     }
6097     myOctreeNode = new SMESH_OctreeNode(nodes) ;
6098
6099     // get max size of a leaf box
6100     SMESH_OctreeNode* tree = myOctreeNode;
6101     while ( !tree->isLeaf() )
6102     {
6103       SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6104       if ( cIt->more() )
6105         tree = cIt->next();
6106     }
6107     myHalfLeafSize = tree->maxSize() / 2.;
6108   }
6109
6110   //---------------------------------------------------------------------
6111   /*!
6112    * \brief Move node and update myOctreeNode accordingly
6113    */
6114   void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
6115   {
6116     myOctreeNode->UpdateByMoveNode( node, toPnt );
6117     myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
6118   }
6119
6120   //---------------------------------------------------------------------
6121   /*!
6122    * \brief Do it's job
6123    */
6124   const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
6125   {
6126     map<double, const SMDS_MeshNode*> dist2Nodes;
6127     myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize );
6128     if ( !dist2Nodes.empty() )
6129       return dist2Nodes.begin()->second;
6130     list<const SMDS_MeshNode*> nodes;
6131     //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
6132
6133     double minSqDist = DBL_MAX;
6134     if ( nodes.empty() )  // get all nodes of OctreeNode's closest to thePnt
6135     {
6136       // sort leafs by their distance from thePnt
6137       typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
6138       TDistTreeMap treeMap;
6139       list< SMESH_OctreeNode* > treeList;
6140       list< SMESH_OctreeNode* >::iterator trIt;
6141       treeList.push_back( myOctreeNode );
6142
6143       gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
6144       bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize );
6145       for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
6146       {
6147         SMESH_OctreeNode* tree = *trIt;
6148         if ( !tree->isLeaf() ) // put children to the queue
6149         {
6150           if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue;
6151           SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6152           while ( cIt->more() )
6153             treeList.push_back( cIt->next() );
6154         }
6155         else if ( tree->NbNodes() ) // put a tree to the treeMap
6156         {
6157           const Bnd_B3d& box = *tree->getBox();
6158           double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
6159           pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
6160           if ( !it_in.second ) // not unique distance to box center
6161             treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
6162         }
6163       }
6164       // find distance after which there is no sense to check tree's
6165       double sqLimit = DBL_MAX;
6166       TDistTreeMap::iterator sqDist_tree = treeMap.begin();
6167       if ( treeMap.size() > 5 ) {
6168         SMESH_OctreeNode* closestTree = sqDist_tree->second;
6169         const Bnd_B3d& box = *closestTree->getBox();
6170         double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
6171         sqLimit = limit * limit;
6172       }
6173       // get all nodes from trees
6174       for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
6175         if ( sqDist_tree->first > sqLimit )
6176           break;
6177         SMESH_OctreeNode* tree = sqDist_tree->second;
6178         tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
6179       }
6180     }
6181     // find closest among nodes
6182     minSqDist = DBL_MAX;
6183     const SMDS_MeshNode* closestNode = 0;
6184     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6185     for ( ; nIt != nodes.end(); ++nIt ) {
6186       double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) );
6187       if ( minSqDist > sqDist ) {
6188         closestNode = *nIt;
6189         minSqDist = sqDist;
6190       }
6191     }
6192     return closestNode;
6193   }
6194
6195   //---------------------------------------------------------------------
6196   /*!
6197    * \brief Destructor
6198    */
6199   ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
6200
6201   //---------------------------------------------------------------------
6202   /*!
6203    * \brief Return the node tree
6204    */
6205   const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
6206
6207 private:
6208   SMESH_OctreeNode* myOctreeNode;
6209   SMESHDS_Mesh*     myMesh;
6210   double            myHalfLeafSize; // max size of a leaf box
6211 };
6212
6213 //=======================================================================
6214 /*!
6215  * \brief Return SMESH_NodeSearcher
6216  */
6217 //=======================================================================
6218
6219 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher()
6220 {
6221   return new SMESH_NodeSearcherImpl( GetMeshDS() );
6222 }
6223
6224 // ========================================================================
6225 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
6226 {
6227   const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
6228   const int MaxLevel         = 7;  // maximal tree height -> nb terminal boxes: 8^7 = 2097152
6229   const double NodeRadius = 1e-9;  // to enlarge bnd box of element
6230
6231   //=======================================================================
6232   /*!
6233    * \brief Octal tree of bounding boxes of elements
6234    */
6235   //=======================================================================
6236
6237   class ElementBndBoxTree : public SMESH_Octree
6238   {
6239   public:
6240
6241     ElementBndBoxTree(const SMDS_Mesh&     mesh,
6242                       SMDSAbs_ElementType  elemType,
6243                       SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(),
6244                       double               tolerance = NodeRadius );
6245     void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems );
6246     void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
6247     void getElementsInSphere ( const gp_XYZ& center,
6248                                const double  radius, TIDSortedElemSet& foundElems);
6249     size_t getSize() { return std::max( _size, _elements.size() ); }
6250     ~ElementBndBoxTree();
6251
6252   protected:
6253     ElementBndBoxTree():_size(0) {}
6254     SMESH_Octree* newChild() const { return new ElementBndBoxTree; }
6255     void          buildChildrenData();
6256     Bnd_B3d*      buildRootBox();
6257   private:
6258     //!< Bounding box of element
6259     struct ElementBox : public Bnd_B3d
6260     {
6261       const SMDS_MeshElement* _element;
6262       int                     _refCount; // an ElementBox can be included in several tree branches
6263       ElementBox(const SMDS_MeshElement* elem, double tolerance);
6264     };
6265     vector< ElementBox* > _elements;
6266     size_t                _size;
6267   };
6268
6269   //================================================================================
6270   /*!
6271    * \brief ElementBndBoxTree creation
6272    */
6273   //================================================================================
6274
6275   ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
6276     :SMESH_Octree( new SMESH_TreeLimit( MaxLevel, /*minSize=*/0. ))
6277   {
6278     int nbElems = mesh.GetMeshInfo().NbElements( elemType );
6279     _elements.reserve( nbElems );
6280
6281     SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType );
6282     while ( elemIt->more() )
6283       _elements.push_back( new ElementBox( elemIt->next(),tolerance  ));
6284
6285     compute();
6286   }
6287
6288   //================================================================================
6289   /*!
6290    * \brief Destructor
6291    */
6292   //================================================================================
6293
6294   ElementBndBoxTree::~ElementBndBoxTree()
6295   {
6296     for ( int i = 0; i < _elements.size(); ++i )
6297       if ( --_elements[i]->_refCount <= 0 )
6298         delete _elements[i];
6299   }
6300
6301   //================================================================================
6302   /*!
6303    * \brief Return the maximal box
6304    */
6305   //================================================================================
6306
6307   Bnd_B3d* ElementBndBoxTree::buildRootBox()
6308   {
6309     Bnd_B3d* box = new Bnd_B3d;
6310     for ( int i = 0; i < _elements.size(); ++i )
6311       box->Add( *_elements[i] );
6312     return box;
6313   }
6314
6315   //================================================================================
6316   /*!
6317    * \brief Redistrubute element boxes among children
6318    */
6319   //================================================================================
6320
6321   void ElementBndBoxTree::buildChildrenData()
6322   {
6323     for ( int i = 0; i < _elements.size(); ++i )
6324     {
6325       for (int j = 0; j < 8; j++)
6326       {
6327         if ( !_elements[i]->IsOut( *myChildren[j]->getBox() ))
6328         {
6329           _elements[i]->_refCount++;
6330           ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
6331         }
6332       }
6333       _elements[i]->_refCount--;
6334     }
6335     _size = _elements.size();
6336     SMESHUtils::FreeVector( _elements ); // = _elements.clear() + free memory
6337
6338     for (int j = 0; j < 8; j++)
6339     {
6340       ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
6341       if ( child->_elements.size() <= MaxNbElemsInLeaf )
6342         child->myIsLeaf = true;
6343
6344       if ( child->_elements.capacity() - child->_elements.size() > 1000 )
6345         SMESHUtils::CompactVector( child->_elements );
6346     }
6347   }
6348
6349   //================================================================================
6350   /*!
6351    * \brief Return elements which can include the point
6352    */
6353   //================================================================================
6354
6355   void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt&     point,
6356                                                 TIDSortedElemSet& foundElems)
6357   {
6358     if ( getBox()->IsOut( point.XYZ() ))
6359       return;
6360
6361     if ( isLeaf() )
6362     {
6363       for ( int i = 0; i < _elements.size(); ++i )
6364         if ( !_elements[i]->IsOut( point.XYZ() ))
6365           foundElems.insert( _elements[i]->_element );
6366     }
6367     else
6368     {
6369       for (int i = 0; i < 8; i++)
6370         ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6371     }
6372   }
6373
6374   //================================================================================
6375   /*!
6376    * \brief Return elements which can be intersected by the line
6377    */
6378   //================================================================================
6379
6380   void ElementBndBoxTree::getElementsNearLine( const gp_Ax1&     line,
6381                                                TIDSortedElemSet& foundElems)
6382   {
6383     if ( getBox()->IsOut( line ))
6384       return;
6385
6386     if ( isLeaf() )
6387     {
6388       for ( int i = 0; i < _elements.size(); ++i )
6389         if ( !_elements[i]->IsOut( line ))
6390           foundElems.insert( _elements[i]->_element );
6391     }
6392     else
6393     {
6394       for (int i = 0; i < 8; i++)
6395         ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6396     }
6397   }
6398
6399   //================================================================================
6400   /*!
6401    * \brief Return elements from leaves intersecting the sphere
6402    */
6403   //================================================================================
6404
6405   void ElementBndBoxTree::getElementsInSphere ( const gp_XYZ&     center,
6406                                                 const double      radius,
6407                                                 TIDSortedElemSet& foundElems)
6408   {
6409     if ( getBox()->IsOut( center, radius ))
6410       return;
6411
6412     if ( isLeaf() )
6413     {
6414       for ( int i = 0; i < _elements.size(); ++i )
6415         if ( !_elements[i]->IsOut( center, radius ))
6416           foundElems.insert( _elements[i]->_element );
6417     }
6418     else
6419     {
6420       for (int i = 0; i < 8; i++)
6421         ((ElementBndBoxTree*) myChildren[i])->getElementsInSphere( center, radius, foundElems );
6422     }
6423   }
6424
6425   //================================================================================
6426   /*!
6427    * \brief Construct the element box
6428    */
6429   //================================================================================
6430
6431   ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance)
6432   {
6433     _element  = elem;
6434     _refCount = 1;
6435     SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6436     while ( nIt->more() )
6437       Add( SMESH_TNodeXYZ( nIt->next() ));
6438     Enlarge( tolerance );
6439   }
6440
6441 } // namespace
6442
6443 //=======================================================================
6444 /*!
6445  * \brief Implementation of search for the elements by point and
6446  *        of classification of point in 2D mesh
6447  */
6448 //=======================================================================
6449
6450 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6451 {
6452   SMESHDS_Mesh*                _mesh;
6453   SMDS_ElemIteratorPtr         _meshPartIt;
6454   ElementBndBoxTree*           _ebbTree;
6455   SMESH_NodeSearcherImpl*      _nodeSearcher;
6456   SMDSAbs_ElementType          _elementType;
6457   double                       _tolerance;
6458   bool                         _outerFacesFound;
6459   set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6460
6461   SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr())
6462     : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {}
6463   ~SMESH_ElementSearcherImpl()
6464   {
6465     if ( _ebbTree )      delete _ebbTree;      _ebbTree      = 0;
6466     if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6467   }
6468   virtual int FindElementsByPoint(const gp_Pnt&                      point,
6469                                   SMDSAbs_ElementType                type,
6470                                   vector< const SMDS_MeshElement* >& foundElements);
6471   virtual TopAbs_State GetPointState(const gp_Pnt& point);
6472   virtual const SMDS_MeshElement* FindClosestTo( const gp_Pnt&       point,
6473                                                  SMDSAbs_ElementType type );
6474
6475   void GetElementsNearLine( const gp_Ax1&                      line,
6476                             SMDSAbs_ElementType                type,
6477                             vector< const SMDS_MeshElement* >& foundElems);
6478   double getTolerance();
6479   bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6480                             const double tolerance, double & param);
6481   void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6482   bool isOuterBoundary(const SMDS_MeshElement* face) const
6483   {
6484     return _outerFaces.empty() || _outerFaces.count(face);
6485   }
6486   struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6487   {
6488     const SMDS_MeshElement* _face;
6489     gp_Vec                  _faceNorm;
6490     bool                    _coincides; //!< the line lays in face plane
6491     TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6492       : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6493   };
6494   struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6495   {
6496     SMESH_TLink      _link;
6497     TIDSortedElemSet _faces;
6498     TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6499       : _link( n1, n2 ), _faces( &face, &face + 1) {}
6500   };
6501 };
6502
6503 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6504 {
6505   return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6506              << ", _coincides="<<i._coincides << ")";
6507 }
6508
6509 //=======================================================================
6510 /*!
6511  * \brief define tolerance for search
6512  */
6513 //=======================================================================
6514
6515 double SMESH_ElementSearcherImpl::getTolerance()
6516 {
6517   if ( _tolerance < 0 )
6518   {
6519     const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6520
6521     _tolerance = 0;
6522     if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6523     {
6524       double boxSize = _nodeSearcher->getTree()->maxSize();
6525       _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6526     }
6527     else if ( _ebbTree && meshInfo.NbElements() > 0 )
6528     {
6529       double boxSize = _ebbTree->maxSize();
6530       _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6531     }
6532     if ( _tolerance == 0 )
6533     {
6534       // define tolerance by size of a most complex element
6535       int complexType = SMDSAbs_Volume;
6536       while ( complexType > SMDSAbs_All &&
6537               meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6538         --complexType;
6539       if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6540       double elemSize;
6541       if ( complexType == int( SMDSAbs_Node ))
6542       {
6543         SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6544         elemSize = 1;
6545         if ( meshInfo.NbNodes() > 2 )
6546           elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6547       }
6548       else
6549       {
6550         SMDS_ElemIteratorPtr elemIt =
6551             _mesh->elementsIterator( SMDSAbs_ElementType( complexType ));
6552         const SMDS_MeshElement* elem = elemIt->next();
6553         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6554         SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() ));
6555         elemSize = 0;
6556         while ( nodeIt->more() )
6557         {
6558           double dist = n1.Distance( cast2Node( nodeIt->next() ));
6559           elemSize = max( dist, elemSize );
6560         }
6561       }
6562       _tolerance = 1e-4 * elemSize;
6563     }
6564   }
6565   return _tolerance;
6566 }
6567
6568 //================================================================================
6569 /*!
6570  * \brief Find intersection of the line and an edge of face and return parameter on line
6571  */
6572 //================================================================================
6573
6574 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin&           line,
6575                                                      const SMDS_MeshElement* face,
6576                                                      const double            tol,
6577                                                      double &                param)
6578 {
6579   int nbInts = 0;
6580   param = 0;
6581
6582   GeomAPI_ExtremaCurveCurve anExtCC;
6583   Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6584
6585   int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6586   for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6587   {
6588     GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )),
6589                          SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) ));
6590     anExtCC.Init( lineCurve, edge);
6591     if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6592     {
6593       Quantity_Parameter pl, pe;
6594       anExtCC.LowerDistanceParameters( pl, pe );
6595       param += pl;
6596       if ( ++nbInts == 2 )
6597         break;
6598     }
6599   }
6600   if ( nbInts > 0 ) param /= nbInts;
6601   return nbInts > 0;
6602 }
6603 //================================================================================
6604 /*!
6605  * \brief Find all faces belonging to the outer boundary of mesh
6606  */
6607 //================================================================================
6608
6609 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6610 {
6611   if ( _outerFacesFound ) return;
6612
6613   // Collect all outer faces by passing from one outer face to another via their links
6614   // and BTW find out if there are internal faces at all.
6615
6616   // checked links and links where outer boundary meets internal one
6617   set< SMESH_TLink > visitedLinks, seamLinks;
6618
6619   // links to treat with already visited faces sharing them
6620   list < TFaceLink > startLinks;
6621
6622   // load startLinks with the first outerFace
6623   startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6624   _outerFaces.insert( outerFace );
6625
6626   TIDSortedElemSet emptySet;
6627   while ( !startLinks.empty() )
6628   {
6629     const SMESH_TLink& link  = startLinks.front()._link;
6630     TIDSortedElemSet&  faces = startLinks.front()._faces;
6631
6632     outerFace = *faces.begin();
6633     // find other faces sharing the link
6634     const SMDS_MeshElement* f;
6635     while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6636       faces.insert( f );
6637
6638     // select another outer face among the found
6639     const SMDS_MeshElement* outerFace2 = 0;
6640     if ( faces.size() == 2 )
6641     {
6642       outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6643     }
6644     else if ( faces.size() > 2 )
6645     {
6646       seamLinks.insert( link );
6647
6648       // link direction within the outerFace
6649       gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()),
6650                    SMESH_TNodeXYZ( link.node2()));
6651       int i1 = outerFace->GetNodeIndex( link.node1() );
6652       int i2 = outerFace->GetNodeIndex( link.node2() );
6653       bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6654       if ( rev ) n1n2.Reverse();
6655       // outerFace normal
6656       gp_XYZ ofNorm, fNorm;
6657       if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6658       {
6659         // direction from the link inside outerFace
6660         gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6661         // sort all other faces by angle with the dirInOF
6662         map< double, const SMDS_MeshElement* > angle2Face;
6663         set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6664         for ( ; face != faces.end(); ++face )
6665         {
6666           if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6667             continue;
6668           gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6669           double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6670           if ( angle < 0 ) angle += 2. * M_PI;
6671           angle2Face.insert( make_pair( angle, *face ));
6672         }
6673         if ( !angle2Face.empty() )
6674           outerFace2 = angle2Face.begin()->second;
6675       }
6676     }
6677     // store the found outer face and add its links to continue seaching from
6678     if ( outerFace2 )
6679     {
6680       _outerFaces.insert( outerFace );
6681       int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6682       for ( int i = 0; i < nbNodes; ++i )
6683       {
6684         SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6685         if ( visitedLinks.insert( link2 ).second )
6686           startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6687       }
6688     }
6689     startLinks.pop_front();
6690   }
6691   _outerFacesFound = true;
6692
6693   if ( !seamLinks.empty() )
6694   {
6695     // There are internal boundaries touching the outher one,
6696     // find all faces of internal boundaries in order to find
6697     // faces of boundaries of holes, if any.
6698
6699   }
6700   else
6701   {
6702     _outerFaces.clear();
6703   }
6704 }
6705
6706 //=======================================================================
6707 /*!
6708  * \brief Find elements of given type where the given point is IN or ON.
6709  *        Returns nb of found elements and elements them-selves.
6710  *
6711  * 'ALL' type means elements of any type excluding nodes, balls and 0D elements
6712  */
6713 //=======================================================================
6714
6715 int SMESH_ElementSearcherImpl::
6716 FindElementsByPoint(const gp_Pnt&                      point,
6717                     SMDSAbs_ElementType                type,
6718                     vector< const SMDS_MeshElement* >& foundElements)
6719 {
6720   foundElements.clear();
6721
6722   double tolerance = getTolerance();
6723
6724   // =================================================================================
6725   if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement || type == SMDSAbs_Ball)
6726   {
6727     if ( !_nodeSearcher )
6728       _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6729
6730     const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6731     if ( !closeNode ) return foundElements.size();
6732
6733     if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance )
6734       return foundElements.size(); // to far from any node
6735
6736     if ( type == SMDSAbs_Node )
6737     {
6738       foundElements.push_back( closeNode );
6739     }
6740     else
6741     {
6742       SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( type );
6743       while ( elemIt->more() )
6744         foundElements.push_back( elemIt->next() );
6745     }
6746   }
6747   // =================================================================================
6748   else // elements more complex than 0D
6749   {
6750     if ( !_ebbTree || _elementType != type )
6751     {
6752       if ( _ebbTree ) delete _ebbTree;
6753       _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance );
6754     }
6755     TIDSortedElemSet suspectElems;
6756     _ebbTree->getElementsNearPoint( point, suspectElems );
6757     TIDSortedElemSet::iterator elem = suspectElems.begin();
6758     for ( ; elem != suspectElems.end(); ++elem )
6759       if ( !SMESH_MeshEditor::IsOut( *elem, point, tolerance ))
6760         foundElements.push_back( *elem );
6761   }
6762   return foundElements.size();
6763 }
6764
6765 //=======================================================================
6766 /*!
6767  * \brief Find an element of given type most close to the given point
6768  *
6769  * WARNING: Only face search is implemeneted so far
6770  */
6771 //=======================================================================
6772
6773 const SMDS_MeshElement*
6774 SMESH_ElementSearcherImpl::FindClosestTo( const gp_Pnt&       point,
6775                                           SMDSAbs_ElementType type )
6776 {
6777   const SMDS_MeshElement* closestElem = 0;
6778
6779   if ( type == SMDSAbs_Face )
6780   {
6781     if ( !_ebbTree || _elementType != type )
6782     {
6783       if ( _ebbTree ) delete _ebbTree;
6784       _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
6785     }
6786     TIDSortedElemSet suspectElems;
6787     _ebbTree->getElementsNearPoint( point, suspectElems );
6788
6789     if ( suspectElems.empty() && _ebbTree->maxSize() > 0 )
6790     {
6791       gp_Pnt boxCenter = 0.5 * ( _ebbTree->getBox()->CornerMin() +
6792                                  _ebbTree->getBox()->CornerMax() );
6793       double radius;
6794       if ( _ebbTree->getBox()->IsOut( point.XYZ() ))
6795         radius = point.Distance( boxCenter ) - 0.5 * _ebbTree->maxSize();
6796       else
6797         radius = _ebbTree->maxSize() / pow( 2., _ebbTree->getHeight()) / 2;
6798       while ( suspectElems.empty() )
6799       {
6800         _ebbTree->getElementsInSphere( point.XYZ(), radius, suspectElems );
6801         radius *= 1.1;
6802       }
6803     }
6804     double minDist = std::numeric_limits<double>::max();
6805     multimap< double, const SMDS_MeshElement* > dist2face;
6806     TIDSortedElemSet::iterator elem = suspectElems.begin();
6807     for ( ; elem != suspectElems.end(); ++elem )
6808     {
6809       double dist = SMESH_MeshEditor::GetDistance( dynamic_cast<const SMDS_MeshFace*>(*elem),
6810                                                    point );
6811       if ( dist < minDist + 1e-10)
6812       {
6813         minDist = dist;
6814         dist2face.insert( dist2face.begin(), make_pair( dist, *elem ));
6815       }
6816     }
6817     if ( !dist2face.empty() )
6818     {
6819       multimap< double, const SMDS_MeshElement* >::iterator d2f = dist2face.begin();
6820       closestElem = d2f->second;
6821       // if there are several elements at the same distance, select one
6822       // with GC closest to the point
6823       typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
6824       double minDistToGC = 0;
6825       for ( ++d2f; d2f != dist2face.end() && fabs( d2f->first - minDist ) < 1e-10; ++d2f )
6826       {
6827         if ( minDistToGC == 0 )
6828         {
6829           gp_XYZ gc(0,0,0);
6830           gc = accumulate( TXyzIterator(closestElem->nodesIterator()),
6831                            TXyzIterator(), gc ) / closestElem->NbNodes();
6832           minDistToGC = point.SquareDistance( gc );
6833         }
6834         gp_XYZ gc(0,0,0);
6835         gc = accumulate( TXyzIterator( d2f->second->nodesIterator()),
6836                          TXyzIterator(), gc ) / d2f->second->NbNodes();
6837         double d = point.SquareDistance( gc );
6838         if ( d < minDistToGC )
6839         {
6840           minDistToGC = d;
6841           closestElem = d2f->second;
6842         }
6843       }
6844       // cout << "FindClosestTo( " <<point.X()<<", "<<point.Y()<<", "<<point.Z()<<" ) FACE "
6845       //      <<closestElem->GetID() << " DIST " << minDist << endl;
6846     }
6847   }
6848   else
6849   {
6850     // NOT IMPLEMENTED SO FAR
6851   }
6852   return closestElem;
6853 }
6854
6855
6856 //================================================================================
6857 /*!
6858  * \brief Classify the given point in the closed 2D mesh
6859  */
6860 //================================================================================
6861
6862 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6863 {
6864   double tolerance = getTolerance();
6865   if ( !_ebbTree || _elementType != SMDSAbs_Face )
6866   {
6867     if ( _ebbTree ) delete _ebbTree;
6868     _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt );
6869   }
6870   // Algo: analyse transition of a line starting at the point through mesh boundary;
6871   // try three lines parallel to axis of the coordinate system and perform rough
6872   // analysis. If solution is not clear perform thorough analysis.
6873
6874   const int nbAxes = 3;
6875   gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6876   map< double, TInters >   paramOnLine2TInters[ nbAxes ];
6877   list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6878   multimap< int, int > nbInt2Axis; // to find the simplest case
6879   for ( int axis = 0; axis < nbAxes; ++axis )
6880   {
6881     gp_Ax1 lineAxis( point, axisDir[axis]);
6882     gp_Lin line    ( lineAxis );
6883
6884     TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
6885     _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
6886
6887     // Intersect faces with the line
6888
6889     map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6890     TIDSortedElemSet::iterator face = suspectFaces.begin();
6891     for ( ; face != suspectFaces.end(); ++face )
6892     {
6893       // get face plane
6894       gp_XYZ fNorm;
6895       if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
6896       gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm );
6897
6898       // perform intersection
6899       IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
6900       if ( !intersection.IsDone() )
6901         continue;
6902       if ( intersection.IsInQuadric() )
6903       {
6904         tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
6905       }
6906       else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
6907       {
6908         gp_Pnt intersectionPoint = intersection.Point(1);
6909         if ( !SMESH_MeshEditor::IsOut( *face, intersectionPoint, tolerance ))
6910           u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
6911       }
6912     }
6913     // Analyse intersections roughly
6914
6915     int nbInter = u2inters.size();
6916     if ( nbInter == 0 )
6917       return TopAbs_OUT;
6918
6919     double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
6920     if ( nbInter == 1 ) // not closed mesh
6921       return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6922
6923     if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6924       return TopAbs_ON;
6925
6926     if ( (f<0) == (l<0) )
6927       return TopAbs_OUT;
6928
6929     int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
6930     int nbIntAfterPoint  = nbInter - nbIntBeforePoint;
6931     if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6932       return TopAbs_IN;
6933
6934     nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
6935
6936     if ( _outerFacesFound ) break; // pass to thorough analysis
6937
6938   } // three attempts - loop on CS axes
6939
6940   // Analyse intersections thoroughly.
6941   // We make two loops maximum, on the first one we only exclude touching intersections,
6942   // on the second, if situation is still unclear, we gather and use information on
6943   // position of faces (internal or outer). If faces position is already gathered,
6944   // we make the second loop right away.
6945
6946   for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
6947   {
6948     multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
6949     for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
6950     {
6951       int axis = nb_axis->second;
6952       map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6953
6954       gp_Ax1 lineAxis( point, axisDir[axis]);
6955       gp_Lin line    ( lineAxis );
6956
6957       // add tangent intersections to u2inters
6958       double param;
6959       list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
6960       for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
6961         if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
6962           u2inters.insert(make_pair( param, *tgtInt ));
6963       tangentInters[ axis ].clear();
6964
6965       // Count intersections before and after the point excluding touching ones.
6966       // If hasPositionInfo we count intersections of outer boundary only
6967
6968       int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
6969       double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
6970       map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
6971       bool ok = ! u_int1->second._coincides;
6972       while ( ok && u_int1 != u2inters.end() )
6973       {
6974         double u = u_int1->first;
6975         bool touchingInt = false;
6976         if ( ++u_int2 != u2inters.end() )
6977         {
6978           // skip intersections at the same point (if the line passes through edge or node)
6979           int nbSamePnt = 0;
6980           while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
6981           {
6982             ++nbSamePnt;
6983             ++u_int2;
6984           }
6985
6986           // skip tangent intersections
6987           int nbTgt = 0;
6988           const SMDS_MeshElement* prevFace = u_int1->second._face;
6989           while ( ok && u_int2->second._coincides )
6990           {
6991             if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
6992               ok = false;
6993             else
6994             {
6995               nbTgt++;
6996               u_int2++;
6997               ok = ( u_int2 != u2inters.end() );
6998             }
6999           }
7000           if ( !ok ) break;
7001
7002           // skip intersections at the same point after tangent intersections
7003           if ( nbTgt > 0 )
7004           {
7005             double u2 = u_int2->first;
7006             ++u_int2;
7007             while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
7008             {
7009               ++nbSamePnt;
7010               ++u_int2;
7011             }
7012           }
7013           // decide if we skipped a touching intersection
7014           if ( nbSamePnt + nbTgt > 0 )
7015           {
7016             double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
7017             map< double, TInters >::iterator u_int = u_int1;
7018             for ( ; u_int != u_int2; ++u_int )
7019             {
7020               if ( u_int->second._coincides ) continue;
7021               double dot = u_int->second._faceNorm * line.Direction();
7022               if ( dot > maxDot ) maxDot = dot;
7023               if ( dot < minDot ) minDot = dot;
7024             }
7025             touchingInt = ( minDot*maxDot < 0 );
7026           }
7027         }
7028         if ( !touchingInt )
7029         {
7030           if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
7031           {
7032             if ( u < 0 )
7033               ++nbIntBeforePoint;
7034             else
7035               ++nbIntAfterPoint;
7036           }
7037           if ( u < f ) f = u;
7038           if ( u > l ) l = u;
7039         }
7040
7041         u_int1 = u_int2; // to next intersection
7042
7043       } // loop on intersections with one line
7044
7045       if ( ok )
7046       {
7047         if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
7048           return TopAbs_ON;
7049
7050         if ( nbIntBeforePoint == 0  || nbIntAfterPoint == 0)
7051           return TopAbs_OUT;
7052
7053         if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
7054           return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7055
7056         if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7057           return TopAbs_IN;
7058
7059         if ( (f<0) == (l<0) )
7060           return TopAbs_OUT;
7061
7062         if ( hasPositionInfo )
7063           return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
7064       }
7065     } // loop on intersections of the tree lines - thorough analysis
7066
7067     if ( !hasPositionInfo )
7068     {
7069       // gather info on faces position - is face in the outer boundary or not
7070       map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
7071       findOuterBoundary( u2inters.begin()->second._face );
7072     }
7073
7074   } // two attempts - with and w/o faces position info in the mesh
7075
7076   return TopAbs_UNKNOWN;
7077 }
7078
7079 //=======================================================================
7080 /*!
7081  * \brief Return elements possibly intersecting the line
7082  */
7083 //=======================================================================
7084
7085 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1&                      line,
7086                                                      SMDSAbs_ElementType                type,
7087                                                      vector< const SMDS_MeshElement* >& foundElems)
7088 {
7089   if ( !_ebbTree || _elementType != type )
7090   {
7091     if ( _ebbTree ) delete _ebbTree;
7092     _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
7093   }
7094   TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
7095   _ebbTree->getElementsNearLine( line, suspectFaces );
7096   foundElems.assign( suspectFaces.begin(), suspectFaces.end());
7097 }
7098
7099 //=======================================================================
7100 /*!
7101  * \brief Return SMESH_ElementSearcher
7102  */
7103 //=======================================================================
7104
7105 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
7106 {
7107   return new SMESH_ElementSearcherImpl( *GetMeshDS() );
7108 }
7109
7110 //=======================================================================
7111 /*!
7112  * \brief Return SMESH_ElementSearcher acting on a sub-set of elements
7113  */
7114 //=======================================================================
7115
7116 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt)
7117 {
7118   return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt );
7119 }
7120
7121 //=======================================================================
7122 /*!
7123  * \brief Return true if the point is IN or ON of the element
7124  */
7125 //=======================================================================
7126
7127 bool SMESH_MeshEditor::IsOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
7128 {
7129   if ( element->GetType() == SMDSAbs_Volume)
7130   {
7131     return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
7132   }
7133
7134   // get ordered nodes
7135
7136   vector< gp_XYZ > xyz;
7137   vector<const SMDS_MeshNode*> nodeList;
7138
7139   SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
7140   if ( element->IsQuadratic() ) {
7141     if (const SMDS_VtkFace* f=dynamic_cast<const SMDS_VtkFace*>(element))
7142       nodeIt = f->interlacedNodesElemIterator();
7143     else if (const SMDS_VtkEdge*  e =dynamic_cast<const SMDS_VtkEdge*>(element))
7144       nodeIt = e->interlacedNodesElemIterator();
7145   }
7146   while ( nodeIt->more() )
7147     {
7148       const SMDS_MeshNode* node = cast2Node( nodeIt->next() );
7149       xyz.push_back( SMESH_TNodeXYZ(node) );
7150       nodeList.push_back(node);
7151     }
7152
7153   int i, nbNodes = element->NbNodes();
7154
7155   if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
7156   {
7157     // compute face normal
7158     gp_Vec faceNorm(0,0,0);
7159     xyz.push_back( xyz.front() );
7160     nodeList.push_back( nodeList.front() );
7161     for ( i = 0; i < nbNodes; ++i )
7162     {
7163       gp_Vec edge1( xyz[i+1], xyz[i]);
7164       gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
7165       faceNorm += edge1 ^ edge2;
7166     }
7167     double normSize = faceNorm.Magnitude();
7168     if ( normSize <= tol )
7169     {
7170       // degenerated face: point is out if it is out of all face edges
7171       for ( i = 0; i < nbNodes; ++i )
7172       {
7173         SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] );
7174         if ( !IsOut( &edge, point, tol ))
7175           return false;
7176       }
7177       return true;
7178     }
7179     faceNorm /= normSize;
7180
7181     // check if the point lays on face plane
7182     gp_Vec n2p( xyz[0], point );
7183     if ( fabs( n2p * faceNorm ) > tol )
7184       return true; // not on face plane
7185
7186     // check if point is out of face boundary:
7187     // define it by closest transition of a ray point->infinity through face boundary
7188     // on the face plane.
7189     // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
7190     // to find intersections of the ray with the boundary.
7191     gp_Vec ray = n2p;
7192     gp_Vec plnNorm = ray ^ faceNorm;
7193     normSize = plnNorm.Magnitude();
7194     if ( normSize <= tol ) return false; // point coincides with the first node
7195     plnNorm /= normSize;
7196     // for each node of the face, compute its signed distance to the plane
7197     vector<double> dist( nbNodes + 1);
7198     for ( i = 0; i < nbNodes; ++i )
7199     {
7200       gp_Vec n2p( xyz[i], point );
7201       dist[i] = n2p * plnNorm;
7202     }
7203     dist.back() = dist.front();
7204     // find the closest intersection
7205     int    iClosest = -1;
7206     double rClosest, distClosest = 1e100;;
7207     gp_Pnt pClosest;
7208     for ( i = 0; i < nbNodes; ++i )
7209     {
7210       double r;
7211       if ( fabs( dist[i]) < tol )
7212         r = 0.;
7213       else if ( fabs( dist[i+1]) < tol )
7214         r = 1.;
7215       else if ( dist[i] * dist[i+1] < 0 )
7216         r = dist[i] / ( dist[i] - dist[i+1] );
7217       else
7218         continue; // no intersection
7219       gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
7220       gp_Vec p2int ( point, pInt);
7221       if ( p2int * ray > -tol ) // right half-space
7222       {
7223         double intDist = p2int.SquareMagnitude();
7224         if ( intDist < distClosest )
7225         {
7226           iClosest = i;
7227           rClosest = r;
7228           pClosest = pInt;
7229           distClosest = intDist;
7230         }
7231       }
7232     }
7233     if ( iClosest < 0 )
7234       return true; // no intesections - out
7235
7236     // analyse transition
7237     gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
7238     gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
7239     gp_Vec p2int ( point, pClosest );
7240     bool out = (edgeNorm * p2int) < -tol;
7241     if ( rClosest > 0. && rClosest < 1. ) // not node intersection
7242       return out;
7243
7244     // ray pass through a face node; analyze transition through an adjacent edge
7245     gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
7246     gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
7247     gp_Vec edgeAdjacent( p1, p2 );
7248     gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
7249     bool out2 = (edgeNorm2 * p2int) < -tol;
7250
7251     bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
7252     return covexCorner ? (out || out2) : (out && out2);
7253   }
7254   if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
7255   {
7256     // point is out of edge if it is NOT ON any straight part of edge
7257     // (we consider quadratic edge as being composed of two straight parts)
7258     for ( i = 1; i < nbNodes; ++i )
7259     {
7260       gp_Vec edge( xyz[i-1], xyz[i]);
7261       gp_Vec n1p ( xyz[i-1], point);
7262       double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
7263       if ( dist > tol )
7264         continue;
7265       gp_Vec n2p( xyz[i], point );
7266       if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
7267         continue;
7268       return false; // point is ON this part
7269     }
7270     return true;
7271   }
7272   // Node or 0D element -------------------------------------------------------------------------
7273   {
7274     gp_Vec n2p ( xyz[0], point );
7275     return n2p.Magnitude() <= tol;
7276   }
7277   return true;
7278 }
7279
7280 //=======================================================================
7281
7282 namespace
7283 {
7284   // Position of a point relative to a segment
7285   //            .           .
7286   //            .  LEFT     .
7287   //            .           .
7288   //  VERTEX 1  o----ON----->  VERTEX 2
7289   //            .           .
7290   //            .  RIGHT    .
7291   //            .           .
7292   enum PositionName { POS_LEFT = 1, POS_VERTEX = 2, POS_RIGHT = 4, //POS_ON = 8,
7293                       POS_ALL = POS_LEFT | POS_RIGHT | POS_VERTEX };
7294   struct PointPos
7295   {
7296     PositionName _name;
7297     int          _index; // index of vertex or segment
7298
7299     PointPos( PositionName n, int i=-1 ): _name(n), _index(i) {}
7300     bool operator < (const PointPos& other ) const
7301     {
7302       if ( _name == other._name )
7303         return  ( _index < 0 || other._index < 0 ) ? false : _index < other._index;
7304       return _name < other._name;
7305     }
7306   };
7307
7308   //================================================================================
7309   /*!
7310    * \brief Return of a point relative to a segment
7311    *  \param point2D      - the point to analyze position of
7312    *  \param xyVec        - end points of segments
7313    *  \param index0       - 0-based index of the first point of segment
7314    *  \param posToFindOut - flags of positions to detect
7315    *  \retval PointPos - point position
7316    */
7317   //================================================================================
7318
7319   PointPos getPointPosition( const gp_XY& point2D,
7320                              const gp_XY* segEnds,
7321                              const int    index0 = 0,
7322                              const int    posToFindOut = POS_ALL)
7323   {
7324     const gp_XY& p1 = segEnds[ index0   ];
7325     const gp_XY& p2 = segEnds[ index0+1 ];
7326     const gp_XY grad = p2 - p1;
7327
7328     if ( posToFindOut & POS_VERTEX )
7329     {
7330       // check if the point2D is at "vertex 1" zone
7331       gp_XY pp1[2] = { p1, gp_XY( p1.X() - grad.Y(),
7332                                   p1.Y() + grad.X() ) };
7333       if ( getPointPosition( point2D, pp1, 0, POS_LEFT|POS_RIGHT )._name == POS_LEFT )
7334         return PointPos( POS_VERTEX, index0 );
7335
7336       // check if the point2D is at "vertex 2" zone
7337       gp_XY pp2[2] = { p2, gp_XY( p2.X() - grad.Y(),
7338                                   p2.Y() + grad.X() ) };
7339       if ( getPointPosition( point2D, pp2, 0, POS_LEFT|POS_RIGHT )._name == POS_RIGHT )
7340         return PointPos( POS_VERTEX, index0 + 1);
7341     }
7342     double edgeEquation =
7343       ( point2D.X() - p1.X() ) * grad.Y() - ( point2D.Y() - p1.Y() ) * grad.X();
7344     return PointPos( edgeEquation < 0 ? POS_LEFT : POS_RIGHT, index0 );
7345   }
7346 }
7347
7348 //=======================================================================
7349 /*!
7350  * \brief Return minimal distance from a point to a face
7351  *
7352  * Currently we ignore non-planarity and 2nd order of face
7353  */
7354 //=======================================================================
7355
7356 double SMESH_MeshEditor::GetDistance( const SMDS_MeshFace* face,
7357                                       const gp_Pnt&        point )
7358 {
7359   double badDistance = -1;
7360   if ( !face ) return badDistance;
7361
7362   // coordinates of nodes (medium nodes, if any, ignored)
7363   typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
7364   vector<gp_XYZ> xyz( TXyzIterator( face->nodesIterator()), TXyzIterator() );
7365   xyz.resize( face->NbCornerNodes()+1 );
7366
7367   // transformation to get xyz[0] lies on the origin, xyz[1] lies on the Z axis,
7368   // and xyz[2] lies in the XZ plane. This is to pass to 2D space on XZ plane.
7369   gp_Trsf trsf;
7370   gp_Vec OZ ( xyz[0], xyz[1] );
7371   gp_Vec OX ( xyz[0], xyz[2] );
7372   if ( OZ.Magnitude() < std::numeric_limits<double>::min() )
7373   {
7374     if ( xyz.size() < 4 ) return badDistance;
7375     OZ = gp_Vec ( xyz[0], xyz[2] );
7376     OX = gp_Vec ( xyz[0], xyz[3] );
7377   }
7378   gp_Ax3 tgtCS;
7379   try {
7380     tgtCS = gp_Ax3( xyz[0], OZ, OX );
7381   }
7382   catch ( Standard_Failure ) {
7383     return badDistance;
7384   }
7385   trsf.SetTransformation( tgtCS );
7386
7387   // move all the nodes to 2D
7388   vector<gp_XY> xy( xyz.size() );
7389   for ( size_t i = 0;i < xyz.size()-1; ++i )
7390   {
7391     gp_XYZ p3d = xyz[i];
7392     trsf.Transforms( p3d );
7393     xy[i].SetCoord( p3d.X(), p3d.Z() );
7394   }
7395   xyz.back() = xyz.front();
7396   xy.back() = xy.front();
7397
7398   // // move the point in 2D
7399   gp_XYZ tmpPnt = point.XYZ();
7400   trsf.Transforms( tmpPnt );
7401   gp_XY point2D( tmpPnt.X(), tmpPnt.Z() );
7402
7403   // loop on segments of the face to analyze point position ralative to the face
7404   set< PointPos > pntPosSet;
7405   for ( size_t i = 1; i < xy.size(); ++i )
7406   {
7407     PointPos pos = getPointPosition( point2D, &xy[0], i-1 );
7408     pntPosSet.insert( pos );
7409   }
7410
7411   // compute distance
7412   PointPos pos = *pntPosSet.begin();
7413   // cout << "Face " << face->GetID() << " DIST: ";
7414   switch ( pos._name )
7415   {
7416   case POS_LEFT: {
7417     // point is most close to a segment
7418     gp_Vec p0p1( point, xyz[ pos._index ] );
7419     gp_Vec p1p2( xyz[ pos._index ], xyz[ pos._index+1 ]); // segment vector
7420     p1p2.Normalize();
7421     double projDist = p0p1 * p1p2; // distance projected to the segment
7422     gp_Vec projVec = p1p2 * projDist;
7423     gp_Vec distVec = p0p1 - projVec;
7424     // cout << distVec.Magnitude()  << ", SEG " << face->GetNode(pos._index)->GetID()
7425     //      << " - " << face->GetNodeWrap(pos._index+1)->GetID() << endl;
7426     return distVec.Magnitude();
7427   }
7428   case POS_RIGHT: {
7429     // point is inside the face
7430     double distToFacePlane = tmpPnt.Y();
7431     // cout << distToFacePlane << ", INSIDE " << endl;
7432     return Abs( distToFacePlane );
7433   }
7434   case POS_VERTEX: {
7435     // point is most close to a node
7436     gp_Vec distVec( point, xyz[ pos._index ]);
7437     // cout << distVec.Magnitude()  << " VERTEX " << face->GetNode(pos._index)->GetID() << endl;
7438     return distVec.Magnitude();
7439   }
7440   }
7441   return badDistance;
7442 }
7443
7444 //=======================================================================
7445 //function : SimplifyFace
7446 //purpose  :
7447 //=======================================================================
7448 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
7449                                     vector<const SMDS_MeshNode *>&      poly_nodes,
7450                                     vector<int>&                        quantities) const
7451 {
7452   int nbNodes = faceNodes.size();
7453
7454   if (nbNodes < 3)
7455     return 0;
7456
7457   set<const SMDS_MeshNode*> nodeSet;
7458
7459   // get simple seq of nodes
7460   //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7461   vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7462   int iSimple = 0, nbUnique = 0;
7463
7464   simpleNodes[iSimple++] = faceNodes[0];
7465   nbUnique++;
7466   for (int iCur = 1; iCur < nbNodes; iCur++) {
7467     if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7468       simpleNodes[iSimple++] = faceNodes[iCur];
7469       if (nodeSet.insert( faceNodes[iCur] ).second)
7470         nbUnique++;
7471     }
7472   }
7473   int nbSimple = iSimple;
7474   if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7475     nbSimple--;
7476     iSimple--;
7477   }
7478
7479   if (nbUnique < 3)
7480     return 0;
7481
7482   // separate loops
7483   int nbNew = 0;
7484   bool foundLoop = (nbSimple > nbUnique);
7485   while (foundLoop) {
7486     foundLoop = false;
7487     set<const SMDS_MeshNode*> loopSet;
7488     for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7489       const SMDS_MeshNode* n = simpleNodes[iSimple];
7490       if (!loopSet.insert( n ).second) {
7491         foundLoop = true;
7492
7493         // separate loop
7494         int iC = 0, curLast = iSimple;
7495         for (; iC < curLast; iC++) {
7496           if (simpleNodes[iC] == n) break;
7497         }
7498         int loopLen = curLast - iC;
7499         if (loopLen > 2) {
7500           // create sub-element
7501           nbNew++;
7502           quantities.push_back(loopLen);
7503           for (; iC < curLast; iC++) {
7504             poly_nodes.push_back(simpleNodes[iC]);
7505           }
7506         }
7507         // shift the rest nodes (place from the first loop position)
7508         for (iC = curLast + 1; iC < nbSimple; iC++) {
7509           simpleNodes[iC - loopLen] = simpleNodes[iC];
7510         }
7511         nbSimple -= loopLen;
7512         iSimple -= loopLen;
7513       }
7514     } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7515   } // while (foundLoop)
7516
7517   if (iSimple > 2) {
7518     nbNew++;
7519     quantities.push_back(iSimple);
7520     for (int i = 0; i < iSimple; i++)
7521       poly_nodes.push_back(simpleNodes[i]);
7522   }
7523
7524   return nbNew;
7525 }
7526
7527 //=======================================================================
7528 //function : MergeNodes
7529 //purpose  : In each group, the cdr of nodes are substituted by the first one
7530 //           in all elements.
7531 //=======================================================================
7532
7533 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7534 {
7535   MESSAGE("MergeNodes");
7536   myLastCreatedElems.Clear();
7537   myLastCreatedNodes.Clear();
7538
7539   SMESHDS_Mesh* aMesh = GetMeshDS();
7540
7541   TNodeNodeMap nodeNodeMap; // node to replace - new node
7542   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7543   list< int > rmElemIds, rmNodeIds;
7544
7545   // Fill nodeNodeMap and elems
7546
7547   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7548   for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7549     list<const SMDS_MeshNode*>& nodes = *grIt;
7550     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7551     const SMDS_MeshNode* nToKeep = *nIt;
7552     //MESSAGE("node to keep " << nToKeep->GetID());
7553     for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7554       const SMDS_MeshNode* nToRemove = *nIt;
7555       nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7556       if ( nToRemove != nToKeep ) {
7557         //MESSAGE("  node to remove " << nToRemove->GetID());
7558         rmNodeIds.push_back( nToRemove->GetID() );
7559         AddToSameGroups( nToKeep, nToRemove, aMesh );
7560       }
7561
7562       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7563       while ( invElemIt->more() ) {
7564         const SMDS_MeshElement* elem = invElemIt->next();
7565         elems.insert(elem);
7566       }
7567     }
7568   }
7569   // Change element nodes or remove an element
7570
7571   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7572   for ( ; eIt != elems.end(); eIt++ ) {
7573     const SMDS_MeshElement* elem = *eIt;
7574     //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7575     int nbNodes = elem->NbNodes();
7576     int aShapeId = FindShape( elem );
7577
7578     set<const SMDS_MeshNode*> nodeSet;
7579     vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7580     int iUnique = 0, iCur = 0, nbRepl = 0;
7581     vector<int> iRepl( nbNodes );
7582
7583     // get new seq of nodes
7584     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7585     while ( itN->more() ) {
7586       const SMDS_MeshNode* n =
7587         static_cast<const SMDS_MeshNode*>( itN->next() );
7588
7589       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7590       if ( nnIt != nodeNodeMap.end() ) { // n sticks
7591         n = (*nnIt).second;
7592         // BUG 0020185: begin
7593         {
7594           bool stopRecur = false;
7595           set<const SMDS_MeshNode*> nodesRecur;
7596           nodesRecur.insert(n);
7597           while (!stopRecur) {
7598             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7599             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7600               n = (*nnIt_i).second;
7601               if (!nodesRecur.insert(n).second) {
7602                 // error: recursive dependancy
7603                 stopRecur = true;
7604               }
7605             }
7606             else
7607               stopRecur = true;
7608           }
7609         }
7610         // BUG 0020185: end
7611       }
7612       curNodes[ iCur ] = n;
7613       bool isUnique = nodeSet.insert( n ).second;
7614       if ( isUnique )
7615         uniqueNodes[ iUnique++ ] = n;
7616       else
7617         iRepl[ nbRepl++ ] = iCur;
7618       iCur++;
7619     }
7620
7621     // Analyse element topology after replacement
7622
7623     bool isOk = true;
7624     int nbUniqueNodes = nodeSet.size();
7625     //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7626     if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7627       // Polygons and Polyhedral volumes
7628       if (elem->IsPoly()) {
7629
7630         if (elem->GetType() == SMDSAbs_Face) {
7631           // Polygon
7632           vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7633           int inode = 0;
7634           for (; inode < nbNodes; inode++) {
7635             face_nodes[inode] = curNodes[inode];
7636           }
7637
7638           vector<const SMDS_MeshNode *> polygons_nodes;
7639           vector<int> quantities;
7640           int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7641           if (nbNew > 0) {
7642             inode = 0;
7643             for (int iface = 0; iface < nbNew; iface++) {
7644               int nbNodes = quantities[iface];
7645               vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7646               for (int ii = 0; ii < nbNodes; ii++, inode++) {
7647                 poly_nodes[ii] = polygons_nodes[inode];
7648               }
7649               SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7650               myLastCreatedElems.Append(newElem);
7651               if (aShapeId)
7652                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7653             }
7654
7655             MESSAGE("ChangeElementNodes MergeNodes Polygon");
7656             //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7657             vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7658             int quid =0;
7659             if (nbNew > 0) quid = nbNew - 1;
7660             vector<int> newquant(quantities.begin()+quid, quantities.end());
7661             const SMDS_MeshElement* newElem = 0;
7662             newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7663             myLastCreatedElems.Append(newElem);
7664             if ( aShapeId && newElem )
7665               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7666             rmElemIds.push_back(elem->GetID());
7667           }
7668           else {
7669             rmElemIds.push_back(elem->GetID());
7670           }
7671
7672         }
7673         else if (elem->GetType() == SMDSAbs_Volume) {
7674           // Polyhedral volume
7675           if (nbUniqueNodes < 4) {
7676             rmElemIds.push_back(elem->GetID());
7677           }
7678           else {
7679             // each face has to be analyzed in order to check volume validity
7680             const SMDS_VtkVolume* aPolyedre =
7681               dynamic_cast<const SMDS_VtkVolume*>( elem );
7682             if (aPolyedre) {
7683               int nbFaces = aPolyedre->NbFaces();
7684
7685               vector<const SMDS_MeshNode *> poly_nodes;
7686               vector<int> quantities;
7687
7688               for (int iface = 1; iface <= nbFaces; iface++) {
7689                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7690                 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7691
7692                 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7693                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7694                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7695                   if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7696                     faceNode = (*nnIt).second;
7697                   }
7698                   faceNodes[inode - 1] = faceNode;
7699                 }
7700
7701                 SimplifyFace(faceNodes, poly_nodes, quantities);
7702               }
7703
7704               if (quantities.size() > 3) {
7705                 // to be done: remove coincident faces
7706               }
7707
7708               if (quantities.size() > 3)
7709                 {
7710                   MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7711                   //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7712                   const SMDS_MeshElement* newElem = 0;
7713                   newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7714                   myLastCreatedElems.Append(newElem);
7715                   if ( aShapeId && newElem )
7716                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
7717                   rmElemIds.push_back(elem->GetID());
7718                 }
7719             }
7720             else {
7721               rmElemIds.push_back(elem->GetID());
7722             }
7723           }
7724         }
7725         else {
7726         }
7727
7728         continue;
7729       } // poly element
7730
7731       // Regular elements
7732       // TODO not all the possible cases are solved. Find something more generic?
7733       switch ( nbNodes ) {
7734       case 2: ///////////////////////////////////// EDGE
7735         isOk = false; break;
7736       case 3: ///////////////////////////////////// TRIANGLE
7737         isOk = false; break;
7738       case 4:
7739         if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7740           isOk = false;
7741         else { //////////////////////////////////// QUADRANGLE
7742           if ( nbUniqueNodes < 3 )
7743             isOk = false;
7744           else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7745             isOk = false; // opposite nodes stick
7746           //MESSAGE("isOk " << isOk);
7747         }
7748         break;
7749       case 6: ///////////////////////////////////// PENTAHEDRON
7750         if ( nbUniqueNodes == 4 ) {
7751           // ---------------------------------> tetrahedron
7752           if (nbRepl == 3 &&
7753               iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7754             // all top nodes stick: reverse a bottom
7755             uniqueNodes[ 0 ] = curNodes [ 1 ];
7756             uniqueNodes[ 1 ] = curNodes [ 0 ];
7757           }
7758           else if (nbRepl == 3 &&
7759                    iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7760             // all bottom nodes stick: set a top before
7761             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7762             uniqueNodes[ 0 ] = curNodes [ 3 ];
7763             uniqueNodes[ 1 ] = curNodes [ 4 ];
7764             uniqueNodes[ 2 ] = curNodes [ 5 ];
7765           }
7766           else if (nbRepl == 4 &&
7767                    iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7768             // a lateral face turns into a line: reverse a bottom
7769             uniqueNodes[ 0 ] = curNodes [ 1 ];
7770             uniqueNodes[ 1 ] = curNodes [ 0 ];
7771           }
7772           else
7773             isOk = false;
7774         }
7775         else if ( nbUniqueNodes == 5 ) {
7776           // PENTAHEDRON --------------------> 2 tetrahedrons
7777           if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7778             // a bottom node sticks with a linked top one
7779             // 1.
7780             SMDS_MeshElement* newElem =
7781               aMesh->AddVolume(curNodes[ 3 ],
7782                                curNodes[ 4 ],
7783                                curNodes[ 5 ],
7784                                curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7785             myLastCreatedElems.Append(newElem);
7786             if ( aShapeId )
7787               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7788             // 2. : reverse a bottom
7789             uniqueNodes[ 0 ] = curNodes [ 1 ];
7790             uniqueNodes[ 1 ] = curNodes [ 0 ];
7791             nbUniqueNodes = 4;
7792           }
7793           else
7794             isOk = false;
7795         }
7796         else
7797           isOk = false;
7798         break;
7799       case 8: {
7800         if(elem->IsQuadratic()) { // Quadratic quadrangle
7801           //   1    5    2
7802           //    +---+---+
7803           //    |       |
7804           //    |       |
7805           //   4+       +6
7806           //    |       |
7807           //    |       |
7808           //    +---+---+
7809           //   0    7    3
7810           isOk = false;
7811           if(nbRepl==2) {
7812             MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7813           }
7814           if(nbRepl==3) {
7815             MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2]);
7816             nbUniqueNodes = 6;
7817             if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7818               uniqueNodes[0] = curNodes[0];
7819               uniqueNodes[1] = curNodes[2];
7820               uniqueNodes[2] = curNodes[3];
7821               uniqueNodes[3] = curNodes[5];
7822               uniqueNodes[4] = curNodes[6];
7823               uniqueNodes[5] = curNodes[7];
7824               isOk = true;
7825             }
7826             if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7827               uniqueNodes[0] = curNodes[0];
7828               uniqueNodes[1] = curNodes[1];
7829               uniqueNodes[2] = curNodes[2];
7830               uniqueNodes[3] = curNodes[4];
7831               uniqueNodes[4] = curNodes[5];
7832               uniqueNodes[5] = curNodes[6];
7833               isOk = true;
7834             }
7835             if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7836               uniqueNodes[0] = curNodes[1];
7837               uniqueNodes[1] = curNodes[2];
7838               uniqueNodes[2] = curNodes[3];
7839               uniqueNodes[3] = curNodes[5];
7840               uniqueNodes[4] = curNodes[6];
7841               uniqueNodes[5] = curNodes[0];
7842               isOk = true;
7843             }
7844             if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7845               uniqueNodes[0] = curNodes[0];
7846               uniqueNodes[1] = curNodes[1];
7847               uniqueNodes[2] = curNodes[3];
7848               uniqueNodes[3] = curNodes[4];
7849               uniqueNodes[4] = curNodes[6];
7850               uniqueNodes[5] = curNodes[7];
7851               isOk = true;
7852             }
7853             if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7854               uniqueNodes[0] = curNodes[0];
7855               uniqueNodes[1] = curNodes[2];
7856               uniqueNodes[2] = curNodes[3];
7857               uniqueNodes[3] = curNodes[1];
7858               uniqueNodes[4] = curNodes[6];
7859               uniqueNodes[5] = curNodes[7];
7860               isOk = true;
7861             }
7862             if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7863               uniqueNodes[0] = curNodes[0];
7864               uniqueNodes[1] = curNodes[1];
7865               uniqueNodes[2] = curNodes[2];
7866               uniqueNodes[3] = curNodes[4];
7867               uniqueNodes[4] = curNodes[5];
7868               uniqueNodes[5] = curNodes[7];
7869               isOk = true;
7870             }
7871             if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7872               uniqueNodes[0] = curNodes[0];
7873               uniqueNodes[1] = curNodes[1];
7874               uniqueNodes[2] = curNodes[3];
7875               uniqueNodes[3] = curNodes[4];
7876               uniqueNodes[4] = curNodes[2];
7877               uniqueNodes[5] = curNodes[7];
7878               isOk = true;
7879             }
7880             if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7881               uniqueNodes[0] = curNodes[0];
7882               uniqueNodes[1] = curNodes[1];
7883               uniqueNodes[2] = curNodes[2];
7884               uniqueNodes[3] = curNodes[4];
7885               uniqueNodes[4] = curNodes[5];
7886               uniqueNodes[5] = curNodes[3];
7887               isOk = true;
7888             }
7889           }
7890           if(nbRepl==4) {
7891             MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3]);
7892           }
7893           if(nbRepl==5) {
7894             MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7895           }
7896           break;
7897         }
7898         //////////////////////////////////// HEXAHEDRON
7899         isOk = false;
7900         SMDS_VolumeTool hexa (elem);
7901         hexa.SetExternalNormal();
7902         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7903           //////////////////////// HEX ---> 1 tetrahedron
7904           for ( int iFace = 0; iFace < 6; iFace++ ) {
7905             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7906             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7907                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7908                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7909               // one face turns into a point ...
7910               int iOppFace = hexa.GetOppFaceIndex( iFace );
7911               ind = hexa.GetFaceNodesIndices( iOppFace );
7912               int nbStick = 0;
7913               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7914                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7915                   nbStick++;
7916               }
7917               if ( nbStick == 1 ) {
7918                 // ... and the opposite one - into a triangle.
7919                 // set a top node
7920                 ind = hexa.GetFaceNodesIndices( iFace );
7921                 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7922                 isOk = true;
7923               }
7924               break;
7925             }
7926           }
7927         }
7928         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7929           //////////////////////// HEX ---> 1 prism
7930           int nbTria = 0, iTria[3];
7931           const int *ind; // indices of face nodes
7932           // look for triangular faces
7933           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7934             ind = hexa.GetFaceNodesIndices( iFace );
7935             TIDSortedNodeSet faceNodes;
7936             for ( iCur = 0; iCur < 4; iCur++ )
7937               faceNodes.insert( curNodes[ind[iCur]] );
7938             if ( faceNodes.size() == 3 )
7939               iTria[ nbTria++ ] = iFace;
7940           }
7941           // check if triangles are opposite
7942           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7943           {
7944             isOk = true;
7945             // set nodes of the bottom triangle
7946             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7947             vector<int> indB;
7948             for ( iCur = 0; iCur < 4; iCur++ )
7949               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7950                 indB.push_back( ind[iCur] );
7951             if ( !hexa.IsForward() )
7952               std::swap( indB[0], indB[2] );
7953             for ( iCur = 0; iCur < 3; iCur++ )
7954               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7955             // set nodes of the top triangle
7956             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7957             for ( iCur = 0; iCur < 3; ++iCur )
7958               for ( int j = 0; j < 4; ++j )
7959                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7960                 {
7961                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7962                   break;
7963                 }
7964           }
7965           break;
7966         }
7967         else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7968           //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7969           for ( int iFace = 0; iFace < 6; iFace++ ) {
7970             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7971             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7972                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7973                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7974               // one face turns into a point ...
7975               int iOppFace = hexa.GetOppFaceIndex( iFace );
7976               ind = hexa.GetFaceNodesIndices( iOppFace );
7977               int nbStick = 0;
7978               iUnique = 2;  // reverse a tetrahedron 1 bottom
7979               for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7980                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7981                   nbStick++;
7982                 else if ( iUnique >= 0 )
7983                   uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7984               }
7985               if ( nbStick == 0 ) {
7986                 // ... and the opposite one is a quadrangle
7987                 // set a top node
7988                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7989                 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7990                 nbUniqueNodes = 4;
7991                 // tetrahedron 2
7992                 SMDS_MeshElement* newElem =
7993                   aMesh->AddVolume(curNodes[ind[ 0 ]],
7994                                    curNodes[ind[ 3 ]],
7995                                    curNodes[ind[ 2 ]],
7996                                    curNodes[indTop[ 0 ]]);
7997                 myLastCreatedElems.Append(newElem);
7998                 if ( aShapeId )
7999                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
8000                 isOk = true;
8001               }
8002               break;
8003             }
8004           }
8005         }
8006         else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
8007           ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
8008           // find indices of quad and tri faces
8009           int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
8010           for ( iFace = 0; iFace < 6; iFace++ ) {
8011             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8012             nodeSet.clear();
8013             for ( iCur = 0; iCur < 4; iCur++ )
8014               nodeSet.insert( curNodes[ind[ iCur ]] );
8015             nbUniqueNodes = nodeSet.size();
8016             if ( nbUniqueNodes == 3 )
8017               iTriFace[ nbTri++ ] = iFace;
8018             else if ( nbUniqueNodes == 4 )
8019               iQuadFace[ nbQuad++ ] = iFace;
8020           }
8021           if (nbQuad == 2 && nbTri == 4 &&
8022               hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
8023             // 2 opposite quadrangles stuck with a diagonal;
8024             // sample groups of merged indices: (0-4)(2-6)
8025             // --------------------------------------------> 2 tetrahedrons
8026             const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
8027             const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
8028             int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
8029             if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
8030                 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
8031               // stuck with 0-2 diagonal
8032               i0  = ind1[ 3 ];
8033               i1d = ind1[ 0 ];
8034               i2  = ind1[ 1 ];
8035               i3d = ind1[ 2 ];
8036               i0t = ind2[ 1 ];
8037               i2t = ind2[ 3 ];
8038             }
8039             else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
8040                      curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
8041               // stuck with 1-3 diagonal
8042               i0  = ind1[ 0 ];
8043               i1d = ind1[ 1 ];
8044               i2  = ind1[ 2 ];
8045               i3d = ind1[ 3 ];
8046               i0t = ind2[ 0 ];
8047               i2t = ind2[ 1 ];
8048             }
8049             else {
8050               ASSERT(0);
8051             }
8052             // tetrahedron 1
8053             uniqueNodes[ 0 ] = curNodes [ i0 ];
8054             uniqueNodes[ 1 ] = curNodes [ i1d ];
8055             uniqueNodes[ 2 ] = curNodes [ i3d ];
8056             uniqueNodes[ 3 ] = curNodes [ i0t ];
8057             nbUniqueNodes = 4;
8058             // tetrahedron 2
8059             SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
8060                                                          curNodes[ i2 ],
8061                                                          curNodes[ i3d ],
8062                                                          curNodes[ i2t ]);
8063             myLastCreatedElems.Append(newElem);
8064             if ( aShapeId )
8065               aMesh->SetMeshElementOnShape( newElem, aShapeId );
8066             isOk = true;
8067           }
8068           else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
8069                    ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
8070             // --------------------------------------------> prism
8071             // find 2 opposite triangles
8072             nbUniqueNodes = 6;
8073             for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
8074               if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
8075                 // find indices of kept and replaced nodes
8076                 // and fill unique nodes of 2 opposite triangles
8077                 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
8078                 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
8079                 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
8080                 // fill unique nodes
8081                 iUnique = 0;
8082                 isOk = true;
8083                 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
8084                   const SMDS_MeshNode* n     = curNodes[ind1[ iCur ]];
8085                   const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
8086                   if ( n == nInit ) {
8087                     // iCur of a linked node of the opposite face (make normals co-directed):
8088                     int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
8089                     // check that correspondent corners of triangles are linked
8090                     if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
8091                       isOk = false;
8092                     else {
8093                       uniqueNodes[ iUnique ] = n;
8094                       uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
8095                       iUnique++;
8096                     }
8097                   }
8098                 }
8099                 break;
8100               }
8101             }
8102           }
8103         } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
8104         else
8105         {
8106           MESSAGE("MergeNodes() removes hexahedron "<< elem);
8107         }
8108         break;
8109       } // HEXAHEDRON
8110
8111       default:
8112         isOk = false;
8113       } // switch ( nbNodes )
8114
8115     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
8116
8117     if ( isOk ) { // the elem remains valid after sticking nodes
8118       if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
8119       {
8120         // Change nodes of polyedre
8121         const SMDS_VtkVolume* aPolyedre =
8122           dynamic_cast<const SMDS_VtkVolume*>( elem );
8123         if (aPolyedre) {
8124           int nbFaces = aPolyedre->NbFaces();
8125
8126           vector<const SMDS_MeshNode *> poly_nodes;
8127           vector<int> quantities (nbFaces);
8128
8129           for (int iface = 1; iface <= nbFaces; iface++) {
8130             int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
8131             quantities[iface - 1] = nbFaceNodes;
8132
8133             for (inode = 1; inode <= nbFaceNodes; inode++) {
8134               const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
8135
8136               TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
8137               if (nnIt != nodeNodeMap.end()) { // curNode sticks
8138                 curNode = (*nnIt).second;
8139               }
8140               poly_nodes.push_back(curNode);
8141             }
8142           }
8143           aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
8144         }
8145       }
8146       else // replace non-polyhedron elements
8147       {
8148         const SMDSAbs_ElementType etyp = elem->GetType();
8149         const int elemId               = elem->GetID();
8150         const bool isPoly              = (elem->GetEntityType() == SMDSEntity_Polygon);
8151         uniqueNodes.resize(nbUniqueNodes);
8152
8153         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
8154
8155         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
8156         SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
8157         if ( sm && newElem )
8158           sm->AddElement( newElem );
8159         if ( elem != newElem )
8160           ReplaceElemInGroups( elem, newElem, aMesh );
8161       }
8162     }
8163     else {
8164       // Remove invalid regular element or invalid polygon
8165       rmElemIds.push_back( elem->GetID() );
8166     }
8167
8168   } // loop on elements
8169
8170   // Remove bad elements, then equal nodes (order important)
8171
8172   Remove( rmElemIds, false );
8173   Remove( rmNodeIds, true );
8174
8175 }
8176
8177
8178 // ========================================================
8179 // class   : SortableElement
8180 // purpose : allow sorting elements basing on their nodes
8181 // ========================================================
8182 class SortableElement : public set <const SMDS_MeshElement*>
8183 {
8184 public:
8185
8186   SortableElement( const SMDS_MeshElement* theElem )
8187   {
8188     myElem = theElem;
8189     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
8190     while ( nodeIt->more() )
8191       this->insert( nodeIt->next() );
8192   }
8193
8194   const SMDS_MeshElement* Get() const
8195   { return myElem; }
8196
8197   void Set(const SMDS_MeshElement* e) const
8198   { myElem = e; }
8199
8200
8201 private:
8202   mutable const SMDS_MeshElement* myElem;
8203 };
8204
8205 //=======================================================================
8206 //function : FindEqualElements
8207 //purpose  : Return list of group of elements built on the same nodes.
8208 //           Search among theElements or in the whole mesh if theElements is empty
8209 //=======================================================================
8210
8211 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
8212                                          TListOfListOfElementsID & theGroupsOfElementsID)
8213 {
8214   myLastCreatedElems.Clear();
8215   myLastCreatedNodes.Clear();
8216
8217   typedef map< SortableElement, int > TMapOfNodeSet;
8218   typedef list<int> TGroupOfElems;
8219
8220   if ( theElements.empty() )
8221   { // get all elements in the mesh
8222     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
8223     while ( eIt->more() )
8224       theElements.insert( theElements.end(), eIt->next());
8225   }
8226
8227   vector< TGroupOfElems > arrayOfGroups;
8228   TGroupOfElems groupOfElems;
8229   TMapOfNodeSet mapOfNodeSet;
8230
8231   TIDSortedElemSet::iterator elemIt = theElements.begin();
8232   for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
8233     const SMDS_MeshElement* curElem = *elemIt;
8234     SortableElement SE(curElem);
8235     int ind = -1;
8236     // check uniqueness
8237     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8238     if( !(pp.second) ) {
8239       TMapOfNodeSet::iterator& itSE = pp.first;
8240       ind = (*itSE).second;
8241       arrayOfGroups[ind].push_back(curElem->GetID());
8242     }
8243     else {
8244       groupOfElems.clear();
8245       groupOfElems.push_back(curElem->GetID());
8246       arrayOfGroups.push_back(groupOfElems);
8247       i++;
8248     }
8249   }
8250
8251   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8252   for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
8253     groupOfElems = *groupIt;
8254     if ( groupOfElems.size() > 1 ) {
8255       groupOfElems.sort();
8256       theGroupsOfElementsID.push_back(groupOfElems);
8257     }
8258   }
8259 }
8260
8261 //=======================================================================
8262 //function : MergeElements
8263 //purpose  : In each given group, substitute all elements by the first one.
8264 //=======================================================================
8265
8266 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8267 {
8268   myLastCreatedElems.Clear();
8269   myLastCreatedNodes.Clear();
8270
8271   typedef list<int> TListOfIDs;
8272   TListOfIDs rmElemIds; // IDs of elems to remove
8273
8274   SMESHDS_Mesh* aMesh = GetMeshDS();
8275
8276   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8277   while ( groupsIt != theGroupsOfElementsID.end() ) {
8278     TListOfIDs& aGroupOfElemID = *groupsIt;
8279     aGroupOfElemID.sort();
8280     int elemIDToKeep = aGroupOfElemID.front();
8281     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8282     aGroupOfElemID.pop_front();
8283     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8284     while ( idIt != aGroupOfElemID.end() ) {
8285       int elemIDToRemove = *idIt;
8286       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8287       // add the kept element in groups of removed one (PAL15188)
8288       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8289       rmElemIds.push_back( elemIDToRemove );
8290       ++idIt;
8291     }
8292     ++groupsIt;
8293   }
8294
8295   Remove( rmElemIds, false );
8296 }
8297
8298 //=======================================================================
8299 //function : MergeEqualElements
8300 //purpose  : Remove all but one of elements built on the same nodes.
8301 //=======================================================================
8302
8303 void SMESH_MeshEditor::MergeEqualElements()
8304 {
8305   TIDSortedElemSet aMeshElements; /* empty input ==
8306                                      to merge equal elements in the whole mesh */
8307   TListOfListOfElementsID aGroupsOfElementsID;
8308   FindEqualElements(aMeshElements, aGroupsOfElementsID);
8309   MergeElements(aGroupsOfElementsID);
8310 }
8311
8312 //=======================================================================
8313 //function : FindFaceInSet
8314 //purpose  : Return a face having linked nodes n1 and n2 and which is
8315 //           - not in avoidSet,
8316 //           - in elemSet provided that !elemSet.empty()
8317 //           i1 and i2 optionally returns indices of n1 and n2
8318 //=======================================================================
8319
8320 const SMDS_MeshElement*
8321 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode*    n1,
8322                                 const SMDS_MeshNode*    n2,
8323                                 const TIDSortedElemSet& elemSet,
8324                                 const TIDSortedElemSet& avoidSet,
8325                                 int*                    n1ind,
8326                                 int*                    n2ind)
8327
8328 {
8329   int i1, i2;
8330   const SMDS_MeshElement* face = 0;
8331
8332   SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
8333   //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
8334   while ( invElemIt->more() && !face ) // loop on inverse faces of n1
8335   {
8336     //MESSAGE("in while ( invElemIt->more() && !face )");
8337     const SMDS_MeshElement* elem = invElemIt->next();
8338     if (avoidSet.count( elem ))
8339       continue;
8340     if ( !elemSet.empty() && !elemSet.count( elem ))
8341       continue;
8342     // index of n1
8343     i1 = elem->GetNodeIndex( n1 );
8344     // find a n2 linked to n1
8345     int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
8346     for ( int di = -1; di < 2 && !face; di += 2 )
8347     {
8348       i2 = (i1+di+nbN) % nbN;
8349       if ( elem->GetNode( i2 ) == n2 )
8350         face = elem;
8351     }
8352     if ( !face && elem->IsQuadratic())
8353     {
8354       // analysis for quadratic elements using all nodes
8355       const SMDS_VtkFace* F =
8356         dynamic_cast<const SMDS_VtkFace*>(elem);
8357       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8358       // use special nodes iterator
8359       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8360       const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
8361       for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
8362       {
8363         const SMDS_MeshNode* n = cast2Node( anIter->next() );
8364         if ( n1 == prevN && n2 == n )
8365         {
8366           face = elem;
8367         }
8368         else if ( n2 == prevN && n1 == n )
8369         {
8370           face = elem; swap( i1, i2 );
8371         }
8372         prevN = n;
8373       }
8374     }
8375   }
8376   if ( n1ind ) *n1ind = i1;
8377   if ( n2ind ) *n2ind = i2;
8378   return face;
8379 }
8380
8381 //=======================================================================
8382 //function : findAdjacentFace
8383 //purpose  :
8384 //=======================================================================
8385
8386 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8387                                                 const SMDS_MeshNode* n2,
8388                                                 const SMDS_MeshElement* elem)
8389 {
8390   TIDSortedElemSet elemSet, avoidSet;
8391   if ( elem )
8392     avoidSet.insert ( elem );
8393   return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
8394 }
8395
8396 //=======================================================================
8397 //function : FindFreeBorder
8398 //purpose  :
8399 //=======================================================================
8400
8401 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8402
8403 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
8404                                        const SMDS_MeshNode*             theSecondNode,
8405                                        const SMDS_MeshNode*             theLastNode,
8406                                        list< const SMDS_MeshNode* > &   theNodes,
8407                                        list< const SMDS_MeshElement* >& theFaces)
8408 {
8409   if ( !theFirstNode || !theSecondNode )
8410     return false;
8411   // find border face between theFirstNode and theSecondNode
8412   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8413   if ( !curElem )
8414     return false;
8415
8416   theFaces.push_back( curElem );
8417   theNodes.push_back( theFirstNode );
8418   theNodes.push_back( theSecondNode );
8419
8420   //vector<const SMDS_MeshNode*> nodes;
8421   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8422   TIDSortedElemSet foundElems;
8423   bool needTheLast = ( theLastNode != 0 );
8424
8425   while ( nStart != theLastNode ) {
8426     if ( nStart == theFirstNode )
8427       return !needTheLast;
8428
8429     // find all free border faces sharing form nStart
8430
8431     list< const SMDS_MeshElement* > curElemList;
8432     list< const SMDS_MeshNode* > nStartList;
8433     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8434     while ( invElemIt->more() ) {
8435       const SMDS_MeshElement* e = invElemIt->next();
8436       if ( e == curElem || foundElems.insert( e ).second ) {
8437         // get nodes
8438         int iNode = 0, nbNodes = e->NbNodes();
8439         //const SMDS_MeshNode* nodes[nbNodes+1];
8440         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8441
8442         if(e->IsQuadratic()) {
8443           const SMDS_VtkFace* F =
8444             dynamic_cast<const SMDS_VtkFace*>(e);
8445           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8446           // use special nodes iterator
8447           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8448           while( anIter->more() ) {
8449             nodes[ iNode++ ] = cast2Node(anIter->next());
8450           }
8451         }
8452         else {
8453           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8454           while ( nIt->more() )
8455             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8456         }
8457         nodes[ iNode ] = nodes[ 0 ];
8458         // check 2 links
8459         for ( iNode = 0; iNode < nbNodes; iNode++ )
8460           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8461                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8462               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8463           {
8464             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8465             curElemList.push_back( e );
8466           }
8467       }
8468     }
8469     // analyse the found
8470
8471     int nbNewBorders = curElemList.size();
8472     if ( nbNewBorders == 0 ) {
8473       // no free border furthermore
8474       return !needTheLast;
8475     }
8476     else if ( nbNewBorders == 1 ) {
8477       // one more element found
8478       nIgnore = nStart;
8479       nStart = nStartList.front();
8480       curElem = curElemList.front();
8481       theFaces.push_back( curElem );
8482       theNodes.push_back( nStart );
8483     }
8484     else {
8485       // several continuations found
8486       list< const SMDS_MeshElement* >::iterator curElemIt;
8487       list< const SMDS_MeshNode* >::iterator nStartIt;
8488       // check if one of them reached the last node
8489       if ( needTheLast ) {
8490         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8491              curElemIt!= curElemList.end();
8492              curElemIt++, nStartIt++ )
8493           if ( *nStartIt == theLastNode ) {
8494             theFaces.push_back( *curElemIt );
8495             theNodes.push_back( *nStartIt );
8496             return true;
8497           }
8498       }
8499       // find the best free border by the continuations
8500       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8501       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8502       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8503            curElemIt!= curElemList.end();
8504            curElemIt++, nStartIt++ )
8505       {
8506         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8507         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8508         // find one more free border
8509         if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8510           cNL->clear();
8511           cFL->clear();
8512         }
8513         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8514           // choice: clear a worse one
8515           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8516           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8517           contNodes[ iWorse ].clear();
8518           contFaces[ iWorse ].clear();
8519         }
8520       }
8521       if ( contNodes[0].empty() && contNodes[1].empty() )
8522         return false;
8523
8524       // append the best free border
8525       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8526       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8527       theNodes.pop_back(); // remove nIgnore
8528       theNodes.pop_back(); // remove nStart
8529       theFaces.pop_back(); // remove curElem
8530       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8531       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8532       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8533       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8534       return true;
8535
8536     } // several continuations found
8537   } // while ( nStart != theLastNode )
8538
8539   return true;
8540 }
8541
8542 //=======================================================================
8543 //function : CheckFreeBorderNodes
8544 //purpose  : Return true if the tree nodes are on a free border
8545 //=======================================================================
8546
8547 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8548                                             const SMDS_MeshNode* theNode2,
8549                                             const SMDS_MeshNode* theNode3)
8550 {
8551   list< const SMDS_MeshNode* > nodes;
8552   list< const SMDS_MeshElement* > faces;
8553   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8554 }
8555
8556 //=======================================================================
8557 //function : SewFreeBorder
8558 //purpose  :
8559 //=======================================================================
8560
8561 SMESH_MeshEditor::Sew_Error
8562 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8563                                  const SMDS_MeshNode* theBordSecondNode,
8564                                  const SMDS_MeshNode* theBordLastNode,
8565                                  const SMDS_MeshNode* theSideFirstNode,
8566                                  const SMDS_MeshNode* theSideSecondNode,
8567                                  const SMDS_MeshNode* theSideThirdNode,
8568                                  const bool           theSideIsFreeBorder,
8569                                  const bool           toCreatePolygons,
8570                                  const bool           toCreatePolyedrs)
8571 {
8572   myLastCreatedElems.Clear();
8573   myLastCreatedNodes.Clear();
8574
8575   MESSAGE("::SewFreeBorder()");
8576   Sew_Error aResult = SEW_OK;
8577
8578   // ====================================
8579   //    find side nodes and elements
8580   // ====================================
8581
8582   list< const SMDS_MeshNode* > nSide[ 2 ];
8583   list< const SMDS_MeshElement* > eSide[ 2 ];
8584   list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8585   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8586
8587   // Free border 1
8588   // --------------
8589   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8590                       nSide[0], eSide[0])) {
8591     MESSAGE(" Free Border 1 not found " );
8592     aResult = SEW_BORDER1_NOT_FOUND;
8593   }
8594   if (theSideIsFreeBorder) {
8595     // Free border 2
8596     // --------------
8597     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8598                         nSide[1], eSide[1])) {
8599       MESSAGE(" Free Border 2 not found " );
8600       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8601     }
8602   }
8603   if ( aResult != SEW_OK )
8604     return aResult;
8605
8606   if (!theSideIsFreeBorder) {
8607     // Side 2
8608     // --------------
8609
8610     // -------------------------------------------------------------------------
8611     // Algo:
8612     // 1. If nodes to merge are not coincident, move nodes of the free border
8613     //    from the coord sys defined by the direction from the first to last
8614     //    nodes of the border to the correspondent sys of the side 2
8615     // 2. On the side 2, find the links most co-directed with the correspondent
8616     //    links of the free border
8617     // -------------------------------------------------------------------------
8618
8619     // 1. Since sewing may break if there are volumes to split on the side 2,
8620     //    we wont move nodes but just compute new coordinates for them
8621     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8622     TNodeXYZMap nBordXYZ;
8623     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8624     list< const SMDS_MeshNode* >::iterator nBordIt;
8625
8626     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8627     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8628     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8629     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8630     double tol2 = 1.e-8;
8631     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8632     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8633       // Need node movement.
8634
8635       // find X and Z axes to create trsf
8636       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8637       gp_Vec X = Zs ^ Zb;
8638       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8639         // Zb || Zs
8640         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8641
8642       // coord systems
8643       gp_Ax3 toBordAx( Pb1, Zb, X );
8644       gp_Ax3 fromSideAx( Ps1, Zs, X );
8645       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8646       // set trsf
8647       gp_Trsf toBordSys, fromSide2Sys;
8648       toBordSys.SetTransformation( toBordAx );
8649       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8650       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8651
8652       // move
8653       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8654         const SMDS_MeshNode* n = *nBordIt;
8655         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8656         toBordSys.Transforms( xyz );
8657         fromSide2Sys.Transforms( xyz );
8658         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8659       }
8660     }
8661     else {
8662       // just insert nodes XYZ in the nBordXYZ map
8663       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8664         const SMDS_MeshNode* n = *nBordIt;
8665         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8666       }
8667     }
8668
8669     // 2. On the side 2, find the links most co-directed with the correspondent
8670     //    links of the free border
8671
8672     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8673     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8674     sideNodes.push_back( theSideFirstNode );
8675
8676     bool hasVolumes = false;
8677     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8678     set<long> foundSideLinkIDs, checkedLinkIDs;
8679     SMDS_VolumeTool volume;
8680     //const SMDS_MeshNode* faceNodes[ 4 ];
8681
8682     const SMDS_MeshNode*    sideNode;
8683     const SMDS_MeshElement* sideElem;
8684     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8685     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8686     nBordIt = bordNodes.begin();
8687     nBordIt++;
8688     // border node position and border link direction to compare with
8689     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8690     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8691     // choose next side node by link direction or by closeness to
8692     // the current border node:
8693     bool searchByDir = ( *nBordIt != theBordLastNode );
8694     do {
8695       // find the next node on the Side 2
8696       sideNode = 0;
8697       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8698       long linkID;
8699       checkedLinkIDs.clear();
8700       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8701
8702       // loop on inverse elements of current node (prevSideNode) on the Side 2
8703       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8704       while ( invElemIt->more() )
8705       {
8706         const SMDS_MeshElement* elem = invElemIt->next();
8707         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8708         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8709         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8710         bool isVolume = volume.Set( elem );
8711         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8712         if ( isVolume ) // --volume
8713           hasVolumes = true;
8714         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8715           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8716           if(elem->IsQuadratic()) {
8717             const SMDS_VtkFace* F =
8718               dynamic_cast<const SMDS_VtkFace*>(elem);
8719             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8720             // use special nodes iterator
8721             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8722             while( anIter->more() ) {
8723               nodes[ iNode ] = cast2Node(anIter->next());
8724               if ( nodes[ iNode++ ] == prevSideNode )
8725                 iPrevNode = iNode - 1;
8726             }
8727           }
8728           else {
8729             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8730             while ( nIt->more() ) {
8731               nodes[ iNode ] = cast2Node( nIt->next() );
8732               if ( nodes[ iNode++ ] == prevSideNode )
8733                 iPrevNode = iNode - 1;
8734             }
8735           }
8736           // there are 2 links to check
8737           nbNodes = 2;
8738         }
8739         else // --edge
8740           continue;
8741         // loop on links, to be precise, on the second node of links
8742         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8743           const SMDS_MeshNode* n = nodes[ iNode ];
8744           if ( isVolume ) {
8745             if ( !volume.IsLinked( n, prevSideNode ))
8746               continue;
8747           }
8748           else {
8749             if ( iNode ) // a node before prevSideNode
8750               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8751             else         // a node after prevSideNode
8752               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8753           }
8754           // check if this link was already used
8755           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8756           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8757           if (!isJustChecked &&
8758               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8759           {
8760             // test a link geometrically
8761             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8762             bool linkIsBetter = false;
8763             double dot = 0.0, dist = 0.0;
8764             if ( searchByDir ) { // choose most co-directed link
8765               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8766               linkIsBetter = ( dot > maxDot );
8767             }
8768             else { // choose link with the node closest to bordPos
8769               dist = ( nextXYZ - bordPos ).SquareModulus();
8770               linkIsBetter = ( dist < minDist );
8771             }
8772             if ( linkIsBetter ) {
8773               maxDot = dot;
8774               minDist = dist;
8775               linkID = iLink;
8776               sideNode = n;
8777               sideElem = elem;
8778             }
8779           }
8780         }
8781       } // loop on inverse elements of prevSideNode
8782
8783       if ( !sideNode ) {
8784         MESSAGE(" Cant find path by links of the Side 2 ");
8785         return SEW_BAD_SIDE_NODES;
8786       }
8787       sideNodes.push_back( sideNode );
8788       sideElems.push_back( sideElem );
8789       foundSideLinkIDs.insert ( linkID );
8790       prevSideNode = sideNode;
8791
8792       if ( *nBordIt == theBordLastNode )
8793         searchByDir = false;
8794       else {
8795         // find the next border link to compare with
8796         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8797         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8798         // move to next border node if sideNode is before forward border node (bordPos)
8799         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8800           prevBordNode = *nBordIt;
8801           nBordIt++;
8802           bordPos = nBordXYZ[ *nBordIt ];
8803           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8804           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8805         }
8806       }
8807     }
8808     while ( sideNode != theSideSecondNode );
8809
8810     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8811       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8812       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8813     }
8814   } // end nodes search on the side 2
8815
8816   // ============================
8817   // sew the border to the side 2
8818   // ============================
8819
8820   int nbNodes[]  = { nSide[0].size(), nSide[1].size() };
8821   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8822
8823   TListOfListOfNodes nodeGroupsToMerge;
8824   if ( nbNodes[0] == nbNodes[1] ||
8825        ( theSideIsFreeBorder && !theSideThirdNode)) {
8826
8827     // all nodes are to be merged
8828
8829     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8830          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8831          nIt[0]++, nIt[1]++ )
8832     {
8833       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8834       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8835       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8836     }
8837   }
8838   else {
8839
8840     // insert new nodes into the border and the side to get equal nb of segments
8841
8842     // get normalized parameters of nodes on the borders
8843     //double param[ 2 ][ maxNbNodes ];
8844     double* param[ 2 ];
8845     param[0] = new double [ maxNbNodes ];
8846     param[1] = new double [ maxNbNodes ];
8847     int iNode, iBord;
8848     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8849       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8850       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8851       const SMDS_MeshNode* nPrev = *nIt;
8852       double bordLength = 0;
8853       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8854         const SMDS_MeshNode* nCur = *nIt;
8855         gp_XYZ segment (nCur->X() - nPrev->X(),
8856                         nCur->Y() - nPrev->Y(),
8857                         nCur->Z() - nPrev->Z());
8858         double segmentLen = segment.Modulus();
8859         bordLength += segmentLen;
8860         param[ iBord ][ iNode ] = bordLength;
8861         nPrev = nCur;
8862       }
8863       // normalize within [0,1]
8864       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8865         param[ iBord ][ iNode ] /= bordLength;
8866       }
8867     }
8868
8869     // loop on border segments
8870     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8871     int i[ 2 ] = { 0, 0 };
8872     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8873     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8874
8875     TElemOfNodeListMap insertMap;
8876     TElemOfNodeListMap::iterator insertMapIt;
8877     // insertMap is
8878     // key:   elem to insert nodes into
8879     // value: 2 nodes to insert between + nodes to be inserted
8880     do {
8881       bool next[ 2 ] = { false, false };
8882
8883       // find min adjacent segment length after sewing
8884       double nextParam = 10., prevParam = 0;
8885       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8886         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8887           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8888         if ( i[ iBord ] > 0 )
8889           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8890       }
8891       double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8892       double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8893       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8894
8895       // choose to insert or to merge nodes
8896       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8897       if ( Abs( du ) <= minSegLen * 0.2 ) {
8898         // merge
8899         // ------
8900         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8901         const SMDS_MeshNode* n0 = *nIt[0];
8902         const SMDS_MeshNode* n1 = *nIt[1];
8903         nodeGroupsToMerge.back().push_back( n1 );
8904         nodeGroupsToMerge.back().push_back( n0 );
8905         // position of node of the border changes due to merge
8906         param[ 0 ][ i[0] ] += du;
8907         // move n1 for the sake of elem shape evaluation during insertion.
8908         // n1 will be removed by MergeNodes() anyway
8909         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8910         next[0] = next[1] = true;
8911       }
8912       else {
8913         // insert
8914         // ------
8915         int intoBord = ( du < 0 ) ? 0 : 1;
8916         const SMDS_MeshElement* elem = *eIt[ intoBord ];
8917         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8918         const SMDS_MeshNode*    n2   = *nIt[ intoBord ];
8919         const SMDS_MeshNode*    nIns = *nIt[ 1 - intoBord ];
8920         if ( intoBord == 1 ) {
8921           // move node of the border to be on a link of elem of the side
8922           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8923           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8924           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8925           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8926           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8927         }
8928         insertMapIt = insertMap.find( elem );
8929         bool notFound = ( insertMapIt == insertMap.end() );
8930         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8931         if ( otherLink ) {
8932           // insert into another link of the same element:
8933           // 1. perform insertion into the other link of the elem
8934           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8935           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8936           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8937           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8938           // 2. perform insertion into the link of adjacent faces
8939           while (true) {
8940             const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8941             if ( adjElem )
8942               InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8943             else
8944               break;
8945           }
8946           if (toCreatePolyedrs) {
8947             // perform insertion into the links of adjacent volumes
8948             UpdateVolumes(n12, n22, nodeList);
8949           }
8950           // 3. find an element appeared on n1 and n2 after the insertion
8951           insertMap.erase( elem );
8952           elem = findAdjacentFace( n1, n2, 0 );
8953         }
8954         if ( notFound || otherLink ) {
8955           // add element and nodes of the side into the insertMap
8956           insertMapIt = insertMap.insert
8957             ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8958           (*insertMapIt).second.push_back( n1 );
8959           (*insertMapIt).second.push_back( n2 );
8960         }
8961         // add node to be inserted into elem
8962         (*insertMapIt).second.push_back( nIns );
8963         next[ 1 - intoBord ] = true;
8964       }
8965
8966       // go to the next segment
8967       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8968         if ( next[ iBord ] ) {
8969           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8970             eIt[ iBord ]++;
8971           nPrev[ iBord ] = *nIt[ iBord ];
8972           nIt[ iBord ]++; i[ iBord ]++;
8973         }
8974       }
8975     }
8976     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8977
8978     // perform insertion of nodes into elements
8979
8980     for (insertMapIt = insertMap.begin();
8981          insertMapIt != insertMap.end();
8982          insertMapIt++ )
8983     {
8984       const SMDS_MeshElement* elem = (*insertMapIt).first;
8985       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8986       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8987       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8988
8989       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8990
8991       if ( !theSideIsFreeBorder ) {
8992         // look for and insert nodes into the faces adjacent to elem
8993         while (true) {
8994           const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8995           if ( adjElem )
8996             InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8997           else
8998             break;
8999         }
9000       }
9001       if (toCreatePolyedrs) {
9002         // perform insertion into the links of adjacent volumes
9003         UpdateVolumes(n1, n2, nodeList);
9004       }
9005     }
9006
9007     delete param[0];
9008     delete param[1];
9009   } // end: insert new nodes
9010
9011   MergeNodes ( nodeGroupsToMerge );
9012
9013   return aResult;
9014 }
9015
9016 //=======================================================================
9017 //function : InsertNodesIntoLink
9018 //purpose  : insert theNodesToInsert into theFace between theBetweenNode1
9019 //           and theBetweenNode2 and split theElement
9020 //=======================================================================
9021
9022 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theFace,
9023                                            const SMDS_MeshNode*        theBetweenNode1,
9024                                            const SMDS_MeshNode*        theBetweenNode2,
9025                                            list<const SMDS_MeshNode*>& theNodesToInsert,
9026                                            const bool                  toCreatePoly)
9027 {
9028   if ( theFace->GetType() != SMDSAbs_Face ) return;
9029
9030   // find indices of 2 link nodes and of the rest nodes
9031   int iNode = 0, il1, il2, i3, i4;
9032   il1 = il2 = i3 = i4 = -1;
9033   //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
9034   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
9035
9036   if(theFace->IsQuadratic()) {
9037     const SMDS_VtkFace* F =
9038       dynamic_cast<const SMDS_VtkFace*>(theFace);
9039     if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9040     // use special nodes iterator
9041     SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9042     while( anIter->more() ) {
9043       const SMDS_MeshNode* n = cast2Node(anIter->next());
9044       if ( n == theBetweenNode1 )
9045         il1 = iNode;
9046       else if ( n == theBetweenNode2 )
9047         il2 = iNode;
9048       else if ( i3 < 0 )
9049         i3 = iNode;
9050       else
9051         i4 = iNode;
9052       nodes[ iNode++ ] = n;
9053     }
9054   }
9055   else {
9056     SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9057     while ( nodeIt->more() ) {
9058       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9059       if ( n == theBetweenNode1 )
9060         il1 = iNode;
9061       else if ( n == theBetweenNode2 )
9062         il2 = iNode;
9063       else if ( i3 < 0 )
9064         i3 = iNode;
9065       else
9066         i4 = iNode;
9067       nodes[ iNode++ ] = n;
9068     }
9069   }
9070   if ( il1 < 0 || il2 < 0 || i3 < 0 )
9071     return ;
9072
9073   // arrange link nodes to go one after another regarding the face orientation
9074   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
9075   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
9076   if ( reverse ) {
9077     iNode = il1;
9078     il1 = il2;
9079     il2 = iNode;
9080     aNodesToInsert.reverse();
9081   }
9082   // check that not link nodes of a quadrangles are in good order
9083   int nbFaceNodes = theFace->NbNodes();
9084   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
9085     iNode = i3;
9086     i3 = i4;
9087     i4 = iNode;
9088   }
9089
9090   if (toCreatePoly || theFace->IsPoly()) {
9091
9092     iNode = 0;
9093     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
9094
9095     // add nodes of face up to first node of link
9096     bool isFLN = false;
9097
9098     if(theFace->IsQuadratic()) {
9099       const SMDS_VtkFace* F =
9100         dynamic_cast<const SMDS_VtkFace*>(theFace);
9101       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9102       // use special nodes iterator
9103       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9104       while( anIter->more()  && !isFLN ) {
9105         const SMDS_MeshNode* n = cast2Node(anIter->next());
9106         poly_nodes[iNode++] = n;
9107         if (n == nodes[il1]) {
9108           isFLN = true;
9109         }
9110       }
9111       // add nodes to insert
9112       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9113       for (; nIt != aNodesToInsert.end(); nIt++) {
9114         poly_nodes[iNode++] = *nIt;
9115       }
9116       // add nodes of face starting from last node of link
9117       while ( anIter->more() ) {
9118         poly_nodes[iNode++] = cast2Node(anIter->next());
9119       }
9120     }
9121     else {
9122       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9123       while ( nodeIt->more() && !isFLN ) {
9124         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9125         poly_nodes[iNode++] = n;
9126         if (n == nodes[il1]) {
9127           isFLN = true;
9128         }
9129       }
9130       // add nodes to insert
9131       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9132       for (; nIt != aNodesToInsert.end(); nIt++) {
9133         poly_nodes[iNode++] = *nIt;
9134       }
9135       // add nodes of face starting from last node of link
9136       while ( nodeIt->more() ) {
9137         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9138         poly_nodes[iNode++] = n;
9139       }
9140     }
9141
9142     // edit or replace the face
9143     SMESHDS_Mesh *aMesh = GetMeshDS();
9144
9145     if (theFace->IsPoly()) {
9146       aMesh->ChangePolygonNodes(theFace, poly_nodes);
9147     }
9148     else {
9149       int aShapeId = FindShape( theFace );
9150
9151       SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
9152       myLastCreatedElems.Append(newElem);
9153       if ( aShapeId && newElem )
9154         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9155
9156       aMesh->RemoveElement(theFace);
9157     }
9158     return;
9159   }
9160
9161   SMESHDS_Mesh *aMesh = GetMeshDS();
9162   if( !theFace->IsQuadratic() ) {
9163
9164     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
9165     int nbLinkNodes = 2 + aNodesToInsert.size();
9166     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
9167     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
9168     linkNodes[ 0 ] = nodes[ il1 ];
9169     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
9170     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9171     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9172       linkNodes[ iNode++ ] = *nIt;
9173     }
9174     // decide how to split a quadrangle: compare possible variants
9175     // and choose which of splits to be a quadrangle
9176     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
9177     if ( nbFaceNodes == 3 ) {
9178       iBestQuad = nbSplits;
9179       i4 = i3;
9180     }
9181     else if ( nbFaceNodes == 4 ) {
9182       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
9183       double aBestRate = DBL_MAX;
9184       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
9185         i1 = 0; i2 = 1;
9186         double aBadRate = 0;
9187         // evaluate elements quality
9188         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
9189           if ( iSplit == iQuad ) {
9190             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
9191                                    linkNodes[ i2++ ],
9192                                    nodes[ i3 ],
9193                                    nodes[ i4 ]);
9194             aBadRate += getBadRate( &quad, aCrit );
9195           }
9196           else {
9197             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
9198                                    linkNodes[ i2++ ],
9199                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
9200             aBadRate += getBadRate( &tria, aCrit );
9201           }
9202         }
9203         // choice
9204         if ( aBadRate < aBestRate ) {
9205           iBestQuad = iQuad;
9206           aBestRate = aBadRate;
9207         }
9208       }
9209     }
9210
9211     // create new elements
9212     int aShapeId = FindShape( theFace );
9213
9214     i1 = 0; i2 = 1;
9215     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
9216       SMDS_MeshElement* newElem = 0;
9217       if ( iSplit == iBestQuad )
9218         newElem = aMesh->AddFace (linkNodes[ i1++ ],
9219                                   linkNodes[ i2++ ],
9220                                   nodes[ i3 ],
9221                                   nodes[ i4 ]);
9222       else
9223         newElem = aMesh->AddFace (linkNodes[ i1++ ],
9224                                   linkNodes[ i2++ ],
9225                                   nodes[ iSplit < iBestQuad ? i4 : i3 ]);
9226       myLastCreatedElems.Append(newElem);
9227       if ( aShapeId && newElem )
9228         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9229     }
9230
9231     // change nodes of theFace
9232     const SMDS_MeshNode* newNodes[ 4 ];
9233     newNodes[ 0 ] = linkNodes[ i1 ];
9234     newNodes[ 1 ] = linkNodes[ i2 ];
9235     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9236     newNodes[ 3 ] = nodes[ i4 ];
9237     //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
9238     const SMDS_MeshElement* newElem = 0;
9239     if (iSplit == iBestQuad)
9240       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
9241     else
9242       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
9243     myLastCreatedElems.Append(newElem);
9244     if ( aShapeId && newElem )
9245       aMesh->SetMeshElementOnShape( newElem, aShapeId );
9246 } // end if(!theFace->IsQuadratic())
9247   else { // theFace is quadratic
9248     // we have to split theFace on simple triangles and one simple quadrangle
9249     int tmp = il1/2;
9250     int nbshift = tmp*2;
9251     // shift nodes in nodes[] by nbshift
9252     int i,j;
9253     for(i=0; i<nbshift; i++) {
9254       const SMDS_MeshNode* n = nodes[0];
9255       for(j=0; j<nbFaceNodes-1; j++) {
9256         nodes[j] = nodes[j+1];
9257       }
9258       nodes[nbFaceNodes-1] = n;
9259     }
9260     il1 = il1 - nbshift;
9261     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9262     //   n0      n1     n2    n0      n1     n2
9263     //     +-----+-----+        +-----+-----+
9264     //      \         /         |           |
9265     //       \       /          |           |
9266     //      n5+     +n3       n7+           +n3
9267     //         \   /            |           |
9268     //          \ /             |           |
9269     //           +              +-----+-----+
9270     //           n4           n6      n5     n4
9271
9272     // create new elements
9273     int aShapeId = FindShape( theFace );
9274
9275     int n1,n2,n3;
9276     if(nbFaceNodes==6) { // quadratic triangle
9277       SMDS_MeshElement* newElem =
9278         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9279       myLastCreatedElems.Append(newElem);
9280       if ( aShapeId && newElem )
9281         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9282       if(theFace->IsMediumNode(nodes[il1])) {
9283         // create quadrangle
9284         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
9285         myLastCreatedElems.Append(newElem);
9286         if ( aShapeId && newElem )
9287           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9288         n1 = 1;
9289         n2 = 2;
9290         n3 = 3;
9291       }
9292       else {
9293         // create quadrangle
9294         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
9295         myLastCreatedElems.Append(newElem);
9296         if ( aShapeId && newElem )
9297           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9298         n1 = 0;
9299         n2 = 1;
9300         n3 = 5;
9301       }
9302     }
9303     else { // nbFaceNodes==8 - quadratic quadrangle
9304       SMDS_MeshElement* newElem =
9305         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9306       myLastCreatedElems.Append(newElem);
9307       if ( aShapeId && newElem )
9308         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9309       newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
9310       myLastCreatedElems.Append(newElem);
9311       if ( aShapeId && newElem )
9312         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9313       newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
9314       myLastCreatedElems.Append(newElem);
9315       if ( aShapeId && newElem )
9316         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9317       if(theFace->IsMediumNode(nodes[il1])) {
9318         // create quadrangle
9319         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
9320         myLastCreatedElems.Append(newElem);
9321         if ( aShapeId && newElem )
9322           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9323         n1 = 1;
9324         n2 = 2;
9325         n3 = 3;
9326       }
9327       else {
9328         // create quadrangle
9329         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
9330         myLastCreatedElems.Append(newElem);
9331         if ( aShapeId && newElem )
9332           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9333         n1 = 0;
9334         n2 = 1;
9335         n3 = 7;
9336       }
9337     }
9338     // create needed triangles using n1,n2,n3 and inserted nodes
9339     int nbn = 2 + aNodesToInsert.size();
9340     //const SMDS_MeshNode* aNodes[nbn];
9341     vector<const SMDS_MeshNode*> aNodes(nbn);
9342     aNodes[0] = nodes[n1];
9343     aNodes[nbn-1] = nodes[n2];
9344     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9345     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9346       aNodes[iNode++] = *nIt;
9347     }
9348     for(i=1; i<nbn; i++) {
9349       SMDS_MeshElement* newElem =
9350         aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
9351       myLastCreatedElems.Append(newElem);
9352       if ( aShapeId && newElem )
9353         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9354     }
9355   }
9356   // remove old face
9357   aMesh->RemoveElement(theFace);
9358 }
9359
9360 //=======================================================================
9361 //function : UpdateVolumes
9362 //purpose  :
9363 //=======================================================================
9364 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
9365                                       const SMDS_MeshNode*        theBetweenNode2,
9366                                       list<const SMDS_MeshNode*>& theNodesToInsert)
9367 {
9368   myLastCreatedElems.Clear();
9369   myLastCreatedNodes.Clear();
9370
9371   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9372   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9373     const SMDS_MeshElement* elem = invElemIt->next();
9374
9375     // check, if current volume has link theBetweenNode1 - theBetweenNode2
9376     SMDS_VolumeTool aVolume (elem);
9377     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9378       continue;
9379
9380     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9381     int iface, nbFaces = aVolume.NbFaces();
9382     vector<const SMDS_MeshNode *> poly_nodes;
9383     vector<int> quantities (nbFaces);
9384
9385     for (iface = 0; iface < nbFaces; iface++) {
9386       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9387       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9388       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9389
9390       for (int inode = 0; inode < nbFaceNodes; inode++) {
9391         poly_nodes.push_back(faceNodes[inode]);
9392
9393         if (nbInserted == 0) {
9394           if (faceNodes[inode] == theBetweenNode1) {
9395             if (faceNodes[inode + 1] == theBetweenNode2) {
9396               nbInserted = theNodesToInsert.size();
9397
9398               // add nodes to insert
9399               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9400               for (; nIt != theNodesToInsert.end(); nIt++) {
9401                 poly_nodes.push_back(*nIt);
9402               }
9403             }
9404           }
9405           else if (faceNodes[inode] == theBetweenNode2) {
9406             if (faceNodes[inode + 1] == theBetweenNode1) {
9407               nbInserted = theNodesToInsert.size();
9408
9409               // add nodes to insert in reversed order
9410               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9411               nIt--;
9412               for (; nIt != theNodesToInsert.begin(); nIt--) {
9413                 poly_nodes.push_back(*nIt);
9414               }
9415               poly_nodes.push_back(*nIt);
9416             }
9417           }
9418           else {
9419           }
9420         }
9421       }
9422       quantities[iface] = nbFaceNodes + nbInserted;
9423     }
9424
9425     // Replace or update the volume
9426     SMESHDS_Mesh *aMesh = GetMeshDS();
9427
9428     if (elem->IsPoly()) {
9429       aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
9430
9431     }
9432     else {
9433       int aShapeId = FindShape( elem );
9434
9435       SMDS_MeshElement* newElem =
9436         aMesh->AddPolyhedralVolume(poly_nodes, quantities);
9437       myLastCreatedElems.Append(newElem);
9438       if (aShapeId && newElem)
9439         aMesh->SetMeshElementOnShape(newElem, aShapeId);
9440
9441       aMesh->RemoveElement(elem);
9442     }
9443   }
9444 }
9445
9446 namespace
9447 {
9448   //================================================================================
9449   /*!
9450    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9451    */
9452   //================================================================================
9453
9454   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9455                            vector<const SMDS_MeshNode *> & nodes,
9456                            vector<int> &                   nbNodeInFaces )
9457   {
9458     nodes.clear();
9459     nbNodeInFaces.clear();
9460     SMDS_VolumeTool vTool ( elem );
9461     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9462     {
9463       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9464       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9465       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9466     }
9467   }
9468 }
9469
9470 //=======================================================================
9471 /*!
9472  * \brief Convert elements contained in a submesh to quadratic
9473  * \return int - nb of checked elements
9474  */
9475 //=======================================================================
9476
9477 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9478                                              SMESH_MesherHelper& theHelper,
9479                                              const bool          theForce3d)
9480 {
9481   int nbElem = 0;
9482   if( !theSm ) return nbElem;
9483
9484   vector<int> nbNodeInFaces;
9485   vector<const SMDS_MeshNode *> nodes;
9486   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9487   while(ElemItr->more())
9488   {
9489     nbElem++;
9490     const SMDS_MeshElement* elem = ElemItr->next();
9491     if( !elem || elem->IsQuadratic() ) continue;
9492
9493     // get elem data needed to re-create it
9494     //
9495     const int id                        = elem->GetID();
9496     const int nbNodes                   = elem->NbNodes();
9497     const SMDSAbs_ElementType aType     = elem->GetType();
9498     const SMDSAbs_EntityType  aGeomType = elem->GetEntityType();
9499     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9500     if ( aGeomType == SMDSEntity_Polyhedra )
9501       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9502     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9503       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9504
9505     // remove a linear element
9506     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9507
9508     const SMDS_MeshElement* NewElem = 0;
9509
9510     switch( aType )
9511     {
9512     case SMDSAbs_Edge :
9513       {
9514         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9515         break;
9516       }
9517     case SMDSAbs_Face :
9518       {
9519         switch(nbNodes)
9520         {
9521         case 3:
9522           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9523           break;
9524         case 4:
9525           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9526           break;
9527         default:
9528           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9529           continue;
9530         }
9531         break;
9532       }
9533     case SMDSAbs_Volume :
9534       {
9535         switch( aGeomType )
9536         {
9537         case SMDSEntity_Tetra:
9538           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9539           break;
9540         case SMDSEntity_Pyramid:
9541           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9542           break;
9543         case SMDSEntity_Penta:
9544           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9545           break;
9546         case SMDSEntity_Hexa:
9547           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9548                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9549           break;
9550         case SMDSEntity_Hexagonal_Prism:
9551         default:
9552           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9553         }
9554         break;
9555       }
9556     default :
9557       continue;
9558     }
9559     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9560     if( NewElem )
9561       theSm->AddElement( NewElem );
9562   }
9563   return nbElem;
9564 }
9565
9566 //=======================================================================
9567 //function : ConvertToQuadratic
9568 //purpose  :
9569 //=======================================================================
9570
9571 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d)
9572 {
9573   SMESHDS_Mesh* meshDS = GetMeshDS();
9574
9575   SMESH_MesherHelper aHelper(*myMesh);
9576   aHelper.SetIsQuadratic( true );
9577
9578   int nbCheckedElems = 0;
9579   if ( myMesh->HasShapeToMesh() )
9580   {
9581     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9582     {
9583       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9584       while ( smIt->more() ) {
9585         SMESH_subMesh* sm = smIt->next();
9586         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9587           aHelper.SetSubShape( sm->GetSubShape() );
9588           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9589         }
9590       }
9591     }
9592   }
9593   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9594   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9595   {
9596     SMESHDS_SubMesh *smDS = 0;
9597     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9598     while(aEdgeItr->more())
9599     {
9600       const SMDS_MeshEdge* edge = aEdgeItr->next();
9601       if(edge && !edge->IsQuadratic())
9602       {
9603         int id = edge->GetID();
9604         //MESSAGE("edge->GetID() " << id);
9605         const SMDS_MeshNode* n1 = edge->GetNode(0);
9606         const SMDS_MeshNode* n2 = edge->GetNode(1);
9607
9608         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9609
9610         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9611         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9612       }
9613     }
9614     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9615     while(aFaceItr->more())
9616     {
9617       const SMDS_MeshFace* face = aFaceItr->next();
9618       if(!face || face->IsQuadratic() ) continue;
9619
9620       const int id = face->GetID();
9621       const SMDSAbs_EntityType type = face->GetEntityType();
9622       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9623
9624       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9625
9626       SMDS_MeshFace * NewFace = 0;
9627       switch( type )
9628       {
9629       case SMDSEntity_Triangle:
9630         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9631         break;
9632       case SMDSEntity_Quadrangle:
9633         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9634         break;
9635       default:
9636         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9637       }
9638       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9639     }
9640     vector<int> nbNodeInFaces;
9641     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9642     while(aVolumeItr->more())
9643     {
9644       const SMDS_MeshVolume* volume = aVolumeItr->next();
9645       if(!volume || volume->IsQuadratic() ) continue;
9646
9647       const int id = volume->GetID();
9648       const SMDSAbs_EntityType type = volume->GetEntityType();
9649       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9650       if ( type == SMDSEntity_Polyhedra )
9651         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9652       else if ( type == SMDSEntity_Hexagonal_Prism )
9653         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9654
9655       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9656
9657       SMDS_MeshVolume * NewVolume = 0;
9658       switch ( type )
9659       {
9660       case SMDSEntity_Tetra:
9661         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9662         break;
9663       case SMDSEntity_Hexa:
9664         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9665                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9666         break;
9667       case SMDSEntity_Pyramid:
9668         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9669                                       nodes[3], nodes[4], id, theForce3d);
9670         break;
9671       case SMDSEntity_Penta:
9672         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9673                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9674         break;
9675       case SMDSEntity_Hexagonal_Prism:
9676       default:
9677         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9678       }
9679       ReplaceElemInGroups(volume, NewVolume, meshDS);
9680     }
9681   }
9682
9683   if ( !theForce3d )
9684   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9685     aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9686     aHelper.FixQuadraticElements(myError);
9687   }
9688 }
9689
9690 //================================================================================
9691 /*!
9692  * \brief Makes given elements quadratic
9693  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9694  *  \param theElements - elements to make quadratic
9695  */
9696 //================================================================================
9697
9698 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9699                                           TIDSortedElemSet& theElements)
9700 {
9701   if ( theElements.empty() ) return;
9702
9703   // we believe that all theElements are of the same type
9704   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9705
9706   // get all nodes shared by theElements
9707   TIDSortedNodeSet allNodes;
9708   TIDSortedElemSet::iterator eIt = theElements.begin();
9709   for ( ; eIt != theElements.end(); ++eIt )
9710     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9711
9712   // complete theElements with elements of lower dim whose all nodes are in allNodes
9713
9714   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9715   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9716   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9717   for ( ; nIt != allNodes.end(); ++nIt )
9718   {
9719     const SMDS_MeshNode* n = *nIt;
9720     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9721     while ( invIt->more() )
9722     {
9723       const SMDS_MeshElement* e = invIt->next();
9724       if ( e->IsQuadratic() )
9725       {
9726         quadAdjacentElems[ e->GetType() ].insert( e );
9727         continue;
9728       }
9729       if ( e->GetType() >= elemType )
9730       {
9731         continue; // same type of more complex linear element
9732       }
9733
9734       if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9735         continue; // e is already checked
9736
9737       // check nodes
9738       bool allIn = true;
9739       SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9740       while ( nodeIt->more() && allIn )
9741         allIn = allNodes.count( cast2Node( nodeIt->next() ));
9742       if ( allIn )
9743         theElements.insert(e );
9744     }
9745   }
9746
9747   SMESH_MesherHelper helper(*myMesh);
9748   helper.SetIsQuadratic( true );
9749
9750   // add links of quadratic adjacent elements to the helper
9751
9752   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9753     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9754           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9755     {
9756       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9757     }
9758   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9759     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9760           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9761     {
9762       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9763     }
9764   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9765     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9766           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9767     {
9768       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9769     }
9770
9771   // make quadratic elements instead of linear ones
9772
9773   SMESHDS_Mesh* meshDS = GetMeshDS();
9774   SMESHDS_SubMesh* smDS = 0;
9775   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9776   {
9777     const SMDS_MeshElement* elem = *eIt;
9778     if( elem->IsQuadratic() || elem->NbNodes() < 2 || elem->IsPoly() )
9779       continue;
9780
9781     const int id                   = elem->GetID();
9782     const SMDSAbs_ElementType type = elem->GetType();
9783     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9784
9785     if ( !smDS || !smDS->Contains( elem ))
9786       smDS = meshDS->MeshElements( elem->getshapeId() );
9787     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9788
9789     SMDS_MeshElement * newElem = 0;
9790     switch( nodes.size() )
9791     {
9792     case 4: // cases for most frequently used element types go first (for optimization)
9793       if ( type == SMDSAbs_Volume )
9794         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9795       else
9796         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9797       break;
9798     case 8:
9799       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9800                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9801       break;
9802     case 3:
9803       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9804       break;
9805     case 2:
9806       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9807       break;
9808     case 5:
9809       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9810                                  nodes[4], id, theForce3d);
9811       break;
9812     case 6:
9813       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9814                                  nodes[4], nodes[5], id, theForce3d);
9815       break;
9816     default:;
9817     }
9818     ReplaceElemInGroups( elem, newElem, meshDS);
9819     if( newElem && smDS )
9820       smDS->AddElement( newElem );
9821   }
9822
9823   if ( !theForce3d  && !getenv("NO_FixQuadraticElements"))
9824   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9825     helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9826     helper.FixQuadraticElements( myError );
9827   }
9828 }
9829
9830 //=======================================================================
9831 /*!
9832  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9833  * \return int - nb of checked elements
9834  */
9835 //=======================================================================
9836
9837 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9838                                      SMDS_ElemIteratorPtr theItr,
9839                                      const int            theShapeID)
9840 {
9841   int nbElem = 0;
9842   SMESHDS_Mesh* meshDS = GetMeshDS();
9843
9844   while( theItr->more() )
9845   {
9846     const SMDS_MeshElement* elem = theItr->next();
9847     nbElem++;
9848     if( elem && elem->IsQuadratic())
9849     {
9850       int id                    = elem->GetID();
9851       int nbCornerNodes         = elem->NbCornerNodes();
9852       SMDSAbs_ElementType aType = elem->GetType();
9853
9854       vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9855
9856       //remove a quadratic element
9857       if ( !theSm || !theSm->Contains( elem ))
9858         theSm = meshDS->MeshElements( elem->getshapeId() );
9859       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9860
9861       // remove medium nodes
9862       for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9863         if ( nodes[i]->NbInverseElements() == 0 )
9864           meshDS->RemoveFreeNode( nodes[i], theSm );
9865
9866       // add a linear element
9867       nodes.resize( nbCornerNodes );
9868       SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9869       ReplaceElemInGroups(elem, newElem, meshDS);
9870       if( theSm && newElem )
9871         theSm->AddElement( newElem );
9872     }
9873   }
9874   return nbElem;
9875 }
9876
9877 //=======================================================================
9878 //function : ConvertFromQuadratic
9879 //purpose  :
9880 //=======================================================================
9881
9882 bool SMESH_MeshEditor::ConvertFromQuadratic()
9883 {
9884   int nbCheckedElems = 0;
9885   if ( myMesh->HasShapeToMesh() )
9886   {
9887     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9888     {
9889       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9890       while ( smIt->more() ) {
9891         SMESH_subMesh* sm = smIt->next();
9892         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9893           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9894       }
9895     }
9896   }
9897
9898   int totalNbElems =
9899     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9900   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9901   {
9902     SMESHDS_SubMesh *aSM = 0;
9903     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9904   }
9905
9906   return true;
9907 }
9908
9909 namespace
9910 {
9911   //================================================================================
9912   /*!
9913    * \brief Return true if all medium nodes of the element are in the node set
9914    */
9915   //================================================================================
9916
9917   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9918   {
9919     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9920       if ( !nodeSet.count( elem->GetNode(i) ))
9921         return false;
9922     return true;
9923   }
9924 }
9925
9926 //================================================================================
9927 /*!
9928  * \brief Makes given elements linear
9929  */
9930 //================================================================================
9931
9932 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9933 {
9934   if ( theElements.empty() ) return;
9935
9936   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9937   set<int> mediumNodeIDs;
9938   TIDSortedElemSet::iterator eIt = theElements.begin();
9939   for ( ; eIt != theElements.end(); ++eIt )
9940   {
9941     const SMDS_MeshElement* e = *eIt;
9942     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9943       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9944   }
9945
9946   // replace given elements by linear ones
9947   typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
9948   SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
9949   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9950
9951   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9952   // except those elements sharing medium nodes of quadratic element whose medium nodes
9953   // are not all in mediumNodeIDs
9954
9955   // get remaining medium nodes
9956   TIDSortedNodeSet mediumNodes;
9957   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9958   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9959     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9960       mediumNodes.insert( mediumNodes.end(), n );
9961
9962   // find more quadratic elements to convert
9963   TIDSortedElemSet moreElemsToConvert;
9964   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9965   for ( ; nIt != mediumNodes.end(); ++nIt )
9966   {
9967     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9968     while ( invIt->more() )
9969     {
9970       const SMDS_MeshElement* e = invIt->next();
9971       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9972       {
9973         // find a more complex element including e and
9974         // whose medium nodes are not in mediumNodes
9975         bool complexFound = false;
9976         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9977         {
9978           SMDS_ElemIteratorPtr invIt2 =
9979             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9980           while ( invIt2->more() )
9981           {
9982             const SMDS_MeshElement* eComplex = invIt2->next();
9983             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9984             {
9985               int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
9986               if ( nbCommonNodes == e->NbNodes())
9987               {
9988                 complexFound = true;
9989                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9990                 break;
9991               }
9992             }
9993           }
9994         }
9995         if ( !complexFound )
9996           moreElemsToConvert.insert( e );
9997       }
9998     }
9999   }
10000   elemIt = SMDS_ElemIteratorPtr
10001     (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
10002   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10003 }
10004
10005 //=======================================================================
10006 //function : SewSideElements
10007 //purpose  :
10008 //=======================================================================
10009
10010 SMESH_MeshEditor::Sew_Error
10011 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
10012                                    TIDSortedElemSet&    theSide2,
10013                                    const SMDS_MeshNode* theFirstNode1,
10014                                    const SMDS_MeshNode* theFirstNode2,
10015                                    const SMDS_MeshNode* theSecondNode1,
10016                                    const SMDS_MeshNode* theSecondNode2)
10017 {
10018   myLastCreatedElems.Clear();
10019   myLastCreatedNodes.Clear();
10020
10021   MESSAGE ("::::SewSideElements()");
10022   if ( theSide1.size() != theSide2.size() )
10023     return SEW_DIFF_NB_OF_ELEMENTS;
10024
10025   Sew_Error aResult = SEW_OK;
10026   // Algo:
10027   // 1. Build set of faces representing each side
10028   // 2. Find which nodes of the side 1 to merge with ones on the side 2
10029   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10030
10031   // =======================================================================
10032   // 1. Build set of faces representing each side:
10033   // =======================================================================
10034   // a. build set of nodes belonging to faces
10035   // b. complete set of faces: find missing faces whose nodes are in set of nodes
10036   // c. create temporary faces representing side of volumes if correspondent
10037   //    face does not exist
10038
10039   SMESHDS_Mesh* aMesh = GetMeshDS();
10040   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
10041   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
10042   TIDSortedElemSet             faceSet1, faceSet2;
10043   set<const SMDS_MeshElement*> volSet1,  volSet2;
10044   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
10045   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
10046   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
10047   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
10048   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
10049   int iSide, iFace, iNode;
10050
10051   list<const SMDS_MeshElement* > tempFaceList;
10052   for ( iSide = 0; iSide < 2; iSide++ ) {
10053     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
10054     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
10055     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
10056     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
10057     set<const SMDS_MeshElement*>::iterator vIt;
10058     TIDSortedElemSet::iterator eIt;
10059     set<const SMDS_MeshNode*>::iterator    nIt;
10060
10061     // check that given nodes belong to given elements
10062     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
10063     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
10064     int firstIndex = -1, secondIndex = -1;
10065     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10066       const SMDS_MeshElement* elem = *eIt;
10067       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
10068       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
10069       if ( firstIndex > -1 && secondIndex > -1 ) break;
10070     }
10071     if ( firstIndex < 0 || secondIndex < 0 ) {
10072       // we can simply return until temporary faces created
10073       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
10074     }
10075
10076     // -----------------------------------------------------------
10077     // 1a. Collect nodes of existing faces
10078     //     and build set of face nodes in order to detect missing
10079     //     faces corresponding to sides of volumes
10080     // -----------------------------------------------------------
10081
10082     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
10083
10084     // loop on the given element of a side
10085     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10086       //const SMDS_MeshElement* elem = *eIt;
10087       const SMDS_MeshElement* elem = *eIt;
10088       if ( elem->GetType() == SMDSAbs_Face ) {
10089         faceSet->insert( elem );
10090         set <const SMDS_MeshNode*> faceNodeSet;
10091         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10092         while ( nodeIt->more() ) {
10093           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10094           nodeSet->insert( n );
10095           faceNodeSet.insert( n );
10096         }
10097         setOfFaceNodeSet.insert( faceNodeSet );
10098       }
10099       else if ( elem->GetType() == SMDSAbs_Volume )
10100         volSet->insert( elem );
10101     }
10102     // ------------------------------------------------------------------------------
10103     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10104     // ------------------------------------------------------------------------------
10105
10106     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10107       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10108       while ( fIt->more() ) { // loop on faces sharing a node
10109         const SMDS_MeshElement* f = fIt->next();
10110         if ( faceSet->find( f ) == faceSet->end() ) {
10111           // check if all nodes are in nodeSet and
10112           // complete setOfFaceNodeSet if they are
10113           set <const SMDS_MeshNode*> faceNodeSet;
10114           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10115           bool allInSet = true;
10116           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10117             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10118             if ( nodeSet->find( n ) == nodeSet->end() )
10119               allInSet = false;
10120             else
10121               faceNodeSet.insert( n );
10122           }
10123           if ( allInSet ) {
10124             faceSet->insert( f );
10125             setOfFaceNodeSet.insert( faceNodeSet );
10126           }
10127         }
10128       }
10129     }
10130
10131     // -------------------------------------------------------------------------
10132     // 1c. Create temporary faces representing sides of volumes if correspondent
10133     //     face does not exist
10134     // -------------------------------------------------------------------------
10135
10136     if ( !volSet->empty() ) {
10137       //int nodeSetSize = nodeSet->size();
10138
10139       // loop on given volumes
10140       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10141         SMDS_VolumeTool vol (*vIt);
10142         // loop on volume faces: find free faces
10143         // --------------------------------------
10144         list<const SMDS_MeshElement* > freeFaceList;
10145         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10146           if ( !vol.IsFreeFace( iFace ))
10147             continue;
10148           // check if there is already a face with same nodes in a face set
10149           const SMDS_MeshElement* aFreeFace = 0;
10150           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10151           int nbNodes = vol.NbFaceNodes( iFace );
10152           set <const SMDS_MeshNode*> faceNodeSet;
10153           vol.GetFaceNodes( iFace, faceNodeSet );
10154           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10155           if ( isNewFace ) {
10156             // no such a face is given but it still can exist, check it
10157             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10158             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10159           }
10160           if ( !aFreeFace ) {
10161             // create a temporary face
10162             if ( nbNodes == 3 ) {
10163               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10164               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10165             }
10166             else if ( nbNodes == 4 ) {
10167               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10168               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10169             }
10170             else {
10171               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10172               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10173               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10174             }
10175             if ( aFreeFace )
10176               tempFaceList.push_back( aFreeFace );
10177           }
10178
10179           if ( aFreeFace )
10180             freeFaceList.push_back( aFreeFace );
10181
10182         } // loop on faces of a volume
10183
10184         // choose one of several free faces of a volume
10185         // --------------------------------------------
10186         if ( freeFaceList.size() > 1 ) {
10187           // choose a face having max nb of nodes shared by other elems of a side
10188           int maxNbNodes = -1;
10189           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10190           while ( fIt != freeFaceList.end() ) { // loop on free faces
10191             int nbSharedNodes = 0;
10192             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10193             while ( nodeIt->more() ) { // loop on free face nodes
10194               const SMDS_MeshNode* n =
10195                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10196               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10197               while ( invElemIt->more() ) {
10198                 const SMDS_MeshElement* e = invElemIt->next();
10199                 nbSharedNodes += faceSet->count( e );
10200                 nbSharedNodes += elemSet->count( e );
10201               }
10202             }
10203             if ( nbSharedNodes > maxNbNodes ) {
10204               maxNbNodes = nbSharedNodes;
10205               freeFaceList.erase( freeFaceList.begin(), fIt++ );
10206             }
10207             else if ( nbSharedNodes == maxNbNodes ) {
10208               fIt++;
10209             }
10210             else {
10211               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10212             }
10213           }
10214           if ( freeFaceList.size() > 1 )
10215           {
10216             // could not choose one face, use another way
10217             // choose a face most close to the bary center of the opposite side
10218             gp_XYZ aBC( 0., 0., 0. );
10219             set <const SMDS_MeshNode*> addedNodes;
10220             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10221             eIt = elemSet2->begin();
10222             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10223               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10224               while ( nodeIt->more() ) { // loop on free face nodes
10225                 const SMDS_MeshNode* n =
10226                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10227                 if ( addedNodes.insert( n ).second )
10228                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10229               }
10230             }
10231             aBC /= addedNodes.size();
10232             double minDist = DBL_MAX;
10233             fIt = freeFaceList.begin();
10234             while ( fIt != freeFaceList.end() ) { // loop on free faces
10235               double dist = 0;
10236               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10237               while ( nodeIt->more() ) { // loop on free face nodes
10238                 const SMDS_MeshNode* n =
10239                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10240                 gp_XYZ p( n->X(),n->Y(),n->Z() );
10241                 dist += ( aBC - p ).SquareModulus();
10242               }
10243               if ( dist < minDist ) {
10244                 minDist = dist;
10245                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10246               }
10247               else
10248                 fIt = freeFaceList.erase( fIt++ );
10249             }
10250           }
10251         } // choose one of several free faces of a volume
10252
10253         if ( freeFaceList.size() == 1 ) {
10254           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10255           faceSet->insert( aFreeFace );
10256           // complete a node set with nodes of a found free face
10257           //           for ( iNode = 0; iNode < ; iNode++ )
10258           //             nodeSet->insert( fNodes[ iNode ] );
10259         }
10260
10261       } // loop on volumes of a side
10262
10263       //       // complete a set of faces if new nodes in a nodeSet appeared
10264       //       // ----------------------------------------------------------
10265       //       if ( nodeSetSize != nodeSet->size() ) {
10266       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10267       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10268       //           while ( fIt->more() ) { // loop on faces sharing a node
10269       //             const SMDS_MeshElement* f = fIt->next();
10270       //             if ( faceSet->find( f ) == faceSet->end() ) {
10271       //               // check if all nodes are in nodeSet and
10272       //               // complete setOfFaceNodeSet if they are
10273       //               set <const SMDS_MeshNode*> faceNodeSet;
10274       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10275       //               bool allInSet = true;
10276       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10277       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10278       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10279       //                   allInSet = false;
10280       //                 else
10281       //                   faceNodeSet.insert( n );
10282       //               }
10283       //               if ( allInSet ) {
10284       //                 faceSet->insert( f );
10285       //                 setOfFaceNodeSet.insert( faceNodeSet );
10286       //               }
10287       //             }
10288       //           }
10289       //         }
10290       //       }
10291     } // Create temporary faces, if there are volumes given
10292   } // loop on sides
10293
10294   if ( faceSet1.size() != faceSet2.size() ) {
10295     // delete temporary faces: they are in reverseElements of actual nodes
10296 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10297 //    while ( tmpFaceIt->more() )
10298 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10299 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10300 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10301 //      aMesh->RemoveElement(*tmpFaceIt);
10302     MESSAGE("Diff nb of faces");
10303     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10304   }
10305
10306   // ============================================================
10307   // 2. Find nodes to merge:
10308   //              bind a node to remove to a node to put instead
10309   // ============================================================
10310
10311   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10312   if ( theFirstNode1 != theFirstNode2 )
10313     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10314   if ( theSecondNode1 != theSecondNode2 )
10315     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10316
10317   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10318   set< long > linkIdSet; // links to process
10319   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10320
10321   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10322   list< NLink > linkList[2];
10323   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10324   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10325   // loop on links in linkList; find faces by links and append links
10326   // of the found faces to linkList
10327   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10328   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10329   {
10330     NLink link[] = { *linkIt[0], *linkIt[1] };
10331     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10332     if ( !linkIdSet.count( linkID ) )
10333       continue;
10334
10335     // by links, find faces in the face sets,
10336     // and find indices of link nodes in the found faces;
10337     // in a face set, there is only one or no face sharing a link
10338     // ---------------------------------------------------------------
10339
10340     const SMDS_MeshElement* face[] = { 0, 0 };
10341     vector<const SMDS_MeshNode*> fnodes[2];
10342     int iLinkNode[2][2];
10343     TIDSortedElemSet avoidSet;
10344     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10345       const SMDS_MeshNode* n1 = link[iSide].first;
10346       const SMDS_MeshNode* n2 = link[iSide].second;
10347       //cout << "Side " << iSide << " ";
10348       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10349       // find a face by two link nodes
10350       face[ iSide ] = FindFaceInSet( n1, n2, *faceSetPtr[ iSide ], avoidSet,
10351                                      &iLinkNode[iSide][0], &iLinkNode[iSide][1] );
10352       if ( face[ iSide ])
10353       {
10354         //cout << " F " << face[ iSide]->GetID() <<endl;
10355         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10356         // put face nodes to fnodes
10357         if ( face[ iSide ]->IsQuadratic() )
10358         {
10359           // use interlaced nodes iterator
10360           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10361           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10362           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10363           while ( nIter->more() )
10364             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10365         }
10366         else
10367         {
10368           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10369                                   face[ iSide ]->end_nodes() );
10370         }
10371         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10372       }
10373     }
10374
10375     // check similarity of elements of the sides
10376     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10377       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10378       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10379         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10380       }
10381       else {
10382         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10383       }
10384       break; // do not return because it's necessary to remove tmp faces
10385     }
10386
10387     // set nodes to merge
10388     // -------------------
10389
10390     if ( face[0] && face[1] )  {
10391       const int nbNodes = face[0]->NbNodes();
10392       if ( nbNodes != face[1]->NbNodes() ) {
10393         MESSAGE("Diff nb of face nodes");
10394         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10395         break; // do not return because it s necessary to remove tmp faces
10396       }
10397       bool reverse[] = { false, false }; // order of nodes in the link
10398       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10399         // analyse link orientation in faces
10400         int i1 = iLinkNode[ iSide ][ 0 ];
10401         int i2 = iLinkNode[ iSide ][ 1 ];
10402         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10403       }
10404       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10405       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10406       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10407       {
10408         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10409                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10410       }
10411
10412       // add other links of the faces to linkList
10413       // -----------------------------------------
10414
10415       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10416         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10417         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10418         if ( !iter_isnew.second ) { // already in a set: no need to process
10419           linkIdSet.erase( iter_isnew.first );
10420         }
10421         else // new in set == encountered for the first time: add
10422         {
10423           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10424           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10425           linkList[0].push_back ( NLink( n1, n2 ));
10426           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10427         }
10428       }
10429     } // 2 faces found
10430
10431     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10432       break;
10433
10434   } // loop on link lists
10435
10436   if ( aResult == SEW_OK &&
10437        ( //linkIt[0] != linkList[0].end() ||
10438          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10439     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10440              " " << (faceSetPtr[1]->empty()));
10441     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10442   }
10443
10444   // ====================================================================
10445   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10446   // ====================================================================
10447
10448   // delete temporary faces
10449 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10450 //  while ( tmpFaceIt->more() )
10451 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10452   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10453   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10454     aMesh->RemoveElement(*tmpFaceIt);
10455
10456   if ( aResult != SEW_OK)
10457     return aResult;
10458
10459   list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10460   // loop on nodes replacement map
10461   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10462   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10463     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10464       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10465       nodeIDsToRemove.push_back( nToRemove->GetID() );
10466       // loop on elements sharing nToRemove
10467       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10468       while ( invElemIt->more() ) {
10469         const SMDS_MeshElement* e = invElemIt->next();
10470         // get a new suite of nodes: make replacement
10471         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10472         vector< const SMDS_MeshNode*> nodes( nbNodes );
10473         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10474         while ( nIt->more() ) {
10475           const SMDS_MeshNode* n =
10476             static_cast<const SMDS_MeshNode*>( nIt->next() );
10477           nnIt = nReplaceMap.find( n );
10478           if ( nnIt != nReplaceMap.end() ) {
10479             nbReplaced++;
10480             n = (*nnIt).second;
10481           }
10482           nodes[ i++ ] = n;
10483         }
10484         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10485         //         elemIDsToRemove.push_back( e->GetID() );
10486         //       else
10487         if ( nbReplaced )
10488           {
10489             SMDSAbs_ElementType etyp = e->GetType();
10490             SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10491             if (newElem)
10492               {
10493                 myLastCreatedElems.Append(newElem);
10494                 AddToSameGroups(newElem, e, aMesh);
10495                 int aShapeId = e->getshapeId();
10496                 if ( aShapeId )
10497                   {
10498                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
10499                   }
10500               }
10501             aMesh->RemoveElement(e);
10502           }
10503       }
10504     }
10505
10506   Remove( nodeIDsToRemove, true );
10507
10508   return aResult;
10509 }
10510
10511 //================================================================================
10512 /*!
10513  * \brief Find corresponding nodes in two sets of faces
10514  * \param theSide1 - first face set
10515  * \param theSide2 - second first face
10516  * \param theFirstNode1 - a boundary node of set 1
10517  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10518  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10519  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10520  * \param nReplaceMap - output map of corresponding nodes
10521  * \return bool  - is a success or not
10522  */
10523 //================================================================================
10524
10525 #ifdef _DEBUG_
10526 //#define DEBUG_MATCHING_NODES
10527 #endif
10528
10529 SMESH_MeshEditor::Sew_Error
10530 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10531                                     set<const SMDS_MeshElement*>& theSide2,
10532                                     const SMDS_MeshNode*          theFirstNode1,
10533                                     const SMDS_MeshNode*          theFirstNode2,
10534                                     const SMDS_MeshNode*          theSecondNode1,
10535                                     const SMDS_MeshNode*          theSecondNode2,
10536                                     TNodeNodeMap &                nReplaceMap)
10537 {
10538   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10539
10540   nReplaceMap.clear();
10541   if ( theFirstNode1 != theFirstNode2 )
10542     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10543   if ( theSecondNode1 != theSecondNode2 )
10544     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10545
10546   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10547   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10548
10549   list< NLink > linkList[2];
10550   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10551   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10552
10553   // loop on links in linkList; find faces by links and append links
10554   // of the found faces to linkList
10555   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10556   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10557     NLink link[] = { *linkIt[0], *linkIt[1] };
10558     if ( linkSet.find( link[0] ) == linkSet.end() )
10559       continue;
10560
10561     // by links, find faces in the face sets,
10562     // and find indices of link nodes in the found faces;
10563     // in a face set, there is only one or no face sharing a link
10564     // ---------------------------------------------------------------
10565
10566     const SMDS_MeshElement* face[] = { 0, 0 };
10567     list<const SMDS_MeshNode*> notLinkNodes[2];
10568     //bool reverse[] = { false, false }; // order of notLinkNodes
10569     int nbNodes[2];
10570     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10571     {
10572       const SMDS_MeshNode* n1 = link[iSide].first;
10573       const SMDS_MeshNode* n2 = link[iSide].second;
10574       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10575       set< const SMDS_MeshElement* > facesOfNode1;
10576       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10577       {
10578         // during a loop of the first node, we find all faces around n1,
10579         // during a loop of the second node, we find one face sharing both n1 and n2
10580         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10581         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10582         while ( fIt->more() ) { // loop on faces sharing a node
10583           const SMDS_MeshElement* f = fIt->next();
10584           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10585               ! facesOfNode1.insert( f ).second ) // f encounters twice
10586           {
10587             if ( face[ iSide ] ) {
10588               MESSAGE( "2 faces per link " );
10589               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10590             }
10591             face[ iSide ] = f;
10592             faceSet->erase( f );
10593
10594             // get not link nodes
10595             int nbN = f->NbNodes();
10596             if ( f->IsQuadratic() )
10597               nbN /= 2;
10598             nbNodes[ iSide ] = nbN;
10599             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10600             int i1 = f->GetNodeIndex( n1 );
10601             int i2 = f->GetNodeIndex( n2 );
10602             int iEnd = nbN, iBeg = -1, iDelta = 1;
10603             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10604             if ( reverse ) {
10605               std::swap( iEnd, iBeg ); iDelta = -1;
10606             }
10607             int i = i2;
10608             while ( true ) {
10609               i += iDelta;
10610               if ( i == iEnd ) i = iBeg + iDelta;
10611               if ( i == i1 ) break;
10612               nodes.push_back ( f->GetNode( i ) );
10613             }
10614           }
10615         }
10616       }
10617     }
10618     // check similarity of elements of the sides
10619     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10620       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10621       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10622         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10623       }
10624       else {
10625         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10626       }
10627     }
10628
10629     // set nodes to merge
10630     // -------------------
10631
10632     if ( face[0] && face[1] )  {
10633       if ( nbNodes[0] != nbNodes[1] ) {
10634         MESSAGE("Diff nb of face nodes");
10635         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10636       }
10637 #ifdef DEBUG_MATCHING_NODES
10638       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10639                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10640                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10641 #endif
10642       int nbN = nbNodes[0];
10643       {
10644         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10645         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10646         for ( int i = 0 ; i < nbN - 2; ++i ) {
10647 #ifdef DEBUG_MATCHING_NODES
10648           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10649 #endif
10650           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10651         }
10652       }
10653
10654       // add other links of the face 1 to linkList
10655       // -----------------------------------------
10656
10657       const SMDS_MeshElement* f0 = face[0];
10658       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10659       for ( int i = 0; i < nbN; i++ )
10660       {
10661         const SMDS_MeshNode* n2 = f0->GetNode( i );
10662         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10663           linkSet.insert( SMESH_TLink( n1, n2 ));
10664         if ( !iter_isnew.second ) { // already in a set: no need to process
10665           linkSet.erase( iter_isnew.first );
10666         }
10667         else // new in set == encountered for the first time: add
10668         {
10669 #ifdef DEBUG_MATCHING_NODES
10670           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10671                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10672 #endif
10673           linkList[0].push_back ( NLink( n1, n2 ));
10674           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10675         }
10676         n1 = n2;
10677       }
10678     } // 2 faces found
10679   } // loop on link lists
10680
10681   return SEW_OK;
10682 }
10683
10684 //================================================================================
10685 /*!
10686   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10687   \param theElems - the list of elements (edges or faces) to be replicated
10688   The nodes for duplication could be found from these elements
10689   \param theNodesNot - list of nodes to NOT replicate
10690   \param theAffectedElems - the list of elements (cells and edges) to which the
10691   replicated nodes should be associated to.
10692   \return TRUE if operation has been completed successfully, FALSE otherwise
10693 */
10694 //================================================================================
10695
10696 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10697                                     const TIDSortedElemSet& theNodesNot,
10698                                     const TIDSortedElemSet& theAffectedElems )
10699 {
10700   myLastCreatedElems.Clear();
10701   myLastCreatedNodes.Clear();
10702
10703   if ( theElems.size() == 0 )
10704     return false;
10705
10706   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10707   if ( !aMeshDS )
10708     return false;
10709
10710   bool res = false;
10711   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10712   // duplicate elements and nodes
10713   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10714   // replce nodes by duplications
10715   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10716   return res;
10717 }
10718
10719 //================================================================================
10720 /*!
10721   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10722   \param theMeshDS - mesh instance
10723   \param theElems - the elements replicated or modified (nodes should be changed)
10724   \param theNodesNot - nodes to NOT replicate
10725   \param theNodeNodeMap - relation of old node to new created node
10726   \param theIsDoubleElem - flag os to replicate element or modify
10727   \return TRUE if operation has been completed successfully, FALSE otherwise
10728 */
10729 //================================================================================
10730
10731 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh*     theMeshDS,
10732                                     const TIDSortedElemSet& theElems,
10733                                     const TIDSortedElemSet& theNodesNot,
10734                                     std::map< const SMDS_MeshNode*,
10735                                     const SMDS_MeshNode* >& theNodeNodeMap,
10736                                     const bool theIsDoubleElem )
10737 {
10738   MESSAGE("doubleNodes");
10739   // iterate on through element and duplicate them (by nodes duplication)
10740   bool res = false;
10741   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10742   for ( ;  elemItr != theElems.end(); ++elemItr )
10743   {
10744     const SMDS_MeshElement* anElem = *elemItr;
10745     if (!anElem)
10746       continue;
10747
10748     bool isDuplicate = false;
10749     // duplicate nodes to duplicate element
10750     std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10751     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10752     int ind = 0;
10753     while ( anIter->more() )
10754     {
10755
10756       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10757       SMDS_MeshNode* aNewNode = aCurrNode;
10758       if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10759         aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10760       else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10761       {
10762         // duplicate node
10763         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10764         theNodeNodeMap[ aCurrNode ] = aNewNode;
10765         myLastCreatedNodes.Append( aNewNode );
10766       }
10767       isDuplicate |= (aCurrNode != aNewNode);
10768       newNodes[ ind++ ] = aNewNode;
10769     }
10770     if ( !isDuplicate )
10771       continue;
10772
10773     if ( theIsDoubleElem )
10774       AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10775     else
10776       {
10777       MESSAGE("ChangeElementNodes");
10778       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10779       }
10780     res = true;
10781   }
10782   return res;
10783 }
10784
10785 //================================================================================
10786 /*!
10787   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10788   \param theNodes - identifiers of nodes to be doubled
10789   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10790          nodes. If list of element identifiers is empty then nodes are doubled but
10791          they not assigned to elements
10792   \return TRUE if operation has been completed successfully, FALSE otherwise
10793 */
10794 //================================================================================
10795
10796 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10797                                     const std::list< int >& theListOfModifiedElems )
10798 {
10799   MESSAGE("DoubleNodes");
10800   myLastCreatedElems.Clear();
10801   myLastCreatedNodes.Clear();
10802
10803   if ( theListOfNodes.size() == 0 )
10804     return false;
10805
10806   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10807   if ( !aMeshDS )
10808     return false;
10809
10810   // iterate through nodes and duplicate them
10811
10812   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10813
10814   std::list< int >::const_iterator aNodeIter;
10815   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10816   {
10817     int aCurr = *aNodeIter;
10818     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10819     if ( !aNode )
10820       continue;
10821
10822     // duplicate node
10823
10824     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10825     if ( aNewNode )
10826     {
10827       anOldNodeToNewNode[ aNode ] = aNewNode;
10828       myLastCreatedNodes.Append( aNewNode );
10829     }
10830   }
10831
10832   // Create map of new nodes for modified elements
10833
10834   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10835
10836   std::list< int >::const_iterator anElemIter;
10837   for ( anElemIter = theListOfModifiedElems.begin();
10838         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10839   {
10840     int aCurr = *anElemIter;
10841     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10842     if ( !anElem )
10843       continue;
10844
10845     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10846
10847     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10848     int ind = 0;
10849     while ( anIter->more() )
10850     {
10851       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10852       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10853       {
10854         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10855         aNodeArr[ ind++ ] = aNewNode;
10856       }
10857       else
10858         aNodeArr[ ind++ ] = aCurrNode;
10859     }
10860     anElemToNodes[ anElem ] = aNodeArr;
10861   }
10862
10863   // Change nodes of elements
10864
10865   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10866     anElemToNodesIter = anElemToNodes.begin();
10867   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10868   {
10869     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10870     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10871     if ( anElem )
10872       {
10873       MESSAGE("ChangeElementNodes");
10874       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10875       }
10876   }
10877
10878   return true;
10879 }
10880
10881 namespace {
10882
10883   //================================================================================
10884   /*!
10885   \brief Check if element located inside shape
10886   \return TRUE if IN or ON shape, FALSE otherwise
10887   */
10888   //================================================================================
10889
10890   template<class Classifier>
10891   bool isInside(const SMDS_MeshElement* theElem,
10892                 Classifier&             theClassifier,
10893                 const double            theTol)
10894   {
10895     gp_XYZ centerXYZ (0, 0, 0);
10896     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10897     while (aNodeItr->more())
10898       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10899
10900     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10901     theClassifier.Perform(aPnt, theTol);
10902     TopAbs_State aState = theClassifier.State();
10903     return (aState == TopAbs_IN || aState == TopAbs_ON );
10904   }
10905
10906   //================================================================================
10907   /*!
10908    * \brief Classifier of the 3D point on the TopoDS_Face
10909    *        with interaface suitable for isInside()
10910    */
10911   //================================================================================
10912
10913   struct _FaceClassifier
10914   {
10915     Extrema_ExtPS       _extremum;
10916     BRepAdaptor_Surface _surface;
10917     TopAbs_State        _state;
10918
10919     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10920     {
10921       _extremum.Initialize( _surface,
10922                             _surface.FirstUParameter(), _surface.LastUParameter(),
10923                             _surface.FirstVParameter(), _surface.LastVParameter(),
10924                             _surface.Tolerance(), _surface.Tolerance() );
10925     }
10926     void Perform(const gp_Pnt& aPnt, double theTol)
10927     {
10928       _state = TopAbs_OUT;
10929       _extremum.Perform(aPnt);
10930       if ( _extremum.IsDone() )
10931         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10932 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10933           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10934 #else
10935           _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10936 #endif
10937     }
10938     TopAbs_State State() const
10939     {
10940       return _state;
10941     }
10942   };
10943 }
10944
10945 //================================================================================
10946 /*!
10947   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed.
10948   This method is the first step of DoubleNodeElemGroupsInRegion.
10949   \param theElems - list of groups of elements (edges or faces) to be replicated
10950   \param theNodesNot - list of groups of nodes not to replicated
10951   \param theShape - shape to detect affected elements (element which geometric center
10952          located on or inside shape).
10953          The replicated nodes should be associated to affected elements.
10954   \return groups of affected elements
10955   \sa DoubleNodeElemGroupsInRegion()
10956  */
10957 //================================================================================
10958
10959 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10960                                                    const TIDSortedElemSet& theNodesNot,
10961                                                    const TopoDS_Shape&     theShape,
10962                                                    TIDSortedElemSet&       theAffectedElems)
10963 {
10964   if ( theShape.IsNull() )
10965     return false;
10966
10967   const double aTol = Precision::Confusion();
10968   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10969   auto_ptr<_FaceClassifier>              aFaceClassifier;
10970   if ( theShape.ShapeType() == TopAbs_SOLID )
10971   {
10972     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10973     bsc3d->PerformInfinitePoint(aTol);
10974   }
10975   else if (theShape.ShapeType() == TopAbs_FACE )
10976   {
10977     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10978   }
10979
10980   // iterates on indicated elements and get elements by back references from their nodes
10981   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10982   for ( ;  elemItr != theElems.end(); ++elemItr )
10983   {
10984     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10985     if (!anElem)
10986       continue;
10987
10988     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10989     while ( nodeItr->more() )
10990     {
10991       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10992       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10993         continue;
10994       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10995       while ( backElemItr->more() )
10996       {
10997         const SMDS_MeshElement* curElem = backElemItr->next();
10998         if ( curElem && theElems.find(curElem) == theElems.end() &&
10999              ( bsc3d.get() ?
11000                isInside( curElem, *bsc3d, aTol ) :
11001                isInside( curElem, *aFaceClassifier, aTol )))
11002           theAffectedElems.insert( curElem );
11003       }
11004     }
11005   }
11006   return true;
11007 }
11008
11009 //================================================================================
11010 /*!
11011   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11012   \param theElems - group of of elements (edges or faces) to be replicated
11013   \param theNodesNot - group of nodes not to replicate
11014   \param theShape - shape to detect affected elements (element which geometric center
11015   located on or inside shape).
11016   The replicated nodes should be associated to affected elements.
11017   \return TRUE if operation has been completed successfully, FALSE otherwise
11018 */
11019 //================================================================================
11020
11021 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11022                                             const TIDSortedElemSet& theNodesNot,
11023                                             const TopoDS_Shape&     theShape )
11024 {
11025   if ( theShape.IsNull() )
11026     return false;
11027
11028   const double aTol = Precision::Confusion();
11029   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11030   auto_ptr<_FaceClassifier>              aFaceClassifier;
11031   if ( theShape.ShapeType() == TopAbs_SOLID )
11032   {
11033     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11034     bsc3d->PerformInfinitePoint(aTol);
11035   }
11036   else if (theShape.ShapeType() == TopAbs_FACE )
11037   {
11038     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11039   }
11040
11041   // iterates on indicated elements and get elements by back references from their nodes
11042   TIDSortedElemSet anAffected;
11043   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11044   for ( ;  elemItr != theElems.end(); ++elemItr )
11045   {
11046     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11047     if (!anElem)
11048       continue;
11049
11050     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11051     while ( nodeItr->more() )
11052     {
11053       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11054       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11055         continue;
11056       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11057       while ( backElemItr->more() )
11058       {
11059         const SMDS_MeshElement* curElem = backElemItr->next();
11060         if ( curElem && theElems.find(curElem) == theElems.end() &&
11061              ( bsc3d.get() ?
11062                isInside( curElem, *bsc3d, aTol ) :
11063                isInside( curElem, *aFaceClassifier, aTol )))
11064           anAffected.insert( curElem );
11065       }
11066     }
11067   }
11068   return DoubleNodes( theElems, theNodesNot, anAffected );
11069 }
11070
11071 /*!
11072  *  \brief compute an oriented angle between two planes defined by four points.
11073  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11074  *  @param p0 base of the rotation axe
11075  *  @param p1 extremity of the rotation axe
11076  *  @param g1 belongs to the first plane
11077  *  @param g2 belongs to the second plane
11078  */
11079 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11080 {
11081 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11082 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11083 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11084 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11085   gp_Vec vref(p0, p1);
11086   gp_Vec v1(p0, g1);
11087   gp_Vec v2(p0, g2);
11088   gp_Vec n1 = vref.Crossed(v1);
11089   gp_Vec n2 = vref.Crossed(v2);
11090   return n2.AngleWithRef(n1, vref);
11091 }
11092
11093 /*!
11094  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11095  * The list of groups must describe a partition of the mesh volumes.
11096  * The nodes of the internal faces at the boundaries of the groups are doubled.
11097  * In option, the internal faces are replaced by flat elements.
11098  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11099  * The flat elements are stored in groups of volumes.
11100  * @param theElems - list of groups of volumes, where a group of volume is a set of
11101  * SMDS_MeshElements sorted by Id.
11102  * @param createJointElems - if TRUE, create the elements
11103  * @return TRUE if operation has been completed successfully, FALSE otherwise
11104  */
11105 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11106                                                      bool createJointElems)
11107 {
11108   MESSAGE("----------------------------------------------");
11109   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11110   MESSAGE("----------------------------------------------");
11111
11112   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11113   meshDS->BuildDownWardConnectivity(true);
11114   CHRONO(50);
11115   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11116
11117   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11118   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11119   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11120
11121   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11122   std::map<int,int>celldom; // cell vtkId --> domain
11123   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11124   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11125   faceDomains.clear();
11126   celldom.clear();
11127   cellDomains.clear();
11128   nodeDomains.clear();
11129   std::map<int,int> emptyMap;
11130   std::set<int> emptySet;
11131   emptyMap.clear();
11132
11133   for (int idom = 0; idom < theElems.size(); idom++)
11134     {
11135
11136       // --- build a map (face to duplicate --> volume to modify)
11137       //     with all the faces shared by 2 domains (group of elements)
11138       //     and corresponding volume of this domain, for each shared face.
11139       //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11140
11141       //MESSAGE("Domain " << idom);
11142       const TIDSortedElemSet& domain = theElems[idom];
11143       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11144       for (; elemItr != domain.end(); ++elemItr)
11145         {
11146           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11147           if (!anElem)
11148             continue;
11149           int vtkId = anElem->getVtkId();
11150           //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11151           int neighborsVtkIds[NBMAXNEIGHBORS];
11152           int downIds[NBMAXNEIGHBORS];
11153           unsigned char downTypes[NBMAXNEIGHBORS];
11154           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11155           for (int n = 0; n < nbNeighbors; n++)
11156             {
11157               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11158               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11159               if (! domain.count(elem)) // neighbor is in another domain : face is shared
11160                 {
11161                   DownIdType face(downIds[n], downTypes[n]);
11162                   if (!faceDomains.count(face))
11163                     faceDomains[face] = emptyMap; // create an empty entry for face
11164                   if (!faceDomains[face].count(idom))
11165                     {
11166                       faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11167                       celldom[vtkId] = idom;
11168                       //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11169                     }
11170                 }
11171             }
11172         }
11173     }
11174
11175   //MESSAGE("Number of shared faces " << faceDomains.size());
11176   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11177
11178   // --- explore the shared faces domain by domain,
11179   //     explore the nodes of the face and see if they belong to a cell in the domain,
11180   //     which has only a node or an edge on the border (not a shared face)
11181
11182   for (int idomain = 0; idomain < theElems.size(); idomain++)
11183     {
11184       //MESSAGE("Domain " << idomain);
11185       const TIDSortedElemSet& domain = theElems[idomain];
11186       itface = faceDomains.begin();
11187       for (; itface != faceDomains.end(); ++itface)
11188         {
11189           std::map<int, int> domvol = itface->second;
11190           if (!domvol.count(idomain))
11191             continue;
11192           DownIdType face = itface->first;
11193           //MESSAGE(" --- face " << face.cellId);
11194           std::set<int> oldNodes;
11195           oldNodes.clear();
11196           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11197           std::set<int>::iterator itn = oldNodes.begin();
11198           for (; itn != oldNodes.end(); ++itn)
11199             {
11200               int oldId = *itn;
11201               //MESSAGE("     node " << oldId);
11202               vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11203               for (int i=0; i<l.ncells; i++)
11204                 {
11205                   int vtkId = l.cells[i];
11206                   const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11207                   if (!domain.count(anElem))
11208                     continue;
11209                   int vtkType = grid->GetCellType(vtkId);
11210                   int downId = grid->CellIdToDownId(vtkId);
11211                   if (downId < 0)
11212                     {
11213                       MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11214                       continue; // not OK at this stage of the algorithm:
11215                                 //no cells created after BuildDownWardConnectivity
11216                     }
11217                   DownIdType aCell(downId, vtkType);
11218                   if (!cellDomains.count(aCell))
11219                     cellDomains[aCell] = emptyMap; // create an empty entry for cell
11220                   cellDomains[aCell][idomain] = vtkId;
11221                   celldom[vtkId] = idomain;
11222                   //MESSAGE("       cell " << vtkId << " domain " << idomain);
11223                 }
11224             }
11225         }
11226     }
11227
11228   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11229   //     for each shared face, get the nodes
11230   //     for each node, for each domain of the face, create a clone of the node
11231
11232   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11233   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11234   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11235
11236   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11237   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11238   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11239
11240   for (int idomain = 0; idomain < theElems.size(); idomain++)
11241     {
11242       itface = faceDomains.begin();
11243       for (; itface != faceDomains.end(); ++itface)
11244         {
11245           std::map<int, int> domvol = itface->second;
11246           if (!domvol.count(idomain))
11247             continue;
11248           DownIdType face = itface->first;
11249           //MESSAGE(" --- face " << face.cellId);
11250           std::set<int> oldNodes;
11251           oldNodes.clear();
11252           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11253           std::set<int>::iterator itn = oldNodes.begin();
11254           for (; itn != oldNodes.end(); ++itn)
11255             {
11256               int oldId = *itn;
11257               //MESSAGE("-+-+-a node " << oldId);
11258               if (!nodeDomains.count(oldId))
11259                 nodeDomains[oldId] = emptyMap; // create an empty entry for node
11260               if (nodeDomains[oldId].empty())
11261                 {
11262                   nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11263                   //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11264                 }
11265               std::map<int, int>::iterator itdom = domvol.begin();
11266               for (; itdom != domvol.end(); ++itdom)
11267                 {
11268                   int idom = itdom->first;
11269                   //MESSAGE("         domain " << idom);
11270                   if (!nodeDomains[oldId].count(idom)) // --- node to clone
11271                     {
11272                       if (nodeDomains[oldId].size() >= 2) // a multiple node
11273                         {
11274                           vector<int> orderedDoms;
11275                           //MESSAGE("multiple node " << oldId);
11276                           if (mutipleNodes.count(oldId))
11277                             orderedDoms = mutipleNodes[oldId];
11278                           else
11279                             {
11280                               map<int,int>::iterator it = nodeDomains[oldId].begin();
11281                               for (; it != nodeDomains[oldId].end(); ++it)
11282                                 orderedDoms.push_back(it->first);
11283                             }
11284                           orderedDoms.push_back(idom); // TODO order ==> push_front or back
11285                           //stringstream txt;
11286                           //for (int i=0; i<orderedDoms.size(); i++)
11287                           //  txt << orderedDoms[i] << " ";
11288                           //MESSAGE("orderedDoms " << txt.str());
11289                           mutipleNodes[oldId] = orderedDoms;
11290                         }
11291                       double *coords = grid->GetPoint(oldId);
11292                       SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11293                       int newId = newNode->getVtkId();
11294                       nodeDomains[oldId][idom] = newId; // cloned node for other domains
11295                       //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11296                     }
11297                 }
11298             }
11299         }
11300     }
11301
11302   for (int idomain = 0; idomain < theElems.size(); idomain++)
11303     {
11304       itface = faceDomains.begin();
11305       for (; itface != faceDomains.end(); ++itface)
11306         {
11307           std::map<int, int> domvol = itface->second;
11308           if (!domvol.count(idomain))
11309             continue;
11310           DownIdType face = itface->first;
11311           //MESSAGE(" --- face " << face.cellId);
11312           std::set<int> oldNodes;
11313           oldNodes.clear();
11314           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11315           int nbMultipleNodes = 0;
11316           std::set<int>::iterator itn = oldNodes.begin();
11317           for (; itn != oldNodes.end(); ++itn)
11318             {
11319               int oldId = *itn;
11320               if (mutipleNodes.count(oldId))
11321                 nbMultipleNodes++;
11322             }
11323           if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11324             {
11325               //MESSAGE("multiple Nodes detected on a shared face");
11326               int downId = itface->first.cellId;
11327               unsigned char cellType = itface->first.cellType;
11328               // --- shared edge or shared face ?
11329               if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11330                 {
11331                   int nodes[3];
11332                   int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11333                   for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11334                     if (mutipleNodes.count(nodes[i]))
11335                       if (!mutipleNodesToFace.count(nodes[i]))
11336                         mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11337                 }
11338               else // shared face (between two volumes)
11339                 {
11340                   int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11341                   const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11342                   const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11343                   for (int ie =0; ie < nbEdges; ie++)
11344                     {
11345                       int nodes[3];
11346                       int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11347                       if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11348                         {
11349                           vector<int> vn0 = mutipleNodes[nodes[0]];
11350                           vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11351                           vector<int> doms;
11352                           for (int i0 = 0; i0 < vn0.size(); i0++)
11353                             for (int i1 = 0; i1 < vn1.size(); i1++)
11354                               if (vn0[i0] == vn1[i1])
11355                                 doms.push_back(vn0[i0]);
11356                           if (doms.size() >2)
11357                             {
11358                               //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11359                               double *coords = grid->GetPoint(nodes[0]);
11360                               gp_Pnt p0(coords[0], coords[1], coords[2]);
11361                               coords = grid->GetPoint(nodes[nbNodes - 1]);
11362                               gp_Pnt p1(coords[0], coords[1], coords[2]);
11363                               gp_Pnt gref;
11364                               int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11365                               map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11366                               map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11367                               int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11368                               for (int id=0; id < doms.size(); id++)
11369                                 {
11370                                   int idom = doms[id];
11371                                   for (int ivol=0; ivol<nbvol; ivol++)
11372                                     {
11373                                       int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11374                                       SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11375                                       if (theElems[idom].count(elem))
11376                                         {
11377                                           SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11378                                           domvol[idom] = svol;
11379                                           //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11380                                           double values[3];
11381                                           vtkIdType npts = 0;
11382                                           vtkIdType* pts = 0;
11383                                           grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11384                                           SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11385                                           if (id ==0)
11386                                             {
11387                                               gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11388                                               angleDom[idom] = 0;
11389                                             }
11390                                           else
11391                                             {
11392                                               gp_Pnt g(values[0], values[1], values[2]);
11393                                               angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11394                                               //MESSAGE("  angle=" << angleDom[idom]);
11395                                             }
11396                                           break;
11397                                         }
11398                                     }
11399                                 }
11400                               map<double, int> sortedDom; // sort domains by angle
11401                               for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11402                                 sortedDom[ia->second] = ia->first;
11403                               vector<int> vnodes;
11404                               vector<int> vdom;
11405                               for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11406                                 {
11407                                   vdom.push_back(ib->second);
11408                                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11409                                 }
11410                               for (int ino = 0; ino < nbNodes; ino++)
11411                                 vnodes.push_back(nodes[ino]);
11412                               edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11413                             }
11414                         }
11415                     }
11416                 }
11417             }
11418         }
11419     }
11420
11421   // --- iterate on shared faces (volumes to modify, face to extrude)
11422   //     get node id's of the face (id SMDS = id VTK)
11423   //     create flat element with old and new nodes if requested
11424
11425   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11426   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11427
11428   std::map<int, std::map<long,int> > nodeQuadDomains;
11429   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11430
11431   if (createJointElems)
11432     {
11433       int idg;
11434       string joints2DName = "joints2D";
11435       mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11436       SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11437       string joints3DName = "joints3D";
11438       mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11439       SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11440
11441       itface = faceDomains.begin();
11442       for (; itface != faceDomains.end(); ++itface)
11443         {
11444           DownIdType face = itface->first;
11445           std::set<int> oldNodes;
11446           std::set<int>::iterator itn;
11447           oldNodes.clear();
11448           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11449
11450           std::map<int, int> domvol = itface->second;
11451           std::map<int, int>::iterator itdom = domvol.begin();
11452           int dom1 = itdom->first;
11453           int vtkVolId = itdom->second;
11454           itdom++;
11455           int dom2 = itdom->first;
11456           SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11457                                                              nodeQuadDomains);
11458           stringstream grpname;
11459           grpname << "j_";
11460           if (dom1 < dom2)
11461             grpname << dom1 << "_" << dom2;
11462           else
11463             grpname << dom2 << "_" << dom1;
11464           string namegrp = grpname.str();
11465           if (!mapOfJunctionGroups.count(namegrp))
11466             mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11467           SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11468           if (sgrp)
11469             sgrp->Add(vol->GetID());
11470           if (vol->GetType() == SMDSAbs_Volume)
11471             joints3DGrp->Add(vol->GetID());
11472           else if (vol->GetType() == SMDSAbs_Face)
11473             joints2DGrp->Add(vol->GetID());
11474         }
11475     }
11476
11477   // --- create volumes on multiple domain intersection if requested
11478   //     iterate on mutipleNodesToFace
11479   //     iterate on edgesMultiDomains
11480
11481   if (createJointElems)
11482     {
11483       // --- iterate on mutipleNodesToFace
11484
11485       std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11486       for (; itn != mutipleNodesToFace.end(); ++itn)
11487         {
11488           int node = itn->first;
11489           vector<int> orderDom = itn->second;
11490           vector<vtkIdType> orderedNodes;
11491           for (int idom = 0; idom <orderDom.size(); idom++)
11492             orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11493             SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11494
11495             stringstream grpname;
11496             grpname << "m2j_";
11497             grpname << 0 << "_" << 0;
11498             int idg;
11499             string namegrp = grpname.str();
11500             if (!mapOfJunctionGroups.count(namegrp))
11501               mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11502             SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11503             if (sgrp)
11504               sgrp->Add(face->GetID());
11505         }
11506
11507       // --- iterate on edgesMultiDomains
11508
11509       std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11510       for (; ite != edgesMultiDomains.end(); ++ite)
11511         {
11512           vector<int> nodes = ite->first;
11513           vector<int> orderDom = ite->second;
11514           vector<vtkIdType> orderedNodes;
11515           if (nodes.size() == 2)
11516             {
11517               //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11518               for (int ino=0; ino < nodes.size(); ino++)
11519                 if (orderDom.size() == 3)
11520                   for (int idom = 0; idom <orderDom.size(); idom++)
11521                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11522                 else
11523                   for (int idom = orderDom.size()-1; idom >=0; idom--)
11524                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11525               SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11526
11527               int idg;
11528               string namegrp = "jointsMultiples";
11529               if (!mapOfJunctionGroups.count(namegrp))
11530                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11531               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11532               if (sgrp)
11533                 sgrp->Add(vol->GetID());
11534             }
11535           else
11536             {
11537               INFOS("Quadratic multiple joints not implemented");
11538               // TODO quadratic nodes
11539             }
11540         }
11541     }
11542
11543   // --- list the explicit faces and edges of the mesh that need to be modified,
11544   //     i.e. faces and edges built with one or more duplicated nodes.
11545   //     associate these faces or edges to their corresponding domain.
11546   //     only the first domain found is kept when a face or edge is shared
11547
11548   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11549   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11550   faceOrEdgeDom.clear();
11551   feDom.clear();
11552
11553   for (int idomain = 0; idomain < theElems.size(); idomain++)
11554     {
11555       std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11556       for (; itnod != nodeDomains.end(); ++itnod)
11557         {
11558           int oldId = itnod->first;
11559           //MESSAGE("     node " << oldId);
11560           vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11561           for (int i = 0; i < l.ncells; i++)
11562             {
11563               int vtkId = l.cells[i];
11564               int vtkType = grid->GetCellType(vtkId);
11565               int downId = grid->CellIdToDownId(vtkId);
11566               if (downId < 0)
11567                 continue; // new cells: not to be modified
11568               DownIdType aCell(downId, vtkType);
11569               int volParents[1000];
11570               int nbvol = grid->GetParentVolumes(volParents, vtkId);
11571               for (int j = 0; j < nbvol; j++)
11572                 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11573                   if (!feDom.count(vtkId))
11574                     {
11575                       feDom[vtkId] = idomain;
11576                       faceOrEdgeDom[aCell] = emptyMap;
11577                       faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11578                       //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11579                       //        << " type " << vtkType << " downId " << downId);
11580                     }
11581             }
11582         }
11583     }
11584
11585   // --- iterate on shared faces (volumes to modify, face to extrude)
11586   //     get node id's of the face
11587   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11588
11589   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11590   for (int m=0; m<3; m++)
11591     {
11592       std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11593       itface = (*amap).begin();
11594       for (; itface != (*amap).end(); ++itface)
11595         {
11596           DownIdType face = itface->first;
11597           std::set<int> oldNodes;
11598           std::set<int>::iterator itn;
11599           oldNodes.clear();
11600           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11601           //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11602           std::map<int, int> localClonedNodeIds;
11603
11604           std::map<int, int> domvol = itface->second;
11605           std::map<int, int>::iterator itdom = domvol.begin();
11606           for (; itdom != domvol.end(); ++itdom)
11607             {
11608               int idom = itdom->first;
11609               int vtkVolId = itdom->second;
11610               //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11611               localClonedNodeIds.clear();
11612               for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11613                 {
11614                   int oldId = *itn;
11615                   if (nodeDomains[oldId].count(idom))
11616                     {
11617                       localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11618                       //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11619                     }
11620                 }
11621               meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11622             }
11623         }
11624     }
11625
11626   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11627   grid->BuildLinks();
11628
11629   CHRONOSTOP(50);
11630   counters::stats();
11631   return true;
11632 }
11633
11634 /*!
11635  * \brief Double nodes on some external faces and create flat elements.
11636  * Flat elements are mainly used by some types of mechanic calculations.
11637  *
11638  * Each group of the list must be constituted of faces.
11639  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11640  * @param theElems - list of groups of faces, where a group of faces is a set of
11641  * SMDS_MeshElements sorted by Id.
11642  * @return TRUE if operation has been completed successfully, FALSE otherwise
11643  */
11644 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11645 {
11646   MESSAGE("-------------------------------------------------");
11647   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11648   MESSAGE("-------------------------------------------------");
11649
11650   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11651
11652   // --- For each group of faces
11653   //     duplicate the nodes, create a flat element based on the face
11654   //     replace the nodes of the faces by their clones
11655
11656   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11657   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11658   clonedNodes.clear();
11659   intermediateNodes.clear();
11660   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11661   mapOfJunctionGroups.clear();
11662
11663   for (int idom = 0; idom < theElems.size(); idom++)
11664     {
11665       const TIDSortedElemSet& domain = theElems[idom];
11666       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11667       for (; elemItr != domain.end(); ++elemItr)
11668         {
11669           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11670           SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11671           if (!aFace)
11672             continue;
11673           // MESSAGE("aFace=" << aFace->GetID());
11674           bool isQuad = aFace->IsQuadratic();
11675           vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11676
11677           // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11678
11679           SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11680           while (nodeIt->more())
11681             {
11682               const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11683               bool isMedium = isQuad && (aFace->IsMediumNode(node));
11684               if (isMedium)
11685                 ln2.push_back(node);
11686               else
11687                 ln0.push_back(node);
11688
11689               const SMDS_MeshNode* clone = 0;
11690               if (!clonedNodes.count(node))
11691                 {
11692                   clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11693                   clonedNodes[node] = clone;
11694                 }
11695               else
11696                 clone = clonedNodes[node];
11697
11698               if (isMedium)
11699                 ln3.push_back(clone);
11700               else
11701                 ln1.push_back(clone);
11702
11703               const SMDS_MeshNode* inter = 0;
11704               if (isQuad && (!isMedium))
11705                 {
11706                   if (!intermediateNodes.count(node))
11707                     {
11708                       inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11709                       intermediateNodes[node] = inter;
11710                     }
11711                   else
11712                     inter = intermediateNodes[node];
11713                   ln4.push_back(inter);
11714                 }
11715             }
11716
11717           // --- extrude the face
11718
11719           vector<const SMDS_MeshNode*> ln;
11720           SMDS_MeshVolume* vol = 0;
11721           vtkIdType aType = aFace->GetVtkType();
11722           switch (aType)
11723           {
11724             case VTK_TRIANGLE:
11725               vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11726               // MESSAGE("vol prism " << vol->GetID());
11727               ln.push_back(ln1[0]);
11728               ln.push_back(ln1[1]);
11729               ln.push_back(ln1[2]);
11730               break;
11731             case VTK_QUAD:
11732               vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11733               // MESSAGE("vol hexa " << vol->GetID());
11734               ln.push_back(ln1[0]);
11735               ln.push_back(ln1[1]);
11736               ln.push_back(ln1[2]);
11737               ln.push_back(ln1[3]);
11738               break;
11739             case VTK_QUADRATIC_TRIANGLE:
11740               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11741                                       ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11742               // MESSAGE("vol quad prism " << vol->GetID());
11743               ln.push_back(ln1[0]);
11744               ln.push_back(ln1[1]);
11745               ln.push_back(ln1[2]);
11746               ln.push_back(ln3[0]);
11747               ln.push_back(ln3[1]);
11748               ln.push_back(ln3[2]);
11749               break;
11750             case VTK_QUADRATIC_QUAD:
11751 //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11752 //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11753 //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11754               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11755                                       ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11756                                       ln4[0], ln4[1], ln4[2], ln4[3]);
11757               // MESSAGE("vol quad hexa " << vol->GetID());
11758               ln.push_back(ln1[0]);
11759               ln.push_back(ln1[1]);
11760               ln.push_back(ln1[2]);
11761               ln.push_back(ln1[3]);
11762               ln.push_back(ln3[0]);
11763               ln.push_back(ln3[1]);
11764               ln.push_back(ln3[2]);
11765               ln.push_back(ln3[3]);
11766               break;
11767             case VTK_POLYGON:
11768               break;
11769             default:
11770               break;
11771           }
11772
11773           if (vol)
11774             {
11775               stringstream grpname;
11776               grpname << "jf_";
11777               grpname << idom;
11778               int idg;
11779               string namegrp = grpname.str();
11780               if (!mapOfJunctionGroups.count(namegrp))
11781                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11782               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11783               if (sgrp)
11784                 sgrp->Add(vol->GetID());
11785             }
11786
11787           // --- modify the face
11788
11789           aFace->ChangeNodes(&ln[0], ln.size());
11790         }
11791     }
11792   return true;
11793 }
11794
11795 /*!
11796  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11797  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11798  *  groups of faces to remove inside the object, (idem edges).
11799  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11800  */
11801 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11802                                       const TopoDS_Shape& theShape,
11803                                       SMESH_NodeSearcher* theNodeSearcher,
11804                                       const char* groupName,
11805                                       std::vector<double>&   nodesCoords,
11806                                       std::vector<std::vector<int> >& listOfListOfNodes)
11807 {
11808   MESSAGE("--------------------------------");
11809   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11810   MESSAGE("--------------------------------");
11811
11812   // --- zone of volumes to remove is given :
11813   //     1 either by a geom shape (one or more vertices) and a radius,
11814   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11815   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11816   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11817   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11818   //     defined by it's name.
11819
11820   SMESHDS_GroupBase* groupDS = 0;
11821   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11822   while ( groupIt->more() )
11823     {
11824       groupDS = 0;
11825       SMESH_Group * group = groupIt->next();
11826       if ( !group ) continue;
11827       groupDS = group->GetGroupDS();
11828       if ( !groupDS || groupDS->IsEmpty() ) continue;
11829       std::string grpName = group->GetName();
11830       //MESSAGE("grpName=" << grpName);
11831       if (grpName == groupName)
11832         break;
11833       else
11834         groupDS = 0;
11835     }
11836
11837   bool isNodeGroup = false;
11838   bool isNodeCoords = false;
11839   if (groupDS)
11840     {
11841       if (groupDS->GetType() != SMDSAbs_Node)
11842         return;
11843       isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11844     }
11845
11846   if (nodesCoords.size() > 0)
11847     isNodeCoords = true; // a list o nodes given by their coordinates
11848   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11849
11850   // --- define groups to build
11851
11852   int idg; // --- group of SMDS volumes
11853   string grpvName = groupName;
11854   grpvName += "_vol";
11855   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11856   if (!grp)
11857     {
11858       MESSAGE("group not created " << grpvName);
11859       return;
11860     }
11861   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11862
11863   int idgs; // --- group of SMDS faces on the skin
11864   string grpsName = groupName;
11865   grpsName += "_skin";
11866   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11867   if (!grps)
11868     {
11869       MESSAGE("group not created " << grpsName);
11870       return;
11871     }
11872   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11873
11874   int idgi; // --- group of SMDS faces internal (several shapes)
11875   string grpiName = groupName;
11876   grpiName += "_internalFaces";
11877   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11878   if (!grpi)
11879     {
11880       MESSAGE("group not created " << grpiName);
11881       return;
11882     }
11883   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11884
11885   int idgei; // --- group of SMDS faces internal (several shapes)
11886   string grpeiName = groupName;
11887   grpeiName += "_internalEdges";
11888   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11889   if (!grpei)
11890     {
11891       MESSAGE("group not created " << grpeiName);
11892       return;
11893     }
11894   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11895
11896   // --- build downward connectivity
11897
11898   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11899   meshDS->BuildDownWardConnectivity(true);
11900   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11901
11902   // --- set of volumes detected inside
11903
11904   std::set<int> setOfInsideVol;
11905   std::set<int> setOfVolToCheck;
11906
11907   std::vector<gp_Pnt> gpnts;
11908   gpnts.clear();
11909
11910   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11911     {
11912       MESSAGE("group of nodes provided");
11913       SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11914       while ( elemIt->more() )
11915         {
11916           const SMDS_MeshElement* elem = elemIt->next();
11917           if (!elem)
11918             continue;
11919           const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11920           if (!node)
11921             continue;
11922           SMDS_MeshElement* vol = 0;
11923           SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11924           while (volItr->more())
11925             {
11926               vol = (SMDS_MeshElement*)volItr->next();
11927               setOfInsideVol.insert(vol->getVtkId());
11928               sgrp->Add(vol->GetID());
11929             }
11930         }
11931     }
11932   else if (isNodeCoords)
11933     {
11934       MESSAGE("list of nodes coordinates provided");
11935       int i = 0;
11936       int k = 0;
11937       while (i < nodesCoords.size()-2)
11938         {
11939           double x = nodesCoords[i++];
11940           double y = nodesCoords[i++];
11941           double z = nodesCoords[i++];
11942           gp_Pnt p = gp_Pnt(x, y ,z);
11943           gpnts.push_back(p);
11944           MESSAGE("TopoDS_Vertex " << k++ << " " << p.X() << " " << p.Y() << " " << p.Z());
11945         }
11946     }
11947   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11948     {
11949       MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11950       TopTools_IndexedMapOfShape vertexMap;
11951       TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11952       gp_Pnt p = gp_Pnt(0,0,0);
11953       if (vertexMap.Extent() < 1)
11954         return;
11955
11956       for ( int i = 1; i <= vertexMap.Extent(); ++i )
11957         {
11958           const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11959           p = BRep_Tool::Pnt(vertex);
11960           gpnts.push_back(p);
11961           MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11962         }
11963     }
11964
11965   if (gpnts.size() > 0)
11966     {
11967       int nodeId = 0;
11968       const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11969       if (startNode)
11970         nodeId = startNode->GetID();
11971       MESSAGE("nodeId " << nodeId);
11972
11973       double radius2 = radius*radius;
11974       MESSAGE("radius2 " << radius2);
11975
11976       // --- volumes on start node
11977
11978       setOfVolToCheck.clear();
11979       SMDS_MeshElement* startVol = 0;
11980       SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11981       while (volItr->more())
11982         {
11983           startVol = (SMDS_MeshElement*)volItr->next();
11984           setOfVolToCheck.insert(startVol->getVtkId());
11985         }
11986       if (setOfVolToCheck.empty())
11987         {
11988           MESSAGE("No volumes found");
11989           return;
11990         }
11991
11992       // --- starting with central volumes then their neighbors, check if they are inside
11993       //     or outside the domain, until no more new neighbor volume is inside.
11994       //     Fill the group of inside volumes
11995
11996       std::map<int, double> mapOfNodeDistance2;
11997       mapOfNodeDistance2.clear();
11998       std::set<int> setOfOutsideVol;
11999       while (!setOfVolToCheck.empty())
12000         {
12001           std::set<int>::iterator it = setOfVolToCheck.begin();
12002           int vtkId = *it;
12003           MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12004           bool volInside = false;
12005           vtkIdType npts = 0;
12006           vtkIdType* pts = 0;
12007           grid->GetCellPoints(vtkId, npts, pts);
12008           for (int i=0; i<npts; i++)
12009             {
12010               double distance2 = 0;
12011               if (mapOfNodeDistance2.count(pts[i]))
12012                 {
12013                   distance2 = mapOfNodeDistance2[pts[i]];
12014                   MESSAGE("point " << pts[i] << " distance2 " << distance2);
12015                 }
12016               else
12017                 {
12018                   double *coords = grid->GetPoint(pts[i]);
12019                   gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12020                   distance2 = 1.E40;
12021                   for (int j=0; j<gpnts.size(); j++)
12022                     {
12023                       double d2 = aPoint.SquareDistance(gpnts[j]);
12024                       if (d2 < distance2)
12025                         {
12026                           distance2 = d2;
12027                           if (distance2 < radius2)
12028                             break;
12029                         }
12030                     }
12031                   mapOfNodeDistance2[pts[i]] = distance2;
12032                   MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12033                 }
12034               if (distance2 < radius2)
12035                 {
12036                   volInside = true; // one or more nodes inside the domain
12037                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12038                   break;
12039                 }
12040             }
12041           if (volInside)
12042             {
12043               setOfInsideVol.insert(vtkId);
12044               MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12045               int neighborsVtkIds[NBMAXNEIGHBORS];
12046               int downIds[NBMAXNEIGHBORS];
12047               unsigned char downTypes[NBMAXNEIGHBORS];
12048               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12049               for (int n = 0; n < nbNeighbors; n++)
12050                 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12051                   setOfVolToCheck.insert(neighborsVtkIds[n]);
12052             }
12053           else
12054             {
12055               setOfOutsideVol.insert(vtkId);
12056               MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12057             }
12058           setOfVolToCheck.erase(vtkId);
12059         }
12060     }
12061
12062   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12063   //     If yes, add the volume to the inside set
12064
12065   bool addedInside = true;
12066   std::set<int> setOfVolToReCheck;
12067   while (addedInside)
12068     {
12069       MESSAGE(" --------------------------- re check");
12070       addedInside = false;
12071       std::set<int>::iterator itv = setOfInsideVol.begin();
12072       for (; itv != setOfInsideVol.end(); ++itv)
12073         {
12074           int vtkId = *itv;
12075           int neighborsVtkIds[NBMAXNEIGHBORS];
12076           int downIds[NBMAXNEIGHBORS];
12077           unsigned char downTypes[NBMAXNEIGHBORS];
12078           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12079           for (int n = 0; n < nbNeighbors; n++)
12080             if (!setOfInsideVol.count(neighborsVtkIds[n]))
12081               setOfVolToReCheck.insert(neighborsVtkIds[n]);
12082         }
12083       setOfVolToCheck = setOfVolToReCheck;
12084       setOfVolToReCheck.clear();
12085       while  (!setOfVolToCheck.empty())
12086         {
12087           std::set<int>::iterator it = setOfVolToCheck.begin();
12088           int vtkId = *it;
12089           if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12090             {
12091               MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12092               int countInside = 0;
12093               int neighborsVtkIds[NBMAXNEIGHBORS];
12094               int downIds[NBMAXNEIGHBORS];
12095               unsigned char downTypes[NBMAXNEIGHBORS];
12096               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12097               for (int n = 0; n < nbNeighbors; n++)
12098                 if (setOfInsideVol.count(neighborsVtkIds[n]))
12099                   countInside++;
12100               MESSAGE("countInside " << countInside);
12101               if (countInside > 1)
12102                 {
12103                   MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12104                   setOfInsideVol.insert(vtkId);
12105                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12106                   addedInside = true;
12107                 }
12108               else
12109                 setOfVolToReCheck.insert(vtkId);
12110             }
12111           setOfVolToCheck.erase(vtkId);
12112         }
12113     }
12114
12115   // --- map of Downward faces at the boundary, inside the global volume
12116   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12117   //     fill group of SMDS faces inside the volume (when several volume shapes)
12118   //     fill group of SMDS faces on the skin of the global volume (if skin)
12119
12120   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12121   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12122   std::set<int>::iterator it = setOfInsideVol.begin();
12123   for (; it != setOfInsideVol.end(); ++it)
12124     {
12125       int vtkId = *it;
12126       //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12127       int neighborsVtkIds[NBMAXNEIGHBORS];
12128       int downIds[NBMAXNEIGHBORS];
12129       unsigned char downTypes[NBMAXNEIGHBORS];
12130       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12131       for (int n = 0; n < nbNeighbors; n++)
12132         {
12133           int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12134           if (neighborDim == 3)
12135             {
12136               if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12137                 {
12138                   DownIdType face(downIds[n], downTypes[n]);
12139                   boundaryFaces[face] = vtkId;
12140                 }
12141               // if the face between to volumes is in the mesh, get it (internal face between shapes)
12142               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12143               if (vtkFaceId >= 0)
12144                 {
12145                   sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12146                   // find also the smds edges on this face
12147                   int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12148                   const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12149                   const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12150                   for (int i = 0; i < nbEdges; i++)
12151                     {
12152                       int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12153                       if (vtkEdgeId >= 0)
12154                         sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12155                     }
12156                 }
12157             }
12158           else if (neighborDim == 2) // skin of the volume
12159             {
12160               DownIdType face(downIds[n], downTypes[n]);
12161               skinFaces[face] = vtkId;
12162               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12163               if (vtkFaceId >= 0)
12164                 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12165             }
12166         }
12167     }
12168
12169   // --- identify the edges constituting the wire of each subshape on the skin
12170   //     define polylines with the nodes of edges, equivalent to wires
12171   //     project polylines on subshapes, and partition, to get geom faces
12172
12173   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12174   std::set<int> emptySet;
12175   emptySet.clear();
12176   std::set<int> shapeIds;
12177
12178   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12179   while (itelem->more())
12180     {
12181       const SMDS_MeshElement *elem = itelem->next();
12182       int shapeId = elem->getshapeId();
12183       int vtkId = elem->getVtkId();
12184       if (!shapeIdToVtkIdSet.count(shapeId))
12185         {
12186           shapeIdToVtkIdSet[shapeId] = emptySet;
12187           shapeIds.insert(shapeId);
12188         }
12189       shapeIdToVtkIdSet[shapeId].insert(vtkId);
12190     }
12191
12192   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12193   std::set<DownIdType, DownIdCompare> emptyEdges;
12194   emptyEdges.clear();
12195
12196   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12197   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12198     {
12199       int shapeId = itShape->first;
12200       MESSAGE(" --- Shape ID --- "<< shapeId);
12201       shapeIdToEdges[shapeId] = emptyEdges;
12202
12203       std::vector<int> nodesEdges;
12204
12205       std::set<int>::iterator its = itShape->second.begin();
12206       for (; its != itShape->second.end(); ++its)
12207         {
12208           int vtkId = *its;
12209           MESSAGE("     " << vtkId);
12210           int neighborsVtkIds[NBMAXNEIGHBORS];
12211           int downIds[NBMAXNEIGHBORS];
12212           unsigned char downTypes[NBMAXNEIGHBORS];
12213           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12214           for (int n = 0; n < nbNeighbors; n++)
12215             {
12216               if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12217                 continue;
12218               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12219               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12220               if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12221                 {
12222                   DownIdType edge(downIds[n], downTypes[n]);
12223                   if (!shapeIdToEdges[shapeId].count(edge))
12224                     {
12225                       shapeIdToEdges[shapeId].insert(edge);
12226                       int vtkNodeId[3];
12227                       int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12228                       nodesEdges.push_back(vtkNodeId[0]);
12229                       nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12230                       MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12231                     }
12232                 }
12233             }
12234         }
12235
12236       std::list<int> order;
12237       order.clear();
12238       if (nodesEdges.size() > 0)
12239         {
12240           order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12241           nodesEdges[0] = -1;
12242           order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
12243           nodesEdges[1] = -1; // do not reuse this edge
12244           bool found = true;
12245           while (found)
12246             {
12247               int nodeTofind = order.back(); // try first to push back
12248               int i = 0;
12249               for (i = 0; i<nodesEdges.size(); i++)
12250                 if (nodesEdges[i] == nodeTofind)
12251                   break;
12252               if (i == nodesEdges.size())
12253                 found = false; // no follower found on back
12254               else
12255                 {
12256                   if (i%2) // odd ==> use the previous one
12257                     if (nodesEdges[i-1] < 0)
12258                       found = false;
12259                     else
12260                       {
12261                         order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
12262                         nodesEdges[i-1] = -1;
12263                       }
12264                   else // even ==> use the next one
12265                     if (nodesEdges[i+1] < 0)
12266                       found = false;
12267                     else
12268                       {
12269                         order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
12270                         nodesEdges[i+1] = -1;
12271                       }
12272                 }
12273               if (found)
12274                 continue;
12275               // try to push front
12276               found = true;
12277               nodeTofind = order.front(); // try to push front
12278               for (i = 0; i<nodesEdges.size(); i++)
12279                 if (nodesEdges[i] == nodeTofind)
12280                   break;
12281               if (i == nodesEdges.size())
12282                 {
12283                   found = false; // no predecessor found on front
12284                   continue;
12285                 }
12286               if (i%2) // odd ==> use the previous one
12287                 if (nodesEdges[i-1] < 0)
12288                   found = false;
12289                 else
12290                   {
12291                     order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
12292                     nodesEdges[i-1] = -1;
12293                   }
12294               else // even ==> use the next one
12295                 if (nodesEdges[i+1] < 0)
12296                   found = false;
12297                 else
12298                   {
12299                     order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
12300                     nodesEdges[i+1] = -1;
12301                   }
12302             }
12303         }
12304
12305
12306       std::vector<int> nodes;
12307       nodes.push_back(shapeId);
12308       std::list<int>::iterator itl = order.begin();
12309       for (; itl != order.end(); itl++)
12310         {
12311           nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12312           MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12313         }
12314       listOfListOfNodes.push_back(nodes);
12315     }
12316
12317   //     partition geom faces with blocFissure
12318   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12319   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12320
12321   return;
12322 }
12323
12324
12325 //================================================================================
12326 /*!
12327  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12328  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12329  * \return TRUE if operation has been completed successfully, FALSE otherwise
12330  */
12331 //================================================================================
12332
12333 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12334 {
12335   // iterates on volume elements and detect all free faces on them
12336   SMESHDS_Mesh* aMesh = GetMeshDS();
12337   if (!aMesh)
12338     return false;
12339   //bool res = false;
12340   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12341   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12342   while(vIt->more())
12343   {
12344     const SMDS_MeshVolume* volume = vIt->next();
12345     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12346     vTool.SetExternalNormal();
12347     //const bool isPoly = volume->IsPoly();
12348     const int iQuad = volume->IsQuadratic();
12349     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12350     {
12351       if (!vTool.IsFreeFace(iface))
12352         continue;
12353       nbFree++;
12354       vector<const SMDS_MeshNode *> nodes;
12355       int nbFaceNodes = vTool.NbFaceNodes(iface);
12356       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12357       int inode = 0;
12358       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12359         nodes.push_back(faceNodes[inode]);
12360       if (iQuad) { // add medium nodes
12361         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12362           nodes.push_back(faceNodes[inode]);
12363         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12364           nodes.push_back(faceNodes[8]);
12365       }
12366       // add new face based on volume nodes
12367       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12368         nbExisted++;
12369         continue; // face already exsist
12370       }
12371       AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12372       nbCreated++;
12373     }
12374   }
12375   return ( nbFree==(nbExisted+nbCreated) );
12376 }
12377
12378 namespace
12379 {
12380   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12381   {
12382     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12383       return n;
12384     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12385   }
12386 }
12387 //================================================================================
12388 /*!
12389  * \brief Creates missing boundary elements
12390  *  \param elements - elements whose boundary is to be checked
12391  *  \param dimension - defines type of boundary elements to create
12392  *  \param group - a group to store created boundary elements in
12393  *  \param targetMesh - a mesh to store created boundary elements in
12394  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12395  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12396  *                                boundary elements will be copied into the targetMesh
12397  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12398  *                                boundary elements will be added into the new group
12399  *  \param aroundElements - if true, elements will be created on boundary of given
12400  *                          elements else, on boundary of the whole mesh.
12401  * \return nb of added boundary elements
12402  */
12403 //================================================================================
12404
12405 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12406                                        Bnd_Dimension           dimension,
12407                                        SMESH_Group*            group/*=0*/,
12408                                        SMESH_Mesh*             targetMesh/*=0*/,
12409                                        bool                    toCopyElements/*=false*/,
12410                                        bool                    toCopyExistingBoundary/*=false*/,
12411                                        bool                    toAddExistingBondary/*= false*/,
12412                                        bool                    aroundElements/*= false*/)
12413 {
12414   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12415   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12416   // hope that all elements are of the same type, do not check them all
12417   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12418     throw SALOME_Exception(LOCALIZED("wrong element type"));
12419
12420   if ( !targetMesh )
12421     toCopyElements = toCopyExistingBoundary = false;
12422
12423   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12424   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12425   int nbAddedBnd = 0;
12426
12427   // editor adding present bnd elements and optionally holding elements to add to the group
12428   SMESH_MeshEditor* presentEditor;
12429   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12430   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12431
12432   SMESH_MesherHelper helper( *myMesh );
12433   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12434   SMDS_VolumeTool vTool;
12435   TIDSortedElemSet avoidSet;
12436   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12437   int inode;
12438
12439   typedef vector<const SMDS_MeshNode*> TConnectivity;
12440
12441   SMDS_ElemIteratorPtr eIt;
12442   if (elements.empty())
12443     eIt = aMesh->elementsIterator(elemType);
12444   else
12445     eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12446
12447   while (eIt->more())
12448   {
12449     const SMDS_MeshElement* elem = eIt->next();
12450     const int iQuad = elem->IsQuadratic();
12451
12452     // ------------------------------------------------------------------------------------
12453     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12454     // ------------------------------------------------------------------------------------
12455     vector<const SMDS_MeshElement*> presentBndElems;
12456     vector<TConnectivity>           missingBndElems;
12457     TConnectivity nodes;
12458     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12459     {
12460       vTool.SetExternalNormal();
12461       const SMDS_MeshElement* otherVol = 0;
12462       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12463       {
12464         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12465              ( !aroundElements || elements.count( otherVol )))
12466           continue;
12467         const int nbFaceNodes = vTool.NbFaceNodes(iface);
12468         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12469         if ( missType == SMDSAbs_Edge ) // boundary edges
12470         {
12471           nodes.resize( 2+iQuad );
12472           for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12473           {
12474             for ( int j = 0; j < nodes.size(); ++j )
12475               nodes[j] =nn[i+j];
12476             if ( const SMDS_MeshElement* edge =
12477                  aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12478               presentBndElems.push_back( edge );
12479             else
12480               missingBndElems.push_back( nodes );
12481           }
12482         }
12483         else // boundary face
12484         {
12485           nodes.clear();
12486           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12487             nodes.push_back( nn[inode] );
12488           if (iQuad) // add medium nodes
12489             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12490               nodes.push_back( nn[inode] );
12491           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12492           if ( iCenter > 0 )
12493             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12494
12495           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12496                                                                SMDSAbs_Face, /*noMedium=*/false ))
12497             presentBndElems.push_back( f );
12498           else
12499             missingBndElems.push_back( nodes );
12500
12501           if ( targetMesh != myMesh )
12502           {
12503             // add 1D elements on face boundary to be added to a new mesh
12504             const SMDS_MeshElement* edge;
12505             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12506             {
12507               if ( iQuad )
12508                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12509               else
12510                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12511               if ( edge && avoidSet.insert( edge ).second )
12512                 presentBndElems.push_back( edge );
12513             }
12514           }
12515         }
12516       }
12517     }
12518     else                     // elem is a face ------------------------------------------
12519     {
12520       avoidSet.clear(), avoidSet.insert( elem );
12521       int nbNodes = elem->NbCornerNodes();
12522       nodes.resize( 2 /*+ iQuad*/);
12523       for ( int i = 0; i < nbNodes; i++ )
12524       {
12525         nodes[0] = elem->GetNode(i);
12526         nodes[1] = elem->GetNode((i+1)%nbNodes);
12527         if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12528           continue; // not free link
12529
12530         //if ( iQuad )
12531         //nodes[2] = elem->GetNode( i + nbNodes );
12532         if ( const SMDS_MeshElement* edge =
12533              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
12534           presentBndElems.push_back( edge );
12535         else
12536           missingBndElems.push_back( nodes );
12537       }
12538     }
12539
12540     // ---------------------------------
12541     // 2. Add missing boundary elements
12542     // ---------------------------------
12543     if ( targetMesh != myMesh )
12544       // instead of making a map of nodes in this mesh and targetMesh,
12545       // we create nodes with same IDs.
12546       for ( int i = 0; i < missingBndElems.size(); ++i )
12547       {
12548         TConnectivity& srcNodes = missingBndElems[i];
12549         TConnectivity  nodes( srcNodes.size() );
12550         for ( inode = 0; inode < nodes.size(); ++inode )
12551           nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12552         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12553                                                                    missType,
12554                                                                    /*noMedium=*/false))
12555           continue;
12556         tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12557         ++nbAddedBnd;
12558       }
12559     else
12560       for ( int i = 0; i < missingBndElems.size(); ++i )
12561       {
12562         TConnectivity& nodes = missingBndElems[i];
12563         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12564                                                                    missType,
12565                                                                    /*noMedium=*/false))
12566           continue;
12567         SMDS_MeshElement* elem =
12568           tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12569         ++nbAddedBnd;
12570
12571         // try to set a new element to a shape
12572         if ( myMesh->HasShapeToMesh() )
12573         {
12574           bool ok = true;
12575           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12576           const int nbN = nodes.size() / (iQuad+1 );
12577           for ( inode = 0; inode < nbN && ok; ++inode )
12578           {
12579             pair<int, TopAbs_ShapeEnum> i_stype =
12580               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12581             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12582               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12583           }
12584           if ( ok && mediumShapes.size() > 1 )
12585           {
12586             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12587             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12588             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12589             {
12590               if (( ok = ( stype_i->first != stype_i_0.first )))
12591                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12592                                         aMesh->IndexToShape( stype_i_0.second ));
12593             }
12594           }
12595           if ( ok && mediumShapes.begin()->first == missShapeType )
12596             aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12597         }
12598       }
12599
12600     // ----------------------------------
12601     // 3. Copy present boundary elements
12602     // ----------------------------------
12603     if ( toCopyExistingBoundary )
12604       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12605       {
12606         const SMDS_MeshElement* e = presentBndElems[i];
12607         TConnectivity nodes( e->NbNodes() );
12608         for ( inode = 0; inode < nodes.size(); ++inode )
12609           nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12610         presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12611       }
12612     else // store present elements to add them to a group
12613       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12614       {
12615         presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12616       }
12617
12618   } // loop on given elements
12619
12620   // ---------------------------------------------
12621   // 4. Fill group with boundary elements
12622   // ---------------------------------------------
12623   if ( group )
12624   {
12625     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12626       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12627         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12628   }
12629   tgtEditor.myLastCreatedElems.Clear();
12630   tgtEditor2.myLastCreatedElems.Clear();
12631
12632   // -----------------------
12633   // 5. Copy given elements
12634   // -----------------------
12635   if ( toCopyElements && targetMesh != myMesh )
12636   {
12637     if (elements.empty())
12638       eIt = aMesh->elementsIterator(elemType);
12639     else
12640       eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12641     while (eIt->more())
12642     {
12643       const SMDS_MeshElement* elem = eIt->next();
12644       TConnectivity nodes( elem->NbNodes() );
12645       for ( inode = 0; inode < nodes.size(); ++inode )
12646         nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12647       tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12648
12649       tgtEditor.myLastCreatedElems.Clear();
12650     }
12651   }
12652   return nbAddedBnd;
12653 }