Salome HOME
acd27c3e55067744a71818b1a94df711cd4dc50e
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2011  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 // SMESH SMESH : idl implementation based on 'SMESH' unit's classes
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 <gp.hxx>
81 #include <gp_Ax1.hxx>
82 #include <gp_Dir.hxx>
83 #include <gp_Lin.hxx>
84 #include <gp_Pln.hxx>
85 #include <gp_Trsf.hxx>
86 #include <gp_Vec.hxx>
87 #include <gp_XY.hxx>
88 #include <gp_XYZ.hxx>
89
90 #include <cmath>
91
92 #include <map>
93 #include <set>
94 #include <numeric>
95 #include <limits>
96 #include <algorithm>
97 #include <sstream>
98
99 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
100
101 using namespace std;
102 using namespace SMESH::Controls;
103
104 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshNode*> >    TElemOfNodeListMap;
105 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshElement*> > TElemOfElemListMap;
106
107 typedef SMDS_SetIterator< SMDS_pElement, TIDSortedElemSet::const_iterator> TSetIterator;
108
109 //=======================================================================
110 //function : SMESH_MeshEditor
111 //purpose  :
112 //=======================================================================
113
114 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
115   :myMesh( theMesh ) // theMesh may be NULL
116 {
117 }
118
119 //=======================================================================
120 /*!
121  * \brief Add element
122  */
123 //=======================================================================
124
125 SMDS_MeshElement*
126 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
127                              const SMDSAbs_ElementType            type,
128                              const bool                           isPoly,
129                              const int                            ID)
130 {
131   //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
132   SMDS_MeshElement* e = 0;
133   int nbnode = node.size();
134   SMESHDS_Mesh* mesh = GetMeshDS();
135   switch ( type ) {
136   case SMDSAbs_Face:
137     if ( !isPoly ) {
138       if      (nbnode == 3) {
139         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
140         else           e = mesh->AddFace      (node[0], node[1], node[2] );
141       }
142       else if (nbnode == 4) {
143         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
144         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
145       }
146       else if (nbnode == 6) {
147         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
148                                                node[4], node[5], ID);
149         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
150                                                node[4], node[5] );
151       }
152       else if (nbnode == 8) {
153         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
154                                                node[4], node[5], node[6], node[7], ID);
155         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
156                                                node[4], node[5], node[6], node[7] );
157       }
158       else if (nbnode == 9) {
159         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
160                                                node[4], node[5], node[6], node[7], node[8], ID);
161         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
162                                                node[4], node[5], node[6], node[7], node[8] );
163       }
164     } else {
165       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
166       else           e = mesh->AddPolygonalFace      (node    );
167     }
168     break;
169
170   case SMDSAbs_Volume:
171     if ( !isPoly ) {
172       if      (nbnode == 4) {
173         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
174         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
175       }
176       else if (nbnode == 5) {
177         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
178                                                  node[4], ID);
179         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
180                                                  node[4] );
181       }
182       else if (nbnode == 6) {
183         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
184                                                  node[4], node[5], ID);
185         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
186                                                  node[4], node[5] );
187       }
188       else if (nbnode == 8) {
189         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
190                                                  node[4], node[5], node[6], node[7], ID);
191         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
192                                                  node[4], node[5], node[6], node[7] );
193       }
194       else if (nbnode == 10) {
195         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
196                                                  node[4], node[5], node[6], node[7],
197                                                  node[8], node[9], ID);
198         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
199                                                  node[4], node[5], node[6], node[7],
200                                                  node[8], node[9] );
201       }
202       else if (nbnode == 12) {
203         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
204                                                  node[4], node[5], node[6], node[7],
205                                                  node[8], node[9], node[10], node[11], ID);
206         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
207                                                  node[4], node[5], node[6], node[7],
208                                                  node[8], node[9], node[10], node[11] );
209       }
210       else if (nbnode == 13) {
211         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
212                                                  node[4], node[5], node[6], node[7],
213                                                  node[8], node[9], node[10],node[11],
214                                                  node[12],ID);
215         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
216                                                  node[4], node[5], node[6], node[7],
217                                                  node[8], node[9], node[10],node[11],
218                                                  node[12] );
219       }
220       else if (nbnode == 15) {
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],
224                                                  node[12],node[13],node[14],ID);
225         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
226                                                  node[4], node[5], node[6], node[7],
227                                                  node[8], node[9], node[10],node[11],
228                                                  node[12],node[13],node[14] );
229       }
230       else if (nbnode == 20) {
231         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
232                                                  node[4], node[5], node[6], node[7],
233                                                  node[8], node[9], node[10],node[11],
234                                                  node[12],node[13],node[14],node[15],
235                                                  node[16],node[17],node[18],node[19],ID);
236         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
237                                                  node[4], node[5], node[6], node[7],
238                                                  node[8], node[9], node[10],node[11],
239                                                  node[12],node[13],node[14],node[15],
240                                                  node[16],node[17],node[18],node[19] );
241       }
242       else if (nbnode == 27) {
243         if ( ID >= 1 ) e = mesh->AddVolumeWithID(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],node[15],
247                                                  node[16],node[17],node[18],node[19],
248                                                  node[20],node[21],node[22],node[23],
249                                                  node[24],node[25],node[26], ID);
250         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
251                                                  node[4], node[5], node[6], node[7],
252                                                  node[8], node[9], node[10],node[11],
253                                                  node[12],node[13],node[14],node[15],
254                                                  node[16],node[17],node[18],node[19],
255                                                  node[20],node[21],node[22],node[23],
256                                                  node[24],node[25],node[26] );
257       }
258     }
259     break;
260
261   case SMDSAbs_Edge:
262     if ( nbnode == 2 ) {
263       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
264       else           e = mesh->AddEdge      (node[0], node[1] );
265     }
266     else if ( nbnode == 3 ) {
267       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
268       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
269     }
270     break;
271
272   case SMDSAbs_0DElement:
273     if ( nbnode == 1 ) {
274       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
275       else           e = mesh->Add0DElement      (node[0] );
276     }
277     break;
278
279   case SMDSAbs_Node:
280     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
281     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z());
282     break;
283
284   default:;
285   }
286   if ( e ) myLastCreatedElems.Append( e );
287   return e;
288 }
289
290 //=======================================================================
291 /*!
292  * \brief Add element
293  */
294 //=======================================================================
295
296 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> &       nodeIDs,
297                                                const SMDSAbs_ElementType type,
298                                                const bool                isPoly,
299                                                const int                 ID)
300 {
301   vector<const SMDS_MeshNode*> nodes;
302   nodes.reserve( nodeIDs.size() );
303   vector<int>::const_iterator id = nodeIDs.begin();
304   while ( id != nodeIDs.end() ) {
305     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
306       nodes.push_back( node );
307     else
308       return 0;
309   }
310   return AddElement( nodes, type, isPoly, ID );
311 }
312
313 //=======================================================================
314 //function : Remove
315 //purpose  : Remove a node or an element.
316 //           Modify a compute state of sub-meshes which become empty
317 //=======================================================================
318
319 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
320                               const bool         isNodes )
321 {
322   myLastCreatedElems.Clear();
323   myLastCreatedNodes.Clear();
324
325   SMESHDS_Mesh* aMesh = GetMeshDS();
326   set< SMESH_subMesh *> smmap;
327
328   int removed = 0;
329   list<int>::const_iterator it = theIDs.begin();
330   for ( ; it != theIDs.end(); it++ ) {
331     const SMDS_MeshElement * elem;
332     if ( isNodes )
333       elem = aMesh->FindNode( *it );
334     else
335       elem = aMesh->FindElement( *it );
336     if ( !elem )
337       continue;
338
339     // Notify VERTEX sub-meshes about modification
340     if ( isNodes ) {
341       const SMDS_MeshNode* node = cast2Node( elem );
342       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
343         if ( int aShapeID = node->getshapeId() )
344           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
345             smmap.insert( sm );
346     }
347     // Find sub-meshes to notify about modification
348     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
349     //     while ( nodeIt->more() ) {
350     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
351     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
352     //       if ( aPosition.get() ) {
353     //         if ( int aShapeID = aPosition->GetShapeId() ) {
354     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
355     //             smmap.insert( sm );
356     //         }
357     //       }
358     //     }
359
360     // Do remove
361     if ( isNodes )
362       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
363     else
364       aMesh->RemoveElement( elem );
365     removed++;
366   }
367
368   // Notify sub-meshes about modification
369   if ( !smmap.empty() ) {
370     set< SMESH_subMesh *>::iterator smIt;
371     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
372       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
373   }
374
375   //   // Check if the whole mesh becomes empty
376   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
377   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
378
379   return removed;
380 }
381
382 //=======================================================================
383 //function : FindShape
384 //purpose  : Return an index of the shape theElem is on
385 //           or zero if a shape not found
386 //=======================================================================
387
388 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
389 {
390   myLastCreatedElems.Clear();
391   myLastCreatedNodes.Clear();
392
393   SMESHDS_Mesh * aMesh = GetMeshDS();
394   if ( aMesh->ShapeToMesh().IsNull() )
395     return 0;
396
397   int aShapeID = theElem->getshapeId();
398   if ( aShapeID < 1 )
399     return 0;
400
401   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
402     if ( sm->Contains( theElem ))
403       return aShapeID;
404
405   if ( theElem->GetType() == SMDSAbs_Node ) {
406     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
407   }
408   else {
409     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
410   }
411
412   TopoDS_Shape aShape; // the shape a node of theElem is on
413   if ( theElem->GetType() != SMDSAbs_Node )
414   {
415     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
416     while ( nodeIt->more() ) {
417       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
418       if ((aShapeID = node->getshapeId()) > 0) {
419         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
420           if ( sm->Contains( theElem ))
421             return aShapeID;
422           if ( aShape.IsNull() )
423             aShape = aMesh->IndexToShape( aShapeID );
424         }
425       }
426     }
427   }
428
429   // None of nodes is on a proper shape,
430   // find the shape among ancestors of aShape on which a node is
431   if ( !aShape.IsNull() ) {
432     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
433     for ( ; ancIt.More(); ancIt.Next() ) {
434       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
435       if ( sm && sm->Contains( theElem ))
436         return aMesh->ShapeToIndex( ancIt.Value() );
437     }
438   }
439   else
440   {
441     const map<int,SMESHDS_SubMesh*>& id2sm = GetMeshDS()->SubMeshes();
442     map<int,SMESHDS_SubMesh*>::const_iterator id_sm = id2sm.begin();
443     for ( ; id_sm != id2sm.end(); ++id_sm )
444       if ( id_sm->second->Contains( theElem ))
445         return id_sm->first;
446   }
447
448   //MESSAGE ("::FindShape() - SHAPE NOT FOUND")
449   return 0;
450 }
451
452 //=======================================================================
453 //function : IsMedium
454 //purpose  :
455 //=======================================================================
456
457 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
458                                 const SMDSAbs_ElementType typeToCheck)
459 {
460   bool isMedium = false;
461   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
462   while (it->more() && !isMedium ) {
463     const SMDS_MeshElement* elem = it->next();
464     isMedium = elem->IsMediumNode(node);
465   }
466   return isMedium;
467 }
468
469 //=======================================================================
470 //function : ShiftNodesQuadTria
471 //purpose  : auxilary
472 //           Shift nodes in the array corresponded to quadratic triangle
473 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
474 //=======================================================================
475 static void ShiftNodesQuadTria(const SMDS_MeshNode* aNodes[])
476 {
477   const SMDS_MeshNode* nd1 = aNodes[0];
478   aNodes[0] = aNodes[1];
479   aNodes[1] = aNodes[2];
480   aNodes[2] = nd1;
481   const SMDS_MeshNode* nd2 = aNodes[3];
482   aNodes[3] = aNodes[4];
483   aNodes[4] = aNodes[5];
484   aNodes[5] = nd2;
485 }
486
487 //=======================================================================
488 //function : edgeConnectivity
489 //purpose  : auxilary 
490 //           return number of the edges connected with the theNode.
491 //           if theEdges has connections with the other type of the
492 //           elements, return -1 
493 //=======================================================================
494 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
495 {
496   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
497   int nb=0;
498   while(elemIt->more()) {
499     elemIt->next();
500     nb++;
501   }
502   return nb;
503 }
504
505
506 //=======================================================================
507 //function : GetNodesFromTwoTria
508 //purpose  : auxilary
509 //           Shift nodes in the array corresponded to quadratic triangle
510 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
511 //=======================================================================
512 static bool GetNodesFromTwoTria(const SMDS_MeshElement * theTria1,
513                                 const SMDS_MeshElement * theTria2,
514                                 const SMDS_MeshNode* N1[],
515                                 const SMDS_MeshNode* N2[])
516 {
517   SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
518   int i=0;
519   while(i<6) {
520     N1[i] = static_cast<const SMDS_MeshNode*>( it->next() );
521     i++;
522   }
523   if(it->more()) return false;
524   it = theTria2->nodesIterator();
525   i=0;
526   while(i<6) {
527     N2[i] = static_cast<const SMDS_MeshNode*>( it->next() );
528     i++;
529   }
530   if(it->more()) return false;
531
532   int sames[3] = {-1,-1,-1};
533   int nbsames = 0;
534   int j;
535   for(i=0; i<3; i++) {
536     for(j=0; j<3; j++) {
537       if(N1[i]==N2[j]) {
538         sames[i] = j;
539         nbsames++;
540         break;
541       }
542     }
543   }
544   if(nbsames!=2) return false;
545   if(sames[0]>-1) {
546     ShiftNodesQuadTria(N1);
547     if(sames[1]>-1) {
548       ShiftNodesQuadTria(N1);
549     }
550   }
551   i = sames[0] + sames[1] + sames[2];
552   for(; i<2; i++) {
553     ShiftNodesQuadTria(N2);
554   }
555   // now we receive following N1 and N2 (using numeration as above image)
556   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
557   // i.e. first nodes from both arrays determ new diagonal
558   return true;
559 }
560
561 //=======================================================================
562 //function : InverseDiag
563 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
564 //           but having other common link.
565 //           Return False if args are improper
566 //=======================================================================
567
568 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
569                                     const SMDS_MeshElement * theTria2 )
570 {
571   MESSAGE("InverseDiag");
572   myLastCreatedElems.Clear();
573   myLastCreatedNodes.Clear();
574
575   if (!theTria1 || !theTria2)
576     return false;
577
578   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
579   if (!F1) return false;
580   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
581   if (!F2) return false;
582   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
583       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
584
585     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
586     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
587     //    |/ |                                         | \|
588     //  B +--+ 2                                     B +--+ 2
589
590     // put nodes in array and find out indices of the same ones
591     const SMDS_MeshNode* aNodes [6];
592     int sameInd [] = { 0, 0, 0, 0, 0, 0 };
593     int i = 0;
594     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
595     while ( it->more() ) {
596       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
597
598       if ( i > 2 ) // theTria2
599         // find same node of theTria1
600         for ( int j = 0; j < 3; j++ )
601           if ( aNodes[ i ] == aNodes[ j ]) {
602             sameInd[ j ] = i;
603             sameInd[ i ] = j;
604             break;
605           }
606       // next
607       i++;
608       if ( i == 3 ) {
609         if ( it->more() )
610           return false; // theTria1 is not a triangle
611         it = theTria2->nodesIterator();
612       }
613       if ( i == 6 && it->more() )
614         return false; // theTria2 is not a triangle
615     }
616
617     // find indices of 1,2 and of A,B in theTria1
618     int iA = 0, iB = 0, i1 = 0, i2 = 0;
619     for ( i = 0; i < 6; i++ ) {
620       if ( sameInd [ i ] == 0 ) {
621         if ( i < 3 ) i1 = i;
622         else         i2 = i;
623       }
624       else if (i < 3) {
625         if ( iA ) iB = i;
626         else      iA = i;
627       }
628     }
629     // nodes 1 and 2 should not be the same
630     if ( aNodes[ i1 ] == aNodes[ i2 ] )
631       return false;
632
633     // theTria1: A->2
634     aNodes[ iA ] = aNodes[ i2 ];
635     // theTria2: B->1
636     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
637
638     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
639     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
640
641     return true;
642
643   } // end if(F1 && F2)
644
645   // check case of quadratic faces
646   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle)
647     return false;
648   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle)
649     return false;
650
651   //       5
652   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
653   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
654   //    |   / |
655   //  7 +  +  + 6
656   //    | /9  |
657   //    |/    |
658   //  4 +--+--+ 3
659   //       8
660
661   const SMDS_MeshNode* N1 [6];
662   const SMDS_MeshNode* N2 [6];
663   if(!GetNodesFromTwoTria(theTria1,theTria2,N1,N2))
664     return false;
665   // now we receive following N1 and N2 (using numeration as above image)
666   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
667   // i.e. first nodes from both arrays determ new diagonal
668
669   const SMDS_MeshNode* N1new [6];
670   const SMDS_MeshNode* N2new [6];
671   N1new[0] = N1[0];
672   N1new[1] = N2[0];
673   N1new[2] = N2[1];
674   N1new[3] = N1[4];
675   N1new[4] = N2[3];
676   N1new[5] = N1[5];
677   N2new[0] = N1[0];
678   N2new[1] = N1[1];
679   N2new[2] = N2[0];
680   N2new[3] = N1[3];
681   N2new[4] = N2[5];
682   N2new[5] = N1[4];
683   // replaces nodes in faces
684   GetMeshDS()->ChangeElementNodes( theTria1, N1new, 6 );
685   GetMeshDS()->ChangeElementNodes( theTria2, N2new, 6 );
686
687   return true;
688 }
689
690 //=======================================================================
691 //function : findTriangles
692 //purpose  : find triangles sharing theNode1-theNode2 link
693 //=======================================================================
694
695 static bool findTriangles(const SMDS_MeshNode *    theNode1,
696                           const SMDS_MeshNode *    theNode2,
697                           const SMDS_MeshElement*& theTria1,
698                           const SMDS_MeshElement*& theTria2)
699 {
700   if ( !theNode1 || !theNode2 ) return false;
701
702   theTria1 = theTria2 = 0;
703
704   set< const SMDS_MeshElement* > emap;
705   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
706   while (it->more()) {
707     const SMDS_MeshElement* elem = it->next();
708     if ( elem->NbNodes() == 3 )
709       emap.insert( elem );
710   }
711   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
712   while (it->more()) {
713     const SMDS_MeshElement* elem = it->next();
714     if ( emap.find( elem ) != emap.end() ) {
715       if ( theTria1 ) {
716         // theTria1 must be element with minimum ID
717         if( theTria1->GetID() < elem->GetID() ) {
718           theTria2 = elem;
719         }
720         else {
721           theTria2 = theTria1;
722           theTria1 = elem;
723         }
724         break;
725       }
726       else {
727         theTria1 = elem;
728       }
729     }
730   }
731   return ( theTria1 && theTria2 );
732 }
733
734 //=======================================================================
735 //function : InverseDiag
736 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
737 //           with ones built on the same 4 nodes but having other common link.
738 //           Return false if proper faces not found
739 //=======================================================================
740
741 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
742                                     const SMDS_MeshNode * theNode2)
743 {
744   myLastCreatedElems.Clear();
745   myLastCreatedNodes.Clear();
746
747   MESSAGE( "::InverseDiag()" );
748
749   const SMDS_MeshElement *tr1, *tr2;
750   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
751     return false;
752
753   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
754   if (!F1) return false;
755   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
756   if (!F2) return false;
757   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
758       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
759
760     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
761     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
762     //    |/ |                                    | \|
763     //  B +--+ 2                                B +--+ 2
764
765     // put nodes in array
766     // and find indices of 1,2 and of A in tr1 and of B in tr2
767     int i, iA1 = 0, i1 = 0;
768     const SMDS_MeshNode* aNodes1 [3];
769     SMDS_ElemIteratorPtr it;
770     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
771       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
772       if ( aNodes1[ i ] == theNode1 )
773         iA1 = i; // node A in tr1
774       else if ( aNodes1[ i ] != theNode2 )
775         i1 = i;  // node 1
776     }
777     int iB2 = 0, i2 = 0;
778     const SMDS_MeshNode* aNodes2 [3];
779     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
780       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
781       if ( aNodes2[ i ] == theNode2 )
782         iB2 = i; // node B in tr2
783       else if ( aNodes2[ i ] != theNode1 )
784         i2 = i;  // node 2
785     }
786
787     // nodes 1 and 2 should not be the same
788     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
789       return false;
790
791     // tr1: A->2
792     aNodes1[ iA1 ] = aNodes2[ i2 ];
793     // tr2: B->1
794     aNodes2[ iB2 ] = aNodes1[ i1 ];
795
796     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
797     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
798
799     return true;
800   }
801
802   // check case of quadratic faces
803   return InverseDiag(tr1,tr2);
804 }
805
806 //=======================================================================
807 //function : getQuadrangleNodes
808 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
809 //           fusion of triangles tr1 and tr2 having shared link on
810 //           theNode1 and theNode2
811 //=======================================================================
812
813 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
814                         const SMDS_MeshNode *    theNode1,
815                         const SMDS_MeshNode *    theNode2,
816                         const SMDS_MeshElement * tr1,
817                         const SMDS_MeshElement * tr2 )
818 {
819   if( tr1->NbNodes() != tr2->NbNodes() )
820     return false;
821   // find the 4-th node to insert into tr1
822   const SMDS_MeshNode* n4 = 0;
823   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
824   int i=0;
825   while ( !n4 && i<3 ) {
826     const SMDS_MeshNode * n = cast2Node( it->next() );
827     i++;
828     bool isDiag = ( n == theNode1 || n == theNode2 );
829     if ( !isDiag )
830       n4 = n;
831   }
832   // Make an array of nodes to be in a quadrangle
833   int iNode = 0, iFirstDiag = -1;
834   it = tr1->nodesIterator();
835   i=0;
836   while ( i<3 ) {
837     const SMDS_MeshNode * n = cast2Node( it->next() );
838     i++;
839     bool isDiag = ( n == theNode1 || n == theNode2 );
840     if ( isDiag ) {
841       if ( iFirstDiag < 0 )
842         iFirstDiag = iNode;
843       else if ( iNode - iFirstDiag == 1 )
844         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
845     }
846     else if ( n == n4 ) {
847       return false; // tr1 and tr2 should not have all the same nodes
848     }
849     theQuadNodes[ iNode++ ] = n;
850   }
851   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
852     theQuadNodes[ iNode ] = n4;
853
854   return true;
855 }
856
857 //=======================================================================
858 //function : DeleteDiag
859 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
860 //           with a quadrangle built on the same 4 nodes.
861 //           Return false if proper faces not found
862 //=======================================================================
863
864 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
865                                    const SMDS_MeshNode * theNode2)
866 {
867   myLastCreatedElems.Clear();
868   myLastCreatedNodes.Clear();
869
870   MESSAGE( "::DeleteDiag()" );
871
872   const SMDS_MeshElement *tr1, *tr2;
873   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
874     return false;
875
876   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
877   if (!F1) return false;
878   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
879   if (!F2) return false;
880   SMESHDS_Mesh * aMesh = GetMeshDS();
881
882   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
883       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
884
885     const SMDS_MeshNode* aNodes [ 4 ];
886     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
887       return false;
888
889     const SMDS_MeshElement* newElem = 0;
890     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
891     myLastCreatedElems.Append(newElem);
892     AddToSameGroups( newElem, tr1, aMesh );
893     int aShapeId = tr1->getshapeId();
894     if ( aShapeId )
895       {
896         aMesh->SetMeshElementOnShape( newElem, aShapeId );
897       }
898     aMesh->RemoveElement( tr1 );
899     aMesh->RemoveElement( tr2 );
900
901     return true;
902   }
903
904   // check case of quadratic faces
905   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
906     return false;
907   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
908     return false;
909
910   //       5
911   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
912   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
913   //    |   / |
914   //  7 +  +  + 6
915   //    | /9  |
916   //    |/    |
917   //  4 +--+--+ 3
918   //       8
919
920   const SMDS_MeshNode* N1 [6];
921   const SMDS_MeshNode* N2 [6];
922   if(!GetNodesFromTwoTria(tr1,tr2,N1,N2))
923     return false;
924   // now we receive following N1 and N2 (using numeration as above image)
925   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
926   // i.e. first nodes from both arrays determ new diagonal
927
928   const SMDS_MeshNode* aNodes[8];
929   aNodes[0] = N1[0];
930   aNodes[1] = N1[1];
931   aNodes[2] = N2[0];
932   aNodes[3] = N2[1];
933   aNodes[4] = N1[3];
934   aNodes[5] = N2[5];
935   aNodes[6] = N2[3];
936   aNodes[7] = N1[5];
937
938   const SMDS_MeshElement* newElem = 0;
939   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
940                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
941   myLastCreatedElems.Append(newElem);
942   AddToSameGroups( newElem, tr1, aMesh );
943   int aShapeId = tr1->getshapeId();
944   if ( aShapeId )
945     {
946       aMesh->SetMeshElementOnShape( newElem, aShapeId );
947     }
948   aMesh->RemoveElement( tr1 );
949   aMesh->RemoveElement( tr2 );
950
951   // remove middle node (9)
952   GetMeshDS()->RemoveNode( N1[4] );
953
954   return true;
955 }
956
957 //=======================================================================
958 //function : Reorient
959 //purpose  : Reverse theElement orientation
960 //=======================================================================
961
962 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
963 {
964   MESSAGE("Reorient");
965   myLastCreatedElems.Clear();
966   myLastCreatedNodes.Clear();
967
968   if (!theElem)
969     return false;
970   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
971   if ( !it || !it->more() )
972     return false;
973
974   switch ( theElem->GetType() ) {
975
976   case SMDSAbs_Edge:
977   case SMDSAbs_Face: {
978     if(!theElem->IsQuadratic()) {
979       int i = theElem->NbNodes();
980       vector<const SMDS_MeshNode*> aNodes( i );
981       while ( it->more() )
982         aNodes[ --i ]= static_cast<const SMDS_MeshNode*>( it->next() );
983       return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], theElem->NbNodes() );
984     }
985     else {
986       // quadratic elements
987       if(theElem->GetType()==SMDSAbs_Edge) {
988         vector<const SMDS_MeshNode*> aNodes(3);
989         aNodes[1]= static_cast<const SMDS_MeshNode*>( it->next() );
990         aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
991         aNodes[2]= static_cast<const SMDS_MeshNode*>( it->next() );
992         return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], 3 );
993       }
994       else {
995         int nbn = theElem->NbNodes();
996         vector<const SMDS_MeshNode*> aNodes(nbn);
997         aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
998         int i=1;
999         for(; i<nbn/2; i++) {
1000           aNodes[nbn/2-i]= static_cast<const SMDS_MeshNode*>( it->next() );
1001         }
1002         for(i=0; i<nbn/2; i++) {
1003           aNodes[nbn-i-1]= static_cast<const SMDS_MeshNode*>( it->next() );
1004         }
1005         return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], nbn );
1006       }
1007     }
1008   }
1009   case SMDSAbs_Volume: {
1010     if (theElem->IsPoly()) {
1011       // TODO reorient vtk polyhedron
1012       MESSAGE("reorient vtk polyhedron ?");
1013       const SMDS_VtkVolume* aPolyedre =
1014         dynamic_cast<const SMDS_VtkVolume*>( theElem );
1015       if (!aPolyedre) {
1016         MESSAGE("Warning: bad volumic element");
1017         return false;
1018       }
1019
1020       int nbFaces = aPolyedre->NbFaces();
1021       vector<const SMDS_MeshNode *> poly_nodes;
1022       vector<int> quantities (nbFaces);
1023
1024       // reverse each face of the polyedre
1025       for (int iface = 1; iface <= nbFaces; iface++) {
1026         int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1027         quantities[iface - 1] = nbFaceNodes;
1028
1029         for (inode = nbFaceNodes; inode >= 1; inode--) {
1030           const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1031           poly_nodes.push_back(curNode);
1032         }
1033       }
1034
1035       return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1036
1037     }
1038     else {
1039       SMDS_VolumeTool vTool;
1040       if ( !vTool.Set( theElem ))
1041         return false;
1042       vTool.Inverse();
1043       MESSAGE("ChangeElementNodes reorient: check vTool.Inverse");
1044       return GetMeshDS()->ChangeElementNodes( theElem, vTool.GetNodes(), vTool.NbNodes() );
1045     }
1046   }
1047   default:;
1048   }
1049
1050   return false;
1051 }
1052
1053 //=======================================================================
1054 //function : getBadRate
1055 //purpose  :
1056 //=======================================================================
1057
1058 static double getBadRate (const SMDS_MeshElement*               theElem,
1059                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1060 {
1061   SMESH::Controls::TSequenceOfXYZ P;
1062   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1063     return 1e100;
1064   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1065   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1066 }
1067
1068 //=======================================================================
1069 //function : QuadToTri
1070 //purpose  : Cut quadrangles into triangles.
1071 //           theCrit is used to select a diagonal to cut
1072 //=======================================================================
1073
1074 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1075                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1076 {
1077   myLastCreatedElems.Clear();
1078   myLastCreatedNodes.Clear();
1079
1080   MESSAGE( "::QuadToTri()" );
1081
1082   if ( !theCrit.get() )
1083     return false;
1084
1085   SMESHDS_Mesh * aMesh = GetMeshDS();
1086
1087   Handle(Geom_Surface) surface;
1088   SMESH_MesherHelper   helper( *GetMesh() );
1089
1090   TIDSortedElemSet::iterator itElem;
1091   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1092     const SMDS_MeshElement* elem = *itElem;
1093     if ( !elem || elem->GetType() != SMDSAbs_Face )
1094       continue;
1095     if ( elem->NbCornerNodes() != 4 )
1096       continue;
1097
1098     // retrieve element nodes
1099     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1100
1101     // compare two sets of possible triangles
1102     double aBadRate1, aBadRate2; // to what extent a set is bad
1103     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1104     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1105     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1106
1107     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1108     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1109     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1110
1111     int aShapeId = FindShape( elem );
1112     const SMDS_MeshElement* newElem1 = 0;
1113     const SMDS_MeshElement* newElem2 = 0;
1114
1115     if( !elem->IsQuadratic() ) {
1116
1117       // split liner quadrangle
1118
1119       if ( aBadRate1 <= aBadRate2 ) {
1120         // tr1 + tr2 is better
1121         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1122         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1123       }
1124       else {
1125         // tr3 + tr4 is better
1126         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1127         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1128       }
1129     }
1130     else {
1131
1132       // split quadratic quadrangle
1133
1134       // get surface elem is on
1135       if ( aShapeId != helper.GetSubShapeID() ) {
1136         surface.Nullify();
1137         TopoDS_Shape shape;
1138         if ( aShapeId > 0 )
1139           shape = aMesh->IndexToShape( aShapeId );
1140         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1141           TopoDS_Face face = TopoDS::Face( shape );
1142           surface = BRep_Tool::Surface( face );
1143           if ( !surface.IsNull() )
1144             helper.SetSubShape( shape );
1145         }
1146       }
1147       // find middle point for (0,1,2,3)
1148       // and create a node in this point;
1149       const SMDS_MeshNode* newN = 0;
1150       if ( aNodes.size() == 9 )
1151       {
1152         // SMDSEntity_BiQuad_Quadrangle
1153         newN = aNodes.back();
1154       }
1155       else
1156       {
1157         gp_XYZ p( 0,0,0 );
1158         if ( surface.IsNull() )
1159         {
1160           for ( int i = 0; i < 4; i++ )
1161             p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1162           p /= 4;
1163         }
1164         else
1165         {
1166           const SMDS_MeshNode* inFaceNode = 0;
1167           if ( helper.GetNodeUVneedInFaceNode() )
1168             for ( size_t i = 0; i < aNodes.size() && !inFaceNode; ++i )
1169               if ( aNodes[ i ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1170                 inFaceNode = aNodes[ i ];
1171
1172           TopoDS_Face face = TopoDS::Face( helper.GetSubShape() );
1173           gp_XY uv( 0,0 );
1174           for ( int i = 0; i < 4; i++ )
1175             uv += helper.GetNodeUV( face, aNodes[i], inFaceNode );
1176           uv /= 4.;
1177           p = surface->Value( uv.X(), uv.Y() ).XYZ();
1178         }
1179         newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1180         myLastCreatedNodes.Append(newN);
1181       }
1182       // create a new element
1183       if ( aBadRate1 <= aBadRate2 ) {
1184         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1185                                   aNodes[6], aNodes[7], newN );
1186         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
1187                                   newN,      aNodes[4], aNodes[5] );
1188       }
1189       else {
1190         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1191                                   aNodes[7], aNodes[4], newN );
1192         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
1193                                   newN,      aNodes[5], aNodes[6] );
1194       }
1195     } // quadratic case
1196
1197     // care of a new element
1198
1199     myLastCreatedElems.Append(newElem1);
1200     myLastCreatedElems.Append(newElem2);
1201     AddToSameGroups( newElem1, elem, aMesh );
1202     AddToSameGroups( newElem2, elem, aMesh );
1203
1204     // put a new triangle on the same shape
1205     if ( aShapeId )
1206       {
1207         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1208         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1209       }
1210     aMesh->RemoveElement( elem );
1211   }
1212   return true;
1213 }
1214
1215 //=======================================================================
1216 //function : BestSplit
1217 //purpose  : Find better diagonal for cutting.
1218 //=======================================================================
1219
1220 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1221                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1222 {
1223   myLastCreatedElems.Clear();
1224   myLastCreatedNodes.Clear();
1225
1226   if (!theCrit.get())
1227     return -1;
1228
1229   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1230     return -1;
1231
1232   if( theQuad->NbNodes()==4 ||
1233       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1234
1235     // retrieve element nodes
1236     const SMDS_MeshNode* aNodes [4];
1237     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1238     int i = 0;
1239     //while (itN->more())
1240     while (i<4) {
1241       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1242     }
1243     // compare two sets of possible triangles
1244     double aBadRate1, aBadRate2; // to what extent a set is bad
1245     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1246     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1247     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1248
1249     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1250     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1251     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1252
1253     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1254       return 1; // diagonal 1-3
1255
1256     return 2; // diagonal 2-4
1257   }
1258   return -1;
1259 }
1260
1261 namespace
1262 {
1263   // Methods of splitting volumes into tetra
1264
1265   const int theHexTo5_1[5*4+1] =
1266     {
1267       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1268     };
1269   const int theHexTo5_2[5*4+1] =
1270     {
1271       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1272     };
1273   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1274
1275   const int theHexTo6_1[6*4+1] =
1276     {
1277       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
1278     };
1279   const int theHexTo6_2[6*4+1] =
1280     {
1281       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
1282     };
1283   const int theHexTo6_3[6*4+1] =
1284     {
1285       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
1286     };
1287   const int theHexTo6_4[6*4+1] =
1288     {
1289       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
1290     };
1291   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1292
1293   const int thePyraTo2_1[2*4+1] =
1294     {
1295       0, 1, 2, 4,    0, 2, 3, 4,   -1
1296     };
1297   const int thePyraTo2_2[2*4+1] =
1298     {
1299       1, 2, 3, 4,    1, 3, 0, 4,   -1
1300     };
1301   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1302
1303   const int thePentaTo3_1[3*4+1] =
1304     {
1305       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1306     };
1307   const int thePentaTo3_2[3*4+1] =
1308     {
1309       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1310     };
1311   const int thePentaTo3_3[3*4+1] =
1312     {
1313       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1314     };
1315   const int thePentaTo3_4[3*4+1] =
1316     {
1317       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1318     };
1319   const int thePentaTo3_5[3*4+1] =
1320     {
1321       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1322     };
1323   const int thePentaTo3_6[3*4+1] =
1324     {
1325       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1326     };
1327   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1328                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1329
1330   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1331   {
1332     int _n1, _n2, _n3;
1333     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1334     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1335     bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1336   };
1337   struct TSplitMethod
1338   {
1339     int        _nbTetra;
1340     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1341     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1342     bool       _ownConn;      //!< to delete _connectivity in destructor
1343     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1344
1345     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1346       : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1347     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1348     bool hasFacet( const TTriangleFacet& facet ) const
1349     {
1350       const int* tetConn = _connectivity;
1351       for ( ; tetConn[0] >= 0; tetConn += 4 )
1352         if (( facet.contains( tetConn[0] ) +
1353               facet.contains( tetConn[1] ) +
1354               facet.contains( tetConn[2] ) +
1355               facet.contains( tetConn[3] )) == 3 )
1356           return true;
1357       return false;
1358     }
1359   };
1360
1361   //=======================================================================
1362   /*!
1363    * \brief return TSplitMethod for the given element
1364    */
1365   //=======================================================================
1366
1367   TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1368   {
1369     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1370
1371     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1372     // an edge and a face barycenter; tertaherdons are based on triangles and
1373     // a volume barycenter
1374     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1375
1376     // Find out how adjacent volumes are split
1377
1378     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1379     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1380     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1381     {
1382       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1383       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1384       if ( nbNodes < 4 ) continue;
1385
1386       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1387       const int* nInd = vol.GetFaceNodesIndices( iF );
1388       if ( nbNodes == 4 )
1389       {
1390         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1391         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1392         if      ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1393         else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1394       }
1395       else
1396       {
1397         int iCom = 0; // common node of triangle faces to split into
1398         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1399         {
1400           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1401                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1402                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1403           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1404                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1405                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1406           if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1407           {
1408             triaSplits.push_back( t012 );
1409             triaSplits.push_back( t023 );
1410             break;
1411           }
1412         }
1413       }
1414       if ( !triaSplits.empty() )
1415         hasAdjacentSplits = true;
1416     }
1417
1418     // Among variants of split method select one compliant with adjacent volumes
1419
1420     TSplitMethod method;
1421     if ( !vol.Element()->IsPoly() && !is24TetMode )
1422     {
1423       int nbVariants = 2, nbTet = 0;
1424       const int** connVariants = 0;
1425       switch ( vol.Element()->GetEntityType() )
1426       {
1427       case SMDSEntity_Hexa:
1428       case SMDSEntity_Quad_Hexa:
1429       case SMDSEntity_TriQuad_Hexa:
1430         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1431           connVariants = theHexTo5, nbTet = 5;
1432         else
1433           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1434         break;
1435       case SMDSEntity_Pyramid:
1436       case SMDSEntity_Quad_Pyramid:
1437         connVariants = thePyraTo2;  nbTet = 2;
1438         break;
1439       case SMDSEntity_Penta:
1440       case SMDSEntity_Quad_Penta:
1441         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1442         break;
1443       default:
1444         nbVariants = 0;
1445       }
1446       for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1447       {
1448         // check method compliancy with adjacent tetras,
1449         // all found splits must be among facets of tetras described by this method
1450         method = TSplitMethod( nbTet, connVariants[variant] );
1451         if ( hasAdjacentSplits && method._nbTetra > 0 )
1452         {
1453           bool facetCreated = true;
1454           for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1455           {
1456             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1457             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1458               facetCreated = method.hasFacet( *facet );
1459           }
1460           if ( !facetCreated )
1461             method = TSplitMethod(0); // incompatible method
1462         }
1463       }
1464     }
1465     if ( method._nbTetra < 1 )
1466     {
1467       // No standard method is applicable, use a generic solution:
1468       // each facet of a volume is split into triangles and
1469       // each of triangles and a volume barycenter form a tetrahedron.
1470
1471       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1472
1473       int* connectivity = new int[ maxTetConnSize + 1 ];
1474       method._connectivity = connectivity;
1475       method._ownConn = true;
1476       method._baryNode = !isHex27; // to create central node or not
1477
1478       int connSize = 0;
1479       int baryCenInd = vol.NbNodes() - int( isHex27 );
1480       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1481       {
1482         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1483         const int*   nInd = vol.GetFaceNodesIndices( iF );
1484         // find common node of triangle facets of tetra to create
1485         int iCommon = 0; // index in linear numeration
1486         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1487         if ( !triaSplits.empty() )
1488         {
1489           // by found facets
1490           const TTriangleFacet* facet = &triaSplits.front();
1491           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1492             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1493                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1494               break;
1495         }
1496         else if ( nbNodes > 3 && !is24TetMode )
1497         {
1498           // find the best method of splitting into triangles by aspect ratio
1499           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1500           map< double, int > badness2iCommon;
1501           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1502           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1503           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1504           {
1505             double badness = 0;
1506             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1507             {
1508               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1509                                       nodes[ iQ*((iLast-1)%nbNodes)],
1510                                       nodes[ iQ*((iLast  )%nbNodes)]);
1511               badness += getBadRate( &tria, aspectRatio );
1512             }
1513             badness2iCommon.insert( make_pair( badness, iCommon ));
1514           }
1515           // use iCommon with lowest badness
1516           iCommon = badness2iCommon.begin()->second;
1517         }
1518         if ( iCommon >= nbNodes )
1519           iCommon = 0; // something wrong
1520
1521         // fill connectivity of tetrahedra based on a current face
1522         int nbTet = nbNodes - 2;
1523         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1524         {
1525           int faceBaryCenInd;
1526           if ( isHex27 )
1527           {
1528             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1529             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1530           }
1531           else
1532           {
1533             method._faceBaryNode[ iF ] = 0;
1534             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1535           }
1536           nbTet = nbNodes;
1537           for ( int i = 0; i < nbTet; ++i )
1538           {
1539             int i1 = i, i2 = (i+1) % nbNodes;
1540             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1541             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1542             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1543             connectivity[ connSize++ ] = faceBaryCenInd;
1544             connectivity[ connSize++ ] = baryCenInd;
1545           }
1546         }
1547         else
1548         {
1549           for ( int i = 0; i < nbTet; ++i )
1550           {
1551             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1552             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1553             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1554             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1555             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1556             connectivity[ connSize++ ] = baryCenInd;
1557           }
1558         }
1559         method._nbTetra += nbTet;
1560
1561       } // loop on volume faces
1562
1563       connectivity[ connSize++ ] = -1;
1564
1565     } // end of generic solution
1566
1567     return method;
1568   }
1569   //================================================================================
1570   /*!
1571    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1572    */
1573   //================================================================================
1574
1575   bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1576   {
1577     // find the tetrahedron including the three nodes of facet
1578     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1579     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1580     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1581     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1582     while ( volIt1->more() )
1583     {
1584       const SMDS_MeshElement* v = volIt1->next();
1585       SMDSAbs_EntityType type = v->GetEntityType();
1586       if ( type != SMDSEntity_Tetra && type != SMDSEntity_Quad_Tetra )
1587         continue;
1588       if ( type == SMDSEntity_Quad_Tetra && v->GetNodeIndex( n1 ) > 3 )
1589         continue; // medium node not allowed
1590       const int ind2 = v->GetNodeIndex( n2 );
1591       if ( ind2 < 0 || 3 < ind2 )
1592         continue;
1593       const int ind3 = v->GetNodeIndex( n3 );
1594       if ( ind3 < 0 || 3 < ind3 )
1595         continue;
1596       return true;
1597     }
1598     return false;
1599   }
1600
1601   //=======================================================================
1602   /*!
1603    * \brief A key of a face of volume
1604    */
1605   //=======================================================================
1606
1607   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
1608   {
1609     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1610     {
1611       TIDSortedNodeSet sortedNodes;
1612       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1613       int nbNodes = vol.NbFaceNodes( iF );
1614       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1615       for ( int i = 0; i < nbNodes; i += iQ )
1616         sortedNodes.insert( fNodes[i] );
1617       TIDSortedNodeSet::iterator n = sortedNodes.begin();
1618       first.first   = (*(n++))->GetID();
1619       first.second  = (*(n++))->GetID();
1620       second.first  = (*(n++))->GetID();
1621       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
1622     }
1623   };
1624 } // namespace
1625
1626 //=======================================================================
1627 //function : SplitVolumesIntoTetra
1628 //purpose  : Split volume elements into tetrahedra.
1629 //=======================================================================
1630
1631 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1632                                               const int                theMethodFlags)
1633 {
1634   // std-like iterator on coordinates of nodes of mesh element
1635   typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1636   NXyzIterator xyzEnd;
1637
1638   SMDS_VolumeTool    volTool;
1639   SMESH_MesherHelper helper( *GetMesh());
1640
1641   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1642   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1643   
1644   SMESH_SequenceOfElemPtr newNodes, newElems;
1645
1646   // map face of volume to it's baricenrtic node
1647   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1648   double bc[3];
1649
1650   TIDSortedElemSet::const_iterator elem = theElems.begin();
1651   for ( ; elem != theElems.end(); ++elem )
1652   {
1653     if ( (*elem)->GetType() != SMDSAbs_Volume )
1654       continue;
1655     SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1656     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
1657       continue;
1658
1659     if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange...
1660
1661     TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1662     if ( splitMethod._nbTetra < 1 ) continue;
1663
1664     // find submesh to add new tetras to
1665     if ( !subMesh || !subMesh->Contains( *elem ))
1666     {
1667       int shapeID = FindShape( *elem );
1668       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1669       subMesh = GetMeshDS()->MeshElements( shapeID );
1670     }
1671     int iQ;
1672     if ( (*elem)->IsQuadratic() )
1673     {
1674       iQ = 2;
1675       // add quadratic links to the helper
1676       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1677       {
1678         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1679         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
1680         for ( int iN = 0; iN < nbN; iN += iQ )
1681           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
1682       }
1683       helper.SetIsQuadratic( true );
1684     }
1685     else
1686     {
1687       iQ = 1;
1688       helper.SetIsQuadratic( false );
1689     }
1690     vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1691     helper.SetElementsOnShape( true );
1692     if ( splitMethod._baryNode )
1693     {
1694       // make a node at barycenter
1695       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1696       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1697       nodes.push_back( gcNode );
1698       newNodes.Append( gcNode );
1699     }
1700     if ( !splitMethod._faceBaryNode.empty() )
1701     {
1702       // make or find baricentric nodes of faces
1703       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1704       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1705       {
1706         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1707           volFace2BaryNode.insert
1708           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
1709         if ( !f_n->second )
1710         {
1711           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1712           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1713         }
1714         nodes.push_back( iF_n->second = f_n->second );
1715       }
1716     }
1717
1718     // make tetras
1719     vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1720     const int* tetConn = splitMethod._connectivity;
1721     for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1722       newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1723                                                        nodes[ tetConn[1] ],
1724                                                        nodes[ tetConn[2] ],
1725                                                        nodes[ tetConn[3] ]));
1726
1727     ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
1728
1729     // Split faces on sides of the split volume
1730
1731     const SMDS_MeshNode** volNodes = volTool.GetNodes();
1732     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1733     {
1734       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
1735       if ( nbNodes < 4 ) continue;
1736
1737       // find an existing face
1738       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
1739                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
1740       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
1741                                                                        /*noMedium=*/false))
1742       {
1743         // make triangles
1744         helper.SetElementsOnShape( false );
1745         vector< const SMDS_MeshElement* > triangles;
1746
1747         // find submesh to add new triangles in
1748         if ( !fSubMesh || !fSubMesh->Contains( face ))
1749         {
1750           int shapeID = FindShape( face );
1751           fSubMesh = GetMeshDS()->MeshElements( shapeID );
1752         }
1753         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
1754         if ( iF_n != splitMethod._faceBaryNode.end() )
1755         {
1756           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
1757           {
1758             const SMDS_MeshNode* n1 = fNodes[iN];
1759             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
1760             const SMDS_MeshNode *n3 = iF_n->second;
1761             if ( !volTool.IsFaceExternal( iF ))
1762               swap( n2, n3 );
1763             triangles.push_back( helper.AddFace( n1,n2,n3 ));
1764
1765             if ( fSubMesh && n3->getshapeId() < 1 )
1766               fSubMesh->AddNode( n3 );
1767           }
1768         }
1769         else
1770         {
1771           // among possible triangles create ones discribed by split method
1772           const int* nInd = volTool.GetFaceNodesIndices( iF );
1773           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1774           int iCom = 0; // common node of triangle faces to split into
1775           list< TTriangleFacet > facets;
1776           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
1777           {
1778             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
1779                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
1780                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
1781             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
1782                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
1783                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
1784             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
1785             {
1786               facets.push_back( t012 );
1787               facets.push_back( t023 );
1788               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
1789                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
1790                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
1791                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
1792               break;
1793             }
1794           }
1795           list< TTriangleFacet >::iterator facet = facets.begin();
1796           for ( ; facet != facets.end(); ++facet )
1797           {
1798             if ( !volTool.IsFaceExternal( iF ))
1799               swap( facet->_n2, facet->_n3 );
1800             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
1801                                                  volNodes[ facet->_n2 ],
1802                                                  volNodes[ facet->_n3 ]));
1803           }
1804         }
1805         for ( int i = 0; i < triangles.size(); ++i )
1806         {
1807           if ( !triangles[i] ) continue;
1808           if ( fSubMesh )
1809             fSubMesh->AddElement( triangles[i]);
1810           newElems.Append( triangles[i] );
1811         }
1812         ReplaceElemInGroups( face, triangles, GetMeshDS() );
1813         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
1814       }
1815
1816     } // loop on volume faces to split them into triangles
1817
1818     GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
1819
1820     if ( geomType == SMDSEntity_TriQuad_Hexa )
1821     {
1822       // remove medium nodes that could become free
1823       for ( int i = 20; i < volTool.NbNodes(); ++i )
1824         if ( volNodes[i]->NbInverseElements() == 0 )
1825           GetMeshDS()->RemoveNode( volNodes[i] );
1826     }
1827   } // loop on volumes to split
1828
1829   myLastCreatedNodes = newNodes;
1830   myLastCreatedElems = newElems;
1831 }
1832
1833 //=======================================================================
1834 //function : AddToSameGroups
1835 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
1836 //=======================================================================
1837
1838 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
1839                                         const SMDS_MeshElement* elemInGroups,
1840                                         SMESHDS_Mesh *          aMesh)
1841 {
1842   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1843   if (!groups.empty()) {
1844     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1845     for ( ; grIt != groups.end(); grIt++ ) {
1846       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
1847       if ( group && group->Contains( elemInGroups ))
1848         group->SMDSGroup().Add( elemToAdd );
1849     }
1850   }
1851 }
1852
1853
1854 //=======================================================================
1855 //function : RemoveElemFromGroups
1856 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
1857 //=======================================================================
1858 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
1859                                              SMESHDS_Mesh *          aMesh)
1860 {
1861   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1862   if (!groups.empty())
1863   {
1864     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
1865     for (; GrIt != groups.end(); GrIt++)
1866     {
1867       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
1868       if (!grp || grp->IsEmpty()) continue;
1869       grp->SMDSGroup().Remove(removeelem);
1870     }
1871   }
1872 }
1873
1874 //================================================================================
1875 /*!
1876  * \brief Replace elemToRm by elemToAdd in the all groups
1877  */
1878 //================================================================================
1879
1880 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
1881                                             const SMDS_MeshElement* elemToAdd,
1882                                             SMESHDS_Mesh *          aMesh)
1883 {
1884   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1885   if (!groups.empty()) {
1886     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1887     for ( ; grIt != groups.end(); grIt++ ) {
1888       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
1889       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
1890         group->SMDSGroup().Add( elemToAdd );
1891     }
1892   }
1893 }
1894
1895 //================================================================================
1896 /*!
1897  * \brief Replace elemToRm by elemToAdd in the all groups
1898  */
1899 //================================================================================
1900
1901 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
1902                                             const vector<const SMDS_MeshElement*>& elemToAdd,
1903                                             SMESHDS_Mesh *                         aMesh)
1904 {
1905   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1906   if (!groups.empty())
1907   {
1908     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1909     for ( ; grIt != groups.end(); grIt++ ) {
1910       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
1911       if ( group && group->SMDSGroup().Remove( elemToRm ) )
1912         for ( int i = 0; i < elemToAdd.size(); ++i )
1913           group->SMDSGroup().Add( elemToAdd[ i ] );
1914     }
1915   }
1916 }
1917
1918 //=======================================================================
1919 //function : QuadToTri
1920 //purpose  : Cut quadrangles into triangles.
1921 //           theCrit is used to select a diagonal to cut
1922 //=======================================================================
1923
1924 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1925                                   const bool         the13Diag)
1926 {
1927   myLastCreatedElems.Clear();
1928   myLastCreatedNodes.Clear();
1929
1930   MESSAGE( "::QuadToTri()" );
1931
1932   SMESHDS_Mesh * aMesh = GetMeshDS();
1933
1934   Handle(Geom_Surface) surface;
1935   SMESH_MesherHelper   helper( *GetMesh() );
1936
1937   TIDSortedElemSet::iterator itElem;
1938   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1939     const SMDS_MeshElement* elem = *itElem;
1940     if ( !elem || elem->GetType() != SMDSAbs_Face )
1941       continue;
1942     bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
1943     if(!isquad) continue;
1944
1945     if(elem->NbNodes()==4) {
1946       // retrieve element nodes
1947       const SMDS_MeshNode* aNodes [4];
1948       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
1949       int i = 0;
1950       while ( itN->more() )
1951         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1952
1953       int aShapeId = FindShape( elem );
1954       const SMDS_MeshElement* newElem1 = 0;
1955       const SMDS_MeshElement* newElem2 = 0;
1956       if ( the13Diag ) {
1957         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1958         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1959       }
1960       else {
1961         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1962         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1963       }
1964       myLastCreatedElems.Append(newElem1);
1965       myLastCreatedElems.Append(newElem2);
1966       // put a new triangle on the same shape and add to the same groups
1967       if ( aShapeId )
1968         {
1969           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1970           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1971         }
1972       AddToSameGroups( newElem1, elem, aMesh );
1973       AddToSameGroups( newElem2, elem, aMesh );
1974       //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
1975       aMesh->RemoveElement( elem );
1976     }
1977
1978     // Quadratic quadrangle
1979
1980     if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
1981
1982       // get surface elem is on
1983       int aShapeId = FindShape( elem );
1984       if ( aShapeId != helper.GetSubShapeID() ) {
1985         surface.Nullify();
1986         TopoDS_Shape shape;
1987         if ( aShapeId > 0 )
1988           shape = aMesh->IndexToShape( aShapeId );
1989         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1990           TopoDS_Face face = TopoDS::Face( shape );
1991           surface = BRep_Tool::Surface( face );
1992           if ( !surface.IsNull() )
1993             helper.SetSubShape( shape );
1994         }
1995       }
1996
1997       const SMDS_MeshNode* aNodes [8];
1998       const SMDS_MeshNode* inFaceNode = 0;
1999       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2000       int i = 0;
2001       while ( itN->more() ) {
2002         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2003         if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2004              aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2005         {
2006           inFaceNode = aNodes[ i-1 ];
2007         }
2008       }
2009
2010       // find middle point for (0,1,2,3)
2011       // and create a node in this point;
2012       gp_XYZ p( 0,0,0 );
2013       if ( surface.IsNull() ) {
2014         for(i=0; i<4; i++)
2015           p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2016         p /= 4;
2017       }
2018       else {
2019         TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2020         gp_XY uv( 0,0 );
2021         for(i=0; i<4; i++)
2022           uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2023         uv /= 4.;
2024         p = surface->Value( uv.X(), uv.Y() ).XYZ();
2025       }
2026       const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2027       myLastCreatedNodes.Append(newN);
2028
2029       // create a new element
2030       const SMDS_MeshElement* newElem1 = 0;
2031       const SMDS_MeshElement* newElem2 = 0;
2032       if ( the13Diag ) {
2033         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2034                                   aNodes[6], aNodes[7], newN );
2035         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2036                                   newN,      aNodes[4], aNodes[5] );
2037       }
2038       else {
2039         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2040                                   aNodes[7], aNodes[4], newN );
2041         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2042                                   newN,      aNodes[5], aNodes[6] );
2043       }
2044       myLastCreatedElems.Append(newElem1);
2045       myLastCreatedElems.Append(newElem2);
2046       // put a new triangle on the same shape and add to the same groups
2047       if ( aShapeId )
2048         {
2049           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2050           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2051         }
2052       AddToSameGroups( newElem1, elem, aMesh );
2053       AddToSameGroups( newElem2, elem, aMesh );
2054       aMesh->RemoveElement( elem );
2055     }
2056   }
2057
2058   return true;
2059 }
2060
2061 //=======================================================================
2062 //function : getAngle
2063 //purpose  :
2064 //=======================================================================
2065
2066 double getAngle(const SMDS_MeshElement * tr1,
2067                 const SMDS_MeshElement * tr2,
2068                 const SMDS_MeshNode *    n1,
2069                 const SMDS_MeshNode *    n2)
2070 {
2071   double angle = 2. * M_PI; // bad angle
2072
2073   // get normals
2074   SMESH::Controls::TSequenceOfXYZ P1, P2;
2075   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2076        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2077     return angle;
2078   gp_Vec N1,N2;
2079   if(!tr1->IsQuadratic())
2080     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2081   else
2082     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2083   if ( N1.SquareMagnitude() <= gp::Resolution() )
2084     return angle;
2085   if(!tr2->IsQuadratic())
2086     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2087   else
2088     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2089   if ( N2.SquareMagnitude() <= gp::Resolution() )
2090     return angle;
2091
2092   // find the first diagonal node n1 in the triangles:
2093   // take in account a diagonal link orientation
2094   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2095   for ( int t = 0; t < 2; t++ ) {
2096     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2097     int i = 0, iDiag = -1;
2098     while ( it->more()) {
2099       const SMDS_MeshElement *n = it->next();
2100       if ( n == n1 || n == n2 ) {
2101         if ( iDiag < 0)
2102           iDiag = i;
2103         else {
2104           if ( i - iDiag == 1 )
2105             nFirst[ t ] = ( n == n1 ? n2 : n1 );
2106           else
2107             nFirst[ t ] = n;
2108           break;
2109         }
2110       }
2111       i++;
2112     }
2113   }
2114   if ( nFirst[ 0 ] == nFirst[ 1 ] )
2115     N2.Reverse();
2116
2117   angle = N1.Angle( N2 );
2118   //SCRUTE( angle );
2119   return angle;
2120 }
2121
2122 // =================================================
2123 // class generating a unique ID for a pair of nodes
2124 // and able to return nodes by that ID
2125 // =================================================
2126 class LinkID_Gen {
2127 public:
2128
2129   LinkID_Gen( const SMESHDS_Mesh* theMesh )
2130     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2131   {}
2132
2133   long GetLinkID (const SMDS_MeshNode * n1,
2134                   const SMDS_MeshNode * n2) const
2135   {
2136     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2137   }
2138
2139   bool GetNodes (const long             theLinkID,
2140                  const SMDS_MeshNode* & theNode1,
2141                  const SMDS_MeshNode* & theNode2) const
2142   {
2143     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2144     if ( !theNode1 ) return false;
2145     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2146     if ( !theNode2 ) return false;
2147     return true;
2148   }
2149
2150 private:
2151   LinkID_Gen();
2152   const SMESHDS_Mesh* myMesh;
2153   long                myMaxID;
2154 };
2155
2156
2157 //=======================================================================
2158 //function : TriToQuad
2159 //purpose  : Fuse neighbour triangles into quadrangles.
2160 //           theCrit is used to select a neighbour to fuse with.
2161 //           theMaxAngle is a max angle between element normals at which
2162 //           fusion is still performed.
2163 //=======================================================================
2164
2165 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
2166                                   SMESH::Controls::NumericalFunctorPtr theCrit,
2167                                   const double                         theMaxAngle)
2168 {
2169   myLastCreatedElems.Clear();
2170   myLastCreatedNodes.Clear();
2171
2172   MESSAGE( "::TriToQuad()" );
2173
2174   if ( !theCrit.get() )
2175     return false;
2176
2177   SMESHDS_Mesh * aMesh = GetMeshDS();
2178
2179   // Prepare data for algo: build
2180   // 1. map of elements with their linkIDs
2181   // 2. map of linkIDs with their elements
2182
2183   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2184   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2185   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
2186   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2187
2188   TIDSortedElemSet::iterator itElem;
2189   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2190     const SMDS_MeshElement* elem = *itElem;
2191     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2192     bool IsTria = elem->NbNodes()==3 || (elem->NbNodes()==6 && elem->IsQuadratic());
2193     if(!IsTria) continue;
2194
2195     // retrieve element nodes
2196     const SMDS_MeshNode* aNodes [4];
2197     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2198     int i = 0;
2199     while ( i<3 )
2200       aNodes[ i++ ] = cast2Node( itN->next() );
2201     aNodes[ 3 ] = aNodes[ 0 ];
2202
2203     // fill maps
2204     for ( i = 0; i < 3; i++ ) {
2205       SMESH_TLink link( aNodes[i], aNodes[i+1] );
2206       // check if elements sharing a link can be fused
2207       itLE = mapLi_listEl.find( link );
2208       if ( itLE != mapLi_listEl.end() ) {
2209         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2210           continue;
2211         const SMDS_MeshElement* elem2 = (*itLE).second.front();
2212         //if ( FindShape( elem ) != FindShape( elem2 ))
2213         //  continue; // do not fuse triangles laying on different shapes
2214         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2215           continue; // avoid making badly shaped quads
2216         (*itLE).second.push_back( elem );
2217       }
2218       else {
2219         mapLi_listEl[ link ].push_back( elem );
2220       }
2221       mapEl_setLi [ elem ].insert( link );
2222     }
2223   }
2224   // Clean the maps from the links shared by a sole element, ie
2225   // links to which only one element is bound in mapLi_listEl
2226
2227   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2228     int nbElems = (*itLE).second.size();
2229     if ( nbElems < 2  ) {
2230       const SMDS_MeshElement* elem = (*itLE).second.front();
2231       SMESH_TLink link = (*itLE).first;
2232       mapEl_setLi[ elem ].erase( link );
2233       if ( mapEl_setLi[ elem ].empty() )
2234         mapEl_setLi.erase( elem );
2235     }
2236   }
2237
2238   // Algo: fuse triangles into quadrangles
2239
2240   while ( ! mapEl_setLi.empty() ) {
2241     // Look for the start element:
2242     // the element having the least nb of shared links
2243     const SMDS_MeshElement* startElem = 0;
2244     int minNbLinks = 4;
2245     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2246       int nbLinks = (*itEL).second.size();
2247       if ( nbLinks < minNbLinks ) {
2248         startElem = (*itEL).first;
2249         minNbLinks = nbLinks;
2250         if ( minNbLinks == 1 )
2251           break;
2252       }
2253     }
2254
2255     // search elements to fuse starting from startElem or links of elements
2256     // fused earlyer - startLinks
2257     list< SMESH_TLink > startLinks;
2258     while ( startElem || !startLinks.empty() ) {
2259       while ( !startElem && !startLinks.empty() ) {
2260         // Get an element to start, by a link
2261         SMESH_TLink linkId = startLinks.front();
2262         startLinks.pop_front();
2263         itLE = mapLi_listEl.find( linkId );
2264         if ( itLE != mapLi_listEl.end() ) {
2265           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2266           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2267           for ( ; itE != listElem.end() ; itE++ )
2268             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2269               startElem = (*itE);
2270           mapLi_listEl.erase( itLE );
2271         }
2272       }
2273
2274       if ( startElem ) {
2275         // Get candidates to be fused
2276         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2277         const SMESH_TLink *link12, *link13;
2278         startElem = 0;
2279         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2280         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2281         ASSERT( !setLi.empty() );
2282         set< SMESH_TLink >::iterator itLi;
2283         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2284         {
2285           const SMESH_TLink & link = (*itLi);
2286           itLE = mapLi_listEl.find( link );
2287           if ( itLE == mapLi_listEl.end() )
2288             continue;
2289
2290           const SMDS_MeshElement* elem = (*itLE).second.front();
2291           if ( elem == tr1 )
2292             elem = (*itLE).second.back();
2293           mapLi_listEl.erase( itLE );
2294           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2295             continue;
2296           if ( tr2 ) {
2297             tr3 = elem;
2298             link13 = &link;
2299           }
2300           else {
2301             tr2 = elem;
2302             link12 = &link;
2303           }
2304
2305           // add other links of elem to list of links to re-start from
2306           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2307           set< SMESH_TLink >::iterator it;
2308           for ( it = links.begin(); it != links.end(); it++ ) {
2309             const SMESH_TLink& link2 = (*it);
2310             if ( link2 != link )
2311               startLinks.push_back( link2 );
2312           }
2313         }
2314
2315         // Get nodes of possible quadrangles
2316         const SMDS_MeshNode *n12 [4], *n13 [4];
2317         bool Ok12 = false, Ok13 = false;
2318         const SMDS_MeshNode *linkNode1, *linkNode2;
2319         if(tr2) {
2320           linkNode1 = link12->first;
2321           linkNode2 = link12->second;
2322           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2323             Ok12 = true;
2324         }
2325         if(tr3) {
2326           linkNode1 = link13->first;
2327           linkNode2 = link13->second;
2328           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2329             Ok13 = true;
2330         }
2331
2332         // Choose a pair to fuse
2333         if ( Ok12 && Ok13 ) {
2334           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2335           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2336           double aBadRate12 = getBadRate( &quad12, theCrit );
2337           double aBadRate13 = getBadRate( &quad13, theCrit );
2338           if (  aBadRate13 < aBadRate12 )
2339             Ok12 = false;
2340           else
2341             Ok13 = false;
2342         }
2343
2344         // Make quadrangles
2345         // and remove fused elems and removed links from the maps
2346         mapEl_setLi.erase( tr1 );
2347         if ( Ok12 ) {
2348           mapEl_setLi.erase( tr2 );
2349           mapLi_listEl.erase( *link12 );
2350           if(tr1->NbNodes()==3) {
2351             const SMDS_MeshElement* newElem = 0;
2352             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2353             myLastCreatedElems.Append(newElem);
2354             AddToSameGroups( newElem, tr1, aMesh );
2355             int aShapeId = tr1->getshapeId();
2356             if ( aShapeId )
2357               {
2358                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2359               }
2360             aMesh->RemoveElement( tr1 );
2361             aMesh->RemoveElement( tr2 );
2362           }
2363           else {
2364             const SMDS_MeshNode* N1 [6];
2365             const SMDS_MeshNode* N2 [6];
2366             GetNodesFromTwoTria(tr1,tr2,N1,N2);
2367             // now we receive following N1 and N2 (using numeration as above image)
2368             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
2369             // i.e. first nodes from both arrays determ new diagonal
2370             const SMDS_MeshNode* aNodes[8];
2371             aNodes[0] = N1[0];
2372             aNodes[1] = N1[1];
2373             aNodes[2] = N2[0];
2374             aNodes[3] = N2[1];
2375             aNodes[4] = N1[3];
2376             aNodes[5] = N2[5];
2377             aNodes[6] = N2[3];
2378             aNodes[7] = N1[5];
2379             const SMDS_MeshElement* newElem = 0;
2380             newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2381                                      aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2382             myLastCreatedElems.Append(newElem);
2383             AddToSameGroups( newElem, tr1, aMesh );
2384             int aShapeId = tr1->getshapeId();
2385             if ( aShapeId )
2386               {
2387                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2388               }
2389             aMesh->RemoveElement( tr1 );
2390             aMesh->RemoveElement( tr2 );
2391             // remove middle node (9)
2392             GetMeshDS()->RemoveNode( N1[4] );
2393           }
2394         }
2395         else if ( Ok13 ) {
2396           mapEl_setLi.erase( tr3 );
2397           mapLi_listEl.erase( *link13 );
2398           if(tr1->NbNodes()==3) {
2399             const SMDS_MeshElement* newElem = 0;
2400             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2401             myLastCreatedElems.Append(newElem);
2402             AddToSameGroups( newElem, tr1, aMesh );
2403             int aShapeId = tr1->getshapeId();
2404             if ( aShapeId )
2405               {
2406                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2407               }
2408             aMesh->RemoveElement( tr1 );
2409             aMesh->RemoveElement( tr3 );
2410           }
2411           else {
2412             const SMDS_MeshNode* N1 [6];
2413             const SMDS_MeshNode* N2 [6];
2414             GetNodesFromTwoTria(tr1,tr3,N1,N2);
2415             // now we receive following N1 and N2 (using numeration as above image)
2416             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
2417             // i.e. first nodes from both arrays determ new diagonal
2418             const SMDS_MeshNode* aNodes[8];
2419             aNodes[0] = N1[0];
2420             aNodes[1] = N1[1];
2421             aNodes[2] = N2[0];
2422             aNodes[3] = N2[1];
2423             aNodes[4] = N1[3];
2424             aNodes[5] = N2[5];
2425             aNodes[6] = N2[3];
2426             aNodes[7] = N1[5];
2427             const SMDS_MeshElement* newElem = 0;
2428             newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2429                                      aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2430             myLastCreatedElems.Append(newElem);
2431             AddToSameGroups( newElem, tr1, aMesh );
2432             int aShapeId = tr1->getshapeId();
2433             if ( aShapeId )
2434               {
2435                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2436               }
2437             aMesh->RemoveElement( tr1 );
2438             aMesh->RemoveElement( tr3 );
2439             // remove middle node (9)
2440             GetMeshDS()->RemoveNode( N1[4] );
2441           }
2442         }
2443
2444         // Next element to fuse: the rejected one
2445         if ( tr3 )
2446           startElem = Ok12 ? tr3 : tr2;
2447
2448       } // if ( startElem )
2449     } // while ( startElem || !startLinks.empty() )
2450   } // while ( ! mapEl_setLi.empty() )
2451
2452   return true;
2453 }
2454
2455
2456 /*#define DUMPSO(txt) \
2457 //  cout << txt << endl;
2458 //=============================================================================
2459 //
2460 //
2461 //
2462 //=============================================================================
2463 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2464 {
2465 if ( i1 == i2 )
2466 return;
2467 int tmp = idNodes[ i1 ];
2468 idNodes[ i1 ] = idNodes[ i2 ];
2469 idNodes[ i2 ] = tmp;
2470 gp_Pnt Ptmp = P[ i1 ];
2471 P[ i1 ] = P[ i2 ];
2472 P[ i2 ] = Ptmp;
2473 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2474 }
2475
2476 //=======================================================================
2477 //function : SortQuadNodes
2478 //purpose  : Set 4 nodes of a quadrangle face in a good order.
2479 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
2480 //           1 or 2 else 0.
2481 //=======================================================================
2482
2483 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2484 int               idNodes[] )
2485 {
2486   gp_Pnt P[4];
2487   int i;
2488   for ( i = 0; i < 4; i++ ) {
2489     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2490     if ( !n ) return 0;
2491     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2492   }
2493
2494   gp_Vec V1(P[0], P[1]);
2495   gp_Vec V2(P[0], P[2]);
2496   gp_Vec V3(P[0], P[3]);
2497
2498   gp_Vec Cross1 = V1 ^ V2;
2499   gp_Vec Cross2 = V2 ^ V3;
2500
2501   i = 0;
2502   if (Cross1.Dot(Cross2) < 0)
2503   {
2504     Cross1 = V2 ^ V1;
2505     Cross2 = V1 ^ V3;
2506
2507     if (Cross1.Dot(Cross2) < 0)
2508       i = 2;
2509     else
2510       i = 1;
2511     swap ( i, i + 1, idNodes, P );
2512
2513     //     for ( int ii = 0; ii < 4; ii++ ) {
2514     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2515     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2516     //     }
2517   }
2518   return i;
2519 }
2520
2521 //=======================================================================
2522 //function : SortHexaNodes
2523 //purpose  : Set 8 nodes of a hexahedron in a good order.
2524 //           Return success status
2525 //=======================================================================
2526
2527 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2528                                       int               idNodes[] )
2529 {
2530   gp_Pnt P[8];
2531   int i;
2532   DUMPSO( "INPUT: ========================================");
2533   for ( i = 0; i < 8; i++ ) {
2534     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2535     if ( !n ) return false;
2536     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2537     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2538   }
2539   DUMPSO( "========================================");
2540
2541
2542   set<int> faceNodes;  // ids of bottom face nodes, to be found
2543   set<int> checkedId1; // ids of tried 2-nd nodes
2544   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2545   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
2546   int iMin, iLoop1 = 0;
2547
2548   // Loop to try the 2-nd nodes
2549
2550   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2551   {
2552     // Find not checked 2-nd node
2553     for ( i = 1; i < 8; i++ )
2554       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2555         int id1 = idNodes[i];
2556         swap ( 1, i, idNodes, P );
2557         checkedId1.insert ( id1 );
2558         break;
2559       }
2560
2561     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2562     // ie that all but meybe one (id3 which is on the same face) nodes
2563     // lay on the same side from the triangle plane.
2564
2565     bool manyInPlane = false; // more than 4 nodes lay in plane
2566     int iLoop2 = 0;
2567     while ( ++iLoop2 < 6 ) {
2568
2569       // get 1-2-3 plane coeffs
2570       Standard_Real A, B, C, D;
2571       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2572       if ( N.SquareMagnitude() > gp::Resolution() )
2573       {
2574         gp_Pln pln ( P[0], N );
2575         pln.Coefficients( A, B, C, D );
2576
2577         // find the node (iMin) closest to pln
2578         Standard_Real dist[ 8 ], minDist = DBL_MAX;
2579         set<int> idInPln;
2580         for ( i = 3; i < 8; i++ ) {
2581           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2582           if ( fabs( dist[i] ) < minDist ) {
2583             minDist = fabs( dist[i] );
2584             iMin = i;
2585           }
2586           if ( fabs( dist[i] ) <= tol )
2587             idInPln.insert( idNodes[i] );
2588         }
2589
2590         // there should not be more than 4 nodes in bottom plane
2591         if ( idInPln.size() > 1 )
2592         {
2593           DUMPSO( "### idInPln.size() = " << idInPln.size());
2594           // idInPlane does not contain the first 3 nodes
2595           if ( manyInPlane || idInPln.size() == 5)
2596             return false; // all nodes in one plane
2597           manyInPlane = true;
2598
2599           // set the 1-st node to be not in plane
2600           for ( i = 3; i < 8; i++ ) {
2601             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2602               DUMPSO( "### Reset 0-th node");
2603               swap( 0, i, idNodes, P );
2604               break;
2605             }
2606           }
2607
2608           // reset to re-check second nodes
2609           leastDist = DBL_MAX;
2610           faceNodes.clear();
2611           checkedId1.clear();
2612           iLoop1 = 0;
2613           break; // from iLoop2;
2614         }
2615
2616         // check that the other 4 nodes are on the same side
2617         bool sameSide = true;
2618         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2619         for ( i = 3; sameSide && i < 8; i++ ) {
2620           if ( i != iMin )
2621             sameSide = ( isNeg == dist[i] <= 0.);
2622         }
2623
2624         // keep best solution
2625         if ( sameSide && minDist < leastDist ) {
2626           leastDist = minDist;
2627           faceNodes.clear();
2628           faceNodes.insert( idNodes[ 1 ] );
2629           faceNodes.insert( idNodes[ 2 ] );
2630           faceNodes.insert( idNodes[ iMin ] );
2631           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2632                   << " leastDist = " << leastDist);
2633           if ( leastDist <= DBL_MIN )
2634             break;
2635         }
2636       }
2637
2638       // set next 3-d node to check
2639       int iNext = 2 + iLoop2;
2640       if ( iNext < 8 ) {
2641         DUMPSO( "Try 2-nd");
2642         swap ( 2, iNext, idNodes, P );
2643       }
2644     } // while ( iLoop2 < 6 )
2645   } // iLoop1
2646
2647   if ( faceNodes.empty() ) return false;
2648
2649   // Put the faceNodes in proper places
2650   for ( i = 4; i < 8; i++ ) {
2651     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2652       // find a place to put
2653       int iTo = 1;
2654       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2655         iTo++;
2656       DUMPSO( "Set faceNodes");
2657       swap ( iTo, i, idNodes, P );
2658     }
2659   }
2660
2661
2662   // Set nodes of the found bottom face in good order
2663   DUMPSO( " Found bottom face: ");
2664   i = SortQuadNodes( theMesh, idNodes );
2665   if ( i ) {
2666     gp_Pnt Ptmp = P[ i ];
2667     P[ i ] = P[ i+1 ];
2668     P[ i+1 ] = Ptmp;
2669   }
2670   //   else
2671   //     for ( int ii = 0; ii < 4; ii++ ) {
2672   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2673   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2674   //    }
2675
2676   // Gravity center of the top and bottom faces
2677   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2678   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2679
2680   // Get direction from the bottom to the top face
2681   gp_Vec upDir ( aGCb, aGCt );
2682   Standard_Real upDirSize = upDir.Magnitude();
2683   if ( upDirSize <= gp::Resolution() ) return false;
2684   upDir / upDirSize;
2685
2686   // Assure that the bottom face normal points up
2687   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2688   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2689   if ( Nb.Dot( upDir ) < 0 ) {
2690     DUMPSO( "Reverse bottom face");
2691     swap( 1, 3, idNodes, P );
2692   }
2693
2694   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2695   Standard_Real minDist = DBL_MAX;
2696   for ( i = 4; i < 8; i++ ) {
2697     // projection of P[i] to the plane defined by P[0] and upDir
2698     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2699     Standard_Real sqDist = P[0].SquareDistance( Pp );
2700     if ( sqDist < minDist ) {
2701       minDist = sqDist;
2702       iMin = i;
2703     }
2704   }
2705   DUMPSO( "Set 4-th");
2706   swap ( 4, iMin, idNodes, P );
2707
2708   // Set nodes of the top face in good order
2709   DUMPSO( "Sort top face");
2710   i = SortQuadNodes( theMesh, &idNodes[4] );
2711   if ( i ) {
2712     i += 4;
2713     gp_Pnt Ptmp = P[ i ];
2714     P[ i ] = P[ i+1 ];
2715     P[ i+1 ] = Ptmp;
2716   }
2717
2718   // Assure that direction of the top face normal is from the bottom face
2719   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
2720   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
2721   if ( Nt.Dot( upDir ) < 0 ) {
2722     DUMPSO( "Reverse top face");
2723     swap( 5, 7, idNodes, P );
2724   }
2725
2726   //   DUMPSO( "OUTPUT: ========================================");
2727   //   for ( i = 0; i < 8; i++ ) {
2728   //     float *p = ugrid->GetPoint(idNodes[i]);
2729   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
2730   //   }
2731
2732   return true;
2733 }*/
2734
2735 //================================================================================
2736 /*!
2737  * \brief Return nodes linked to the given one
2738  * \param theNode - the node
2739  * \param linkedNodes - the found nodes
2740  * \param type - the type of elements to check
2741  *
2742  * Medium nodes are ignored
2743  */
2744 //================================================================================
2745
2746 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
2747                                        TIDSortedElemSet &   linkedNodes,
2748                                        SMDSAbs_ElementType  type )
2749 {
2750   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
2751   while ( elemIt->more() )
2752   {
2753     const SMDS_MeshElement* elem = elemIt->next();
2754     if(elem->GetType() == SMDSAbs_0DElement)
2755       continue;
2756     
2757     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
2758     if ( elem->GetType() == SMDSAbs_Volume )
2759     {
2760       SMDS_VolumeTool vol( elem );
2761       while ( nodeIt->more() ) {
2762         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2763         if ( theNode != n && vol.IsLinked( theNode, n ))
2764           linkedNodes.insert( n );
2765       }
2766     }
2767     else
2768     {
2769       for ( int i = 0; nodeIt->more(); ++i ) {
2770         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2771         if ( n == theNode ) {
2772           int iBefore = i - 1;
2773           int iAfter  = i + 1;
2774           if ( elem->IsQuadratic() ) {
2775             int nb = elem->NbNodes() / 2;
2776             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
2777             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
2778           }
2779           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
2780           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
2781         }
2782       }
2783     }
2784   }
2785 }
2786
2787 //=======================================================================
2788 //function : laplacianSmooth
2789 //purpose  : pulls theNode toward the center of surrounding nodes directly
2790 //           connected to that node along an element edge
2791 //=======================================================================
2792
2793 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
2794                      const Handle(Geom_Surface)&          theSurface,
2795                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2796 {
2797   // find surrounding nodes
2798
2799   TIDSortedElemSet nodeSet;
2800   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
2801
2802   // compute new coodrs
2803
2804   double coord[] = { 0., 0., 0. };
2805   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
2806   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
2807     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
2808     if ( theSurface.IsNull() ) { // smooth in 3D
2809       coord[0] += node->X();
2810       coord[1] += node->Y();
2811       coord[2] += node->Z();
2812     }
2813     else { // smooth in 2D
2814       ASSERT( theUVMap.find( node ) != theUVMap.end() );
2815       gp_XY* uv = theUVMap[ node ];
2816       coord[0] += uv->X();
2817       coord[1] += uv->Y();
2818     }
2819   }
2820   int nbNodes = nodeSet.size();
2821   if ( !nbNodes )
2822     return;
2823   coord[0] /= nbNodes;
2824   coord[1] /= nbNodes;
2825
2826   if ( !theSurface.IsNull() ) {
2827     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
2828     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
2829     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
2830     coord[0] = p3d.X();
2831     coord[1] = p3d.Y();
2832     coord[2] = p3d.Z();
2833   }
2834   else
2835     coord[2] /= nbNodes;
2836
2837   // move node
2838
2839   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
2840 }
2841
2842 //=======================================================================
2843 //function : centroidalSmooth
2844 //purpose  : pulls theNode toward the element-area-weighted centroid of the
2845 //           surrounding elements
2846 //=======================================================================
2847
2848 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
2849                       const Handle(Geom_Surface)&          theSurface,
2850                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2851 {
2852   gp_XYZ aNewXYZ(0.,0.,0.);
2853   SMESH::Controls::Area anAreaFunc;
2854   double totalArea = 0.;
2855   int nbElems = 0;
2856
2857   // compute new XYZ
2858
2859   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
2860   while ( elemIt->more() )
2861   {
2862     const SMDS_MeshElement* elem = elemIt->next();
2863     nbElems++;
2864
2865     gp_XYZ elemCenter(0.,0.,0.);
2866     SMESH::Controls::TSequenceOfXYZ aNodePoints;
2867     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2868     int nn = elem->NbNodes();
2869     if(elem->IsQuadratic()) nn = nn/2;
2870     int i=0;
2871     //while ( itN->more() ) {
2872     while ( i<nn ) {
2873       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
2874       i++;
2875       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
2876       aNodePoints.push_back( aP );
2877       if ( !theSurface.IsNull() ) { // smooth in 2D
2878         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
2879         gp_XY* uv = theUVMap[ aNode ];
2880         aP.SetCoord( uv->X(), uv->Y(), 0. );
2881       }
2882       elemCenter += aP;
2883     }
2884     double elemArea = anAreaFunc.GetValue( aNodePoints );
2885     totalArea += elemArea;
2886     elemCenter /= nn;
2887     aNewXYZ += elemCenter * elemArea;
2888   }
2889   aNewXYZ /= totalArea;
2890   if ( !theSurface.IsNull() ) {
2891     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
2892     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
2893   }
2894
2895   // move node
2896
2897   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
2898 }
2899
2900 //=======================================================================
2901 //function : getClosestUV
2902 //purpose  : return UV of closest projection
2903 //=======================================================================
2904
2905 static bool getClosestUV (Extrema_GenExtPS& projector,
2906                           const gp_Pnt&     point,
2907                           gp_XY &           result)
2908 {
2909   projector.Perform( point );
2910   if ( projector.IsDone() ) {
2911     double u, v, minVal = DBL_MAX;
2912     for ( int i = projector.NbExt(); i > 0; i-- )
2913 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
2914       if ( projector.SquareDistance( i ) < minVal ) {
2915         minVal = projector.SquareDistance( i );
2916 #else
2917       if ( projector.Value( i ) < minVal ) {
2918         minVal = projector.Value( i );
2919 #endif
2920         projector.Point( i ).Parameter( u, v );
2921       }
2922     result.SetCoord( u, v );
2923     return true;
2924   }
2925   return false;
2926 }
2927
2928 //=======================================================================
2929 //function : Smooth
2930 //purpose  : Smooth theElements during theNbIterations or until a worst
2931 //           element has aspect ratio <= theTgtAspectRatio.
2932 //           Aspect Ratio varies in range [1.0, inf].
2933 //           If theElements is empty, the whole mesh is smoothed.
2934 //           theFixedNodes contains additionally fixed nodes. Nodes built
2935 //           on edges and boundary nodes are always fixed.
2936 //=======================================================================
2937
2938 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
2939                                set<const SMDS_MeshNode*> & theFixedNodes,
2940                                const SmoothMethod          theSmoothMethod,
2941                                const int                   theNbIterations,
2942                                double                      theTgtAspectRatio,
2943                                const bool                  the2D)
2944 {
2945   myLastCreatedElems.Clear();
2946   myLastCreatedNodes.Clear();
2947
2948   MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
2949
2950   if ( theTgtAspectRatio < 1.0 )
2951     theTgtAspectRatio = 1.0;
2952
2953   const double disttol = 1.e-16;
2954
2955   SMESH::Controls::AspectRatio aQualityFunc;
2956
2957   SMESHDS_Mesh* aMesh = GetMeshDS();
2958
2959   if ( theElems.empty() ) {
2960     // add all faces to theElems
2961     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
2962     while ( fIt->more() ) {
2963       const SMDS_MeshElement* face = fIt->next();
2964       theElems.insert( face );
2965     }
2966   }
2967   // get all face ids theElems are on
2968   set< int > faceIdSet;
2969   TIDSortedElemSet::iterator itElem;
2970   if ( the2D )
2971     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2972       int fId = FindShape( *itElem );
2973       // check that corresponding submesh exists and a shape is face
2974       if (fId &&
2975           faceIdSet.find( fId ) == faceIdSet.end() &&
2976           aMesh->MeshElements( fId )) {
2977         TopoDS_Shape F = aMesh->IndexToShape( fId );
2978         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
2979           faceIdSet.insert( fId );
2980       }
2981     }
2982   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
2983
2984   // ===============================================
2985   // smooth elements on each TopoDS_Face separately
2986   // ===============================================
2987
2988   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
2989   for ( ; fId != faceIdSet.rend(); ++fId ) {
2990     // get face surface and submesh
2991     Handle(Geom_Surface) surface;
2992     SMESHDS_SubMesh* faceSubMesh = 0;
2993     TopoDS_Face face;
2994     double fToler2 = 0, f,l;
2995     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
2996     bool isUPeriodic = false, isVPeriodic = false;
2997     if ( *fId ) {
2998       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
2999       surface = BRep_Tool::Surface( face );
3000       faceSubMesh = aMesh->MeshElements( *fId );
3001       fToler2 = BRep_Tool::Tolerance( face );
3002       fToler2 *= fToler2 * 10.;
3003       isUPeriodic = surface->IsUPeriodic();
3004       if ( isUPeriodic )
3005         surface->UPeriod();
3006       isVPeriodic = surface->IsVPeriodic();
3007       if ( isVPeriodic )
3008         surface->VPeriod();
3009       surface->Bounds( u1, u2, v1, v2 );
3010     }
3011     // ---------------------------------------------------------
3012     // for elements on a face, find movable and fixed nodes and
3013     // compute UV for them
3014     // ---------------------------------------------------------
3015     bool checkBoundaryNodes = false;
3016     bool isQuadratic = false;
3017     set<const SMDS_MeshNode*> setMovableNodes;
3018     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3019     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3020     list< const SMDS_MeshElement* > elemsOnFace;
3021
3022     Extrema_GenExtPS projector;
3023     GeomAdaptor_Surface surfAdaptor;
3024     if ( !surface.IsNull() ) {
3025       surfAdaptor.Load( surface );
3026       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3027     }
3028     int nbElemOnFace = 0;
3029     itElem = theElems.begin();
3030     // loop on not yet smoothed elements: look for elems on a face
3031     while ( itElem != theElems.end() ) {
3032       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3033         break; // all elements found
3034
3035       const SMDS_MeshElement* elem = *itElem;
3036       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3037            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3038         ++itElem;
3039         continue;
3040       }
3041       elemsOnFace.push_back( elem );
3042       theElems.erase( itElem++ );
3043       nbElemOnFace++;
3044
3045       if ( !isQuadratic )
3046         isQuadratic = elem->IsQuadratic();
3047
3048       // get movable nodes of elem
3049       const SMDS_MeshNode* node;
3050       SMDS_TypeOfPosition posType;
3051       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3052       int nn = 0, nbn =  elem->NbNodes();
3053       if(elem->IsQuadratic())
3054         nbn = nbn/2;
3055       while ( nn++ < nbn ) {
3056         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3057         const SMDS_PositionPtr& pos = node->GetPosition();
3058         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3059         if (posType != SMDS_TOP_EDGE &&
3060             posType != SMDS_TOP_VERTEX &&
3061             theFixedNodes.find( node ) == theFixedNodes.end())
3062         {
3063           // check if all faces around the node are on faceSubMesh
3064           // because a node on edge may be bound to face
3065           SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3066           bool all = true;
3067           if ( faceSubMesh ) {
3068             while ( eIt->more() && all ) {
3069               const SMDS_MeshElement* e = eIt->next();
3070               all = faceSubMesh->Contains( e );
3071             }
3072           }
3073           if ( all )
3074             setMovableNodes.insert( node );
3075           else
3076             checkBoundaryNodes = true;
3077         }
3078         if ( posType == SMDS_TOP_3DSPACE )
3079           checkBoundaryNodes = true;
3080       }
3081
3082       if ( surface.IsNull() )
3083         continue;
3084
3085       // get nodes to check UV
3086       list< const SMDS_MeshNode* > uvCheckNodes;
3087       itN = elem->nodesIterator();
3088       nn = 0; nbn =  elem->NbNodes();
3089       if(elem->IsQuadratic())
3090         nbn = nbn/2;
3091       while ( nn++ < nbn ) {
3092         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3093         if ( uvMap.find( node ) == uvMap.end() )
3094           uvCheckNodes.push_back( node );
3095         // add nodes of elems sharing node
3096         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3097         //         while ( eIt->more() ) {
3098         //           const SMDS_MeshElement* e = eIt->next();
3099         //           if ( e != elem ) {
3100         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3101         //             while ( nIt->more() ) {
3102         //               const SMDS_MeshNode* n =
3103         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3104         //               if ( uvMap.find( n ) == uvMap.end() )
3105         //                 uvCheckNodes.push_back( n );
3106         //             }
3107         //           }
3108         //         }
3109       }
3110       // check UV on face
3111       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3112       for ( ; n != uvCheckNodes.end(); ++n ) {
3113         node = *n;
3114         gp_XY uv( 0, 0 );
3115         const SMDS_PositionPtr& pos = node->GetPosition();
3116         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3117         // get existing UV
3118         switch ( posType ) {
3119         case SMDS_TOP_FACE: {
3120           SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3121           uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3122           break;
3123         }
3124         case SMDS_TOP_EDGE: {
3125           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3126           Handle(Geom2d_Curve) pcurve;
3127           if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3128             pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3129           if ( !pcurve.IsNull() ) {
3130             double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3131             uv = pcurve->Value( u ).XY();
3132           }
3133           break;
3134         }
3135         case SMDS_TOP_VERTEX: {
3136           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3137           if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3138             uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3139           break;
3140         }
3141         default:;
3142         }
3143         // check existing UV
3144         bool project = true;
3145         gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3146         double dist1 = DBL_MAX, dist2 = 0;
3147         if ( posType != SMDS_TOP_3DSPACE ) {
3148           dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3149           project = dist1 > fToler2;
3150         }
3151         if ( project ) { // compute new UV
3152           gp_XY newUV;
3153           if ( !getClosestUV( projector, pNode, newUV )) {
3154             MESSAGE("Node Projection Failed " << node);
3155           }
3156           else {
3157             if ( isUPeriodic )
3158               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3159             if ( isVPeriodic )
3160               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3161             // check new UV
3162             if ( posType != SMDS_TOP_3DSPACE )
3163               dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3164             if ( dist2 < dist1 )
3165               uv = newUV;
3166           }
3167         }
3168         // store UV in the map
3169         listUV.push_back( uv );
3170         uvMap.insert( make_pair( node, &listUV.back() ));
3171       }
3172     } // loop on not yet smoothed elements
3173
3174     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3175       checkBoundaryNodes = true;
3176
3177     // fix nodes on mesh boundary
3178
3179     if ( checkBoundaryNodes ) {
3180       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3181       map< SMESH_TLink, int >::iterator link_nb;
3182       // put all elements links to linkNbMap
3183       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3184       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3185         const SMDS_MeshElement* elem = (*elemIt);
3186         int nbn =  elem->NbCornerNodes();
3187         // loop on elem links: insert them in linkNbMap
3188         for ( int iN = 0; iN < nbn; ++iN ) {
3189           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3190           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3191           SMESH_TLink link( n1, n2 );
3192           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3193           link_nb->second++;
3194         }
3195       }
3196       // remove nodes that are in links encountered only once from setMovableNodes
3197       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3198         if ( link_nb->second == 1 ) {
3199           setMovableNodes.erase( link_nb->first.node1() );
3200           setMovableNodes.erase( link_nb->first.node2() );
3201         }
3202       }
3203     }
3204
3205     // -----------------------------------------------------
3206     // for nodes on seam edge, compute one more UV ( uvMap2 );
3207     // find movable nodes linked to nodes on seam and which
3208     // are to be smoothed using the second UV ( uvMap2 )
3209     // -----------------------------------------------------
3210
3211     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3212     if ( !surface.IsNull() ) {
3213       TopExp_Explorer eExp( face, TopAbs_EDGE );
3214       for ( ; eExp.More(); eExp.Next() ) {
3215         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3216         if ( !BRep_Tool::IsClosed( edge, face ))
3217           continue;
3218         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3219         if ( !sm ) continue;
3220         // find out which parameter varies for a node on seam
3221         double f,l;
3222         gp_Pnt2d uv1, uv2;
3223         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3224         if ( pcurve.IsNull() ) continue;
3225         uv1 = pcurve->Value( f );
3226         edge.Reverse();
3227         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3228         if ( pcurve.IsNull() ) continue;
3229         uv2 = pcurve->Value( f );
3230         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3231         // assure uv1 < uv2
3232         if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3233           gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3234         }
3235         // get nodes on seam and its vertices
3236         list< const SMDS_MeshNode* > seamNodes;
3237         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3238         while ( nSeamIt->more() ) {
3239           const SMDS_MeshNode* node = nSeamIt->next();
3240           if ( !isQuadratic || !IsMedium( node ))
3241             seamNodes.push_back( node );
3242         }
3243         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3244         for ( ; vExp.More(); vExp.Next() ) {
3245           sm = aMesh->MeshElements( vExp.Current() );
3246           if ( sm ) {
3247             nSeamIt = sm->GetNodes();
3248             while ( nSeamIt->more() )
3249               seamNodes.push_back( nSeamIt->next() );
3250           }
3251         }
3252         // loop on nodes on seam
3253         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3254         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3255           const SMDS_MeshNode* nSeam = *noSeIt;
3256           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3257           if ( n_uv == uvMap.end() )
3258             continue;
3259           // set the first UV
3260           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3261           // set the second UV
3262           listUV.push_back( *n_uv->second );
3263           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3264           if ( uvMap2.empty() )
3265             uvMap2 = uvMap; // copy the uvMap contents
3266           uvMap2[ nSeam ] = &listUV.back();
3267
3268           // collect movable nodes linked to ones on seam in nodesNearSeam
3269           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3270           while ( eIt->more() ) {
3271             const SMDS_MeshElement* e = eIt->next();
3272             int nbUseMap1 = 0, nbUseMap2 = 0;
3273             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3274             int nn = 0, nbn =  e->NbNodes();
3275             if(e->IsQuadratic()) nbn = nbn/2;
3276             while ( nn++ < nbn )
3277             {
3278               const SMDS_MeshNode* n =
3279                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3280               if (n == nSeam ||
3281                   setMovableNodes.find( n ) == setMovableNodes.end() )
3282                 continue;
3283               // add only nodes being closer to uv2 than to uv1
3284               gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3285                            0.5 * ( n->Y() + nSeam->Y() ),
3286                            0.5 * ( n->Z() + nSeam->Z() ));
3287               gp_XY uv;
3288               getClosestUV( projector, pMid, uv );
3289               if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3290                 nodesNearSeam.insert( n );
3291                 nbUseMap2++;
3292               }
3293               else
3294                 nbUseMap1++;
3295             }
3296             // for centroidalSmooth all element nodes must
3297             // be on one side of a seam
3298             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3299               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3300               nn = 0;
3301               while ( nn++ < nbn ) {
3302                 const SMDS_MeshNode* n =
3303                   static_cast<const SMDS_MeshNode*>( nIt->next() );
3304                 setMovableNodes.erase( n );
3305               }
3306             }
3307           }
3308         } // loop on nodes on seam
3309       } // loop on edge of a face
3310     } // if ( !face.IsNull() )
3311
3312     if ( setMovableNodes.empty() ) {
3313       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3314       continue; // goto next face
3315     }
3316
3317     // -------------
3318     // SMOOTHING //
3319     // -------------
3320
3321     int it = -1;
3322     double maxRatio = -1., maxDisplacement = -1.;
3323     set<const SMDS_MeshNode*>::iterator nodeToMove;
3324     for ( it = 0; it < theNbIterations; it++ ) {
3325       maxDisplacement = 0.;
3326       nodeToMove = setMovableNodes.begin();
3327       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3328         const SMDS_MeshNode* node = (*nodeToMove);
3329         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3330
3331         // smooth
3332         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3333         if ( theSmoothMethod == LAPLACIAN )
3334           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3335         else
3336           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3337
3338         // node displacement
3339         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3340         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3341         if ( aDispl > maxDisplacement )
3342           maxDisplacement = aDispl;
3343       }
3344       // no node movement => exit
3345       //if ( maxDisplacement < 1.e-16 ) {
3346       if ( maxDisplacement < disttol ) {
3347         MESSAGE("-- no node movement --");
3348         break;
3349       }
3350
3351       // check elements quality
3352       maxRatio  = 0;
3353       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3354       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3355         const SMDS_MeshElement* elem = (*elemIt);
3356         if ( !elem || elem->GetType() != SMDSAbs_Face )
3357           continue;
3358         SMESH::Controls::TSequenceOfXYZ aPoints;
3359         if ( aQualityFunc.GetPoints( elem, aPoints )) {
3360           double aValue = aQualityFunc.GetValue( aPoints );
3361           if ( aValue > maxRatio )
3362             maxRatio = aValue;
3363         }
3364       }
3365       if ( maxRatio <= theTgtAspectRatio ) {
3366         MESSAGE("-- quality achived --");
3367         break;
3368       }
3369       if (it+1 == theNbIterations) {
3370         MESSAGE("-- Iteration limit exceeded --");
3371       }
3372     } // smoothing iterations
3373
3374     MESSAGE(" Face id: " << *fId <<
3375             " Nb iterstions: " << it <<
3376             " Displacement: " << maxDisplacement <<
3377             " Aspect Ratio " << maxRatio);
3378
3379     // ---------------------------------------
3380     // new nodes positions are computed,
3381     // record movement in DS and set new UV
3382     // ---------------------------------------
3383     nodeToMove = setMovableNodes.begin();
3384     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3385       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3386       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3387       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3388       if ( node_uv != uvMap.end() ) {
3389         gp_XY* uv = node_uv->second;
3390         node->SetPosition
3391           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3392       }
3393     }
3394
3395     // move medium nodes of quadratic elements
3396     if ( isQuadratic )
3397     {
3398       SMESH_MesherHelper helper( *GetMesh() );
3399       if ( !face.IsNull() )
3400         helper.SetSubShape( face );
3401       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3402       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3403         const SMDS_VtkFace* QF =
3404           dynamic_cast<const SMDS_VtkFace*> (*elemIt);
3405         if(QF && QF->IsQuadratic()) {
3406           vector<const SMDS_MeshNode*> Ns;
3407           Ns.reserve(QF->NbNodes()+1);
3408           SMDS_ElemIteratorPtr anIter = QF->interlacedNodesElemIterator();
3409           while ( anIter->more() )
3410             Ns.push_back( cast2Node(anIter->next()) );
3411           Ns.push_back( Ns[0] );
3412           double x, y, z;
3413           for(int i=0; i<QF->NbNodes(); i=i+2) {
3414             if ( !surface.IsNull() ) {
3415               gp_XY uv1 = helper.GetNodeUV( face, Ns[i], Ns[i+2] );
3416               gp_XY uv2 = helper.GetNodeUV( face, Ns[i+2], Ns[i] );
3417               gp_XY uv = ( uv1 + uv2 ) / 2.;
3418               gp_Pnt xyz = surface->Value( uv.X(), uv.Y() );
3419               x = xyz.X(); y = xyz.Y(); z = xyz.Z();
3420             }
3421             else {
3422               x = (Ns[i]->X() + Ns[i+2]->X())/2;
3423               y = (Ns[i]->Y() + Ns[i+2]->Y())/2;
3424               z = (Ns[i]->Z() + Ns[i+2]->Z())/2;
3425             }
3426             if( fabs( Ns[i+1]->X() - x ) > disttol ||
3427                 fabs( Ns[i+1]->Y() - y ) > disttol ||
3428                 fabs( Ns[i+1]->Z() - z ) > disttol ) {
3429               // we have to move i+1 node
3430               aMesh->MoveNode( Ns[i+1], x, y, z );
3431             }
3432           }
3433         }
3434       }
3435     }
3436
3437   } // loop on face ids
3438
3439 }
3440
3441 //=======================================================================
3442 //function : isReverse
3443 //purpose  : Return true if normal of prevNodes is not co-directied with
3444 //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3445 //           iNotSame is where prevNodes and nextNodes are different.
3446 //           If result is true then future volume orientation is OK
3447 //=======================================================================
3448
3449 static bool isReverse(const SMDS_MeshElement*             face,
3450                       const vector<const SMDS_MeshNode*>& prevNodes,
3451                       const vector<const SMDS_MeshNode*>& nextNodes,
3452                       const int                           iNotSame)
3453 {
3454
3455   SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
3456   SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
3457   gp_XYZ extrDir( pN - pP ), faceNorm;
3458   SMESH_Algo::FaceNormal( face, faceNorm, /*normalized=*/false );
3459
3460   return faceNorm * extrDir < 0.0;
3461 }
3462
3463 //=======================================================================
3464 /*!
3465  * \brief Create elements by sweeping an element
3466  * \param elem - element to sweep
3467  * \param newNodesItVec - nodes generated from each node of the element
3468  * \param newElems - generated elements
3469  * \param nbSteps - number of sweeping steps
3470  * \param srcElements - to append elem for each generated element
3471  */
3472 //=======================================================================
3473
3474 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
3475                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3476                                     list<const SMDS_MeshElement*>&        newElems,
3477                                     const int                             nbSteps,
3478                                     SMESH_SequenceOfElemPtr&              srcElements)
3479 {
3480   //MESSAGE("sweepElement " << nbSteps);
3481   SMESHDS_Mesh* aMesh = GetMeshDS();
3482
3483   const int           nbNodes = elem->NbNodes();          
3484   const int         nbCorners = elem->NbCornerNodes();
3485   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of 
3486                                                           polyhedron creation !!! */
3487   // Loop on elem nodes:
3488   // find new nodes and detect same nodes indices
3489   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3490   vector<const SMDS_MeshNode*> prevNod( nbNodes );
3491   vector<const SMDS_MeshNode*> nextNod( nbNodes );
3492   vector<const SMDS_MeshNode*> midlNod( nbNodes );
3493
3494   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
3495   vector<int> sames(nbNodes);
3496   vector<bool> isSingleNode(nbNodes);
3497
3498   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3499     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
3500     const SMDS_MeshNode*                         node = nnIt->first;
3501     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3502     if ( listNewNodes.empty() )
3503       return;
3504
3505     itNN   [ iNode ] = listNewNodes.begin();
3506     prevNod[ iNode ] = node;
3507     nextNod[ iNode ] = listNewNodes.front();
3508
3509     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
3510                                                              corner node of linear */
3511     if ( prevNod[ iNode ] != nextNod [ iNode ])
3512       nbDouble += !isSingleNode[iNode];
3513
3514     if( iNode < nbCorners ) { // check corners only
3515       if ( prevNod[ iNode ] == nextNod [ iNode ])
3516         sames[nbSame++] = iNode;
3517       else
3518         iNotSameNode = iNode;
3519     }
3520   }
3521
3522   if ( nbSame == nbNodes || nbSame > 2) {
3523     MESSAGE( " Too many same nodes of element " << elem->GetID() );
3524     return;
3525   }
3526
3527   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
3528   {
3529     // fix nodes order to have bottom normal external
3530     if ( baseType == SMDSEntity_Polygon )
3531     {
3532       std::reverse( itNN.begin(), itNN.end() );
3533       std::reverse( prevNod.begin(), prevNod.end() );
3534       std::reverse( midlNod.begin(), midlNod.end() );
3535       std::reverse( nextNod.begin(), nextNod.end() );
3536       std::reverse( isSingleNode.begin(), isSingleNode.end() );
3537     }
3538     else
3539     {
3540       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
3541       SMDS_MeshCell::applyInterlace( ind, itNN );
3542       SMDS_MeshCell::applyInterlace( ind, prevNod );
3543       SMDS_MeshCell::applyInterlace( ind, nextNod );
3544       SMDS_MeshCell::applyInterlace( ind, midlNod );
3545       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3546       if ( nbSame > 0 )
3547       {
3548         sames[nbSame] = iNotSameNode;
3549         for ( int j = 0; j <= nbSame; ++j )
3550           for ( size_t i = 0; i < ind.size(); ++i )
3551             if ( ind[i] == sames[j] )
3552             {
3553               sames[j] = i;
3554               break;
3555             }
3556         iNotSameNode = sames[nbSame];
3557       }
3558     }
3559   }
3560
3561   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3562   if ( nbSame > 0 ) {
3563     iSameNode    = sames[ nbSame-1 ];
3564     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
3565     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
3566     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
3567   }
3568
3569   // make new elements
3570   for (int iStep = 0; iStep < nbSteps; iStep++ )
3571   {
3572     // get next nodes
3573     for ( iNode = 0; iNode < nbNodes; iNode++ )
3574     {
3575       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
3576       nextNod[ iNode ] = *itNN[ iNode ]++;
3577     }
3578
3579     SMDS_MeshElement* aNewElem = 0;
3580     /*if(!elem->IsPoly())*/ {
3581       switch ( baseType ) {
3582       case SMDSEntity_0D:
3583       case SMDSEntity_Node: { // sweep NODE
3584         if ( nbSame == 0 ) {
3585           if ( isSingleNode[0] )
3586             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3587           else
3588             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3589         }
3590         else
3591           return;
3592         break;
3593       }
3594       case SMDSEntity_Edge: { // sweep EDGE
3595         if ( nbDouble == 0 )
3596         {
3597           if ( nbSame == 0 ) // ---> quadrangle
3598             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3599                                       nextNod[ 1 ], nextNod[ 0 ] );
3600           else               // ---> triangle
3601             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3602                                       nextNod[ iNotSameNode ] );
3603         }
3604         else                 // ---> polygon
3605         {
3606           vector<const SMDS_MeshNode*> poly_nodes;
3607           poly_nodes.push_back( prevNod[0] );
3608           poly_nodes.push_back( prevNod[1] );
3609           if ( prevNod[1] != nextNod[1] )
3610           {
3611             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
3612             poly_nodes.push_back( nextNod[1] );
3613           }
3614           if ( prevNod[0] != nextNod[0] )
3615           {
3616             poly_nodes.push_back( nextNod[0] );
3617             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
3618           }
3619           switch ( poly_nodes.size() ) {
3620           case 3:
3621             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
3622             break;
3623           case 4:
3624             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
3625                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
3626             break;
3627           default:
3628             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
3629           }
3630         }
3631         break;
3632       }
3633       case SMDSEntity_Triangle: // TRIANGLE --->
3634         {
3635           if ( nbDouble > 0 ) break;
3636           if ( nbSame == 0 )       // ---> pentahedron
3637             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3638                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
3639
3640           else if ( nbSame == 1 )  // ---> pyramid
3641             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3642                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
3643                                          nextNod[ iSameNode ]);
3644
3645           else // 2 same nodes:       ---> tetrahedron
3646             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3647                                          nextNod[ iNotSameNode ]);
3648           break;
3649         }
3650       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
3651         {
3652           if ( nbSame == 2 )
3653             return;
3654           if ( nbDouble+nbSame == 2 )
3655           {
3656             if(nbSame==0) {      // ---> quadratic quadrangle
3657               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3658                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
3659             }
3660             else { //(nbSame==1) // ---> quadratic triangle
3661               if(sames[0]==2) {
3662                 return; // medium node on axis
3663               }
3664               else if(sames[0]==0)
3665                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3666                                           nextNod[2], midlNod[1], prevNod[2]);
3667               else // sames[0]==1
3668                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3669                                           midlNod[0], nextNod[2], prevNod[2]);
3670             }
3671           }
3672           else if ( nbDouble == 3 )
3673           {
3674             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
3675               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3676                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
3677             }
3678           }
3679           else
3680             return;
3681           break;
3682         }
3683       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
3684         if ( nbDouble > 0 ) break;
3685
3686         if ( nbSame == 0 )       // ---> hexahedron
3687           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
3688                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
3689
3690         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
3691           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3692                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
3693                                        nextNod[ iSameNode ]);
3694           newElems.push_back( aNewElem );
3695           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
3696                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3697                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
3698         }
3699         else if ( nbSame == 2 ) { // ---> pentahedron
3700           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3701             // iBeforeSame is same too
3702             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3703                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
3704                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
3705           else
3706             // iAfterSame is same too
3707             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
3708                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3709                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
3710         }
3711         break;
3712       }
3713       case SMDSEntity_Quad_Triangle: { // sweep Quadratic TRIANGLE --->
3714         if ( nbDouble+nbSame != 3 ) break;
3715         if(nbSame==0) {
3716           // --->  pentahedron with 15 nodes
3717           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3718                                        nextNod[0], nextNod[1], nextNod[2],
3719                                        prevNod[3], prevNod[4], prevNod[5],
3720                                        nextNod[3], nextNod[4], nextNod[5],
3721                                        midlNod[0], midlNod[1], midlNod[2]);
3722         }
3723         else if(nbSame==1) {
3724           // --->  2d order pyramid of 13 nodes
3725           int apex = iSameNode;
3726           int i0 = ( apex + 1 ) % nbCorners;
3727           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
3728           int i0a = apex + 3;
3729           int i1a = i1 + 3;
3730           int i01 = i0 + 3;
3731           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
3732                                       nextNod[i0], nextNod[i1], prevNod[apex],
3733                                       prevNod[i01], midlNod[i0],
3734                                       nextNod[i01], midlNod[i1],
3735                                       prevNod[i1a], prevNod[i0a],
3736                                       nextNod[i0a], nextNod[i1a]);
3737         }
3738         else if(nbSame==2) {
3739           // --->  2d order tetrahedron of 10 nodes
3740           int n1 = iNotSameNode;
3741           int n2 = ( n1 + 1             ) % nbCorners;
3742           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
3743           int n12 = n1 + 3;
3744           int n23 = n2 + 3;
3745           int n31 = n3 + 3;
3746           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
3747                                        prevNod[n12], prevNod[n23], prevNod[n31],
3748                                        midlNod[n1], nextNod[n12], nextNod[n31]);
3749         }
3750         break;
3751       }
3752       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
3753         if ( nbDouble != 4 ) break;
3754         if( nbSame == 0 ) {
3755           // --->  hexahedron with 20 nodes
3756           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3757                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3758                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3759                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3760                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
3761         }
3762         else if(nbSame==1) {
3763           // ---> pyramid + pentahedron - can not be created since it is needed 
3764           // additional middle node at the center of face
3765           INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
3766           return;
3767         }
3768         else if( nbSame == 2 ) {
3769           // --->  2d order Pentahedron with 15 nodes
3770           int n1,n2,n4,n5;
3771           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
3772             // iBeforeSame is same too
3773             n1 = iBeforeSame;
3774             n2 = iOpposSame;
3775             n4 = iSameNode;
3776             n5 = iAfterSame;
3777           }
3778           else {
3779             // iAfterSame is same too
3780             n1 = iSameNode;
3781             n2 = iBeforeSame;
3782             n4 = iAfterSame;
3783             n5 = iOpposSame;
3784           }
3785           int n12 = n2 + 4;
3786           int n45 = n4 + 4;
3787           int n14 = n1 + 4;
3788           int n25 = n5 + 4;
3789           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
3790                                        prevNod[n4], prevNod[n5], nextNod[n5],
3791                                        prevNod[n12], midlNod[n2], nextNod[n12],
3792                                        prevNod[n45], midlNod[n5], nextNod[n45],
3793                                        prevNod[n14], prevNod[n25], nextNod[n25]);
3794         }
3795         break;
3796       }
3797       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
3798
3799         if( nbSame == 0 && nbDouble == 9 ) {
3800           // --->  tri-quadratic hexahedron with 27 nodes
3801           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3802                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3803                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3804                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3805                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
3806                                        prevNod[8], // bottom center
3807                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
3808                                        nextNod[8], // top center
3809                                        midlNod[8]);// elem center
3810         }
3811         else
3812         {
3813           return;
3814         }
3815         break;
3816       }
3817       case SMDSEntity_Polygon: { // sweep POLYGON
3818
3819         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
3820           // --->  hexagonal prism
3821           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3822                                        prevNod[3], prevNod[4], prevNod[5],
3823                                        nextNod[0], nextNod[1], nextNod[2],
3824                                        nextNod[3], nextNod[4], nextNod[5]);
3825         }
3826         break;
3827       }
3828       default:
3829         break;
3830       }
3831     }
3832
3833     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
3834     {
3835       if ( baseType != SMDSEntity_Polygon )
3836       {
3837         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
3838         SMDS_MeshCell::applyInterlace( ind, prevNod );
3839         SMDS_MeshCell::applyInterlace( ind, nextNod );
3840         SMDS_MeshCell::applyInterlace( ind, midlNod );
3841         SMDS_MeshCell::applyInterlace( ind, itNN );
3842         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3843         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
3844       }
3845       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
3846       vector<int> quantities (nbNodes + 2);
3847       polyedre_nodes.clear();
3848       quantities.clear();
3849
3850       // bottom of prism
3851       for (int inode = 0; inode < nbNodes; inode++)
3852         polyedre_nodes.push_back( prevNod[inode] );
3853       quantities.push_back( nbNodes );
3854
3855       // top of prism
3856       polyedre_nodes.push_back( nextNod[0] );
3857       for (int inode = nbNodes; inode-1; --inode )
3858         polyedre_nodes.push_back( nextNod[inode-1] );
3859       quantities.push_back( nbNodes );
3860
3861       // side faces
3862       for (int iface = 0; iface < nbNodes; iface++)
3863       {
3864         const int prevNbNodes = polyedre_nodes.size();
3865         int inextface = (iface+1) % nbNodes;
3866         polyedre_nodes.push_back( prevNod[inextface] );
3867         polyedre_nodes.push_back( prevNod[iface] );
3868         if ( prevNod[iface] != nextNod[iface] )
3869         {
3870           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
3871           polyedre_nodes.push_back( nextNod[iface] );
3872         }
3873         if ( prevNod[inextface] != nextNod[inextface] )
3874         {
3875           polyedre_nodes.push_back( nextNod[inextface] );
3876           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
3877         }
3878         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
3879         if ( nbFaceNodes > 2 )
3880           quantities.push_back( nbFaceNodes );
3881         else // degenerated face
3882           polyedre_nodes.resize( prevNbNodes );
3883       }
3884       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
3885     }
3886
3887     if ( aNewElem ) {
3888       newElems.push_back( aNewElem );
3889       myLastCreatedElems.Append(aNewElem);
3890       srcElements.Append( elem );
3891     }
3892
3893     // set new prev nodes
3894     for ( iNode = 0; iNode < nbNodes; iNode++ )
3895       prevNod[ iNode ] = nextNod[ iNode ];
3896
3897   } // for steps
3898 }
3899
3900 //=======================================================================
3901 /*!
3902  * \brief Create 1D and 2D elements around swept elements
3903  * \param mapNewNodes - source nodes and ones generated from them
3904  * \param newElemsMap - source elements and ones generated from them
3905  * \param elemNewNodesMap - nodes generated from each node of each element
3906  * \param elemSet - all swept elements
3907  * \param nbSteps - number of sweeping steps
3908  * \param srcElements - to append elem for each generated element
3909  */
3910 //=======================================================================
3911
3912 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
3913                                   TElemOfElemListMap &     newElemsMap,
3914                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
3915                                   TIDSortedElemSet&        elemSet,
3916                                   const int                nbSteps,
3917                                   SMESH_SequenceOfElemPtr& srcElements)
3918 {
3919   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
3920   SMESHDS_Mesh* aMesh = GetMeshDS();
3921
3922   // Find nodes belonging to only one initial element - sweep them to get edges.
3923
3924   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
3925   for ( ; nList != mapNewNodes.end(); nList++ )
3926   {
3927     const SMDS_MeshNode* node =
3928       static_cast<const SMDS_MeshNode*>( nList->first );
3929     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
3930     int nbInitElems = 0;
3931     const SMDS_MeshElement* el = 0;
3932     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
3933     while ( eIt->more() && nbInitElems < 2 ) {
3934       el = eIt->next();
3935       SMDSAbs_ElementType type = el->GetType();
3936       if ( type == SMDSAbs_Volume || type < highType ) continue;
3937       if ( type > highType ) {
3938         nbInitElems = 0;
3939         highType = type;
3940       }
3941       nbInitElems += elemSet.count(el);
3942     }
3943     if ( nbInitElems < 2 ) {
3944       bool NotCreateEdge = el && el->IsMediumNode(node);
3945       if(!NotCreateEdge) {
3946         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
3947         list<const SMDS_MeshElement*> newEdges;
3948         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
3949       }
3950     }
3951   }
3952
3953   // Make a ceiling for each element ie an equal element of last new nodes.
3954   // Find free links of faces - make edges and sweep them into faces.
3955
3956   TElemOfElemListMap::iterator   itElem      = newElemsMap.begin();
3957   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
3958   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
3959   {
3960     const SMDS_MeshElement* elem = itElem->first;
3961     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
3962
3963     if(itElem->second.size()==0) continue;
3964
3965     const bool isQuadratic = elem->IsQuadratic();
3966
3967     if ( elem->GetType() == SMDSAbs_Edge ) {
3968       // create a ceiling edge
3969       if ( !isQuadratic ) {
3970         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
3971                                vecNewNodes[ 1 ]->second.back())) {
3972           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
3973                                                    vecNewNodes[ 1 ]->second.back()));
3974           srcElements.Append( myLastCreatedElems.Last() );
3975         }
3976       }
3977       else {
3978         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
3979                                vecNewNodes[ 1 ]->second.back(),
3980                                vecNewNodes[ 2 ]->second.back())) {
3981           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
3982                                                    vecNewNodes[ 1 ]->second.back(),
3983                                                    vecNewNodes[ 2 ]->second.back()));
3984           srcElements.Append( myLastCreatedElems.Last() );
3985         }
3986       }
3987     }
3988     if ( elem->GetType() != SMDSAbs_Face )
3989       continue;
3990
3991     bool hasFreeLinks = false;
3992
3993     TIDSortedElemSet avoidSet;
3994     avoidSet.insert( elem );
3995
3996     set<const SMDS_MeshNode*> aFaceLastNodes;
3997     int iNode, nbNodes = vecNewNodes.size();
3998     if ( !isQuadratic ) {
3999       // loop on the face nodes
4000       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4001         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4002         // look for free links of the face
4003         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4004         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4005         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4006         // check if a link is free
4007         if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4008           hasFreeLinks = true;
4009           // make an edge and a ceiling for a new edge
4010           if ( !aMesh->FindEdge( n1, n2 )) {
4011             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // free link edge
4012             srcElements.Append( myLastCreatedElems.Last() );
4013           }
4014           n1 = vecNewNodes[ iNode ]->second.back();
4015           n2 = vecNewNodes[ iNext ]->second.back();
4016           if ( !aMesh->FindEdge( n1, n2 )) {
4017             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // ceiling edge
4018             srcElements.Append( myLastCreatedElems.Last() );
4019           }
4020         }
4021       }
4022     }
4023     else { // elem is quadratic face
4024       int nbn = nbNodes/2;
4025       for ( iNode = 0; iNode < nbn; iNode++ ) {
4026         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4027         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4028         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4029         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4030         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4031         // check if a link is free
4032         if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4033              ! SMESH_MeshEditor::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4034              ! SMESH_MeshEditor::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4035           hasFreeLinks = true;
4036           // make an edge and a ceiling for a new edge
4037           // find medium node
4038           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4039             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4040             srcElements.Append( myLastCreatedElems.Last() );
4041           }
4042           n1 = vecNewNodes[ iNode ]->second.back();
4043           n2 = vecNewNodes[ iNext ]->second.back();
4044           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4045           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4046             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4047             srcElements.Append( myLastCreatedElems.Last() );
4048           }
4049         }
4050       }
4051       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4052         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4053       }
4054     }
4055
4056     // sweep free links into faces
4057
4058     if ( hasFreeLinks )  {
4059       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4060       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4061
4062       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4063       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4064         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4065         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4066       }
4067       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4068         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4069         std::advance( v, volNb );
4070         // find indices of free faces of a volume and their source edges
4071         list< int > freeInd;
4072         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4073         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4074         int iF, nbF = vTool.NbFaces();
4075         for ( iF = 0; iF < nbF; iF ++ ) {
4076           if (vTool.IsFreeFace( iF ) &&
4077               vTool.GetFaceNodes( iF, faceNodeSet ) &&
4078               initNodeSet != faceNodeSet) // except an initial face
4079           {
4080             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4081               continue;
4082             freeInd.push_back( iF );
4083             // find source edge of a free face iF
4084             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4085             commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4086             std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4087                                    initNodeSet.begin(), initNodeSet.end(),
4088                                    commonNodes.begin());
4089             if ( (*v)->IsQuadratic() )
4090               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4091             else
4092               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4093 #ifdef _DEBUG_
4094             if ( !srcEdges.back() )
4095             {
4096               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4097                    << iF << " of volume #" << vTool.ID() << endl;
4098             }
4099 #endif
4100           }
4101         }
4102         if ( freeInd.empty() )
4103           continue;
4104
4105         // create faces for all steps;
4106         // if such a face has been already created by sweep of edge,
4107         // assure that its orientation is OK
4108         for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4109           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4110           vTool.SetExternalNormal();
4111           const int nextShift = vTool.IsForward() ? +1 : -1;
4112           list< int >::iterator ind = freeInd.begin();
4113           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4114           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4115           {
4116             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4117             int nbn = vTool.NbFaceNodes( *ind );
4118             const SMDS_MeshElement * f = 0;
4119             if ( nbn == 3 )              ///// triangle
4120             {
4121               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4122               if ( !f ||
4123                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4124               {
4125                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4126                                                      nodes[ 1 ],
4127                                                      nodes[ 1 + nextShift ] };
4128                 if ( f )
4129                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4130                 else
4131                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4132                                                             newOrder[ 2 ] ));
4133               }
4134             }
4135             else if ( nbn == 4 )       ///// quadrangle
4136             {
4137               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4138               if ( !f ||
4139                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4140               {
4141                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4142                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4143                 if ( f )
4144                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4145                 else
4146                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4147                                                             newOrder[ 2 ], newOrder[ 3 ]));
4148               }
4149             }
4150             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4151             {
4152               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4153               if ( !f ||
4154                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4155               {
4156                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4157                                                      nodes[2],
4158                                                      nodes[2 + 2*nextShift],
4159                                                      nodes[3 - 2*nextShift],
4160                                                      nodes[3],
4161                                                      nodes[3 + 2*nextShift]};
4162                 if ( f )
4163                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4164                 else
4165                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4166                                                             newOrder[ 1 ],
4167                                                             newOrder[ 2 ],
4168                                                             newOrder[ 3 ],
4169                                                             newOrder[ 4 ],
4170                                                             newOrder[ 5 ] ));
4171               }
4172             }
4173             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4174             {
4175               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4176                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4177               if ( !f ||
4178                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4179               {
4180                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4181                                                      nodes[4 - 2*nextShift],
4182                                                      nodes[4],
4183                                                      nodes[4 + 2*nextShift],
4184                                                      nodes[1],
4185                                                      nodes[5 - 2*nextShift],
4186                                                      nodes[5],
4187                                                      nodes[5 + 2*nextShift] };
4188                 if ( f )
4189                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4190                 else
4191                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4192                                                            newOrder[ 2 ], newOrder[ 3 ],
4193                                                            newOrder[ 4 ], newOrder[ 5 ],
4194                                                            newOrder[ 6 ], newOrder[ 7 ]));
4195               }
4196             }
4197             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4198             {
4199               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4200                                       SMDSAbs_Face, /*noMedium=*/false);
4201               if ( !f ||
4202                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4203               {
4204                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4205                                                      nodes[4 - 2*nextShift],
4206                                                      nodes[4],
4207                                                      nodes[4 + 2*nextShift],
4208                                                      nodes[1],
4209                                                      nodes[5 - 2*nextShift],
4210                                                      nodes[5],
4211                                                      nodes[5 + 2*nextShift],
4212                                                      nodes[8] };
4213                 if ( f )
4214                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4215                 else
4216                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4217                                                            newOrder[ 2 ], newOrder[ 3 ],
4218                                                            newOrder[ 4 ], newOrder[ 5 ],
4219                                                            newOrder[ 6 ], newOrder[ 7 ],
4220                                                            newOrder[ 8 ]));
4221               }
4222             }
4223             else  //////// polygon
4224             {
4225               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4226               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4227               if ( !f ||
4228                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4229               {
4230                 if ( !vTool.IsForward() )
4231                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4232                 if ( f )
4233                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4234                 else
4235                   AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4236               }
4237             }
4238
4239             while ( srcElements.Length() < myLastCreatedElems.Length() )
4240               srcElements.Append( *srcEdge );
4241
4242           }  // loop on free faces
4243
4244           // go to the next volume
4245           iVol = 0;
4246           while ( iVol++ < nbVolumesByStep ) v++;
4247
4248         } // loop on steps
4249       } // loop on volumes of one step
4250     } // sweep free links into faces
4251
4252     // Make a ceiling face with a normal external to a volume
4253
4254     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4255
4256     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4257     if ( iF >= 0 ) {
4258       lastVol.SetExternalNormal();
4259       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4260       int nbn = lastVol.NbFaceNodes( iF );
4261       if ( nbn == 3 ) {
4262         if (!hasFreeLinks ||
4263             !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
4264           myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
4265       }
4266       else if ( nbn == 4 )
4267       {
4268         if (!hasFreeLinks ||
4269             !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
4270           myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]));
4271       }
4272       else if ( nbn == 6 && isQuadratic )
4273       {
4274         if (!hasFreeLinks ||
4275             !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5]) )
4276           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
4277                                                    nodes[1], nodes[3], nodes[5]));
4278       }
4279       else if ( nbn == 8 && isQuadratic )
4280       {
4281         if (!hasFreeLinks ||
4282             !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
4283                              nodes[1], nodes[3], nodes[5], nodes[7]) )
4284           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4285                                                    nodes[1], nodes[3], nodes[5], nodes[7]));
4286       }
4287       else if ( nbn == 9 && isQuadratic )
4288       {
4289         if (!hasFreeLinks ||
4290             !aMesh->FindElement(vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4291                                 SMDSAbs_Face, /*noMedium=*/false) )
4292           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4293                                                    nodes[1], nodes[3], nodes[5], nodes[7],
4294                                                    nodes[8]));
4295       }
4296       else {
4297         vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes + nbn );
4298         if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
4299           myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4300       }
4301
4302       while ( srcElements.Length() < myLastCreatedElems.Length() )
4303         srcElements.Append( myLastCreatedElems.Last() );
4304     }
4305   } // loop on swept elements
4306 }
4307
4308 //=======================================================================
4309 //function : RotationSweep
4310 //purpose  :
4311 //=======================================================================
4312
4313 SMESH_MeshEditor::PGroupIDs
4314 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4315                                 const gp_Ax1&      theAxis,
4316                                 const double       theAngle,
4317                                 const int          theNbSteps,
4318                                 const double       theTol,
4319                                 const bool         theMakeGroups,
4320                                 const bool         theMakeWalls)
4321 {
4322   myLastCreatedElems.Clear();
4323   myLastCreatedNodes.Clear();
4324
4325   // source elements for each generated one
4326   SMESH_SequenceOfElemPtr srcElems, srcNodes;
4327
4328   MESSAGE( "RotationSweep()");
4329   gp_Trsf aTrsf;
4330   aTrsf.SetRotation( theAxis, theAngle );
4331   gp_Trsf aTrsf2;
4332   aTrsf2.SetRotation( theAxis, theAngle/2. );
4333
4334   gp_Lin aLine( theAxis );
4335   double aSqTol = theTol * theTol;
4336
4337   SMESHDS_Mesh* aMesh = GetMeshDS();
4338
4339   TNodeOfNodeListMap mapNewNodes;
4340   TElemOfVecOfNnlmiMap mapElemNewNodes;
4341   TElemOfElemListMap newElemsMap;
4342
4343   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4344                                      myMesh->NbFaces(ORDER_QUADRATIC) +
4345                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
4346   // loop on theElems
4347   TIDSortedElemSet::iterator itElem;
4348   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4349     const SMDS_MeshElement* elem = *itElem;
4350     if ( !elem || elem->GetType() == SMDSAbs_Volume )
4351       continue;
4352     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4353     newNodesItVec.reserve( elem->NbNodes() );
4354
4355     // loop on elem nodes
4356     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4357     while ( itN->more() )
4358     {
4359       // check if a node has been already sweeped
4360       const SMDS_MeshNode* node = cast2Node( itN->next() );
4361
4362       gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4363       double coord[3];
4364       aXYZ.Coord( coord[0], coord[1], coord[2] );
4365       bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4366
4367       TNodeOfNodeListMapItr nIt =
4368         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4369       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4370       if ( listNewNodes.empty() )
4371       {
4372         // check if we are to create medium nodes between corner ones
4373         bool needMediumNodes = false;
4374         if ( isQuadraticMesh )
4375         {
4376           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4377           while (it->more() && !needMediumNodes )
4378           {
4379             const SMDS_MeshElement* invElem = it->next();
4380             if ( invElem != elem && !theElems.count( invElem )) continue;
4381             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4382             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4383               needMediumNodes = true;
4384           }
4385         }
4386
4387         // make new nodes
4388         const SMDS_MeshNode * newNode = node;
4389         for ( int i = 0; i < theNbSteps; i++ ) {
4390           if ( !isOnAxis ) {
4391             if ( needMediumNodes )  // create a medium node
4392             {
4393               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4394               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4395               myLastCreatedNodes.Append(newNode);
4396               srcNodes.Append( node );
4397               listNewNodes.push_back( newNode );
4398               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4399             }
4400             else {
4401               aTrsf.Transforms( coord[0], coord[1], coord[2] );
4402             }
4403             // create a corner node
4404             newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4405             myLastCreatedNodes.Append(newNode);
4406             srcNodes.Append( node );
4407             listNewNodes.push_back( newNode );
4408           }
4409           else {
4410             listNewNodes.push_back( newNode );
4411             // if ( needMediumNodes )
4412             //   listNewNodes.push_back( newNode );
4413           }
4414         }
4415       }
4416       newNodesItVec.push_back( nIt );
4417     }
4418     // make new elements
4419     sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4420   }
4421
4422   if ( theMakeWalls )
4423     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4424
4425   PGroupIDs newGroupIDs;
4426   if ( theMakeGroups )
4427     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4428
4429   return newGroupIDs;
4430 }
4431
4432
4433 //=======================================================================
4434 //function : CreateNode
4435 //purpose  :
4436 //=======================================================================
4437 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4438                                                   const double y,
4439                                                   const double z,
4440                                                   const double tolnode,
4441                                                   SMESH_SequenceOfNode& aNodes)
4442 {
4443   // myLastCreatedElems.Clear();
4444   // myLastCreatedNodes.Clear();
4445
4446   gp_Pnt P1(x,y,z);
4447   SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4448
4449   // try to search in sequence of existing nodes
4450   // if aNodes.Length()>0 we 'nave to use given sequence
4451   // else - use all nodes of mesh
4452   if(aNodes.Length()>0) {
4453     int i;
4454     for(i=1; i<=aNodes.Length(); i++) {
4455       gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4456       if(P1.Distance(P2)<tolnode)
4457         return aNodes.Value(i);
4458     }
4459   }
4460   else {
4461     SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4462     while(itn->more()) {
4463       const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4464       gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4465       if(P1.Distance(P2)<tolnode)
4466         return aN;
4467     }
4468   }
4469
4470   // create new node and return it
4471   const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4472   //myLastCreatedNodes.Append(NewNode);
4473   return NewNode;
4474 }
4475
4476
4477 //=======================================================================
4478 //function : ExtrusionSweep
4479 //purpose  :
4480 //=======================================================================
4481
4482 SMESH_MeshEditor::PGroupIDs
4483 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &  theElems,
4484                                   const gp_Vec&       theStep,
4485                                   const int           theNbSteps,
4486                                   TElemOfElemListMap& newElemsMap,
4487                                   const bool          theMakeGroups,
4488                                   const int           theFlags,
4489                                   const double        theTolerance)
4490 {
4491   ExtrusParam aParams;
4492   aParams.myDir = gp_Dir(theStep);
4493   aParams.myNodes.Clear();
4494   aParams.mySteps = new TColStd_HSequenceOfReal;
4495   int i;
4496   for(i=1; i<=theNbSteps; i++)
4497     aParams.mySteps->Append(theStep.Magnitude());
4498
4499   return
4500     ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4501 }
4502
4503
4504 //=======================================================================
4505 //function : ExtrusionSweep
4506 //purpose  :
4507 //=======================================================================
4508
4509 SMESH_MeshEditor::PGroupIDs
4510 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &  theElems,
4511                                   ExtrusParam&        theParams,
4512                                   TElemOfElemListMap& newElemsMap,
4513                                   const bool          theMakeGroups,
4514                                   const int           theFlags,
4515                                   const double        theTolerance)
4516 {
4517   myLastCreatedElems.Clear();
4518   myLastCreatedNodes.Clear();
4519
4520   // source elements for each generated one
4521   SMESH_SequenceOfElemPtr srcElems, srcNodes;
4522
4523   SMESHDS_Mesh* aMesh = GetMeshDS();
4524
4525   int nbsteps = theParams.mySteps->Length();
4526
4527   TNodeOfNodeListMap mapNewNodes;
4528   //TNodeOfNodeVecMap mapNewNodes;
4529   TElemOfVecOfNnlmiMap mapElemNewNodes;
4530   //TElemOfVecOfMapNodesMap mapElemNewNodes;
4531
4532   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4533                                      myMesh->NbFaces(ORDER_QUADRATIC) +
4534                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
4535   // loop on theElems
4536   TIDSortedElemSet::iterator itElem;
4537   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4538     // check element type
4539     const SMDS_MeshElement* elem = *itElem;
4540     if ( !elem  || elem->GetType() == SMDSAbs_Volume )
4541       continue;
4542
4543     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4544     newNodesItVec.reserve( elem->NbNodes() );
4545
4546     // loop on elem nodes
4547     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4548     while ( itN->more() )
4549     {
4550       // check if a node has been already sweeped
4551       const SMDS_MeshNode* node = cast2Node( itN->next() );
4552       TNodeOfNodeListMap::iterator nIt =
4553         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4554       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4555       if ( listNewNodes.empty() )
4556       {
4557         // make new nodes
4558
4559         // check if we are to create medium nodes between corner ones
4560         bool needMediumNodes = false;
4561         if ( isQuadraticMesh )
4562         {
4563           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4564           while (it->more() && !needMediumNodes )
4565           {
4566             const SMDS_MeshElement* invElem = it->next();
4567             if ( invElem != elem && !theElems.count( invElem )) continue;
4568             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4569             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4570               needMediumNodes = true;
4571           }
4572         }
4573
4574         double coord[] = { node->X(), node->Y(), node->Z() };
4575         for ( int i = 0; i < nbsteps; i++ )
4576         {
4577           if ( needMediumNodes ) // create a medium node
4578           {
4579             double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4580             double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4581             double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4582             if( theFlags & EXTRUSION_FLAG_SEW ) {
4583               const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4584                                                          theTolerance, theParams.myNodes);
4585               listNewNodes.push_back( newNode );
4586             }
4587             else {
4588               const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4589               myLastCreatedNodes.Append(newNode);
4590               srcNodes.Append( node );
4591               listNewNodes.push_back( newNode );
4592             }
4593           }
4594           // create a corner node
4595           coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4596           coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4597           coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4598           if( theFlags & EXTRUSION_FLAG_SEW ) {
4599             const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4600                                                        theTolerance, theParams.myNodes);
4601             listNewNodes.push_back( newNode );
4602           }
4603           else {
4604             const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4605             myLastCreatedNodes.Append(newNode);
4606             srcNodes.Append( node );
4607             listNewNodes.push_back( newNode );
4608           }
4609         }
4610       }
4611       newNodesItVec.push_back( nIt );
4612     }
4613     // make new elements
4614     sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4615   }
4616
4617   if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4618     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4619   }
4620   PGroupIDs newGroupIDs;
4621   if ( theMakeGroups )
4622     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4623
4624   return newGroupIDs;
4625 }
4626
4627 //=======================================================================
4628 //function : ExtrusionAlongTrack
4629 //purpose  :
4630 //=======================================================================
4631 SMESH_MeshEditor::Extrusion_Error
4632 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
4633                                        SMESH_subMesh*       theTrack,
4634                                        const SMDS_MeshNode* theN1,
4635                                        const bool           theHasAngles,
4636                                        list<double>&        theAngles,
4637                                        const bool           theLinearVariation,
4638                                        const bool           theHasRefPoint,
4639                                        const gp_Pnt&        theRefPoint,
4640                                        const bool           theMakeGroups)
4641 {
4642   MESSAGE("ExtrusionAlongTrack");
4643   myLastCreatedElems.Clear();
4644   myLastCreatedNodes.Clear();
4645
4646   int aNbE;
4647   std::list<double> aPrms;
4648   TIDSortedElemSet::iterator itElem;
4649
4650   gp_XYZ aGC;
4651   TopoDS_Edge aTrackEdge;
4652   TopoDS_Vertex aV1, aV2;
4653
4654   SMDS_ElemIteratorPtr aItE;
4655   SMDS_NodeIteratorPtr aItN;
4656   SMDSAbs_ElementType aTypeE;
4657
4658   TNodeOfNodeListMap mapNewNodes;
4659
4660   // 1. Check data
4661   aNbE = theElements.size();
4662   // nothing to do
4663   if ( !aNbE )
4664     return EXTR_NO_ELEMENTS;
4665
4666   // 1.1 Track Pattern
4667   ASSERT( theTrack );
4668
4669   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4670
4671   aItE = pSubMeshDS->GetElements();
4672   while ( aItE->more() ) {
4673     const SMDS_MeshElement* pE = aItE->next();
4674     aTypeE = pE->GetType();
4675     // Pattern must contain links only
4676     if ( aTypeE != SMDSAbs_Edge )
4677       return EXTR_PATH_NOT_EDGE;
4678   }
4679
4680   list<SMESH_MeshEditor_PathPoint> fullList;
4681
4682   const TopoDS_Shape& aS = theTrack->GetSubShape();
4683   // Sub-shape for the Pattern must be an Edge or Wire
4684   if( aS.ShapeType() == TopAbs_EDGE ) {
4685     aTrackEdge = TopoDS::Edge( aS );
4686     // the Edge must not be degenerated
4687     if ( BRep_Tool::Degenerated( aTrackEdge ) )
4688       return EXTR_BAD_PATH_SHAPE;
4689     TopExp::Vertices( aTrackEdge, aV1, aV2 );
4690     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4691     const SMDS_MeshNode* aN1 = aItN->next();
4692     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4693     const SMDS_MeshNode* aN2 = aItN->next();
4694     // starting node must be aN1 or aN2
4695     if ( !( aN1 == theN1 || aN2 == theN1 ) )
4696       return EXTR_BAD_STARTING_NODE;
4697     aItN = pSubMeshDS->GetNodes();
4698     while ( aItN->more() ) {
4699       const SMDS_MeshNode* pNode = aItN->next();
4700       const SMDS_EdgePosition* pEPos =
4701         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4702       double aT = pEPos->GetUParameter();
4703       aPrms.push_back( aT );
4704     }
4705     //Extrusion_Error err =
4706     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4707   } else if( aS.ShapeType() == TopAbs_WIRE ) {
4708     list< SMESH_subMesh* > LSM;
4709     TopTools_SequenceOfShape Edges;
4710     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4711     while(itSM->more()) {
4712       SMESH_subMesh* SM = itSM->next();
4713       LSM.push_back(SM);
4714       const TopoDS_Shape& aS = SM->GetSubShape();
4715       Edges.Append(aS);
4716     }
4717     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4718     int startNid = theN1->GetID();
4719     TColStd_MapOfInteger UsedNums;
4720     
4721     int NbEdges = Edges.Length();
4722     int i = 1;
4723     for(; i<=NbEdges; i++) {
4724       int k = 0;
4725       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4726       for(; itLSM!=LSM.end(); itLSM++) {
4727         k++;
4728         if(UsedNums.Contains(k)) continue;
4729         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4730         SMESH_subMesh* locTrack = *itLSM;
4731         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4732         TopExp::Vertices( aTrackEdge, aV1, aV2 );
4733         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4734         const SMDS_MeshNode* aN1 = aItN->next();
4735         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4736         const SMDS_MeshNode* aN2 = aItN->next();
4737         // starting node must be aN1 or aN2
4738         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4739         // 2. Collect parameters on the track edge
4740         aPrms.clear();
4741         aItN = locMeshDS->GetNodes();
4742         while ( aItN->more() ) {
4743           const SMDS_MeshNode* pNode = aItN->next();
4744           const SMDS_EdgePosition* pEPos =
4745             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4746           double aT = pEPos->GetUParameter();
4747           aPrms.push_back( aT );
4748         }
4749         list<SMESH_MeshEditor_PathPoint> LPP;
4750         //Extrusion_Error err =
4751         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4752         LLPPs.push_back(LPP);
4753         UsedNums.Add(k);
4754         // update startN for search following egde
4755         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4756         else startNid = aN1->GetID();
4757         break;
4758       }
4759     }
4760     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4761     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4762     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4763     for(; itPP!=firstList.end(); itPP++) {
4764       fullList.push_back( *itPP );
4765     }
4766     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4767     fullList.pop_back();
4768     itLLPP++;
4769     for(; itLLPP!=LLPPs.end(); itLLPP++) {
4770       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4771       itPP = currList.begin();
4772       SMESH_MeshEditor_PathPoint PP2 = currList.front();
4773       gp_Dir D1 = PP1.Tangent();
4774       gp_Dir D2 = PP2.Tangent();
4775       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4776                            (D1.Z()+D2.Z())/2 ) );
4777       PP1.SetTangent(Dnew);
4778       fullList.push_back(PP1);
4779       itPP++;
4780       for(; itPP!=firstList.end(); itPP++) {
4781         fullList.push_back( *itPP );
4782       }
4783       PP1 = fullList.back();
4784       fullList.pop_back();
4785     }
4786     // if wire not closed
4787     fullList.push_back(PP1);
4788     // else ???
4789   }
4790   else {
4791     return EXTR_BAD_PATH_SHAPE;
4792   }
4793
4794   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4795                           theHasRefPoint, theRefPoint, theMakeGroups);
4796 }
4797
4798
4799 //=======================================================================
4800 //function : ExtrusionAlongTrack
4801 //purpose  :
4802 //=======================================================================
4803 SMESH_MeshEditor::Extrusion_Error
4804 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
4805                                        SMESH_Mesh*          theTrack,
4806                                        const SMDS_MeshNode* theN1,
4807                                        const bool           theHasAngles,
4808                                        list<double>&        theAngles,
4809                                        const bool           theLinearVariation,
4810                                        const bool           theHasRefPoint,
4811                                        const gp_Pnt&        theRefPoint,
4812                                        const bool           theMakeGroups)
4813 {
4814   myLastCreatedElems.Clear();
4815   myLastCreatedNodes.Clear();
4816
4817   int aNbE;
4818   std::list<double> aPrms;
4819   TIDSortedElemSet::iterator itElem;
4820
4821   gp_XYZ aGC;
4822   TopoDS_Edge aTrackEdge;
4823   TopoDS_Vertex aV1, aV2;
4824
4825   SMDS_ElemIteratorPtr aItE;
4826   SMDS_NodeIteratorPtr aItN;
4827   SMDSAbs_ElementType aTypeE;
4828
4829   TNodeOfNodeListMap mapNewNodes;
4830
4831   // 1. Check data
4832   aNbE = theElements.size();
4833   // nothing to do
4834   if ( !aNbE )
4835     return EXTR_NO_ELEMENTS;
4836
4837   // 1.1 Track Pattern
4838   ASSERT( theTrack );
4839
4840   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
4841
4842   aItE = pMeshDS->elementsIterator();
4843   while ( aItE->more() ) {
4844     const SMDS_MeshElement* pE = aItE->next();
4845     aTypeE = pE->GetType();
4846     // Pattern must contain links only
4847     if ( aTypeE != SMDSAbs_Edge )
4848       return EXTR_PATH_NOT_EDGE;
4849   }
4850
4851   list<SMESH_MeshEditor_PathPoint> fullList;
4852
4853   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
4854
4855   if( aS == SMESH_Mesh::PseudoShape() ) {
4856     //Mesh without shape
4857     const SMDS_MeshNode* currentNode = NULL;
4858     const SMDS_MeshNode* prevNode = theN1;
4859     std::vector<const SMDS_MeshNode*> aNodesList;
4860     aNodesList.push_back(theN1);
4861     int nbEdges = 0, conn=0;
4862     const SMDS_MeshElement* prevElem = NULL;
4863     const SMDS_MeshElement* currentElem = NULL;
4864     int totalNbEdges = theTrack->NbEdges();
4865     SMDS_ElemIteratorPtr nIt;
4866     bool isClosed = false;
4867
4868     //check start node
4869     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
4870       return EXTR_BAD_STARTING_NODE;
4871     }
4872     
4873     conn = nbEdgeConnectivity(theN1);
4874     if(conn > 2)
4875       return EXTR_PATH_NOT_EDGE;
4876
4877     aItE = theN1->GetInverseElementIterator();
4878     prevElem = aItE->next();
4879     currentElem = prevElem;
4880     //Get all nodes
4881     if(totalNbEdges == 1 ) {
4882       nIt = currentElem->nodesIterator();
4883       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4884       if(currentNode == prevNode)
4885         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4886       aNodesList.push_back(currentNode);
4887     } else { 
4888       nIt = currentElem->nodesIterator();
4889       while( nIt->more() ) {
4890         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4891         if(currentNode == prevNode)
4892           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4893         aNodesList.push_back(currentNode);
4894         
4895         //case of the closed mesh
4896         if(currentNode == theN1) {
4897           nbEdges++;
4898           isClosed = true;
4899           break;
4900         }
4901
4902         conn = nbEdgeConnectivity(currentNode);
4903         if(conn > 2) {
4904           return EXTR_PATH_NOT_EDGE;    
4905         }else if( conn == 1 && nbEdges > 0 ) {
4906           //End of the path
4907           nbEdges++;
4908           break;
4909         }else {
4910           prevNode = currentNode;
4911           aItE = currentNode->GetInverseElementIterator();
4912           currentElem = aItE->next();
4913           if( currentElem  == prevElem)
4914             currentElem = aItE->next();
4915           nIt = currentElem->nodesIterator();
4916           prevElem = currentElem;
4917           nbEdges++;
4918         }
4919       }
4920     } 
4921     
4922     if(nbEdges != totalNbEdges)
4923       return EXTR_PATH_NOT_EDGE;
4924
4925     TopTools_SequenceOfShape Edges;
4926     double x1,x2,y1,y2,z1,z2;
4927     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4928     int startNid = theN1->GetID();
4929     for(int i = 1; i < aNodesList.size(); i++) {
4930       x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
4931       y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
4932       z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
4933       TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));  
4934       list<SMESH_MeshEditor_PathPoint> LPP;
4935       aPrms.clear();
4936       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
4937       LLPPs.push_back(LPP);
4938       if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
4939       else startNid = aNodesList[i-1]->GetID();
4940
4941     }
4942
4943     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4944     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4945     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4946     for(; itPP!=firstList.end(); itPP++) {
4947       fullList.push_back( *itPP );
4948     }
4949
4950     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4951     SMESH_MeshEditor_PathPoint PP2;
4952     fullList.pop_back();
4953     itLLPP++;
4954     for(; itLLPP!=LLPPs.end(); itLLPP++) {
4955       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4956       itPP = currList.begin();
4957       PP2 = currList.front();
4958       gp_Dir D1 = PP1.Tangent();
4959       gp_Dir D2 = PP2.Tangent();
4960       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4961                            (D1.Z()+D2.Z())/2 ) );
4962       PP1.SetTangent(Dnew);
4963       fullList.push_back(PP1);
4964       itPP++;
4965       for(; itPP!=currList.end(); itPP++) {
4966         fullList.push_back( *itPP );
4967       }
4968       PP1 = fullList.back();
4969       fullList.pop_back();
4970     }
4971     fullList.push_back(PP1);
4972     
4973   } // Sub-shape for the Pattern must be an Edge or Wire
4974   else if( aS.ShapeType() == TopAbs_EDGE ) {
4975     aTrackEdge = TopoDS::Edge( aS );
4976     // the Edge must not be degenerated
4977     if ( BRep_Tool::Degenerated( aTrackEdge ) )
4978       return EXTR_BAD_PATH_SHAPE;
4979     TopExp::Vertices( aTrackEdge, aV1, aV2 );
4980     aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4981     const SMDS_MeshNode* aN1 = aItN->next();
4982     aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4983     const SMDS_MeshNode* aN2 = aItN->next();
4984     // starting node must be aN1 or aN2
4985     if ( !( aN1 == theN1 || aN2 == theN1 ) )
4986       return EXTR_BAD_STARTING_NODE;
4987     aItN = pMeshDS->nodesIterator();
4988     while ( aItN->more() ) {
4989       const SMDS_MeshNode* pNode = aItN->next();
4990       if( pNode==aN1 || pNode==aN2 ) continue;
4991       const SMDS_EdgePosition* pEPos =
4992         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4993       double aT = pEPos->GetUParameter();
4994       aPrms.push_back( aT );
4995     }
4996     //Extrusion_Error err =
4997     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4998   }
4999   else if( aS.ShapeType() == TopAbs_WIRE ) {
5000     list< SMESH_subMesh* > LSM;
5001     TopTools_SequenceOfShape Edges;
5002     TopExp_Explorer eExp(aS, TopAbs_EDGE);
5003     for(; eExp.More(); eExp.Next()) {
5004       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5005       if( BRep_Tool::Degenerated(E) ) continue;
5006       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5007       if(SM) {
5008         LSM.push_back(SM);
5009         Edges.Append(E);
5010       }
5011     }
5012     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5013     int startNid = theN1->GetID();
5014     TColStd_MapOfInteger UsedNums;
5015     int NbEdges = Edges.Length();
5016     int i = 1;
5017     for(; i<=NbEdges; i++) {
5018       int k = 0;
5019       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5020       for(; itLSM!=LSM.end(); itLSM++) {
5021         k++;
5022         if(UsedNums.Contains(k)) continue;
5023         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5024         SMESH_subMesh* locTrack = *itLSM;
5025         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5026         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5027         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5028         const SMDS_MeshNode* aN1 = aItN->next();
5029         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5030         const SMDS_MeshNode* aN2 = aItN->next();
5031         // starting node must be aN1 or aN2
5032         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5033         // 2. Collect parameters on the track edge
5034         aPrms.clear();
5035         aItN = locMeshDS->GetNodes();
5036         while ( aItN->more() ) {
5037           const SMDS_MeshNode* pNode = aItN->next();
5038           const SMDS_EdgePosition* pEPos =
5039             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5040           double aT = pEPos->GetUParameter();
5041           aPrms.push_back( aT );
5042         }
5043         list<SMESH_MeshEditor_PathPoint> LPP;
5044         //Extrusion_Error err =
5045         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5046         LLPPs.push_back(LPP);
5047         UsedNums.Add(k);
5048         // update startN for search following egde
5049         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5050         else startNid = aN1->GetID();
5051         break;
5052       }
5053     }
5054     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5055     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5056     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5057     for(; itPP!=firstList.end(); itPP++) {
5058       fullList.push_back( *itPP );
5059     }
5060     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5061     fullList.pop_back();
5062     itLLPP++;
5063     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5064       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5065       itPP = currList.begin();
5066       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5067       gp_Dir D1 = PP1.Tangent();
5068       gp_Dir D2 = PP2.Tangent();
5069       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5070                            (D1.Z()+D2.Z())/2 ) );
5071       PP1.SetTangent(Dnew);
5072       fullList.push_back(PP1);
5073       itPP++;
5074       for(; itPP!=currList.end(); itPP++) {
5075         fullList.push_back( *itPP );
5076       }
5077       PP1 = fullList.back();
5078       fullList.pop_back();
5079     }
5080     // if wire not closed
5081     fullList.push_back(PP1);
5082     // else ???
5083   }
5084   else {
5085     return EXTR_BAD_PATH_SHAPE;
5086   }
5087
5088   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5089                           theHasRefPoint, theRefPoint, theMakeGroups);
5090 }
5091
5092
5093 //=======================================================================
5094 //function : MakeEdgePathPoints
5095 //purpose  : auxilary for ExtrusionAlongTrack
5096 //=======================================================================
5097 SMESH_MeshEditor::Extrusion_Error
5098 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5099                                      const TopoDS_Edge& aTrackEdge,
5100                                      bool FirstIsStart,
5101                                      list<SMESH_MeshEditor_PathPoint>& LPP)
5102 {
5103   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5104   aTolVec=1.e-7;
5105   aTolVec2=aTolVec*aTolVec;
5106   double aT1, aT2;
5107   TopoDS_Vertex aV1, aV2;
5108   TopExp::Vertices( aTrackEdge, aV1, aV2 );
5109   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5110   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5111   // 2. Collect parameters on the track edge
5112   aPrms.push_front( aT1 );
5113   aPrms.push_back( aT2 );
5114   // sort parameters
5115   aPrms.sort();
5116   if( FirstIsStart ) {
5117     if ( aT1 > aT2 ) {
5118       aPrms.reverse();
5119     }
5120   }
5121   else {
5122     if ( aT2 > aT1 ) {
5123       aPrms.reverse();
5124     }
5125   }
5126   // 3. Path Points
5127   SMESH_MeshEditor_PathPoint aPP;
5128   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5129   std::list<double>::iterator aItD = aPrms.begin();
5130   for(; aItD != aPrms.end(); ++aItD) {
5131     double aT = *aItD;
5132     gp_Pnt aP3D;
5133     gp_Vec aVec;
5134     aC3D->D1( aT, aP3D, aVec );
5135     aL2 = aVec.SquareMagnitude();
5136     if ( aL2 < aTolVec2 )
5137       return EXTR_CANT_GET_TANGENT;
5138     gp_Dir aTgt( aVec );
5139     aPP.SetPnt( aP3D );
5140     aPP.SetTangent( aTgt );
5141     aPP.SetParameter( aT );
5142     LPP.push_back(aPP);
5143   }
5144   return EXTR_OK;
5145 }
5146
5147
5148 //=======================================================================
5149 //function : MakeExtrElements
5150 //purpose  : auxilary for ExtrusionAlongTrack
5151 //=======================================================================
5152 SMESH_MeshEditor::Extrusion_Error
5153 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet&  theElements,
5154                                    list<SMESH_MeshEditor_PathPoint>& fullList,
5155                                    const bool theHasAngles,
5156                                    list<double>& theAngles,
5157                                    const bool theLinearVariation,
5158                                    const bool theHasRefPoint,
5159                                    const gp_Pnt& theRefPoint,
5160                                    const bool theMakeGroups)
5161 {
5162   MESSAGE("MakeExtrElements");
5163   //cout<<"MakeExtrElements  fullList.size() = "<<fullList.size()<<endl;
5164   int aNbTP = fullList.size();
5165   vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5166   // Angles
5167   if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5168     LinearAngleVariation(aNbTP-1, theAngles);
5169   }
5170   vector<double> aAngles( aNbTP );
5171   int j = 0;
5172   for(; j<aNbTP; ++j) {
5173     aAngles[j] = 0.;
5174   }
5175   if ( theHasAngles ) {
5176     double anAngle;;
5177     std::list<double>::iterator aItD = theAngles.begin();
5178     for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5179       anAngle = *aItD;
5180       aAngles[j] = anAngle;
5181     }
5182   }
5183   // fill vector of path points with angles
5184   //aPPs.resize(fullList.size());
5185   j = -1;
5186   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5187   for(; itPP!=fullList.end(); itPP++) {
5188     j++;
5189     SMESH_MeshEditor_PathPoint PP = *itPP;
5190     PP.SetAngle(aAngles[j]);
5191     aPPs[j] = PP;
5192   }
5193
5194   TNodeOfNodeListMap mapNewNodes;
5195   TElemOfVecOfNnlmiMap mapElemNewNodes;
5196   TElemOfElemListMap newElemsMap;
5197   TIDSortedElemSet::iterator itElem;
5198   double aX, aY, aZ;
5199   int aNb;
5200   SMDSAbs_ElementType aTypeE;
5201   // source elements for each generated one
5202   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5203
5204   // 3. Center of rotation aV0
5205   gp_Pnt aV0 = theRefPoint;
5206   gp_XYZ aGC;
5207   if ( !theHasRefPoint ) {
5208     aNb = 0;
5209     aGC.SetCoord( 0.,0.,0. );
5210
5211     itElem = theElements.begin();
5212     for ( ; itElem != theElements.end(); itElem++ ) {
5213       const SMDS_MeshElement* elem = *itElem;
5214
5215       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5216       while ( itN->more() ) {
5217         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5218         aX = node->X();
5219         aY = node->Y();
5220         aZ = node->Z();
5221
5222         if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5223           list<const SMDS_MeshNode*> aLNx;
5224           mapNewNodes[node] = aLNx;
5225           //
5226           gp_XYZ aXYZ( aX, aY, aZ );
5227           aGC += aXYZ;
5228           ++aNb;
5229         }
5230       }
5231     }
5232     aGC /= aNb;
5233     aV0.SetXYZ( aGC );
5234   } // if (!theHasRefPoint) {
5235   mapNewNodes.clear();
5236
5237   // 4. Processing the elements
5238   SMESHDS_Mesh* aMesh = GetMeshDS();
5239
5240   for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5241     // check element type
5242     const SMDS_MeshElement* elem = *itElem;
5243     aTypeE = elem->GetType();
5244     if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5245       continue;
5246
5247     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5248     newNodesItVec.reserve( elem->NbNodes() );
5249
5250     // loop on elem nodes
5251     int nodeIndex = -1;
5252     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5253     while ( itN->more() )
5254     {
5255       ++nodeIndex;
5256       // check if a node has been already processed
5257       const SMDS_MeshNode* node =
5258         static_cast<const SMDS_MeshNode*>( itN->next() );
5259       TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5260       if ( nIt == mapNewNodes.end() ) {
5261         nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5262         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5263
5264         // make new nodes
5265         aX = node->X();  aY = node->Y(); aZ = node->Z();
5266
5267         Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5268         gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5269         gp_Ax1 anAx1, anAxT1T0;
5270         gp_Dir aDT1x, aDT0x, aDT1T0;
5271
5272         aTolAng=1.e-4;
5273
5274         aV0x = aV0;
5275         aPN0.SetCoord(aX, aY, aZ);
5276
5277         const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5278         aP0x = aPP0.Pnt();
5279         aDT0x= aPP0.Tangent();
5280         //cout<<"j = 0   PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5281
5282         for ( j = 1; j < aNbTP; ++j ) {
5283           const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5284           aP1x = aPP1.Pnt();
5285           aDT1x = aPP1.Tangent();
5286           aAngle1x = aPP1.Angle();
5287
5288           gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5289           // Translation
5290           gp_Vec aV01x( aP0x, aP1x );
5291           aTrsf.SetTranslation( aV01x );
5292
5293           // traslated point
5294           aV1x = aV0x.Transformed( aTrsf );
5295           aPN1 = aPN0.Transformed( aTrsf );
5296
5297           // rotation 1 [ T1,T0 ]
5298           aAngleT1T0=-aDT1x.Angle( aDT0x );
5299           if (fabs(aAngleT1T0) > aTolAng) {
5300             aDT1T0=aDT1x^aDT0x;
5301             anAxT1T0.SetLocation( aV1x );
5302             anAxT1T0.SetDirection( aDT1T0 );
5303             aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5304
5305             aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5306           }
5307
5308           // rotation 2
5309           if ( theHasAngles ) {
5310             anAx1.SetLocation( aV1x );
5311             anAx1.SetDirection( aDT1x );
5312             aTrsfRot.SetRotation( anAx1, aAngle1x );
5313
5314             aPN1 = aPN1.Transformed( aTrsfRot );
5315           }
5316
5317           // make new node
5318           //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5319           if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5320             // create additional node
5321             double x = ( aPN1.X() + aPN0.X() )/2.;
5322             double y = ( aPN1.Y() + aPN0.Y() )/2.;
5323             double z = ( aPN1.Z() + aPN0.Z() )/2.;
5324             const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5325             myLastCreatedNodes.Append(newNode);
5326             srcNodes.Append( node );
5327             listNewNodes.push_back( newNode );
5328           }
5329           aX = aPN1.X();
5330           aY = aPN1.Y();
5331           aZ = aPN1.Z();
5332           const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5333           myLastCreatedNodes.Append(newNode);
5334           srcNodes.Append( node );
5335           listNewNodes.push_back( newNode );
5336
5337           aPN0 = aPN1;
5338           aP0x = aP1x;
5339           aV0x = aV1x;
5340           aDT0x = aDT1x;
5341         }
5342       }
5343
5344       else {
5345         // if current elem is quadratic and current node is not medium
5346         // we have to check - may be it is needed to insert additional nodes
5347         if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5348           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5349           if(listNewNodes.size()==aNbTP-1) {
5350             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5351             gp_XYZ P(node->X(), node->Y(), node->Z());
5352             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5353             int i;
5354             for(i=0; i<aNbTP-1; i++) {
5355               const SMDS_MeshNode* N = *it;
5356               double x = ( N->X() + P.X() )/2.;
5357               double y = ( N->Y() + P.Y() )/2.;
5358               double z = ( N->Z() + P.Z() )/2.;
5359               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5360               srcNodes.Append( node );
5361               myLastCreatedNodes.Append(newN);
5362               aNodes[2*i] = newN;
5363               aNodes[2*i+1] = N;
5364               P = gp_XYZ(N->X(),N->Y(),N->Z());
5365             }
5366             listNewNodes.clear();
5367             for(i=0; i<2*(aNbTP-1); i++) {
5368               listNewNodes.push_back(aNodes[i]);
5369             }
5370           }
5371         }
5372       }
5373
5374       newNodesItVec.push_back( nIt );
5375     }
5376     // make new elements
5377     //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5378     //              newNodesItVec[0]->second.size(), myLastCreatedElems );
5379     sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5380   }
5381
5382   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5383
5384   if ( theMakeGroups )
5385     generateGroups( srcNodes, srcElems, "extruded");
5386
5387   return EXTR_OK;
5388 }
5389
5390
5391 //=======================================================================
5392 //function : LinearAngleVariation
5393 //purpose  : auxilary for ExtrusionAlongTrack
5394 //=======================================================================
5395 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5396                                             list<double>& Angles)
5397 {
5398   int nbAngles = Angles.size();
5399   if( nbSteps > nbAngles ) {
5400     vector<double> theAngles(nbAngles);
5401     list<double>::iterator it = Angles.begin();
5402     int i = -1;
5403     for(; it!=Angles.end(); it++) {
5404       i++;
5405       theAngles[i] = (*it);
5406     }
5407     list<double> res;
5408     double rAn2St = double( nbAngles ) / double( nbSteps );
5409     double angPrev = 0, angle;
5410     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5411       double angCur = rAn2St * ( iSt+1 );
5412       double angCurFloor  = floor( angCur );
5413       double angPrevFloor = floor( angPrev );
5414       if ( angPrevFloor == angCurFloor )
5415         angle = rAn2St * theAngles[ int( angCurFloor ) ];
5416       else {
5417         int iP = int( angPrevFloor );
5418         double angPrevCeil = ceil(angPrev);
5419         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5420
5421         int iC = int( angCurFloor );
5422         if ( iC < nbAngles )
5423           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5424
5425         iP = int( angPrevCeil );
5426         while ( iC-- > iP )
5427           angle += theAngles[ iC ];
5428       }
5429       res.push_back(angle);
5430       angPrev = angCur;
5431     }
5432     Angles.clear();
5433     it = res.begin();
5434     for(; it!=res.end(); it++)
5435       Angles.push_back( *it );
5436   }
5437 }
5438
5439
5440 //================================================================================
5441 /*!
5442  * \brief Move or copy theElements applying theTrsf to their nodes
5443  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5444  *  \param theTrsf - transformation to apply
5445  *  \param theCopy - if true, create translated copies of theElems
5446  *  \param theMakeGroups - if true and theCopy, create translated groups
5447  *  \param theTargetMesh - mesh to copy translated elements into
5448  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5449  */
5450 //================================================================================
5451
5452 SMESH_MeshEditor::PGroupIDs
5453 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5454                              const gp_Trsf&     theTrsf,
5455                              const bool         theCopy,
5456                              const bool         theMakeGroups,
5457                              SMESH_Mesh*        theTargetMesh)
5458 {
5459   myLastCreatedElems.Clear();
5460   myLastCreatedNodes.Clear();
5461
5462   bool needReverse = false;
5463   string groupPostfix;
5464   switch ( theTrsf.Form() ) {
5465   case gp_PntMirror:
5466     MESSAGE("gp_PntMirror");
5467     needReverse = true;
5468     groupPostfix = "mirrored";
5469     break;
5470   case gp_Ax1Mirror:
5471     MESSAGE("gp_Ax1Mirror");
5472     groupPostfix = "mirrored";
5473     break;
5474   case gp_Ax2Mirror:
5475     MESSAGE("gp_Ax2Mirror");
5476     needReverse = true;
5477     groupPostfix = "mirrored";
5478     break;
5479   case gp_Rotation:
5480     MESSAGE("gp_Rotation");
5481     groupPostfix = "rotated";
5482     break;
5483   case gp_Translation:
5484     MESSAGE("gp_Translation");
5485     groupPostfix = "translated";
5486     break;
5487   case gp_Scale:
5488     MESSAGE("gp_Scale");
5489     groupPostfix = "scaled";
5490     break;
5491   case gp_CompoundTrsf: // different scale by axis
5492     MESSAGE("gp_CompoundTrsf");
5493     groupPostfix = "scaled";
5494     break;
5495   default:
5496     MESSAGE("default");
5497     needReverse = false;
5498     groupPostfix = "transformed";
5499   }
5500
5501   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5502   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5503   SMESHDS_Mesh* aMesh    = GetMeshDS();
5504
5505
5506   // map old node to new one
5507   TNodeNodeMap nodeMap;
5508
5509   // elements sharing moved nodes; those of them which have all
5510   // nodes mirrored but are not in theElems are to be reversed
5511   TIDSortedElemSet inverseElemSet;
5512
5513   // source elements for each generated one
5514   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5515
5516   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5517   TIDSortedElemSet orphanNode;
5518
5519   if ( theElems.empty() ) // transform the whole mesh
5520   {
5521     // add all elements
5522     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5523     while ( eIt->more() ) theElems.insert( eIt->next() );
5524     // add orphan nodes
5525     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5526     while ( nIt->more() )
5527     {
5528       const SMDS_MeshNode* node = nIt->next();
5529       if ( node->NbInverseElements() == 0)
5530         orphanNode.insert( node );
5531     }
5532   }
5533
5534   // loop on elements to transform nodes : first orphan nodes then elems
5535   TIDSortedElemSet::iterator itElem;
5536   TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5537   for (int i=0; i<2; i++)
5538   for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5539     const SMDS_MeshElement* elem = *itElem;
5540     if ( !elem )
5541       continue;
5542
5543     // loop on elem nodes
5544     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5545     while ( itN->more() ) {
5546
5547       const SMDS_MeshNode* node = cast2Node( itN->next() );
5548       // check if a node has been already transformed
5549       pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5550         nodeMap.insert( make_pair ( node, node ));
5551       if ( !n2n_isnew.second )
5552         continue;
5553
5554       double coord[3];
5555       coord[0] = node->X();
5556       coord[1] = node->Y();
5557       coord[2] = node->Z();
5558       theTrsf.Transforms( coord[0], coord[1], coord[2] );
5559       if ( theTargetMesh ) {
5560         const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5561         n2n_isnew.first->second = newNode;
5562         myLastCreatedNodes.Append(newNode);
5563         srcNodes.Append( node );
5564       }
5565       else if ( theCopy ) {
5566         const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5567         n2n_isnew.first->second = newNode;
5568         myLastCreatedNodes.Append(newNode);
5569         srcNodes.Append( node );
5570       }
5571       else {
5572         aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5573         // node position on shape becomes invalid
5574         const_cast< SMDS_MeshNode* > ( node )->SetPosition
5575           ( SMDS_SpacePosition::originSpacePosition() );
5576       }
5577
5578       // keep inverse elements
5579       if ( !theCopy && !theTargetMesh && needReverse ) {
5580         SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5581         while ( invElemIt->more() ) {
5582           const SMDS_MeshElement* iel = invElemIt->next();
5583           inverseElemSet.insert( iel );
5584         }
5585       }
5586     }
5587   }
5588
5589   // either create new elements or reverse mirrored ones
5590   if ( !theCopy && !needReverse && !theTargetMesh )
5591     return PGroupIDs();
5592
5593   TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5594   for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5595     theElems.insert( *invElemIt );
5596
5597   // Replicate or reverse elements
5598
5599   std::vector<int> iForw;
5600   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5601   {
5602     const SMDS_MeshElement* elem = *itElem;
5603     if ( !elem || elem->GetType() == SMDSAbs_Node )
5604       continue;
5605
5606     int nbNodes = elem->NbNodes();
5607     int elemType = elem->GetType();
5608
5609     if (elem->IsPoly()) {
5610
5611       // polygon or polyhedral volume
5612       switch ( elemType ) {
5613       case SMDSAbs_Face:
5614         {
5615           vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5616           int iNode = 0;
5617           SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5618           while (itN->more()) {
5619             const SMDS_MeshNode* node =
5620               static_cast<const SMDS_MeshNode*>(itN->next());
5621             TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5622             if (nodeMapIt == nodeMap.end())
5623               break; // not all nodes transformed
5624             if (needReverse) {
5625               // reverse mirrored faces and volumes
5626               poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5627             } else {
5628               poly_nodes[iNode] = (*nodeMapIt).second;
5629             }
5630             iNode++;
5631           }
5632           if ( iNode != nbNodes )
5633             continue; // not all nodes transformed
5634
5635           if ( theTargetMesh ) {
5636             myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5637             srcElems.Append( elem );
5638           }
5639           else if ( theCopy ) {
5640             myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5641             srcElems.Append( elem );
5642           }
5643           else {
5644             aMesh->ChangePolygonNodes(elem, poly_nodes);
5645           }
5646         }
5647         break;
5648       case SMDSAbs_Volume:
5649         {
5650           const SMDS_VtkVolume* aPolyedre =
5651             dynamic_cast<const SMDS_VtkVolume*>( elem );
5652           if (!aPolyedre) {
5653             MESSAGE("Warning: bad volumic element");
5654             continue;
5655           }
5656
5657           vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5658           vector<int> quantities;
5659
5660           bool allTransformed = true;
5661           int nbFaces = aPolyedre->NbFaces();
5662           for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5663             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5664             for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5665               const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5666               TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5667               if (nodeMapIt == nodeMap.end()) {
5668                 allTransformed = false; // not all nodes transformed
5669               } else {
5670                 poly_nodes.push_back((*nodeMapIt).second);
5671               }
5672               if ( needReverse && allTransformed )
5673                 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5674             }
5675             quantities.push_back(nbFaceNodes);
5676           }
5677           if ( !allTransformed )
5678             continue; // not all nodes transformed
5679
5680           if ( theTargetMesh ) {
5681             myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5682             srcElems.Append( elem );
5683           }
5684           else if ( theCopy ) {
5685             myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5686             srcElems.Append( elem );
5687           }
5688           else {
5689             aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5690           }
5691         }
5692         break;
5693       default:;
5694       }
5695       continue;
5696
5697     } // elem->isPoly()
5698
5699     // Regular elements
5700
5701     while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
5702     const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
5703     const std::vector<int>& i = needReverse ? iRev : iForw;
5704
5705     // find transformed nodes
5706     vector<const SMDS_MeshNode*> nodes(nbNodes);
5707     int iNode = 0;
5708     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5709     while ( itN->more() ) {
5710       const SMDS_MeshNode* node =
5711         static_cast<const SMDS_MeshNode*>( itN->next() );
5712       TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5713       if ( nodeMapIt == nodeMap.end() )
5714         break; // not all nodes transformed
5715       nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5716     }
5717     if ( iNode != nbNodes )
5718       continue; // not all nodes transformed
5719
5720     if ( theTargetMesh ) {
5721       if ( SMDS_MeshElement* copy =
5722            targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5723         myLastCreatedElems.Append( copy );
5724         srcElems.Append( elem );
5725       }
5726     }
5727     else if ( theCopy ) {
5728       if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5729         srcElems.Append( elem );
5730     }
5731     else {
5732       // reverse element as it was reversed by transformation
5733       if ( nbNodes > 2 )
5734         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5735     }
5736
5737   } // loop on elements
5738
5739   PGroupIDs newGroupIDs;
5740
5741   if ( ( theMakeGroups && theCopy ) ||
5742        ( theMakeGroups && theTargetMesh ) )
5743     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5744
5745   return newGroupIDs;
5746 }
5747
5748 //=======================================================================
5749 /*!
5750  * \brief Create groups of elements made during transformation
5751  * \param nodeGens - nodes making corresponding myLastCreatedNodes
5752  * \param elemGens - elements making corresponding myLastCreatedElems
5753  * \param postfix - to append to names of new groups
5754  */
5755 //=======================================================================
5756
5757 SMESH_MeshEditor::PGroupIDs
5758 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5759                                  const SMESH_SequenceOfElemPtr& elemGens,
5760                                  const std::string&             postfix,
5761                                  SMESH_Mesh*                    targetMesh)
5762 {
5763   PGroupIDs newGroupIDs( new list<int> );
5764   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
5765
5766   // Sort existing groups by types and collect their names
5767
5768   // to store an old group and a generated new one
5769   typedef pair< SMESHDS_GroupBase*, SMDS_MeshGroup* > TOldNewGroup;
5770   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
5771   // group names
5772   set< string > groupNames;
5773   //
5774   SMDS_MeshGroup* nullNewGroup = (SMDS_MeshGroup*) 0;
5775   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
5776   while ( groupIt->more() ) {
5777     SMESH_Group * group = groupIt->next();
5778     if ( !group ) continue;
5779     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
5780     if ( !groupDS || groupDS->IsEmpty() ) continue;
5781     groupNames.insert( group->GetName() );
5782     groupDS->SetStoreName( group->GetName() );
5783     groupsByType[ groupDS->GetType() ].push_back( make_pair( groupDS, nullNewGroup ));
5784   }
5785
5786   // Groups creation
5787
5788   // loop on nodes and elements
5789   for ( int isNodes = 0; isNodes < 2; ++isNodes )
5790   {
5791     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
5792     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
5793     if ( gens.Length() != elems.Length() )
5794       throw SALOME_Exception(LOCALIZED("invalid args"));
5795
5796     // loop on created elements
5797     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
5798     {
5799       const SMDS_MeshElement* sourceElem = gens( iElem );
5800       if ( !sourceElem ) {
5801         MESSAGE("generateGroups(): NULL source element");
5802         continue;
5803       }
5804       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
5805       if ( groupsOldNew.empty() ) {
5806         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
5807           ++iElem; // skip all elements made by sourceElem
5808         continue;
5809       }
5810       // collect all elements made by sourceElem
5811       list< const SMDS_MeshElement* > resultElems;
5812       if ( const SMDS_MeshElement* resElem = elems( iElem ))
5813         if ( resElem != sourceElem )
5814           resultElems.push_back( resElem );
5815       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
5816         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
5817           if ( resElem != sourceElem )
5818             resultElems.push_back( resElem );
5819       // do not generate element groups from node ones
5820 //      if ( sourceElem->GetType() == SMDSAbs_Node &&
5821 //           elems( iElem )->GetType() != SMDSAbs_Node )
5822 //        continue;
5823
5824       // add resultElems to groups made by ones the sourceElem belongs to
5825       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
5826       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
5827       {
5828         SMESHDS_GroupBase* oldGroup = gOldNew->first;
5829         if ( oldGroup->Contains( sourceElem )) // sourceElem in oldGroup
5830         {
5831           SMDS_MeshGroup* & newGroup = gOldNew->second;
5832           if ( !newGroup )// create a new group
5833           {
5834             // make a name
5835             string name = oldGroup->GetStoreName();
5836             if ( !targetMesh ) {
5837               name += "_";
5838               name += postfix;
5839               int nb = 0;
5840               while ( !groupNames.insert( name ).second ) // name exists
5841               {
5842                 if ( nb == 0 ) {
5843                   name += "_1";
5844                 }
5845                 else {
5846                   TCollection_AsciiString nbStr(nb+1);
5847                   name.resize( name.rfind('_')+1 );
5848                   name += nbStr.ToCString();
5849                 }
5850                 ++nb;
5851               }
5852             }
5853             // make a group
5854             int id;
5855             SMESH_Group* group = mesh->AddGroup( resultElems.back()->GetType(),
5856                                                  name.c_str(), id );
5857             SMESHDS_Group* groupDS = static_cast<SMESHDS_Group*>(group->GetGroupDS());
5858             newGroup = & groupDS->SMDSGroup();
5859             newGroupIDs->push_back( id );
5860           }
5861
5862           // fill in a new group
5863           list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
5864           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
5865             newGroup->Add( *resElemIt );
5866         }
5867       }
5868     } // loop on created elements
5869   }// loop on nodes and elements
5870
5871   return newGroupIDs;
5872 }
5873
5874 //================================================================================
5875 /*!
5876  * \brief Return list of group of nodes close to each other within theTolerance
5877  *        Search among theNodes or in the whole mesh if theNodes is empty using
5878  *        an Octree algorithm
5879  */
5880 //================================================================================
5881
5882 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
5883                                             const double         theTolerance,
5884                                             TListOfListOfNodes & theGroupsOfNodes)
5885 {
5886   myLastCreatedElems.Clear();
5887   myLastCreatedNodes.Clear();
5888
5889   if ( theNodes.empty() )
5890   { // get all nodes in the mesh
5891     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
5892     while ( nIt->more() )
5893       theNodes.insert( theNodes.end(),nIt->next());
5894   }
5895
5896   SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
5897 }
5898
5899
5900 //=======================================================================
5901 /*!
5902  * \brief Implementation of search for the node closest to point
5903  */
5904 //=======================================================================
5905
5906 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
5907 {
5908   //---------------------------------------------------------------------
5909   /*!
5910    * \brief Constructor
5911    */
5912   SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
5913   {
5914     myMesh = ( SMESHDS_Mesh* ) theMesh;
5915
5916     TIDSortedNodeSet nodes;
5917     if ( theMesh ) {
5918       SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
5919       while ( nIt->more() )
5920         nodes.insert( nodes.end(), nIt->next() );
5921     }
5922     myOctreeNode = new SMESH_OctreeNode(nodes) ;
5923
5924     // get max size of a leaf box
5925     SMESH_OctreeNode* tree = myOctreeNode;
5926     while ( !tree->isLeaf() )
5927     {
5928       SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
5929       if ( cIt->more() )
5930         tree = cIt->next();
5931     }
5932     myHalfLeafSize = tree->maxSize() / 2.;
5933   }
5934
5935   //---------------------------------------------------------------------
5936   /*!
5937    * \brief Move node and update myOctreeNode accordingly
5938    */
5939   void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
5940   {
5941     myOctreeNode->UpdateByMoveNode( node, toPnt );
5942     myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
5943   }
5944
5945   //---------------------------------------------------------------------
5946   /*!
5947    * \brief Do it's job
5948    */
5949   const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
5950   {
5951     map<double, const SMDS_MeshNode*> dist2Nodes;
5952     myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize );
5953     if ( !dist2Nodes.empty() )
5954       return dist2Nodes.begin()->second;
5955     list<const SMDS_MeshNode*> nodes;
5956     //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
5957
5958     double minSqDist = DBL_MAX;
5959     if ( nodes.empty() )  // get all nodes of OctreeNode's closest to thePnt
5960     {
5961       // sort leafs by their distance from thePnt
5962       typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
5963       TDistTreeMap treeMap;
5964       list< SMESH_OctreeNode* > treeList;
5965       list< SMESH_OctreeNode* >::iterator trIt;
5966       treeList.push_back( myOctreeNode );
5967
5968       gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
5969       bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize );
5970       for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
5971       {
5972         SMESH_OctreeNode* tree = *trIt;
5973         if ( !tree->isLeaf() ) // put children to the queue
5974         {
5975           if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue;
5976           SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
5977           while ( cIt->more() )
5978             treeList.push_back( cIt->next() );
5979         }
5980         else if ( tree->NbNodes() ) // put a tree to the treeMap
5981         {
5982           const Bnd_B3d& box = tree->getBox();
5983           double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
5984           pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
5985           if ( !it_in.second ) // not unique distance to box center
5986             treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
5987         }
5988       }
5989       // find distance after which there is no sense to check tree's
5990       double sqLimit = DBL_MAX;
5991       TDistTreeMap::iterator sqDist_tree = treeMap.begin();
5992       if ( treeMap.size() > 5 ) {
5993         SMESH_OctreeNode* closestTree = sqDist_tree->second;
5994         const Bnd_B3d& box = closestTree->getBox();
5995         double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
5996         sqLimit = limit * limit;
5997       }
5998       // get all nodes from trees
5999       for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
6000         if ( sqDist_tree->first > sqLimit )
6001           break;
6002         SMESH_OctreeNode* tree = sqDist_tree->second;
6003         tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
6004       }
6005     }
6006     // find closest among nodes
6007     minSqDist = DBL_MAX;
6008     const SMDS_MeshNode* closestNode = 0;
6009     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6010     for ( ; nIt != nodes.end(); ++nIt ) {
6011       double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) );
6012       if ( minSqDist > sqDist ) {
6013         closestNode = *nIt;
6014         minSqDist = sqDist;
6015       }
6016     }
6017     return closestNode;
6018   }
6019
6020   //---------------------------------------------------------------------
6021   /*!
6022    * \brief Destructor
6023    */
6024   ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
6025
6026   //---------------------------------------------------------------------
6027   /*!
6028    * \brief Return the node tree
6029    */
6030   const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
6031
6032 private:
6033   SMESH_OctreeNode* myOctreeNode;
6034   SMESHDS_Mesh*     myMesh;
6035   double            myHalfLeafSize; // max size of a leaf box
6036 };
6037
6038 //=======================================================================
6039 /*!
6040  * \brief Return SMESH_NodeSearcher
6041  */
6042 //=======================================================================
6043
6044 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher() 
6045 {
6046   return new SMESH_NodeSearcherImpl( GetMeshDS() );
6047 }
6048
6049 // ========================================================================
6050 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
6051 {
6052   const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
6053   const int MaxLevel         = 7;  // maximal tree height -> nb terminal boxes: 8^7 = 2097152
6054   const double NodeRadius = 1e-9;  // to enlarge bnd box of element
6055
6056   //=======================================================================
6057   /*!
6058    * \brief Octal tree of bounding boxes of elements
6059    */
6060   //=======================================================================
6061
6062   class ElementBndBoxTree : public SMESH_Octree
6063   {
6064   public:
6065
6066     ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(), double tolerance = NodeRadius );
6067     void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems);
6068     void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
6069     ~ElementBndBoxTree();
6070
6071   protected:
6072     ElementBndBoxTree() {}
6073     SMESH_Octree* allocateOctreeChild() const { return new ElementBndBoxTree; }
6074     void buildChildrenData();
6075     Bnd_B3d* buildRootBox();
6076   private:
6077     //!< Bounding box of element
6078     struct ElementBox : public Bnd_B3d
6079     {
6080       const SMDS_MeshElement* _element;
6081       int                     _refCount; // an ElementBox can be included in several tree branches
6082       ElementBox(const SMDS_MeshElement* elem, double tolerance);
6083     };
6084     vector< ElementBox* > _elements;
6085   };
6086
6087   //================================================================================
6088   /*!
6089    * \brief ElementBndBoxTree creation
6090    */
6091   //================================================================================
6092
6093   ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
6094     :SMESH_Octree( new SMESH_Octree::Limit( MaxLevel, /*minSize=*/0. ))
6095   {
6096     int nbElems = mesh.GetMeshInfo().NbElements( elemType );
6097     _elements.reserve( nbElems );
6098
6099     SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType );
6100     while ( elemIt->more() )
6101       _elements.push_back( new ElementBox( elemIt->next(),tolerance  ));
6102
6103     if ( _elements.size() > MaxNbElemsInLeaf )
6104       compute();
6105     else
6106       myIsLeaf = true;
6107   }
6108
6109   //================================================================================
6110   /*!
6111    * \brief Destructor
6112    */
6113   //================================================================================
6114
6115   ElementBndBoxTree::~ElementBndBoxTree()
6116   {
6117     for ( int i = 0; i < _elements.size(); ++i )
6118       if ( --_elements[i]->_refCount <= 0 )
6119         delete _elements[i];
6120   }
6121
6122   //================================================================================
6123   /*!
6124    * \brief Return the maximal box
6125    */
6126   //================================================================================
6127
6128   Bnd_B3d* ElementBndBoxTree::buildRootBox()
6129   {
6130     Bnd_B3d* box = new Bnd_B3d;
6131     for ( int i = 0; i < _elements.size(); ++i )
6132       box->Add( *_elements[i] );
6133     return box;
6134   }
6135
6136   //================================================================================
6137   /*!
6138    * \brief Redistrubute element boxes among children
6139    */
6140   //================================================================================
6141
6142   void ElementBndBoxTree::buildChildrenData()
6143   {
6144     for ( int i = 0; i < _elements.size(); ++i )
6145     {
6146       for (int j = 0; j < 8; j++)
6147       {
6148         if ( !_elements[i]->IsOut( myChildren[j]->getBox() ))
6149         {
6150           _elements[i]->_refCount++;
6151           ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
6152         }
6153       }
6154       _elements[i]->_refCount--;
6155     }
6156     _elements.clear();
6157
6158     for (int j = 0; j < 8; j++)
6159     {
6160       ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
6161       if ( child->_elements.size() <= MaxNbElemsInLeaf )
6162         child->myIsLeaf = true;
6163
6164       if ( child->_elements.capacity() - child->_elements.size() > 1000 )
6165         child->_elements.resize( child->_elements.size() ); // compact
6166     }
6167   }
6168
6169   //================================================================================
6170   /*!
6171    * \brief Return elements which can include the point
6172    */
6173   //================================================================================
6174
6175   void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt&     point,
6176                                                 TIDSortedElemSet& foundElems)
6177   {
6178     if ( level() && getBox().IsOut( point.XYZ() ))
6179       return;
6180
6181     if ( isLeaf() )
6182     {
6183       for ( int i = 0; i < _elements.size(); ++i )
6184         if ( !_elements[i]->IsOut( point.XYZ() ))
6185           foundElems.insert( _elements[i]->_element );
6186     }
6187     else
6188     {
6189       for (int i = 0; i < 8; i++)
6190         ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6191     }
6192   }
6193
6194   //================================================================================
6195   /*!
6196    * \brief Return elements which can be intersected by the line
6197    */
6198   //================================================================================
6199
6200   void ElementBndBoxTree::getElementsNearLine( const gp_Ax1&     line,
6201                                                TIDSortedElemSet& foundElems)
6202   {
6203     if ( level() && getBox().IsOut( line ))
6204       return;
6205
6206     if ( isLeaf() )
6207     {
6208       for ( int i = 0; i < _elements.size(); ++i )
6209         if ( !_elements[i]->IsOut( line ))
6210           foundElems.insert( _elements[i]->_element );
6211     }
6212     else
6213     {
6214       for (int i = 0; i < 8; i++)
6215         ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6216     }
6217   }
6218
6219   //================================================================================
6220   /*!
6221    * \brief Construct the element box
6222    */
6223   //================================================================================
6224
6225   ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance)
6226   {
6227     _element  = elem;
6228     _refCount = 1;
6229     SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6230     while ( nIt->more() )
6231       Add( SMESH_TNodeXYZ( cast2Node( nIt->next() )));
6232     Enlarge( tolerance );
6233   }
6234
6235 } // namespace
6236
6237 //=======================================================================
6238 /*!
6239  * \brief Implementation of search for the elements by point and
6240  *        of classification of point in 2D mesh
6241  */
6242 //=======================================================================
6243
6244 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6245 {
6246   SMESHDS_Mesh*                _mesh;
6247   SMDS_ElemIteratorPtr         _meshPartIt;
6248   ElementBndBoxTree*           _ebbTree;
6249   SMESH_NodeSearcherImpl*      _nodeSearcher;
6250   SMDSAbs_ElementType          _elementType;
6251   double                       _tolerance;
6252   bool                         _outerFacesFound;
6253   set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6254
6255   SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr())
6256     : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {}
6257   ~SMESH_ElementSearcherImpl()
6258   {
6259     if ( _ebbTree )      delete _ebbTree;      _ebbTree      = 0;
6260     if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6261   }
6262   virtual int FindElementsByPoint(const gp_Pnt&                      point,
6263                                   SMDSAbs_ElementType                type,
6264                                   vector< const SMDS_MeshElement* >& foundElements);
6265   virtual TopAbs_State GetPointState(const gp_Pnt& point);
6266
6267   void GetElementsNearLine( const gp_Ax1&                      line,
6268                             SMDSAbs_ElementType                type,
6269                             vector< const SMDS_MeshElement* >& foundElems);
6270   double getTolerance();
6271   bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6272                             const double tolerance, double & param);
6273   void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6274   bool isOuterBoundary(const SMDS_MeshElement* face) const
6275   {
6276     return _outerFaces.empty() || _outerFaces.count(face);
6277   }
6278   struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6279   {
6280     const SMDS_MeshElement* _face;
6281     gp_Vec                  _faceNorm;
6282     bool                    _coincides; //!< the line lays in face plane
6283     TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6284       : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6285   };
6286   struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6287   {
6288     SMESH_TLink      _link;
6289     TIDSortedElemSet _faces;
6290     TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6291       : _link( n1, n2 ), _faces( &face, &face + 1) {}
6292   };
6293 };
6294
6295 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6296 {
6297   return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6298              << ", _coincides="<<i._coincides << ")";
6299 }
6300
6301 //=======================================================================
6302 /*!
6303  * \brief define tolerance for search
6304  */
6305 //=======================================================================
6306
6307 double SMESH_ElementSearcherImpl::getTolerance()
6308 {
6309   if ( _tolerance < 0 )
6310   {
6311     const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6312
6313     _tolerance = 0;
6314     if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6315     {
6316       double boxSize = _nodeSearcher->getTree()->maxSize();
6317       _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6318     }
6319     else if ( _ebbTree && meshInfo.NbElements() > 0 )
6320     {
6321       double boxSize = _ebbTree->maxSize();
6322       _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6323     }
6324     if ( _tolerance == 0 )
6325     {
6326       // define tolerance by size of a most complex element
6327       int complexType = SMDSAbs_Volume;
6328       while ( complexType > SMDSAbs_All &&
6329               meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6330         --complexType;
6331       if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6332       double elemSize;
6333       if ( complexType == int( SMDSAbs_Node ))
6334       {
6335         SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6336         elemSize = 1;
6337         if ( meshInfo.NbNodes() > 2 )
6338           elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6339       }
6340       else
6341       {
6342         SMDS_ElemIteratorPtr elemIt =
6343             _mesh->elementsIterator( SMDSAbs_ElementType( complexType ));
6344         const SMDS_MeshElement* elem = elemIt->next();
6345         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6346         SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() ));
6347         elemSize = 0;
6348         while ( nodeIt->more() )
6349         {
6350           double dist = n1.Distance( cast2Node( nodeIt->next() ));
6351           elemSize = max( dist, elemSize );
6352         }
6353       }
6354       _tolerance = 1e-4 * elemSize;
6355     }
6356   }
6357   return _tolerance;
6358 }
6359
6360 //================================================================================
6361 /*!
6362  * \brief Find intersection of the line and an edge of face and return parameter on line
6363  */
6364 //================================================================================
6365
6366 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin&           line,
6367                                                      const SMDS_MeshElement* face,
6368                                                      const double            tol,
6369                                                      double &                param)
6370 {
6371   int nbInts = 0;
6372   param = 0;
6373
6374   GeomAPI_ExtremaCurveCurve anExtCC;
6375   Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6376   
6377   int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6378   for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6379   {
6380     GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )),
6381                          SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) )); 
6382     anExtCC.Init( lineCurve, edge);
6383     if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6384     {
6385       Quantity_Parameter pl, pe;
6386       anExtCC.LowerDistanceParameters( pl, pe );
6387       param += pl;
6388       if ( ++nbInts == 2 )
6389         break;
6390     }
6391   }
6392   if ( nbInts > 0 ) param /= nbInts;
6393   return nbInts > 0;
6394 }
6395 //================================================================================
6396 /*!
6397  * \brief Find all faces belonging to the outer boundary of mesh
6398  */
6399 //================================================================================
6400
6401 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6402 {
6403   if ( _outerFacesFound ) return;
6404
6405   // Collect all outer faces by passing from one outer face to another via their links
6406   // and BTW find out if there are internal faces at all.
6407
6408   // checked links and links where outer boundary meets internal one
6409   set< SMESH_TLink > visitedLinks, seamLinks;
6410
6411   // links to treat with already visited faces sharing them
6412   list < TFaceLink > startLinks;
6413
6414   // load startLinks with the first outerFace
6415   startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6416   _outerFaces.insert( outerFace );
6417
6418   TIDSortedElemSet emptySet;
6419   while ( !startLinks.empty() )
6420   {
6421     const SMESH_TLink& link  = startLinks.front()._link;
6422     TIDSortedElemSet&  faces = startLinks.front()._faces;
6423
6424     outerFace = *faces.begin();
6425     // find other faces sharing the link
6426     const SMDS_MeshElement* f;
6427     while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6428       faces.insert( f );
6429
6430     // select another outer face among the found 
6431     const SMDS_MeshElement* outerFace2 = 0;
6432     if ( faces.size() == 2 )
6433     {
6434       outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6435     }
6436     else if ( faces.size() > 2 )
6437     {
6438       seamLinks.insert( link );
6439
6440       // link direction within the outerFace
6441       gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()),
6442                    SMESH_TNodeXYZ( link.node2()));
6443       int i1 = outerFace->GetNodeIndex( link.node1() );
6444       int i2 = outerFace->GetNodeIndex( link.node2() );
6445       bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6446       if ( rev ) n1n2.Reverse();
6447       // outerFace normal
6448       gp_XYZ ofNorm, fNorm;
6449       if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6450       {
6451         // direction from the link inside outerFace
6452         gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6453         // sort all other faces by angle with the dirInOF
6454         map< double, const SMDS_MeshElement* > angle2Face;
6455         set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6456         for ( ; face != faces.end(); ++face )
6457         {
6458           if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6459             continue;
6460           gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6461           double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6462           if ( angle < 0 ) angle += 2. * M_PI;
6463           angle2Face.insert( make_pair( angle, *face ));
6464         }
6465         if ( !angle2Face.empty() )
6466           outerFace2 = angle2Face.begin()->second;
6467       }
6468     }
6469     // store the found outer face and add its links to continue seaching from
6470     if ( outerFace2 )
6471     {
6472       _outerFaces.insert( outerFace );
6473       int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6474       for ( int i = 0; i < nbNodes; ++i )
6475       {
6476         SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6477         if ( visitedLinks.insert( link2 ).second )
6478           startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6479       }
6480     }
6481     startLinks.pop_front();
6482   }
6483   _outerFacesFound = true;
6484
6485   if ( !seamLinks.empty() )
6486   {
6487     // There are internal boundaries touching the outher one,
6488     // find all faces of internal boundaries in order to find
6489     // faces of boundaries of holes, if any.
6490     
6491   }
6492   else
6493   {
6494     _outerFaces.clear();
6495   }
6496 }
6497
6498 //=======================================================================
6499 /*!
6500  * \brief Find elements of given type where the given point is IN or ON.
6501  *        Returns nb of found elements and elements them-selves.
6502  *
6503  * 'ALL' type means elements of any type excluding nodes and 0D elements
6504  */
6505 //=======================================================================
6506
6507 int SMESH_ElementSearcherImpl::
6508 FindElementsByPoint(const gp_Pnt&                      point,
6509                     SMDSAbs_ElementType                type,
6510                     vector< const SMDS_MeshElement* >& foundElements)
6511 {
6512   foundElements.clear();
6513
6514   double tolerance = getTolerance();
6515
6516   // =================================================================================
6517   if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement )
6518   {
6519     if ( !_nodeSearcher )
6520       _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6521
6522     const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6523     if ( !closeNode ) return foundElements.size();
6524
6525     if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance )
6526       return foundElements.size(); // to far from any node
6527
6528     if ( type == SMDSAbs_Node )
6529     {
6530       foundElements.push_back( closeNode );
6531     }
6532     else
6533     {
6534       SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( SMDSAbs_0DElement );
6535       while ( elemIt->more() )
6536         foundElements.push_back( elemIt->next() );
6537     }
6538   }
6539   // =================================================================================
6540   else // elements more complex than 0D
6541   {
6542     if ( !_ebbTree || _elementType != type )
6543     {
6544       if ( _ebbTree ) delete _ebbTree;
6545       _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance );
6546     }
6547     TIDSortedElemSet suspectElems;
6548     _ebbTree->getElementsNearPoint( point, suspectElems );
6549     TIDSortedElemSet::iterator elem = suspectElems.begin();
6550     for ( ; elem != suspectElems.end(); ++elem )
6551       if ( !SMESH_MeshEditor::isOut( *elem, point, tolerance ))
6552         foundElements.push_back( *elem );
6553   }
6554   return foundElements.size();
6555 }
6556
6557 //================================================================================
6558 /*!
6559  * \brief Classify the given point in the closed 2D mesh
6560  */
6561 //================================================================================
6562
6563 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6564 {
6565   double tolerance = getTolerance();
6566   if ( !_ebbTree || _elementType != SMDSAbs_Face )
6567   {
6568     if ( _ebbTree ) delete _ebbTree;
6569     _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt );
6570   }
6571   // Algo: analyse transition of a line starting at the point through mesh boundary;
6572   // try three lines parallel to axis of the coordinate system and perform rough
6573   // analysis. If solution is not clear perform thorough analysis.
6574
6575   const int nbAxes = 3;
6576   gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6577   map< double, TInters >   paramOnLine2TInters[ nbAxes ];
6578   list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6579   multimap< int, int > nbInt2Axis; // to find the simplest case
6580   for ( int axis = 0; axis < nbAxes; ++axis )
6581   {
6582     gp_Ax1 lineAxis( point, axisDir[axis]);
6583     gp_Lin line    ( lineAxis );
6584
6585     TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
6586     _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
6587
6588     // Intersect faces with the line
6589
6590     map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6591     TIDSortedElemSet::iterator face = suspectFaces.begin();
6592     for ( ; face != suspectFaces.end(); ++face )
6593     {
6594       // get face plane
6595       gp_XYZ fNorm;
6596       if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
6597       gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm );
6598
6599       // perform intersection
6600       IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
6601       if ( !intersection.IsDone() )
6602         continue;
6603       if ( intersection.IsInQuadric() )
6604       {
6605         tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
6606       }
6607       else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
6608       {
6609         gp_Pnt intersectionPoint = intersection.Point(1);
6610         if ( !SMESH_MeshEditor::isOut( *face, intersectionPoint, tolerance ))
6611           u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
6612       }
6613     }
6614     // Analyse intersections roughly
6615
6616     int nbInter = u2inters.size();
6617     if ( nbInter == 0 )
6618       return TopAbs_OUT; 
6619
6620     double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
6621     if ( nbInter == 1 ) // not closed mesh
6622       return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6623
6624     if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6625       return TopAbs_ON;
6626
6627     if ( (f<0) == (l<0) )
6628       return TopAbs_OUT;
6629
6630     int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
6631     int nbIntAfterPoint  = nbInter - nbIntBeforePoint;
6632     if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6633       return TopAbs_IN;
6634
6635     nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
6636
6637     if ( _outerFacesFound ) break; // pass to thorough analysis
6638
6639   } // three attempts - loop on CS axes
6640
6641   // Analyse intersections thoroughly.
6642   // We make two loops maximum, on the first one we only exclude touching intersections,
6643   // on the second, if situation is still unclear, we gather and use information on
6644   // position of faces (internal or outer). If faces position is already gathered,
6645   // we make the second loop right away.
6646
6647   for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
6648   {
6649     multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
6650     for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
6651     {
6652       int axis = nb_axis->second;
6653       map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6654
6655       gp_Ax1 lineAxis( point, axisDir[axis]);
6656       gp_Lin line    ( lineAxis );
6657
6658       // add tangent intersections to u2inters
6659       double param;
6660       list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
6661       for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
6662         if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
6663           u2inters.insert(make_pair( param, *tgtInt ));
6664       tangentInters[ axis ].clear();
6665
6666       // Count intersections before and after the point excluding touching ones.
6667       // If hasPositionInfo we count intersections of outer boundary only
6668
6669       int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
6670       double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
6671       map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
6672       bool ok = ! u_int1->second._coincides;
6673       while ( ok && u_int1 != u2inters.end() )
6674       {
6675         double u = u_int1->first;
6676         bool touchingInt = false;
6677         if ( ++u_int2 != u2inters.end() )
6678         {
6679           // skip intersections at the same point (if the line passes through edge or node)
6680           int nbSamePnt = 0;
6681           while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
6682           {
6683             ++nbSamePnt;
6684             ++u_int2;
6685           }
6686
6687           // skip tangent intersections
6688           int nbTgt = 0;
6689           const SMDS_MeshElement* prevFace = u_int1->second._face;
6690           while ( ok && u_int2->second._coincides )
6691           {
6692             if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
6693               ok = false;
6694             else
6695             {
6696               nbTgt++;
6697               u_int2++;
6698               ok = ( u_int2 != u2inters.end() );
6699             }
6700           }
6701           if ( !ok ) break;
6702
6703           // skip intersections at the same point after tangent intersections
6704           if ( nbTgt > 0 )
6705           {
6706             double u2 = u_int2->first;
6707             ++u_int2;
6708             while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
6709             {
6710               ++nbSamePnt;
6711               ++u_int2;
6712             }
6713           }
6714           // decide if we skipped a touching intersection
6715           if ( nbSamePnt + nbTgt > 0 )
6716           {
6717             double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
6718             map< double, TInters >::iterator u_int = u_int1;
6719             for ( ; u_int != u_int2; ++u_int )
6720             {
6721               if ( u_int->second._coincides ) continue;
6722               double dot = u_int->second._faceNorm * line.Direction();
6723               if ( dot > maxDot ) maxDot = dot;
6724               if ( dot < minDot ) minDot = dot;
6725             }
6726             touchingInt = ( minDot*maxDot < 0 );
6727           }
6728         }
6729         if ( !touchingInt )
6730         {
6731           if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
6732           {
6733             if ( u < 0 )
6734               ++nbIntBeforePoint;
6735             else
6736               ++nbIntAfterPoint;
6737           }
6738           if ( u < f ) f = u;
6739           if ( u > l ) l = u;
6740         }
6741
6742         u_int1 = u_int2; // to next intersection
6743
6744       } // loop on intersections with one line
6745
6746       if ( ok )
6747       {
6748         if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6749           return TopAbs_ON;
6750
6751         if ( nbIntBeforePoint == 0  || nbIntAfterPoint == 0)
6752           return TopAbs_OUT; 
6753
6754         if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
6755           return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6756
6757         if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6758           return TopAbs_IN;
6759
6760         if ( (f<0) == (l<0) )
6761           return TopAbs_OUT;
6762
6763         if ( hasPositionInfo )
6764           return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
6765       }
6766     } // loop on intersections of the tree lines - thorough analysis
6767
6768     if ( !hasPositionInfo )
6769     {
6770       // gather info on faces position - is face in the outer boundary or not
6771       map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
6772       findOuterBoundary( u2inters.begin()->second._face );
6773     }
6774
6775   } // two attempts - with and w/o faces position info in the mesh
6776
6777   return TopAbs_UNKNOWN;
6778 }
6779
6780 //=======================================================================
6781 /*!
6782  * \brief Return elements possibly intersecting the line
6783  */
6784 //=======================================================================
6785
6786 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1&                      line,
6787                                                      SMDSAbs_ElementType                type,
6788                                                      vector< const SMDS_MeshElement* >& foundElems)
6789 {
6790   if ( !_ebbTree || _elementType != type )
6791   {
6792     if ( _ebbTree ) delete _ebbTree;
6793     _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
6794   }
6795   TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
6796   _ebbTree->getElementsNearLine( line, suspectFaces );
6797   foundElems.assign( suspectFaces.begin(), suspectFaces.end());
6798 }
6799
6800 //=======================================================================
6801 /*!
6802  * \brief Return SMESH_ElementSearcher
6803  */
6804 //=======================================================================
6805
6806 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
6807 {
6808   return new SMESH_ElementSearcherImpl( *GetMeshDS() );
6809 }
6810
6811 //=======================================================================
6812 /*!
6813  * \brief Return SMESH_ElementSearcher acting on a sub-set of elements
6814  */
6815 //=======================================================================
6816
6817 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt)
6818 {
6819   return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt );
6820 }
6821
6822 //=======================================================================
6823 /*!
6824  * \brief Return true if the point is IN or ON of the element
6825  */
6826 //=======================================================================
6827
6828 bool SMESH_MeshEditor::isOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
6829 {
6830   if ( element->GetType() == SMDSAbs_Volume)
6831   {
6832     return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
6833   }
6834
6835   // get ordered nodes
6836
6837   vector< gp_XYZ > xyz;
6838   vector<const SMDS_MeshNode*> nodeList;
6839
6840   SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
6841   if ( element->IsQuadratic() ) {
6842     if (const SMDS_VtkFace* f=dynamic_cast<const SMDS_VtkFace*>(element))
6843       nodeIt = f->interlacedNodesElemIterator();
6844     else if (const SMDS_VtkEdge*  e =dynamic_cast<const SMDS_VtkEdge*>(element))
6845       nodeIt = e->interlacedNodesElemIterator();
6846   }
6847   while ( nodeIt->more() )
6848     {
6849       const SMDS_MeshNode* node = cast2Node( nodeIt->next() );
6850       xyz.push_back( SMESH_TNodeXYZ(node) );
6851       nodeList.push_back(node);
6852     }
6853
6854   int i, nbNodes = element->NbNodes();
6855
6856   if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
6857   {
6858     // compute face normal
6859     gp_Vec faceNorm(0,0,0);
6860     xyz.push_back( xyz.front() );
6861     nodeList.push_back( nodeList.front() );
6862     for ( i = 0; i < nbNodes; ++i )
6863     {
6864       gp_Vec edge1( xyz[i+1], xyz[i]);
6865       gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
6866       faceNorm += edge1 ^ edge2;
6867     }
6868     double normSize = faceNorm.Magnitude();
6869     if ( normSize <= tol )
6870     {
6871       // degenerated face: point is out if it is out of all face edges
6872       for ( i = 0; i < nbNodes; ++i )
6873       {
6874         SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] );
6875         if ( !isOut( &edge, point, tol ))
6876           return false;
6877       }
6878       return true;
6879     }
6880     faceNorm /= normSize;
6881
6882     // check if the point lays on face plane
6883     gp_Vec n2p( xyz[0], point );
6884     if ( fabs( n2p * faceNorm ) > tol )
6885       return true; // not on face plane
6886
6887     // check if point is out of face boundary:
6888     // define it by closest transition of a ray point->infinity through face boundary
6889     // on the face plane.
6890     // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
6891     // to find intersections of the ray with the boundary.
6892     gp_Vec ray = n2p;
6893     gp_Vec plnNorm = ray ^ faceNorm;
6894     normSize = plnNorm.Magnitude();
6895     if ( normSize <= tol ) return false; // point coincides with the first node
6896     plnNorm /= normSize;
6897     // for each node of the face, compute its signed distance to the plane
6898     vector<double> dist( nbNodes + 1);
6899     for ( i = 0; i < nbNodes; ++i )
6900     {
6901       gp_Vec n2p( xyz[i], point );
6902       dist[i] = n2p * plnNorm;
6903     }
6904     dist.back() = dist.front();
6905     // find the closest intersection
6906     int    iClosest = -1;
6907     double rClosest, distClosest = 1e100;;
6908     gp_Pnt pClosest;
6909     for ( i = 0; i < nbNodes; ++i )
6910     {
6911       double r;
6912       if ( fabs( dist[i]) < tol )
6913         r = 0.;
6914       else if ( fabs( dist[i+1]) < tol )
6915         r = 1.;
6916       else if ( dist[i] * dist[i+1] < 0 )
6917         r = dist[i] / ( dist[i] - dist[i+1] );
6918       else
6919         continue; // no intersection
6920       gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
6921       gp_Vec p2int ( point, pInt);
6922       if ( p2int * ray > -tol ) // right half-space
6923       {
6924         double intDist = p2int.SquareMagnitude();
6925         if ( intDist < distClosest )
6926         {
6927           iClosest = i;
6928           rClosest = r;
6929           pClosest = pInt;
6930           distClosest = intDist;
6931         }
6932       }
6933     }
6934     if ( iClosest < 0 )
6935       return true; // no intesections - out
6936
6937     // analyse transition
6938     gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
6939     gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
6940     gp_Vec p2int ( point, pClosest );
6941     bool out = (edgeNorm * p2int) < -tol;
6942     if ( rClosest > 0. && rClosest < 1. ) // not node intersection
6943       return out;
6944
6945     // ray pass through a face node; analyze transition through an adjacent edge
6946     gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
6947     gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
6948     gp_Vec edgeAdjacent( p1, p2 );
6949     gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
6950     bool out2 = (edgeNorm2 * p2int) < -tol;
6951
6952     bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
6953     return covexCorner ? (out || out2) : (out && out2);
6954   }
6955   if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
6956   {
6957     // point is out of edge if it is NOT ON any straight part of edge
6958     // (we consider quadratic edge as being composed of two straight parts)
6959     for ( i = 1; i < nbNodes; ++i )
6960     {
6961       gp_Vec edge( xyz[i-1], xyz[i]);
6962       gp_Vec n1p ( xyz[i-1], point);
6963       double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
6964       if ( dist > tol )
6965         continue;
6966       gp_Vec n2p( xyz[i], point );
6967       if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
6968         continue;
6969       return false; // point is ON this part
6970     }
6971     return true;
6972   }
6973   // Node or 0D element -------------------------------------------------------------------------
6974   {
6975     gp_Vec n2p ( xyz[0], point );
6976     return n2p.Magnitude() <= tol;
6977   }
6978   return true;
6979 }
6980
6981 //=======================================================================
6982 //function : SimplifyFace
6983 //purpose  :
6984 //=======================================================================
6985 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
6986                                     vector<const SMDS_MeshNode *>&      poly_nodes,
6987                                     vector<int>&                        quantities) const
6988 {
6989   int nbNodes = faceNodes.size();
6990
6991   if (nbNodes < 3)
6992     return 0;
6993
6994   set<const SMDS_MeshNode*> nodeSet;
6995
6996   // get simple seq of nodes
6997   //const SMDS_MeshNode* simpleNodes[ nbNodes ];
6998   vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
6999   int iSimple = 0, nbUnique = 0;
7000
7001   simpleNodes[iSimple++] = faceNodes[0];
7002   nbUnique++;
7003   for (int iCur = 1; iCur < nbNodes; iCur++) {
7004     if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7005       simpleNodes[iSimple++] = faceNodes[iCur];
7006       if (nodeSet.insert( faceNodes[iCur] ).second)
7007         nbUnique++;
7008     }
7009   }
7010   int nbSimple = iSimple;
7011   if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7012     nbSimple--;
7013     iSimple--;
7014   }
7015
7016   if (nbUnique < 3)
7017     return 0;
7018
7019   // separate loops
7020   int nbNew = 0;
7021   bool foundLoop = (nbSimple > nbUnique);
7022   while (foundLoop) {
7023     foundLoop = false;
7024     set<const SMDS_MeshNode*> loopSet;
7025     for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7026       const SMDS_MeshNode* n = simpleNodes[iSimple];
7027       if (!loopSet.insert( n ).second) {
7028         foundLoop = true;
7029
7030         // separate loop
7031         int iC = 0, curLast = iSimple;
7032         for (; iC < curLast; iC++) {
7033           if (simpleNodes[iC] == n) break;
7034         }
7035         int loopLen = curLast - iC;
7036         if (loopLen > 2) {
7037           // create sub-element
7038           nbNew++;
7039           quantities.push_back(loopLen);
7040           for (; iC < curLast; iC++) {
7041             poly_nodes.push_back(simpleNodes[iC]);
7042           }
7043         }
7044         // shift the rest nodes (place from the first loop position)
7045         for (iC = curLast + 1; iC < nbSimple; iC++) {
7046           simpleNodes[iC - loopLen] = simpleNodes[iC];
7047         }
7048         nbSimple -= loopLen;
7049         iSimple -= loopLen;
7050       }
7051     } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7052   } // while (foundLoop)
7053
7054   if (iSimple > 2) {
7055     nbNew++;
7056     quantities.push_back(iSimple);
7057     for (int i = 0; i < iSimple; i++)
7058       poly_nodes.push_back(simpleNodes[i]);
7059   }
7060
7061   return nbNew;
7062 }
7063
7064 //=======================================================================
7065 //function : MergeNodes
7066 //purpose  : In each group, the cdr of nodes are substituted by the first one
7067 //           in all elements.
7068 //=======================================================================
7069
7070 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7071 {
7072   MESSAGE("MergeNodes");
7073   myLastCreatedElems.Clear();
7074   myLastCreatedNodes.Clear();
7075
7076   SMESHDS_Mesh* aMesh = GetMeshDS();
7077
7078   TNodeNodeMap nodeNodeMap; // node to replace - new node
7079   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7080   list< int > rmElemIds, rmNodeIds;
7081
7082   // Fill nodeNodeMap and elems
7083
7084   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7085   for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7086     list<const SMDS_MeshNode*>& nodes = *grIt;
7087     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7088     const SMDS_MeshNode* nToKeep = *nIt;
7089     //MESSAGE("node to keep " << nToKeep->GetID());
7090     for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7091       const SMDS_MeshNode* nToRemove = *nIt;
7092       nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7093       if ( nToRemove != nToKeep ) {
7094         //MESSAGE("  node to remove " << nToRemove->GetID());
7095         rmNodeIds.push_back( nToRemove->GetID() );
7096         AddToSameGroups( nToKeep, nToRemove, aMesh );
7097       }
7098
7099       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7100       while ( invElemIt->more() ) {
7101         const SMDS_MeshElement* elem = invElemIt->next();
7102         elems.insert(elem);
7103       }
7104     }
7105   }
7106   // Change element nodes or remove an element
7107
7108   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7109   for ( ; eIt != elems.end(); eIt++ ) {
7110     const SMDS_MeshElement* elem = *eIt;
7111     //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7112     int nbNodes = elem->NbNodes();
7113     int aShapeId = FindShape( elem );
7114
7115     set<const SMDS_MeshNode*> nodeSet;
7116     vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7117     int iUnique = 0, iCur = 0, nbRepl = 0;
7118     vector<int> iRepl( nbNodes );
7119
7120     // get new seq of nodes
7121     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7122     while ( itN->more() ) {
7123       const SMDS_MeshNode* n =
7124         static_cast<const SMDS_MeshNode*>( itN->next() );
7125
7126       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7127       if ( nnIt != nodeNodeMap.end() ) { // n sticks
7128         n = (*nnIt).second;
7129         // BUG 0020185: begin
7130         {
7131           bool stopRecur = false;
7132           set<const SMDS_MeshNode*> nodesRecur;
7133           nodesRecur.insert(n);
7134           while (!stopRecur) {
7135             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7136             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7137               n = (*nnIt_i).second;
7138               if (!nodesRecur.insert(n).second) {
7139                 // error: recursive dependancy
7140                 stopRecur = true;
7141               }
7142             }
7143             else
7144               stopRecur = true;
7145           }
7146         }
7147         // BUG 0020185: end
7148       }
7149       curNodes[ iCur ] = n;
7150       bool isUnique = nodeSet.insert( n ).second;
7151       if ( isUnique )
7152         uniqueNodes[ iUnique++ ] = n;
7153       else
7154         iRepl[ nbRepl++ ] = iCur;
7155       iCur++;
7156     }
7157
7158     // Analyse element topology after replacement
7159
7160     bool isOk = true;
7161     int nbUniqueNodes = nodeSet.size();
7162     //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7163     if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7164       // Polygons and Polyhedral volumes
7165       if (elem->IsPoly()) {
7166
7167         if (elem->GetType() == SMDSAbs_Face) {
7168           // Polygon
7169           vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7170           int inode = 0;
7171           for (; inode < nbNodes; inode++) {
7172             face_nodes[inode] = curNodes[inode];
7173           }
7174
7175           vector<const SMDS_MeshNode *> polygons_nodes;
7176           vector<int> quantities;
7177           int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7178           if (nbNew > 0) {
7179             inode = 0;
7180             for (int iface = 0; iface < nbNew; iface++) {
7181               int nbNodes = quantities[iface];
7182               vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7183               for (int ii = 0; ii < nbNodes; ii++, inode++) {
7184                 poly_nodes[ii] = polygons_nodes[inode];
7185               }
7186               SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7187               myLastCreatedElems.Append(newElem);
7188               if (aShapeId)
7189                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7190             }
7191
7192             MESSAGE("ChangeElementNodes MergeNodes Polygon");
7193             //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7194             vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7195             int quid =0;
7196             if (nbNew > 0) quid = nbNew - 1;
7197             vector<int> newquant(quantities.begin()+quid, quantities.end());
7198             const SMDS_MeshElement* newElem = 0;
7199             newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7200             myLastCreatedElems.Append(newElem);
7201             if ( aShapeId && newElem )
7202               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7203             rmElemIds.push_back(elem->GetID());
7204           }
7205           else {
7206             rmElemIds.push_back(elem->GetID());
7207           }
7208
7209         }
7210         else if (elem->GetType() == SMDSAbs_Volume) {
7211           // Polyhedral volume
7212           if (nbUniqueNodes < 4) {
7213             rmElemIds.push_back(elem->GetID());
7214           }
7215           else {
7216             // each face has to be analyzed in order to check volume validity
7217             const SMDS_VtkVolume* aPolyedre =
7218               dynamic_cast<const SMDS_VtkVolume*>( elem );
7219             if (aPolyedre) {
7220               int nbFaces = aPolyedre->NbFaces();
7221
7222               vector<const SMDS_MeshNode *> poly_nodes;
7223               vector<int> quantities;
7224
7225               for (int iface = 1; iface <= nbFaces; iface++) {
7226                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7227                 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7228
7229                 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7230                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7231                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7232                   if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7233                     faceNode = (*nnIt).second;
7234                   }
7235                   faceNodes[inode - 1] = faceNode;
7236                 }
7237
7238                 SimplifyFace(faceNodes, poly_nodes, quantities);
7239               }
7240
7241               if (quantities.size() > 3) {
7242                 // to be done: remove coincident faces
7243               }
7244
7245               if (quantities.size() > 3)
7246                 {
7247                   MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7248                   //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7249                   const SMDS_MeshElement* newElem = 0;
7250                   newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7251                   myLastCreatedElems.Append(newElem);
7252                   if ( aShapeId && newElem )
7253                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
7254                   rmElemIds.push_back(elem->GetID());
7255                 }
7256             }
7257             else {
7258               rmElemIds.push_back(elem->GetID());
7259             }
7260           }
7261         }
7262         else {
7263         }
7264
7265         continue;
7266       } // poly element
7267
7268       // Regular elements
7269       // TODO not all the possible cases are solved. Find something more generic?
7270       switch ( nbNodes ) {
7271       case 2: ///////////////////////////////////// EDGE
7272         isOk = false; break;
7273       case 3: ///////////////////////////////////// TRIANGLE
7274         isOk = false; break;
7275       case 4:
7276         if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7277           isOk = false;
7278         else { //////////////////////////////////// QUADRANGLE
7279           if ( nbUniqueNodes < 3 )
7280             isOk = false;
7281           else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7282             isOk = false; // opposite nodes stick
7283           //MESSAGE("isOk " << isOk);
7284         }
7285         break;
7286       case 6: ///////////////////////////////////// PENTAHEDRON
7287         if ( nbUniqueNodes == 4 ) {
7288           // ---------------------------------> tetrahedron
7289           if (nbRepl == 3 &&
7290               iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7291             // all top nodes stick: reverse a bottom
7292             uniqueNodes[ 0 ] = curNodes [ 1 ];
7293             uniqueNodes[ 1 ] = curNodes [ 0 ];
7294           }
7295           else if (nbRepl == 3 &&
7296                    iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7297             // all bottom nodes stick: set a top before
7298             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7299             uniqueNodes[ 0 ] = curNodes [ 3 ];
7300             uniqueNodes[ 1 ] = curNodes [ 4 ];
7301             uniqueNodes[ 2 ] = curNodes [ 5 ];
7302           }
7303           else if (nbRepl == 4 &&
7304                    iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7305             // a lateral face turns into a line: reverse a bottom
7306             uniqueNodes[ 0 ] = curNodes [ 1 ];
7307             uniqueNodes[ 1 ] = curNodes [ 0 ];
7308           }
7309           else
7310             isOk = false;
7311         }
7312         else if ( nbUniqueNodes == 5 ) {
7313           // PENTAHEDRON --------------------> 2 tetrahedrons
7314           if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7315             // a bottom node sticks with a linked top one
7316             // 1.
7317             SMDS_MeshElement* newElem =
7318               aMesh->AddVolume(curNodes[ 3 ],
7319                                curNodes[ 4 ],
7320                                curNodes[ 5 ],
7321                                curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7322             myLastCreatedElems.Append(newElem);
7323             if ( aShapeId )
7324               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7325             // 2. : reverse a bottom
7326             uniqueNodes[ 0 ] = curNodes [ 1 ];
7327             uniqueNodes[ 1 ] = curNodes [ 0 ];
7328             nbUniqueNodes = 4;
7329           }
7330           else
7331             isOk = false;
7332         }
7333         else
7334           isOk = false;
7335         break;
7336       case 8: {
7337         if(elem->IsQuadratic()) { // Quadratic quadrangle
7338           //   1    5    2
7339           //    +---+---+
7340           //    |       |
7341           //    |       |
7342           //   4+       +6
7343           //    |       |
7344           //    |       |
7345           //    +---+---+
7346           //   0    7    3
7347           isOk = false;
7348           if(nbRepl==2) {
7349             MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7350           }
7351           if(nbRepl==3) {
7352             MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2]);
7353             nbUniqueNodes = 6;
7354             if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7355               uniqueNodes[0] = curNodes[0];
7356               uniqueNodes[1] = curNodes[2];
7357               uniqueNodes[2] = curNodes[3];
7358               uniqueNodes[3] = curNodes[5];
7359               uniqueNodes[4] = curNodes[6];
7360               uniqueNodes[5] = curNodes[7];
7361               isOk = true;
7362             }
7363             if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7364               uniqueNodes[0] = curNodes[0];
7365               uniqueNodes[1] = curNodes[1];
7366               uniqueNodes[2] = curNodes[2];
7367               uniqueNodes[3] = curNodes[4];
7368               uniqueNodes[4] = curNodes[5];
7369               uniqueNodes[5] = curNodes[6];
7370               isOk = true;
7371             }
7372             if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7373               uniqueNodes[0] = curNodes[1];
7374               uniqueNodes[1] = curNodes[2];
7375               uniqueNodes[2] = curNodes[3];
7376               uniqueNodes[3] = curNodes[5];
7377               uniqueNodes[4] = curNodes[6];
7378               uniqueNodes[5] = curNodes[0];
7379               isOk = true;
7380             }
7381             if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7382               uniqueNodes[0] = curNodes[0];
7383               uniqueNodes[1] = curNodes[1];
7384               uniqueNodes[2] = curNodes[3];
7385               uniqueNodes[3] = curNodes[4];
7386               uniqueNodes[4] = curNodes[6];
7387               uniqueNodes[5] = curNodes[7];
7388               isOk = true;
7389             }
7390             if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7391               uniqueNodes[0] = curNodes[0];
7392               uniqueNodes[1] = curNodes[2];
7393               uniqueNodes[2] = curNodes[3];
7394               uniqueNodes[3] = curNodes[1];
7395               uniqueNodes[4] = curNodes[6];
7396               uniqueNodes[5] = curNodes[7];
7397               isOk = true;
7398             }
7399             if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7400               uniqueNodes[0] = curNodes[0];
7401               uniqueNodes[1] = curNodes[1];
7402               uniqueNodes[2] = curNodes[2];
7403               uniqueNodes[3] = curNodes[4];
7404               uniqueNodes[4] = curNodes[5];
7405               uniqueNodes[5] = curNodes[7];
7406               isOk = true;
7407             }
7408             if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7409               uniqueNodes[0] = curNodes[0];
7410               uniqueNodes[1] = curNodes[1];
7411               uniqueNodes[2] = curNodes[3];
7412               uniqueNodes[3] = curNodes[4];
7413               uniqueNodes[4] = curNodes[2];
7414               uniqueNodes[5] = curNodes[7];
7415               isOk = true;
7416             }
7417             if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7418               uniqueNodes[0] = curNodes[0];
7419               uniqueNodes[1] = curNodes[1];
7420               uniqueNodes[2] = curNodes[2];
7421               uniqueNodes[3] = curNodes[4];
7422               uniqueNodes[4] = curNodes[5];
7423               uniqueNodes[5] = curNodes[3];
7424               isOk = true;
7425             }
7426           }
7427           if(nbRepl==4) {
7428             MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3]);
7429           }
7430           if(nbRepl==5) {
7431             MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7432           }
7433           break;
7434         }
7435         //////////////////////////////////// HEXAHEDRON
7436         isOk = false;
7437         SMDS_VolumeTool hexa (elem);
7438         hexa.SetExternalNormal();
7439         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7440           //////////////////////// HEX ---> 1 tetrahedron
7441           for ( int iFace = 0; iFace < 6; iFace++ ) {
7442             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7443             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7444                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7445                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7446               // one face turns into a point ...
7447               int iOppFace = hexa.GetOppFaceIndex( iFace );
7448               ind = hexa.GetFaceNodesIndices( iOppFace );
7449               int nbStick = 0;
7450               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7451                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7452                   nbStick++;
7453               }
7454               if ( nbStick == 1 ) {
7455                 // ... and the opposite one - into a triangle.
7456                 // set a top node
7457                 ind = hexa.GetFaceNodesIndices( iFace );
7458                 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7459                 isOk = true;
7460               }
7461               break;
7462             }
7463           }
7464         }
7465         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7466           //////////////////////// HEX ---> 1 prism
7467           int nbTria = 0, iTria[3];
7468           const int *ind; // indices of face nodes
7469           // look for triangular faces
7470           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7471             ind = hexa.GetFaceNodesIndices( iFace );
7472             TIDSortedNodeSet faceNodes;
7473             for ( iCur = 0; iCur < 4; iCur++ )
7474               faceNodes.insert( curNodes[ind[iCur]] );
7475             if ( faceNodes.size() == 3 )
7476               iTria[ nbTria++ ] = iFace;
7477           }
7478           // check if triangles are opposite
7479           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7480           {
7481             isOk = true;
7482             // set nodes of the bottom triangle
7483             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7484             vector<int> indB;
7485             for ( iCur = 0; iCur < 4; iCur++ )
7486               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7487                 indB.push_back( ind[iCur] );
7488             if ( !hexa.IsForward() )
7489               std::swap( indB[0], indB[2] );
7490             for ( iCur = 0; iCur < 3; iCur++ )
7491               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7492             // set nodes of the top triangle
7493             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7494             for ( iCur = 0; iCur < 3; ++iCur )
7495               for ( int j = 0; j < 4; ++j )
7496                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7497                 {
7498                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7499                   break;
7500                 }
7501           }
7502           break;
7503         }
7504         else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7505           //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7506           for ( int iFace = 0; iFace < 6; iFace++ ) {
7507             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7508             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7509                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7510                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7511               // one face turns into a point ...
7512               int iOppFace = hexa.GetOppFaceIndex( iFace );
7513               ind = hexa.GetFaceNodesIndices( iOppFace );
7514               int nbStick = 0;
7515               iUnique = 2;  // reverse a tetrahedron 1 bottom
7516               for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7517                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7518                   nbStick++;
7519                 else if ( iUnique >= 0 )
7520                   uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7521               }
7522               if ( nbStick == 0 ) {
7523                 // ... and the opposite one is a quadrangle
7524                 // set a top node
7525                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7526                 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7527                 nbUniqueNodes = 4;
7528                 // tetrahedron 2
7529                 SMDS_MeshElement* newElem =
7530                   aMesh->AddVolume(curNodes[ind[ 0 ]],
7531                                    curNodes[ind[ 3 ]],
7532                                    curNodes[ind[ 2 ]],
7533                                    curNodes[indTop[ 0 ]]);
7534                 myLastCreatedElems.Append(newElem);
7535                 if ( aShapeId )
7536                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7537                 isOk = true;
7538               }
7539               break;
7540             }
7541           }
7542         }
7543         else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7544           ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7545           // find indices of quad and tri faces
7546           int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7547           for ( iFace = 0; iFace < 6; iFace++ ) {
7548             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7549             nodeSet.clear();
7550             for ( iCur = 0; iCur < 4; iCur++ )
7551               nodeSet.insert( curNodes[ind[ iCur ]] );
7552             nbUniqueNodes = nodeSet.size();
7553             if ( nbUniqueNodes == 3 )
7554               iTriFace[ nbTri++ ] = iFace;
7555             else if ( nbUniqueNodes == 4 )
7556               iQuadFace[ nbQuad++ ] = iFace;
7557           }
7558           if (nbQuad == 2 && nbTri == 4 &&
7559               hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7560             // 2 opposite quadrangles stuck with a diagonal;
7561             // sample groups of merged indices: (0-4)(2-6)
7562             // --------------------------------------------> 2 tetrahedrons
7563             const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7564             const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7565             int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7566             if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7567                 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7568               // stuck with 0-2 diagonal
7569               i0  = ind1[ 3 ];
7570               i1d = ind1[ 0 ];
7571               i2  = ind1[ 1 ];
7572               i3d = ind1[ 2 ];
7573               i0t = ind2[ 1 ];
7574               i2t = ind2[ 3 ];
7575             }
7576             else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7577                      curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7578               // stuck with 1-3 diagonal
7579               i0  = ind1[ 0 ];
7580               i1d = ind1[ 1 ];
7581               i2  = ind1[ 2 ];
7582               i3d = ind1[ 3 ];
7583               i0t = ind2[ 0 ];
7584               i2t = ind2[ 1 ];
7585             }
7586             else {
7587               ASSERT(0);
7588             }
7589             // tetrahedron 1
7590             uniqueNodes[ 0 ] = curNodes [ i0 ];
7591             uniqueNodes[ 1 ] = curNodes [ i1d ];
7592             uniqueNodes[ 2 ] = curNodes [ i3d ];
7593             uniqueNodes[ 3 ] = curNodes [ i0t ];
7594             nbUniqueNodes = 4;
7595             // tetrahedron 2
7596             SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7597                                                          curNodes[ i2 ],
7598                                                          curNodes[ i3d ],
7599                                                          curNodes[ i2t ]);
7600             myLastCreatedElems.Append(newElem);
7601             if ( aShapeId )
7602               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7603             isOk = true;
7604           }
7605           else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7606                    ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7607             // --------------------------------------------> prism
7608             // find 2 opposite triangles
7609             nbUniqueNodes = 6;
7610             for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7611               if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7612                 // find indices of kept and replaced nodes
7613                 // and fill unique nodes of 2 opposite triangles
7614                 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7615                 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7616                 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7617                 // fill unique nodes
7618                 iUnique = 0;
7619                 isOk = true;
7620                 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7621                   const SMDS_MeshNode* n     = curNodes[ind1[ iCur ]];
7622                   const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7623                   if ( n == nInit ) {
7624                     // iCur of a linked node of the opposite face (make normals co-directed):
7625                     int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7626                     // check that correspondent corners of triangles are linked
7627                     if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7628                       isOk = false;
7629                     else {
7630                       uniqueNodes[ iUnique ] = n;
7631                       uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7632                       iUnique++;
7633                     }
7634                   }
7635                 }
7636                 break;
7637               }
7638             }
7639           }
7640         } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7641         else
7642         {
7643           MESSAGE("MergeNodes() removes hexahedron "<< elem);
7644         }
7645         break;
7646       } // HEXAHEDRON
7647
7648       default:
7649         isOk = false;
7650       } // switch ( nbNodes )
7651
7652     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7653
7654     if ( isOk ) { // the elem remains valid after sticking nodes
7655       if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
7656       {
7657         // Change nodes of polyedre
7658         const SMDS_VtkVolume* aPolyedre =
7659           dynamic_cast<const SMDS_VtkVolume*>( elem );
7660         if (aPolyedre) {
7661           int nbFaces = aPolyedre->NbFaces();
7662
7663           vector<const SMDS_MeshNode *> poly_nodes;
7664           vector<int> quantities (nbFaces);
7665
7666           for (int iface = 1; iface <= nbFaces; iface++) {
7667             int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7668             quantities[iface - 1] = nbFaceNodes;
7669
7670             for (inode = 1; inode <= nbFaceNodes; inode++) {
7671               const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7672
7673               TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7674               if (nnIt != nodeNodeMap.end()) { // curNode sticks
7675                 curNode = (*nnIt).second;
7676               }
7677               poly_nodes.push_back(curNode);
7678             }
7679           }
7680           aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7681         }
7682       }
7683       else // replace non-polyhedron elements
7684       {
7685         const SMDSAbs_ElementType etyp = elem->GetType();
7686         const int elemId               = elem->GetID();
7687         const bool isPoly              = (elem->GetEntityType() == SMDSEntity_Polygon);
7688         uniqueNodes.resize(nbUniqueNodes);
7689
7690         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7691
7692         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7693         SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
7694         if ( sm && newElem )
7695           sm->AddElement( newElem );
7696         if ( elem != newElem )
7697           ReplaceElemInGroups( elem, newElem, aMesh );
7698       }
7699     }
7700     else {
7701       // Remove invalid regular element or invalid polygon
7702       rmElemIds.push_back( elem->GetID() );
7703     }
7704
7705   } // loop on elements
7706
7707   // Remove bad elements, then equal nodes (order important)
7708
7709   Remove( rmElemIds, false );
7710   Remove( rmNodeIds, true );
7711
7712 }
7713
7714
7715 // ========================================================
7716 // class   : SortableElement
7717 // purpose : allow sorting elements basing on their nodes
7718 // ========================================================
7719 class SortableElement : public set <const SMDS_MeshElement*>
7720 {
7721 public:
7722
7723   SortableElement( const SMDS_MeshElement* theElem )
7724   {
7725     myElem = theElem;
7726     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7727     while ( nodeIt->more() )
7728       this->insert( nodeIt->next() );
7729   }
7730
7731   const SMDS_MeshElement* Get() const
7732   { return myElem; }
7733
7734   void Set(const SMDS_MeshElement* e) const
7735   { myElem = e; }
7736
7737
7738 private:
7739   mutable const SMDS_MeshElement* myElem;
7740 };
7741
7742 //=======================================================================
7743 //function : FindEqualElements
7744 //purpose  : Return list of group of elements built on the same nodes.
7745 //           Search among theElements or in the whole mesh if theElements is empty
7746 //=======================================================================
7747 void SMESH_MeshEditor::FindEqualElements(set<const SMDS_MeshElement*> & theElements,
7748                                          TListOfListOfElementsID &      theGroupsOfElementsID)
7749 {
7750   myLastCreatedElems.Clear();
7751   myLastCreatedNodes.Clear();
7752
7753   typedef set<const SMDS_MeshElement*> TElemsSet;
7754   typedef map< SortableElement, int > TMapOfNodeSet;
7755   typedef list<int> TGroupOfElems;
7756
7757   TElemsSet elems;
7758   if ( theElements.empty() )
7759   { // get all elements in the mesh
7760     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7761     while ( eIt->more() )
7762       elems.insert( elems.end(), eIt->next());
7763   }
7764   else
7765     elems = theElements;
7766
7767   vector< TGroupOfElems > arrayOfGroups;
7768   TGroupOfElems groupOfElems;
7769   TMapOfNodeSet mapOfNodeSet;
7770
7771   TElemsSet::iterator elemIt = elems.begin();
7772   for ( int i = 0, j=0; elemIt != elems.end(); ++elemIt, ++j ) {
7773     const SMDS_MeshElement* curElem = *elemIt;
7774     SortableElement SE(curElem);
7775     int ind = -1;
7776     // check uniqueness
7777     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7778     if( !(pp.second) ) {
7779       TMapOfNodeSet::iterator& itSE = pp.first;
7780       ind = (*itSE).second;
7781       arrayOfGroups[ind].push_back(curElem->GetID());
7782     }
7783     else {
7784       groupOfElems.clear();
7785       groupOfElems.push_back(curElem->GetID());
7786       arrayOfGroups.push_back(groupOfElems);
7787       i++;
7788     }
7789   }
7790
7791   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7792   for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7793     groupOfElems = *groupIt;
7794     if ( groupOfElems.size() > 1 ) {
7795       groupOfElems.sort();
7796       theGroupsOfElementsID.push_back(groupOfElems);
7797     }
7798   }
7799 }
7800
7801 //=======================================================================
7802 //function : MergeElements
7803 //purpose  : In each given group, substitute all elements by the first one.
7804 //=======================================================================
7805
7806 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7807 {
7808   myLastCreatedElems.Clear();
7809   myLastCreatedNodes.Clear();
7810
7811   typedef list<int> TListOfIDs;
7812   TListOfIDs rmElemIds; // IDs of elems to remove
7813
7814   SMESHDS_Mesh* aMesh = GetMeshDS();
7815
7816   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7817   while ( groupsIt != theGroupsOfElementsID.end() ) {
7818     TListOfIDs& aGroupOfElemID = *groupsIt;
7819     aGroupOfElemID.sort();
7820     int elemIDToKeep = aGroupOfElemID.front();
7821     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7822     aGroupOfElemID.pop_front();
7823     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7824     while ( idIt != aGroupOfElemID.end() ) {
7825       int elemIDToRemove = *idIt;
7826       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7827       // add the kept element in groups of removed one (PAL15188)
7828       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7829       rmElemIds.push_back( elemIDToRemove );
7830       ++idIt;
7831     }
7832     ++groupsIt;
7833   }
7834
7835   Remove( rmElemIds, false );
7836 }
7837
7838 //=======================================================================
7839 //function : MergeEqualElements
7840 //purpose  : Remove all but one of elements built on the same nodes.
7841 //=======================================================================
7842
7843 void SMESH_MeshEditor::MergeEqualElements()
7844 {
7845   set<const SMDS_MeshElement*> aMeshElements; /* empty input -
7846                                                  to merge equal elements in the whole mesh */
7847   TListOfListOfElementsID aGroupsOfElementsID;
7848   FindEqualElements(aMeshElements, aGroupsOfElementsID);
7849   MergeElements(aGroupsOfElementsID);
7850 }
7851
7852 //=======================================================================
7853 //function : FindFaceInSet
7854 //purpose  : Return a face having linked nodes n1 and n2 and which is
7855 //           - not in avoidSet,
7856 //           - in elemSet provided that !elemSet.empty()
7857 //           i1 and i2 optionally returns indices of n1 and n2
7858 //=======================================================================
7859
7860 const SMDS_MeshElement*
7861 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode*    n1,
7862                                 const SMDS_MeshNode*    n2,
7863                                 const TIDSortedElemSet& elemSet,
7864                                 const TIDSortedElemSet& avoidSet,
7865                                 int*                    n1ind,
7866                                 int*                    n2ind)
7867
7868 {
7869   int i1, i2;
7870   const SMDS_MeshElement* face = 0;
7871
7872   SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
7873   //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
7874   while ( invElemIt->more() && !face ) // loop on inverse faces of n1
7875   {
7876     //MESSAGE("in while ( invElemIt->more() && !face )");
7877     const SMDS_MeshElement* elem = invElemIt->next();
7878     if (avoidSet.count( elem ))
7879       continue;
7880     if ( !elemSet.empty() && !elemSet.count( elem ))
7881       continue;
7882     // index of n1
7883     i1 = elem->GetNodeIndex( n1 );
7884     // find a n2 linked to n1
7885     int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
7886     for ( int di = -1; di < 2 && !face; di += 2 )
7887     {
7888       i2 = (i1+di+nbN) % nbN;
7889       if ( elem->GetNode( i2 ) == n2 )
7890         face = elem;
7891     }
7892     if ( !face && elem->IsQuadratic())
7893     {
7894       // analysis for quadratic elements using all nodes
7895       const SMDS_VtkFace* F =
7896         dynamic_cast<const SMDS_VtkFace*>(elem);
7897       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7898       // use special nodes iterator
7899       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7900       const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
7901       for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
7902       {
7903         const SMDS_MeshNode* n = cast2Node( anIter->next() );
7904         if ( n1 == prevN && n2 == n )
7905         {
7906           face = elem;
7907         }
7908         else if ( n2 == prevN && n1 == n )
7909         {
7910           face = elem; swap( i1, i2 );
7911         }
7912         prevN = n;
7913       }
7914     }
7915   }
7916   if ( n1ind ) *n1ind = i1;
7917   if ( n2ind ) *n2ind = i2;
7918   return face;
7919 }
7920
7921 //=======================================================================
7922 //function : findAdjacentFace
7923 //purpose  :
7924 //=======================================================================
7925
7926 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7927                                                 const SMDS_MeshNode* n2,
7928                                                 const SMDS_MeshElement* elem)
7929 {
7930   TIDSortedElemSet elemSet, avoidSet;
7931   if ( elem )
7932     avoidSet.insert ( elem );
7933   return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
7934 }
7935
7936 //=======================================================================
7937 //function : FindFreeBorder
7938 //purpose  :
7939 //=======================================================================
7940
7941 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7942
7943 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7944                                        const SMDS_MeshNode*             theSecondNode,
7945                                        const SMDS_MeshNode*             theLastNode,
7946                                        list< const SMDS_MeshNode* > &   theNodes,
7947                                        list< const SMDS_MeshElement* >& theFaces)
7948 {
7949   if ( !theFirstNode || !theSecondNode )
7950     return false;
7951   // find border face between theFirstNode and theSecondNode
7952   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7953   if ( !curElem )
7954     return false;
7955
7956   theFaces.push_back( curElem );
7957   theNodes.push_back( theFirstNode );
7958   theNodes.push_back( theSecondNode );
7959
7960   //vector<const SMDS_MeshNode*> nodes;
7961   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7962   TIDSortedElemSet foundElems;
7963   bool needTheLast = ( theLastNode != 0 );
7964
7965   while ( nStart != theLastNode ) {
7966     if ( nStart == theFirstNode )
7967       return !needTheLast;
7968
7969     // find all free border faces sharing form nStart
7970
7971     list< const SMDS_MeshElement* > curElemList;
7972     list< const SMDS_MeshNode* > nStartList;
7973     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7974     while ( invElemIt->more() ) {
7975       const SMDS_MeshElement* e = invElemIt->next();
7976       if ( e == curElem || foundElems.insert( e ).second ) {
7977         // get nodes
7978         int iNode = 0, nbNodes = e->NbNodes();
7979         //const SMDS_MeshNode* nodes[nbNodes+1];
7980         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7981
7982         if(e->IsQuadratic()) {
7983           const SMDS_VtkFace* F =
7984             dynamic_cast<const SMDS_VtkFace*>(e);
7985           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7986           // use special nodes iterator
7987           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7988           while( anIter->more() ) {
7989             nodes[ iNode++ ] = cast2Node(anIter->next());
7990           }
7991         }
7992         else {
7993           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7994           while ( nIt->more() )
7995             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7996         }
7997         nodes[ iNode ] = nodes[ 0 ];
7998         // check 2 links
7999         for ( iNode = 0; iNode < nbNodes; iNode++ )
8000           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8001                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8002               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8003           {
8004             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8005             curElemList.push_back( e );
8006           }
8007       }
8008     }
8009     // analyse the found
8010
8011     int nbNewBorders = curElemList.size();
8012     if ( nbNewBorders == 0 ) {
8013       // no free border furthermore
8014       return !needTheLast;
8015     }
8016     else if ( nbNewBorders == 1 ) {
8017       // one more element found
8018       nIgnore = nStart;
8019       nStart = nStartList.front();
8020       curElem = curElemList.front();
8021       theFaces.push_back( curElem );
8022       theNodes.push_back( nStart );
8023     }
8024     else {
8025       // several continuations found
8026       list< const SMDS_MeshElement* >::iterator curElemIt;
8027       list< const SMDS_MeshNode* >::iterator nStartIt;
8028       // check if one of them reached the last node
8029       if ( needTheLast ) {
8030         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8031              curElemIt!= curElemList.end();
8032              curElemIt++, nStartIt++ )
8033           if ( *nStartIt == theLastNode ) {
8034             theFaces.push_back( *curElemIt );
8035             theNodes.push_back( *nStartIt );
8036             return true;
8037           }
8038       }
8039       // find the best free border by the continuations
8040       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8041       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8042       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8043            curElemIt!= curElemList.end();
8044            curElemIt++, nStartIt++ )
8045       {
8046         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8047         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8048         // find one more free border
8049         if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8050           cNL->clear();
8051           cFL->clear();
8052         }
8053         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8054           // choice: clear a worse one
8055           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8056           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8057           contNodes[ iWorse ].clear();
8058           contFaces[ iWorse ].clear();
8059         }
8060       }
8061       if ( contNodes[0].empty() && contNodes[1].empty() )
8062         return false;
8063
8064       // append the best free border
8065       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8066       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8067       theNodes.pop_back(); // remove nIgnore
8068       theNodes.pop_back(); // remove nStart
8069       theFaces.pop_back(); // remove curElem
8070       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8071       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8072       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8073       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8074       return true;
8075
8076     } // several continuations found
8077   } // while ( nStart != theLastNode )
8078
8079   return true;
8080 }
8081
8082 //=======================================================================
8083 //function : CheckFreeBorderNodes
8084 //purpose  : Return true if the tree nodes are on a free border
8085 //=======================================================================
8086
8087 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8088                                             const SMDS_MeshNode* theNode2,
8089                                             const SMDS_MeshNode* theNode3)
8090 {
8091   list< const SMDS_MeshNode* > nodes;
8092   list< const SMDS_MeshElement* > faces;
8093   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8094 }
8095
8096 //=======================================================================
8097 //function : SewFreeBorder
8098 //purpose  :
8099 //=======================================================================
8100
8101 SMESH_MeshEditor::Sew_Error
8102 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8103                                  const SMDS_MeshNode* theBordSecondNode,
8104                                  const SMDS_MeshNode* theBordLastNode,
8105                                  const SMDS_MeshNode* theSideFirstNode,
8106                                  const SMDS_MeshNode* theSideSecondNode,
8107                                  const SMDS_MeshNode* theSideThirdNode,
8108                                  const bool           theSideIsFreeBorder,
8109                                  const bool           toCreatePolygons,
8110                                  const bool           toCreatePolyedrs)
8111 {
8112   myLastCreatedElems.Clear();
8113   myLastCreatedNodes.Clear();
8114
8115   MESSAGE("::SewFreeBorder()");
8116   Sew_Error aResult = SEW_OK;
8117
8118   // ====================================
8119   //    find side nodes and elements
8120   // ====================================
8121
8122   list< const SMDS_MeshNode* > nSide[ 2 ];
8123   list< const SMDS_MeshElement* > eSide[ 2 ];
8124   list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8125   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8126
8127   // Free border 1
8128   // --------------
8129   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8130                       nSide[0], eSide[0])) {
8131     MESSAGE(" Free Border 1 not found " );
8132     aResult = SEW_BORDER1_NOT_FOUND;
8133   }
8134   if (theSideIsFreeBorder) {
8135     // Free border 2
8136     // --------------
8137     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8138                         nSide[1], eSide[1])) {
8139       MESSAGE(" Free Border 2 not found " );
8140       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8141     }
8142   }
8143   if ( aResult != SEW_OK )
8144     return aResult;
8145
8146   if (!theSideIsFreeBorder) {
8147     // Side 2
8148     // --------------
8149
8150     // -------------------------------------------------------------------------
8151     // Algo:
8152     // 1. If nodes to merge are not coincident, move nodes of the free border
8153     //    from the coord sys defined by the direction from the first to last
8154     //    nodes of the border to the correspondent sys of the side 2
8155     // 2. On the side 2, find the links most co-directed with the correspondent
8156     //    links of the free border
8157     // -------------------------------------------------------------------------
8158
8159     // 1. Since sewing may break if there are volumes to split on the side 2,
8160     //    we wont move nodes but just compute new coordinates for them
8161     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8162     TNodeXYZMap nBordXYZ;
8163     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8164     list< const SMDS_MeshNode* >::iterator nBordIt;
8165
8166     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8167     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8168     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8169     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8170     double tol2 = 1.e-8;
8171     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8172     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8173       // Need node movement.
8174
8175       // find X and Z axes to create trsf
8176       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8177       gp_Vec X = Zs ^ Zb;
8178       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8179         // Zb || Zs
8180         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8181
8182       // coord systems
8183       gp_Ax3 toBordAx( Pb1, Zb, X );
8184       gp_Ax3 fromSideAx( Ps1, Zs, X );
8185       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8186       // set trsf
8187       gp_Trsf toBordSys, fromSide2Sys;
8188       toBordSys.SetTransformation( toBordAx );
8189       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8190       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8191
8192       // move
8193       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8194         const SMDS_MeshNode* n = *nBordIt;
8195         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8196         toBordSys.Transforms( xyz );
8197         fromSide2Sys.Transforms( xyz );
8198         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8199       }
8200     }
8201     else {
8202       // just insert nodes XYZ in the nBordXYZ map
8203       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8204         const SMDS_MeshNode* n = *nBordIt;
8205         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8206       }
8207     }
8208
8209     // 2. On the side 2, find the links most co-directed with the correspondent
8210     //    links of the free border
8211
8212     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8213     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8214     sideNodes.push_back( theSideFirstNode );
8215
8216     bool hasVolumes = false;
8217     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8218     set<long> foundSideLinkIDs, checkedLinkIDs;
8219     SMDS_VolumeTool volume;
8220     //const SMDS_MeshNode* faceNodes[ 4 ];
8221
8222     const SMDS_MeshNode*    sideNode;
8223     const SMDS_MeshElement* sideElem;
8224     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8225     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8226     nBordIt = bordNodes.begin();
8227     nBordIt++;
8228     // border node position and border link direction to compare with
8229     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8230     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8231     // choose next side node by link direction or by closeness to
8232     // the current border node:
8233     bool searchByDir = ( *nBordIt != theBordLastNode );
8234     do {
8235       // find the next node on the Side 2
8236       sideNode = 0;
8237       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8238       long linkID;
8239       checkedLinkIDs.clear();
8240       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8241
8242       // loop on inverse elements of current node (prevSideNode) on the Side 2
8243       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8244       while ( invElemIt->more() )
8245       {
8246         const SMDS_MeshElement* elem = invElemIt->next();
8247         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8248         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8249         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8250         bool isVolume = volume.Set( elem );
8251         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8252         if ( isVolume ) // --volume
8253           hasVolumes = true;
8254         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8255           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8256           if(elem->IsQuadratic()) {
8257             const SMDS_VtkFace* F =
8258               dynamic_cast<const SMDS_VtkFace*>(elem);
8259             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8260             // use special nodes iterator
8261             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8262             while( anIter->more() ) {
8263               nodes[ iNode ] = cast2Node(anIter->next());
8264               if ( nodes[ iNode++ ] == prevSideNode )
8265                 iPrevNode = iNode - 1;
8266             }
8267           }
8268           else {
8269             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8270             while ( nIt->more() ) {
8271               nodes[ iNode ] = cast2Node( nIt->next() );
8272               if ( nodes[ iNode++ ] == prevSideNode )
8273                 iPrevNode = iNode - 1;
8274             }
8275           }
8276           // there are 2 links to check
8277           nbNodes = 2;
8278         }
8279         else // --edge
8280           continue;
8281         // loop on links, to be precise, on the second node of links
8282         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8283           const SMDS_MeshNode* n = nodes[ iNode ];
8284           if ( isVolume ) {
8285             if ( !volume.IsLinked( n, prevSideNode ))
8286               continue;
8287           }
8288           else {
8289             if ( iNode ) // a node before prevSideNode
8290               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8291             else         // a node after prevSideNode
8292               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8293           }
8294           // check if this link was already used
8295           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8296           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8297           if (!isJustChecked &&
8298               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8299           {
8300             // test a link geometrically
8301             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8302             bool linkIsBetter = false;
8303             double dot = 0.0, dist = 0.0;
8304             if ( searchByDir ) { // choose most co-directed link
8305               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8306               linkIsBetter = ( dot > maxDot );
8307             }
8308             else { // choose link with the node closest to bordPos
8309               dist = ( nextXYZ - bordPos ).SquareModulus();
8310               linkIsBetter = ( dist < minDist );
8311             }
8312             if ( linkIsBetter ) {
8313               maxDot = dot;
8314               minDist = dist;
8315               linkID = iLink;
8316               sideNode = n;
8317               sideElem = elem;
8318             }
8319           }
8320         }
8321       } // loop on inverse elements of prevSideNode
8322
8323       if ( !sideNode ) {
8324         MESSAGE(" Cant find path by links of the Side 2 ");
8325         return SEW_BAD_SIDE_NODES;
8326       }
8327       sideNodes.push_back( sideNode );
8328       sideElems.push_back( sideElem );
8329       foundSideLinkIDs.insert ( linkID );
8330       prevSideNode = sideNode;
8331
8332       if ( *nBordIt == theBordLastNode )
8333         searchByDir = false;
8334       else {
8335         // find the next border link to compare with
8336         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8337         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8338         // move to next border node if sideNode is before forward border node (bordPos)
8339         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8340           prevBordNode = *nBordIt;
8341           nBordIt++;
8342           bordPos = nBordXYZ[ *nBordIt ];
8343           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8344           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8345         }
8346       }
8347     }
8348     while ( sideNode != theSideSecondNode );
8349
8350     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8351       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8352       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8353     }
8354   } // end nodes search on the side 2
8355
8356   // ============================
8357   // sew the border to the side 2
8358   // ============================
8359
8360   int nbNodes[]  = { nSide[0].size(), nSide[1].size() };
8361   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8362
8363   TListOfListOfNodes nodeGroupsToMerge;
8364   if ( nbNodes[0] == nbNodes[1] ||
8365        ( theSideIsFreeBorder && !theSideThirdNode)) {
8366
8367     // all nodes are to be merged
8368
8369     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8370          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8371          nIt[0]++, nIt[1]++ )
8372     {
8373       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8374       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8375       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8376     }
8377   }
8378   else {
8379
8380     // insert new nodes into the border and the side to get equal nb of segments
8381
8382     // get normalized parameters of nodes on the borders
8383     //double param[ 2 ][ maxNbNodes ];
8384     double* param[ 2 ];
8385     param[0] = new double [ maxNbNodes ];
8386     param[1] = new double [ maxNbNodes ];
8387     int iNode, iBord;
8388     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8389       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8390       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8391       const SMDS_MeshNode* nPrev = *nIt;
8392       double bordLength = 0;
8393       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8394         const SMDS_MeshNode* nCur = *nIt;
8395         gp_XYZ segment (nCur->X() - nPrev->X(),
8396                         nCur->Y() - nPrev->Y(),
8397                         nCur->Z() - nPrev->Z());
8398         double segmentLen = segment.Modulus();
8399         bordLength += segmentLen;
8400         param[ iBord ][ iNode ] = bordLength;
8401         nPrev = nCur;
8402       }
8403       // normalize within [0,1]
8404       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8405         param[ iBord ][ iNode ] /= bordLength;
8406       }
8407     }
8408
8409     // loop on border segments
8410     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8411     int i[ 2 ] = { 0, 0 };
8412     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8413     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8414
8415     TElemOfNodeListMap insertMap;
8416     TElemOfNodeListMap::iterator insertMapIt;
8417     // insertMap is
8418     // key:   elem to insert nodes into
8419     // value: 2 nodes to insert between + nodes to be inserted
8420     do {
8421       bool next[ 2 ] = { false, false };
8422
8423       // find min adjacent segment length after sewing
8424       double nextParam = 10., prevParam = 0;
8425       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8426         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8427           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8428         if ( i[ iBord ] > 0 )
8429           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8430       }
8431       double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8432       double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8433       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8434
8435       // choose to insert or to merge nodes
8436       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8437       if ( Abs( du ) <= minSegLen * 0.2 ) {
8438         // merge
8439         // ------
8440         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8441         const SMDS_MeshNode* n0 = *nIt[0];
8442         const SMDS_MeshNode* n1 = *nIt[1];
8443         nodeGroupsToMerge.back().push_back( n1 );
8444         nodeGroupsToMerge.back().push_back( n0 );
8445         // position of node of the border changes due to merge
8446         param[ 0 ][ i[0] ] += du;
8447         // move n1 for the sake of elem shape evaluation during insertion.
8448         // n1 will be removed by MergeNodes() anyway
8449         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8450         next[0] = next[1] = true;
8451       }
8452       else {
8453         // insert
8454         // ------
8455         int intoBord = ( du < 0 ) ? 0 : 1;
8456         const SMDS_MeshElement* elem = *eIt[ intoBord ];
8457         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8458         const SMDS_MeshNode*    n2   = *nIt[ intoBord ];
8459         const SMDS_MeshNode*    nIns = *nIt[ 1 - intoBord ];
8460         if ( intoBord == 1 ) {
8461           // move node of the border to be on a link of elem of the side
8462           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8463           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8464           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8465           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8466           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8467         }
8468         insertMapIt = insertMap.find( elem );
8469         bool notFound = ( insertMapIt == insertMap.end() );
8470         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8471         if ( otherLink ) {
8472           // insert into another link of the same element:
8473           // 1. perform insertion into the other link of the elem
8474           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8475           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8476           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8477           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8478           // 2. perform insertion into the link of adjacent faces
8479           while (true) {
8480             const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8481             if ( adjElem )
8482               InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8483             else
8484               break;
8485           }
8486           if (toCreatePolyedrs) {
8487             // perform insertion into the links of adjacent volumes
8488             UpdateVolumes(n12, n22, nodeList);
8489           }
8490           // 3. find an element appeared on n1 and n2 after the insertion
8491           insertMap.erase( elem );
8492           elem = findAdjacentFace( n1, n2, 0 );
8493         }
8494         if ( notFound || otherLink ) {
8495           // add element and nodes of the side into the insertMap
8496           insertMapIt = insertMap.insert
8497             ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8498           (*insertMapIt).second.push_back( n1 );
8499           (*insertMapIt).second.push_back( n2 );
8500         }
8501         // add node to be inserted into elem
8502         (*insertMapIt).second.push_back( nIns );
8503         next[ 1 - intoBord ] = true;
8504       }
8505
8506       // go to the next segment
8507       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8508         if ( next[ iBord ] ) {
8509           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8510             eIt[ iBord ]++;
8511           nPrev[ iBord ] = *nIt[ iBord ];
8512           nIt[ iBord ]++; i[ iBord ]++;
8513         }
8514       }
8515     }
8516     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8517
8518     // perform insertion of nodes into elements
8519
8520     for (insertMapIt = insertMap.begin();
8521          insertMapIt != insertMap.end();
8522          insertMapIt++ )
8523     {
8524       const SMDS_MeshElement* elem = (*insertMapIt).first;
8525       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8526       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8527       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8528
8529       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8530
8531       if ( !theSideIsFreeBorder ) {
8532         // look for and insert nodes into the faces adjacent to elem
8533         while (true) {
8534           const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8535           if ( adjElem )
8536             InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8537           else
8538             break;
8539         }
8540       }
8541       if (toCreatePolyedrs) {
8542         // perform insertion into the links of adjacent volumes
8543         UpdateVolumes(n1, n2, nodeList);
8544       }
8545     }
8546
8547     delete param[0];
8548     delete param[1];
8549   } // end: insert new nodes
8550
8551   MergeNodes ( nodeGroupsToMerge );
8552
8553   return aResult;
8554 }
8555
8556 //=======================================================================
8557 //function : InsertNodesIntoLink
8558 //purpose  : insert theNodesToInsert into theFace between theBetweenNode1
8559 //           and theBetweenNode2 and split theElement
8560 //=======================================================================
8561
8562 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theFace,
8563                                            const SMDS_MeshNode*        theBetweenNode1,
8564                                            const SMDS_MeshNode*        theBetweenNode2,
8565                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8566                                            const bool                  toCreatePoly)
8567 {
8568   if ( theFace->GetType() != SMDSAbs_Face ) return;
8569
8570   // find indices of 2 link nodes and of the rest nodes
8571   int iNode = 0, il1, il2, i3, i4;
8572   il1 = il2 = i3 = i4 = -1;
8573   //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8574   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8575
8576   if(theFace->IsQuadratic()) {
8577     const SMDS_VtkFace* F =
8578       dynamic_cast<const SMDS_VtkFace*>(theFace);
8579     if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8580     // use special nodes iterator
8581     SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8582     while( anIter->more() ) {
8583       const SMDS_MeshNode* n = cast2Node(anIter->next());
8584       if ( n == theBetweenNode1 )
8585         il1 = iNode;
8586       else if ( n == theBetweenNode2 )
8587         il2 = iNode;
8588       else if ( i3 < 0 )
8589         i3 = iNode;
8590       else
8591         i4 = iNode;
8592       nodes[ iNode++ ] = n;
8593     }
8594   }
8595   else {
8596     SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8597     while ( nodeIt->more() ) {
8598       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8599       if ( n == theBetweenNode1 )
8600         il1 = iNode;
8601       else if ( n == theBetweenNode2 )
8602         il2 = iNode;
8603       else if ( i3 < 0 )
8604         i3 = iNode;
8605       else
8606         i4 = iNode;
8607       nodes[ iNode++ ] = n;
8608     }
8609   }
8610   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8611     return ;
8612
8613   // arrange link nodes to go one after another regarding the face orientation
8614   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8615   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8616   if ( reverse ) {
8617     iNode = il1;
8618     il1 = il2;
8619     il2 = iNode;
8620     aNodesToInsert.reverse();
8621   }
8622   // check that not link nodes of a quadrangles are in good order
8623   int nbFaceNodes = theFace->NbNodes();
8624   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8625     iNode = i3;
8626     i3 = i4;
8627     i4 = iNode;
8628   }
8629
8630   if (toCreatePoly || theFace->IsPoly()) {
8631
8632     iNode = 0;
8633     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8634
8635     // add nodes of face up to first node of link
8636     bool isFLN = false;
8637
8638     if(theFace->IsQuadratic()) {
8639       const SMDS_VtkFace* F =
8640         dynamic_cast<const SMDS_VtkFace*>(theFace);
8641       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8642       // use special nodes iterator
8643       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8644       while( anIter->more()  && !isFLN ) {
8645         const SMDS_MeshNode* n = cast2Node(anIter->next());
8646         poly_nodes[iNode++] = n;
8647         if (n == nodes[il1]) {
8648           isFLN = true;
8649         }
8650       }
8651       // add nodes to insert
8652       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8653       for (; nIt != aNodesToInsert.end(); nIt++) {
8654         poly_nodes[iNode++] = *nIt;
8655       }
8656       // add nodes of face starting from last node of link
8657       while ( anIter->more() ) {
8658         poly_nodes[iNode++] = cast2Node(anIter->next());
8659       }
8660     }
8661     else {
8662       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8663       while ( nodeIt->more() && !isFLN ) {
8664         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8665         poly_nodes[iNode++] = n;
8666         if (n == nodes[il1]) {
8667           isFLN = true;
8668         }
8669       }
8670       // add nodes to insert
8671       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8672       for (; nIt != aNodesToInsert.end(); nIt++) {
8673         poly_nodes[iNode++] = *nIt;
8674       }
8675       // add nodes of face starting from last node of link
8676       while ( nodeIt->more() ) {
8677         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8678         poly_nodes[iNode++] = n;
8679       }
8680     }
8681
8682     // edit or replace the face
8683     SMESHDS_Mesh *aMesh = GetMeshDS();
8684
8685     if (theFace->IsPoly()) {
8686       aMesh->ChangePolygonNodes(theFace, poly_nodes);
8687     }
8688     else {
8689       int aShapeId = FindShape( theFace );
8690
8691       SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8692       myLastCreatedElems.Append(newElem);
8693       if ( aShapeId && newElem )
8694         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8695
8696       aMesh->RemoveElement(theFace);
8697     }
8698     return;
8699   }
8700
8701   SMESHDS_Mesh *aMesh = GetMeshDS();
8702   if( !theFace->IsQuadratic() ) {
8703
8704     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8705     int nbLinkNodes = 2 + aNodesToInsert.size();
8706     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8707     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8708     linkNodes[ 0 ] = nodes[ il1 ];
8709     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8710     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8711     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8712       linkNodes[ iNode++ ] = *nIt;
8713     }
8714     // decide how to split a quadrangle: compare possible variants
8715     // and choose which of splits to be a quadrangle
8716     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8717     if ( nbFaceNodes == 3 ) {
8718       iBestQuad = nbSplits;
8719       i4 = i3;
8720     }
8721     else if ( nbFaceNodes == 4 ) {
8722       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8723       double aBestRate = DBL_MAX;
8724       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8725         i1 = 0; i2 = 1;
8726         double aBadRate = 0;
8727         // evaluate elements quality
8728         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8729           if ( iSplit == iQuad ) {
8730             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8731                                    linkNodes[ i2++ ],
8732                                    nodes[ i3 ],
8733                                    nodes[ i4 ]);
8734             aBadRate += getBadRate( &quad, aCrit );
8735           }
8736           else {
8737             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8738                                    linkNodes[ i2++ ],
8739                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8740             aBadRate += getBadRate( &tria, aCrit );
8741           }
8742         }
8743         // choice
8744         if ( aBadRate < aBestRate ) {
8745           iBestQuad = iQuad;
8746           aBestRate = aBadRate;
8747         }
8748       }
8749     }
8750
8751     // create new elements
8752     int aShapeId = FindShape( theFace );
8753
8754     i1 = 0; i2 = 1;
8755     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8756       SMDS_MeshElement* newElem = 0;
8757       if ( iSplit == iBestQuad )
8758         newElem = aMesh->AddFace (linkNodes[ i1++ ],
8759                                   linkNodes[ i2++ ],
8760                                   nodes[ i3 ],
8761                                   nodes[ i4 ]);
8762       else
8763         newElem = aMesh->AddFace (linkNodes[ i1++ ],
8764                                   linkNodes[ i2++ ],
8765                                   nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8766       myLastCreatedElems.Append(newElem);
8767       if ( aShapeId && newElem )
8768         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8769     }
8770
8771     // change nodes of theFace
8772     const SMDS_MeshNode* newNodes[ 4 ];
8773     newNodes[ 0 ] = linkNodes[ i1 ];
8774     newNodes[ 1 ] = linkNodes[ i2 ];
8775     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8776     newNodes[ 3 ] = nodes[ i4 ];
8777     //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8778     const SMDS_MeshElement* newElem = 0;
8779     if (iSplit == iBestQuad)
8780       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8781     else
8782       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8783     myLastCreatedElems.Append(newElem);
8784     if ( aShapeId && newElem )
8785       aMesh->SetMeshElementOnShape( newElem, aShapeId );
8786 } // end if(!theFace->IsQuadratic())
8787   else { // theFace is quadratic
8788     // we have to split theFace on simple triangles and one simple quadrangle
8789     int tmp = il1/2;
8790     int nbshift = tmp*2;
8791     // shift nodes in nodes[] by nbshift
8792     int i,j;
8793     for(i=0; i<nbshift; i++) {
8794       const SMDS_MeshNode* n = nodes[0];
8795       for(j=0; j<nbFaceNodes-1; j++) {
8796         nodes[j] = nodes[j+1];
8797       }
8798       nodes[nbFaceNodes-1] = n;
8799     }
8800     il1 = il1 - nbshift;
8801     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8802     //   n0      n1     n2    n0      n1     n2
8803     //     +-----+-----+        +-----+-----+
8804     //      \         /         |           |
8805     //       \       /          |           |
8806     //      n5+     +n3       n7+           +n3
8807     //         \   /            |           |
8808     //          \ /             |           |
8809     //           +              +-----+-----+
8810     //           n4           n6      n5     n4
8811
8812     // create new elements
8813     int aShapeId = FindShape( theFace );
8814
8815     int n1,n2,n3;
8816     if(nbFaceNodes==6) { // quadratic triangle
8817       SMDS_MeshElement* newElem =
8818         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8819       myLastCreatedElems.Append(newElem);
8820       if ( aShapeId && newElem )
8821         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8822       if(theFace->IsMediumNode(nodes[il1])) {
8823         // create quadrangle
8824         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8825         myLastCreatedElems.Append(newElem);
8826         if ( aShapeId && newElem )
8827           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8828         n1 = 1;
8829         n2 = 2;
8830         n3 = 3;
8831       }
8832       else {
8833         // create quadrangle
8834         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8835         myLastCreatedElems.Append(newElem);
8836         if ( aShapeId && newElem )
8837           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8838         n1 = 0;
8839         n2 = 1;
8840         n3 = 5;
8841       }
8842     }
8843     else { // nbFaceNodes==8 - quadratic quadrangle
8844       SMDS_MeshElement* newElem =
8845         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8846       myLastCreatedElems.Append(newElem);
8847       if ( aShapeId && newElem )
8848         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8849       newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8850       myLastCreatedElems.Append(newElem);
8851       if ( aShapeId && newElem )
8852         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8853       newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8854       myLastCreatedElems.Append(newElem);
8855       if ( aShapeId && newElem )
8856         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8857       if(theFace->IsMediumNode(nodes[il1])) {
8858         // create quadrangle
8859         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8860         myLastCreatedElems.Append(newElem);
8861         if ( aShapeId && newElem )
8862           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8863         n1 = 1;
8864         n2 = 2;
8865         n3 = 3;
8866       }
8867       else {
8868         // create quadrangle
8869         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8870         myLastCreatedElems.Append(newElem);
8871         if ( aShapeId && newElem )
8872           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8873         n1 = 0;
8874         n2 = 1;
8875         n3 = 7;
8876       }
8877     }
8878     // create needed triangles using n1,n2,n3 and inserted nodes
8879     int nbn = 2 + aNodesToInsert.size();
8880     //const SMDS_MeshNode* aNodes[nbn];
8881     vector<const SMDS_MeshNode*> aNodes(nbn);
8882     aNodes[0] = nodes[n1];
8883     aNodes[nbn-1] = nodes[n2];
8884     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8885     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8886       aNodes[iNode++] = *nIt;
8887     }
8888     for(i=1; i<nbn; i++) {
8889       SMDS_MeshElement* newElem =
8890         aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8891       myLastCreatedElems.Append(newElem);
8892       if ( aShapeId && newElem )
8893         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8894     }
8895   }
8896   // remove old face
8897   aMesh->RemoveElement(theFace);
8898 }
8899
8900 //=======================================================================
8901 //function : UpdateVolumes
8902 //purpose  :
8903 //=======================================================================
8904 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8905                                       const SMDS_MeshNode*        theBetweenNode2,
8906                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8907 {
8908   myLastCreatedElems.Clear();
8909   myLastCreatedNodes.Clear();
8910
8911   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8912   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8913     const SMDS_MeshElement* elem = invElemIt->next();
8914
8915     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8916     SMDS_VolumeTool aVolume (elem);
8917     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8918       continue;
8919
8920     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8921     int iface, nbFaces = aVolume.NbFaces();
8922     vector<const SMDS_MeshNode *> poly_nodes;
8923     vector<int> quantities (nbFaces);
8924
8925     for (iface = 0; iface < nbFaces; iface++) {
8926       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8927       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8928       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8929
8930       for (int inode = 0; inode < nbFaceNodes; inode++) {
8931         poly_nodes.push_back(faceNodes[inode]);
8932
8933         if (nbInserted == 0) {
8934           if (faceNodes[inode] == theBetweenNode1) {
8935             if (faceNodes[inode + 1] == theBetweenNode2) {
8936               nbInserted = theNodesToInsert.size();
8937
8938               // add nodes to insert
8939               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8940               for (; nIt != theNodesToInsert.end(); nIt++) {
8941                 poly_nodes.push_back(*nIt);
8942               }
8943             }
8944           }
8945           else if (faceNodes[inode] == theBetweenNode2) {
8946             if (faceNodes[inode + 1] == theBetweenNode1) {
8947               nbInserted = theNodesToInsert.size();
8948
8949               // add nodes to insert in reversed order
8950               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8951               nIt--;
8952               for (; nIt != theNodesToInsert.begin(); nIt--) {
8953                 poly_nodes.push_back(*nIt);
8954               }
8955               poly_nodes.push_back(*nIt);
8956             }
8957           }
8958           else {
8959           }
8960         }
8961       }
8962       quantities[iface] = nbFaceNodes + nbInserted;
8963     }
8964
8965     // Replace or update the volume
8966     SMESHDS_Mesh *aMesh = GetMeshDS();
8967
8968     if (elem->IsPoly()) {
8969       aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8970
8971     }
8972     else {
8973       int aShapeId = FindShape( elem );
8974
8975       SMDS_MeshElement* newElem =
8976         aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8977       myLastCreatedElems.Append(newElem);
8978       if (aShapeId && newElem)
8979         aMesh->SetMeshElementOnShape(newElem, aShapeId);
8980
8981       aMesh->RemoveElement(elem);
8982     }
8983   }
8984 }
8985
8986 namespace
8987 {
8988   //================================================================================
8989   /*!
8990    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8991    */
8992   //================================================================================
8993
8994   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8995                            vector<const SMDS_MeshNode *> & nodes,
8996                            vector<int> &                   nbNodeInFaces )
8997   {
8998     nodes.clear();
8999     nbNodeInFaces.clear();
9000     SMDS_VolumeTool vTool ( elem );
9001     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9002     {
9003       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9004       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9005       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9006     }
9007   }
9008 }
9009
9010 //=======================================================================
9011 /*!
9012  * \brief Convert elements contained in a submesh to quadratic
9013  * \return int - nb of checked elements
9014  */
9015 //=======================================================================
9016
9017 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9018                                              SMESH_MesherHelper& theHelper,
9019                                              const bool          theForce3d)
9020 {
9021   int nbElem = 0;
9022   if( !theSm ) return nbElem;
9023
9024   vector<int> nbNodeInFaces;
9025   vector<const SMDS_MeshNode *> nodes;
9026   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9027   while(ElemItr->more())
9028   {
9029     nbElem++;
9030     const SMDS_MeshElement* elem = ElemItr->next();
9031     if( !elem || elem->IsQuadratic() ) continue;
9032
9033     // get elem data needed to re-create it
9034     //
9035     const int id                        = elem->GetID();
9036     const int nbNodes                   = elem->NbNodes();
9037     const SMDSAbs_ElementType aType     = elem->GetType();
9038     const SMDSAbs_EntityType  aGeomType = elem->GetEntityType();
9039     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9040     if ( aGeomType == SMDSEntity_Polyhedra )
9041       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9042     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9043       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9044
9045     // remove a linear element
9046     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9047
9048     const SMDS_MeshElement* NewElem = 0;
9049
9050     switch( aType )
9051     {
9052     case SMDSAbs_Edge :
9053       {
9054         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9055         break;
9056       }
9057     case SMDSAbs_Face :
9058       {
9059         switch(nbNodes)
9060         {
9061         case 3:
9062           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9063           break;
9064         case 4:
9065           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9066           break;
9067         default:
9068           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9069           continue;
9070         }
9071         break;
9072       }
9073     case SMDSAbs_Volume :
9074       {
9075         switch( aGeomType )
9076         {
9077         case SMDSEntity_Tetra:
9078           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9079           break;
9080         case SMDSEntity_Pyramid:
9081           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9082           break;
9083         case SMDSEntity_Penta:
9084           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9085           break;
9086         case SMDSEntity_Hexa:
9087           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9088                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9089           break;
9090         case SMDSEntity_Hexagonal_Prism:
9091         default:
9092           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9093         }
9094         break;
9095       }
9096     default :
9097       continue;
9098     }
9099     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9100     if( NewElem )
9101       theSm->AddElement( NewElem );
9102   }
9103   return nbElem;
9104 }
9105
9106 //=======================================================================
9107 //function : ConvertToQuadratic
9108 //purpose  :
9109 //=======================================================================
9110
9111 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d)
9112 {
9113   SMESHDS_Mesh* meshDS = GetMeshDS();
9114
9115   SMESH_MesherHelper aHelper(*myMesh);
9116   aHelper.SetIsQuadratic( true );
9117
9118   int nbCheckedElems = 0;
9119   if ( myMesh->HasShapeToMesh() )
9120   {
9121     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9122     {
9123       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9124       while ( smIt->more() ) {
9125         SMESH_subMesh* sm = smIt->next();
9126         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9127           aHelper.SetSubShape( sm->GetSubShape() );
9128           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9129         }
9130       }
9131     }
9132   }
9133   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9134   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9135   {
9136     SMESHDS_SubMesh *smDS = 0;
9137     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9138     while(aEdgeItr->more())
9139     {
9140       const SMDS_MeshEdge* edge = aEdgeItr->next();
9141       if(edge && !edge->IsQuadratic())
9142       {
9143         int id = edge->GetID();
9144         //MESSAGE("edge->GetID() " << id);
9145         const SMDS_MeshNode* n1 = edge->GetNode(0);
9146         const SMDS_MeshNode* n2 = edge->GetNode(1);
9147
9148         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9149
9150         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9151         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9152       }
9153     }
9154     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9155     while(aFaceItr->more())
9156     {
9157       const SMDS_MeshFace* face = aFaceItr->next();
9158       if(!face || face->IsQuadratic() ) continue;
9159
9160       const int id = face->GetID();
9161       const SMDSAbs_EntityType type = face->GetEntityType();
9162       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9163
9164       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9165
9166       SMDS_MeshFace * NewFace = 0;
9167       switch( type )
9168       {
9169       case SMDSEntity_Triangle:
9170         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9171         break;
9172       case SMDSEntity_Quadrangle:
9173         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9174         break;
9175       default:
9176         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9177       }
9178       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9179     }
9180     vector<int> nbNodeInFaces;
9181     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9182     while(aVolumeItr->more())
9183     {
9184       const SMDS_MeshVolume* volume = aVolumeItr->next();
9185       if(!volume || volume->IsQuadratic() ) continue;
9186
9187       const int id = volume->GetID();
9188       const SMDSAbs_EntityType type = volume->GetEntityType();
9189       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9190       if ( type == SMDSEntity_Polyhedra )
9191         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9192       else if ( type == SMDSEntity_Hexagonal_Prism )
9193         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9194
9195       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9196
9197       SMDS_MeshVolume * NewVolume = 0;
9198       switch ( type )
9199       {
9200       case SMDSEntity_Tetra:
9201         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9202         break;
9203       case SMDSEntity_Hexa:
9204         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9205                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9206         break;
9207       case SMDSEntity_Pyramid:
9208         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9209                                       nodes[3], nodes[4], id, theForce3d);
9210         break;
9211       case SMDSEntity_Penta:
9212         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9213                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9214         break;
9215       case SMDSEntity_Hexagonal_Prism:
9216       default:
9217         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9218       }
9219       ReplaceElemInGroups(volume, NewVolume, meshDS);
9220     }
9221   }
9222
9223   if ( !theForce3d )
9224   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9225     aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9226     aHelper.FixQuadraticElements();
9227   }
9228 }
9229
9230 //================================================================================
9231 /*!
9232  * \brief Makes given elements quadratic
9233  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9234  *  \param theElements - elements to make quadratic 
9235  */
9236 //================================================================================
9237
9238 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9239                                           TIDSortedElemSet& theElements)
9240 {
9241   if ( theElements.empty() ) return;
9242
9243   // we believe that all theElements are of the same type
9244   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9245   
9246   // get all nodes shared by theElements
9247   TIDSortedNodeSet allNodes;
9248   TIDSortedElemSet::iterator eIt = theElements.begin();
9249   for ( ; eIt != theElements.end(); ++eIt )
9250     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9251
9252   // complete theElements with elements of lower dim whose all nodes are in allNodes
9253
9254   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9255   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9256   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9257   for ( ; nIt != allNodes.end(); ++nIt )
9258   {
9259     const SMDS_MeshNode* n = *nIt;
9260     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9261     while ( invIt->more() )
9262     {
9263       const SMDS_MeshElement* e = invIt->next();
9264       if ( e->IsQuadratic() )
9265       {
9266         quadAdjacentElems[ e->GetType() ].insert( e );
9267         continue;
9268       }
9269       if ( e->GetType() >= elemType )
9270       {
9271         continue; // same type of more complex linear element
9272       }
9273
9274       if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9275         continue; // e is already checked
9276
9277       // check nodes
9278       bool allIn = true;
9279       SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9280       while ( nodeIt->more() && allIn )
9281         allIn = allNodes.count( cast2Node( nodeIt->next() ));
9282       if ( allIn )
9283         theElements.insert(e );
9284     }
9285   }
9286
9287   SMESH_MesherHelper helper(*myMesh);
9288   helper.SetIsQuadratic( true );
9289
9290   // add links of quadratic adjacent elements to the helper
9291
9292   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9293     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9294           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9295     {
9296       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9297     }
9298   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9299     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9300           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9301     {
9302       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9303     }
9304   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9305     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9306           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9307     {
9308       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9309     }
9310
9311   // make quadratic elements instead of linear ones
9312
9313   SMESHDS_Mesh* meshDS = GetMeshDS();
9314   SMESHDS_SubMesh* smDS = 0;
9315   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9316   {
9317     const SMDS_MeshElement* elem = *eIt;
9318     if( elem->IsQuadratic() || elem->NbNodes() < 2 || elem->IsPoly() )
9319       continue;
9320
9321     const int id                   = elem->GetID();
9322     const SMDSAbs_ElementType type = elem->GetType();
9323     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9324
9325     if ( !smDS || !smDS->Contains( elem ))
9326       smDS = meshDS->MeshElements( elem->getshapeId() );
9327     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9328
9329     SMDS_MeshElement * newElem = 0;
9330     switch( nodes.size() )
9331     {
9332     case 4: // cases for most frequently used element types go first (for optimization)
9333       if ( type == SMDSAbs_Volume )
9334         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9335       else
9336         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9337       break;
9338     case 8:
9339       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9340                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9341       break;
9342     case 3:
9343       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9344       break;
9345     case 2:
9346       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9347       break;
9348     case 5:
9349       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9350                                  nodes[4], id, theForce3d);
9351       break;
9352     case 6:
9353       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9354                                  nodes[4], nodes[5], id, theForce3d);
9355       break;
9356     default:;
9357     }
9358     ReplaceElemInGroups( elem, newElem, meshDS);
9359     if( newElem && smDS )
9360       smDS->AddElement( newElem );
9361   }
9362
9363   if ( !theForce3d  && !getenv("NO_FixQuadraticElements"))
9364   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9365     helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9366     helper.FixQuadraticElements();
9367   }
9368 }
9369
9370 //=======================================================================
9371 /*!
9372  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9373  * \return int - nb of checked elements
9374  */
9375 //=======================================================================
9376
9377 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9378                                      SMDS_ElemIteratorPtr theItr,
9379                                      const int            theShapeID)
9380 {
9381   int nbElem = 0;
9382   SMESHDS_Mesh* meshDS = GetMeshDS();
9383
9384   while( theItr->more() )
9385   {
9386     const SMDS_MeshElement* elem = theItr->next();
9387     nbElem++;
9388     if( elem && elem->IsQuadratic())
9389     {
9390       int id                    = elem->GetID();
9391       int nbCornerNodes         = elem->NbCornerNodes();
9392       SMDSAbs_ElementType aType = elem->GetType();
9393
9394       vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9395
9396       //remove a quadratic element
9397       if ( !theSm || !theSm->Contains( elem ))
9398         theSm = meshDS->MeshElements( elem->getshapeId() );
9399       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9400
9401       // remove medium nodes
9402       for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9403         if ( nodes[i]->NbInverseElements() == 0 )
9404           meshDS->RemoveFreeNode( nodes[i], theSm );
9405
9406       // add a linear element
9407       nodes.resize( nbCornerNodes );
9408       SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9409       ReplaceElemInGroups(elem, newElem, meshDS);
9410       if( theSm && newElem )
9411         theSm->AddElement( newElem );
9412     }
9413   }
9414   return nbElem;
9415 }
9416
9417 //=======================================================================
9418 //function : ConvertFromQuadratic
9419 //purpose  :
9420 //=======================================================================
9421
9422 bool SMESH_MeshEditor::ConvertFromQuadratic()
9423 {
9424   int nbCheckedElems = 0;
9425   if ( myMesh->HasShapeToMesh() )
9426   {
9427     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9428     {
9429       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9430       while ( smIt->more() ) {
9431         SMESH_subMesh* sm = smIt->next();
9432         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9433           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9434       }
9435     }
9436   }
9437
9438   int totalNbElems =
9439     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9440   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9441   {
9442     SMESHDS_SubMesh *aSM = 0;
9443     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9444   }
9445
9446   return true;
9447 }
9448
9449 namespace
9450 {
9451   //================================================================================
9452   /*!
9453    * \brief Return true if all medium nodes of the element are in the node set
9454    */
9455   //================================================================================
9456
9457   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9458   {
9459     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9460       if ( !nodeSet.count( elem->GetNode(i) ))
9461         return false;
9462     return true;
9463   }
9464 }
9465
9466 //================================================================================
9467 /*!
9468  * \brief Makes given elements linear
9469  */
9470 //================================================================================
9471
9472 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9473 {
9474   if ( theElements.empty() ) return;
9475
9476   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9477   set<int> mediumNodeIDs;
9478   TIDSortedElemSet::iterator eIt = theElements.begin();
9479   for ( ; eIt != theElements.end(); ++eIt )
9480   {
9481     const SMDS_MeshElement* e = *eIt;
9482     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9483       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9484   }
9485
9486   // replace given elements by linear ones
9487   typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
9488   SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
9489   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9490
9491   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9492   // except those elements sharing medium nodes of quadratic element whose medium nodes
9493   // are not all in mediumNodeIDs
9494
9495   // get remaining medium nodes
9496   TIDSortedNodeSet mediumNodes;
9497   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9498   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9499     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9500       mediumNodes.insert( mediumNodes.end(), n );
9501
9502   // find more quadratic elements to convert
9503   TIDSortedElemSet moreElemsToConvert;
9504   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9505   for ( ; nIt != mediumNodes.end(); ++nIt )
9506   {
9507     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9508     while ( invIt->more() )
9509     {
9510       const SMDS_MeshElement* e = invIt->next();
9511       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9512       {
9513         // find a more complex element including e and
9514         // whose medium nodes are not in mediumNodes
9515         bool complexFound = false;
9516         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9517         {
9518           SMDS_ElemIteratorPtr invIt2 =
9519             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9520           while ( invIt2->more() )
9521           {
9522             const SMDS_MeshElement* eComplex = invIt2->next();
9523             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9524             {
9525               int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
9526               if ( nbCommonNodes == e->NbNodes())
9527               {
9528                 complexFound = true;
9529                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9530                 break;
9531               }
9532             }
9533           }
9534         }
9535         if ( !complexFound )
9536           moreElemsToConvert.insert( e );
9537       }
9538     }
9539   }
9540   elemIt = SMDS_ElemIteratorPtr
9541     (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
9542   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9543 }
9544
9545 //=======================================================================
9546 //function : SewSideElements
9547 //purpose  :
9548 //=======================================================================
9549
9550 SMESH_MeshEditor::Sew_Error
9551 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9552                                    TIDSortedElemSet&    theSide2,
9553                                    const SMDS_MeshNode* theFirstNode1,
9554                                    const SMDS_MeshNode* theFirstNode2,
9555                                    const SMDS_MeshNode* theSecondNode1,
9556                                    const SMDS_MeshNode* theSecondNode2)
9557 {
9558   myLastCreatedElems.Clear();
9559   myLastCreatedNodes.Clear();
9560
9561   MESSAGE ("::::SewSideElements()");
9562   if ( theSide1.size() != theSide2.size() )
9563     return SEW_DIFF_NB_OF_ELEMENTS;
9564
9565   Sew_Error aResult = SEW_OK;
9566   // Algo:
9567   // 1. Build set of faces representing each side
9568   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9569   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9570
9571   // =======================================================================
9572   // 1. Build set of faces representing each side:
9573   // =======================================================================
9574   // a. build set of nodes belonging to faces
9575   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9576   // c. create temporary faces representing side of volumes if correspondent
9577   //    face does not exist
9578
9579   SMESHDS_Mesh* aMesh = GetMeshDS();
9580   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9581   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9582   set<const SMDS_MeshElement*> faceSet1, faceSet2;
9583   set<const SMDS_MeshElement*> volSet1,  volSet2;
9584   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9585   set<const SMDS_MeshElement*> * faceSetPtr[] = { &faceSet1, &faceSet2 };
9586   set<const SMDS_MeshElement*>  * volSetPtr[] = { &volSet1,  &volSet2  };
9587   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9588   TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9589   int iSide, iFace, iNode;
9590
9591   list<const SMDS_MeshElement* > tempFaceList;
9592   for ( iSide = 0; iSide < 2; iSide++ ) {
9593     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9594     TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9595     set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9596     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9597     set<const SMDS_MeshElement*>::iterator vIt;
9598     TIDSortedElemSet::iterator eIt;
9599     set<const SMDS_MeshNode*>::iterator    nIt;
9600
9601     // check that given nodes belong to given elements
9602     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9603     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9604     int firstIndex = -1, secondIndex = -1;
9605     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9606       const SMDS_MeshElement* elem = *eIt;
9607       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9608       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9609       if ( firstIndex > -1 && secondIndex > -1 ) break;
9610     }
9611     if ( firstIndex < 0 || secondIndex < 0 ) {
9612       // we can simply return until temporary faces created
9613       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9614     }
9615
9616     // -----------------------------------------------------------
9617     // 1a. Collect nodes of existing faces
9618     //     and build set of face nodes in order to detect missing
9619     //     faces corresponding to sides of volumes
9620     // -----------------------------------------------------------
9621
9622     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9623
9624     // loop on the given element of a side
9625     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9626       //const SMDS_MeshElement* elem = *eIt;
9627       const SMDS_MeshElement* elem = *eIt;
9628       if ( elem->GetType() == SMDSAbs_Face ) {
9629         faceSet->insert( elem );
9630         set <const SMDS_MeshNode*> faceNodeSet;
9631         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9632         while ( nodeIt->more() ) {
9633           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9634           nodeSet->insert( n );
9635           faceNodeSet.insert( n );
9636         }
9637         setOfFaceNodeSet.insert( faceNodeSet );
9638       }
9639       else if ( elem->GetType() == SMDSAbs_Volume )
9640         volSet->insert( elem );
9641     }
9642     // ------------------------------------------------------------------------------
9643     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9644     // ------------------------------------------------------------------------------
9645
9646     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9647       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9648       while ( fIt->more() ) { // loop on faces sharing a node
9649         const SMDS_MeshElement* f = fIt->next();
9650         if ( faceSet->find( f ) == faceSet->end() ) {
9651           // check if all nodes are in nodeSet and
9652           // complete setOfFaceNodeSet if they are
9653           set <const SMDS_MeshNode*> faceNodeSet;
9654           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9655           bool allInSet = true;
9656           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9657             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9658             if ( nodeSet->find( n ) == nodeSet->end() )
9659               allInSet = false;
9660             else
9661               faceNodeSet.insert( n );
9662           }
9663           if ( allInSet ) {
9664             faceSet->insert( f );
9665             setOfFaceNodeSet.insert( faceNodeSet );
9666           }
9667         }
9668       }
9669     }
9670
9671     // -------------------------------------------------------------------------
9672     // 1c. Create temporary faces representing sides of volumes if correspondent
9673     //     face does not exist
9674     // -------------------------------------------------------------------------
9675
9676     if ( !volSet->empty() ) {
9677       //int nodeSetSize = nodeSet->size();
9678
9679       // loop on given volumes
9680       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9681         SMDS_VolumeTool vol (*vIt);
9682         // loop on volume faces: find free faces
9683         // --------------------------------------
9684         list<const SMDS_MeshElement* > freeFaceList;
9685         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9686           if ( !vol.IsFreeFace( iFace ))
9687             continue;
9688           // check if there is already a face with same nodes in a face set
9689           const SMDS_MeshElement* aFreeFace = 0;
9690           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9691           int nbNodes = vol.NbFaceNodes( iFace );
9692           set <const SMDS_MeshNode*> faceNodeSet;
9693           vol.GetFaceNodes( iFace, faceNodeSet );
9694           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9695           if ( isNewFace ) {
9696             // no such a face is given but it still can exist, check it
9697             if ( nbNodes == 3 ) {
9698               aFreeFace = aMesh->FindFace( fNodes[0],fNodes[1],fNodes[2] );
9699             }
9700             else if ( nbNodes == 4 ) {
9701               aFreeFace = aMesh->FindFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9702             }
9703             else {
9704               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9705               aFreeFace = aMesh->FindFace(poly_nodes);
9706             }
9707           }
9708           if ( !aFreeFace ) {
9709             // create a temporary face
9710             if ( nbNodes == 3 ) {
9711               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9712               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9713             }
9714             else if ( nbNodes == 4 ) {
9715               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9716               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9717             }
9718             else {
9719               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9720               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9721               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9722             }
9723           }
9724           if ( aFreeFace ) {
9725             freeFaceList.push_back( aFreeFace );
9726             tempFaceList.push_back( aFreeFace );
9727           }
9728
9729         } // loop on faces of a volume
9730
9731         // choose one of several free faces
9732         // --------------------------------------
9733         if ( freeFaceList.size() > 1 ) {
9734           // choose a face having max nb of nodes shared by other elems of a side
9735           int maxNbNodes = -1/*, nbExcludedFaces = 0*/;
9736           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9737           while ( fIt != freeFaceList.end() ) { // loop on free faces
9738             int nbSharedNodes = 0;
9739             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9740             while ( nodeIt->more() ) { // loop on free face nodes
9741               const SMDS_MeshNode* n =
9742                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9743               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9744               while ( invElemIt->more() ) {
9745                 const SMDS_MeshElement* e = invElemIt->next();
9746                 if ( faceSet->find( e ) != faceSet->end() )
9747                   nbSharedNodes++;
9748                 if ( elemSet->find( e ) != elemSet->end() )
9749                   nbSharedNodes++;
9750               }
9751             }
9752             if ( nbSharedNodes >= maxNbNodes ) {
9753               maxNbNodes = nbSharedNodes;
9754               fIt++;
9755             }
9756             else
9757               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9758           }
9759           if ( freeFaceList.size() > 1 )
9760           {
9761             // could not choose one face, use another way
9762             // choose a face most close to the bary center of the opposite side
9763             gp_XYZ aBC( 0., 0., 0. );
9764             set <const SMDS_MeshNode*> addedNodes;
9765             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9766             eIt = elemSet2->begin();
9767             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9768               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9769               while ( nodeIt->more() ) { // loop on free face nodes
9770                 const SMDS_MeshNode* n =
9771                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9772                 if ( addedNodes.insert( n ).second )
9773                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9774               }
9775             }
9776             aBC /= addedNodes.size();
9777             double minDist = DBL_MAX;
9778             fIt = freeFaceList.begin();
9779             while ( fIt != freeFaceList.end() ) { // loop on free faces
9780               double dist = 0;
9781               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9782               while ( nodeIt->more() ) { // loop on free face nodes
9783                 const SMDS_MeshNode* n =
9784                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9785                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9786                 dist += ( aBC - p ).SquareModulus();
9787               }
9788               if ( dist < minDist ) {
9789                 minDist = dist;
9790                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9791               }
9792               else
9793                 fIt = freeFaceList.erase( fIt++ );
9794             }
9795           }
9796         } // choose one of several free faces of a volume
9797
9798         if ( freeFaceList.size() == 1 ) {
9799           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9800           faceSet->insert( aFreeFace );
9801           // complete a node set with nodes of a found free face
9802           //           for ( iNode = 0; iNode < ; iNode++ )
9803           //             nodeSet->insert( fNodes[ iNode ] );
9804         }
9805
9806       } // loop on volumes of a side
9807
9808       //       // complete a set of faces if new nodes in a nodeSet appeared
9809       //       // ----------------------------------------------------------
9810       //       if ( nodeSetSize != nodeSet->size() ) {
9811       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9812       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9813       //           while ( fIt->more() ) { // loop on faces sharing a node
9814       //             const SMDS_MeshElement* f = fIt->next();
9815       //             if ( faceSet->find( f ) == faceSet->end() ) {
9816       //               // check if all nodes are in nodeSet and
9817       //               // complete setOfFaceNodeSet if they are
9818       //               set <const SMDS_MeshNode*> faceNodeSet;
9819       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9820       //               bool allInSet = true;
9821       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9822       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9823       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9824       //                   allInSet = false;
9825       //                 else
9826       //                   faceNodeSet.insert( n );
9827       //               }
9828       //               if ( allInSet ) {
9829       //                 faceSet->insert( f );
9830       //                 setOfFaceNodeSet.insert( faceNodeSet );
9831       //               }
9832       //             }
9833       //           }
9834       //         }
9835       //       }
9836     } // Create temporary faces, if there are volumes given
9837   } // loop on sides
9838
9839   if ( faceSet1.size() != faceSet2.size() ) {
9840     // delete temporary faces: they are in reverseElements of actual nodes
9841 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9842 //    while ( tmpFaceIt->more() )
9843 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9844 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9845 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9846 //      aMesh->RemoveElement(*tmpFaceIt);
9847     MESSAGE("Diff nb of faces");
9848     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9849   }
9850
9851   // ============================================================
9852   // 2. Find nodes to merge:
9853   //              bind a node to remove to a node to put instead
9854   // ============================================================
9855
9856   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9857   if ( theFirstNode1 != theFirstNode2 )
9858     nReplaceMap.insert( TNodeNodeMap::value_type( theFirstNode1, theFirstNode2 ));
9859   if ( theSecondNode1 != theSecondNode2 )
9860     nReplaceMap.insert( TNodeNodeMap::value_type( theSecondNode1, theSecondNode2 ));
9861
9862   LinkID_Gen aLinkID_Gen( GetMeshDS() );
9863   set< long > linkIdSet; // links to process
9864   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9865
9866   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9867   list< NLink > linkList[2];
9868   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9869   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9870   // loop on links in linkList; find faces by links and append links
9871   // of the found faces to linkList
9872   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9873   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9874     NLink link[] = { *linkIt[0], *linkIt[1] };
9875     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9876     if ( linkIdSet.find( linkID ) == linkIdSet.end() )
9877       continue;
9878
9879     // by links, find faces in the face sets,
9880     // and find indices of link nodes in the found faces;
9881     // in a face set, there is only one or no face sharing a link
9882     // ---------------------------------------------------------------
9883
9884     const SMDS_MeshElement* face[] = { 0, 0 };
9885     //const SMDS_MeshNode* faceNodes[ 2 ][ 5 ];
9886     vector<const SMDS_MeshNode*> fnodes1(9);
9887     vector<const SMDS_MeshNode*> fnodes2(9);
9888     //const SMDS_MeshNode* notLinkNodes[ 2 ][ 2 ] = {{ 0, 0 },{ 0, 0 }} ;
9889     vector<const SMDS_MeshNode*> notLinkNodes1(6);
9890     vector<const SMDS_MeshNode*> notLinkNodes2(6);
9891     int iLinkNode[2][2];
9892     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9893       const SMDS_MeshNode* n1 = link[iSide].first;
9894       const SMDS_MeshNode* n2 = link[iSide].second;
9895       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9896       set< const SMDS_MeshElement* > fMap;
9897       for ( int i = 0; i < 2; i++ ) { // loop on 2 nodes of a link
9898         const SMDS_MeshNode* n = i ? n1 : n2; // a node of a link
9899         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9900         while ( fIt->more() ) { // loop on faces sharing a node
9901           const SMDS_MeshElement* f = fIt->next();
9902           if (faceSet->find( f ) != faceSet->end() && // f is in face set
9903               ! fMap.insert( f ).second ) // f encounters twice
9904           {
9905             if ( face[ iSide ] ) {
9906               MESSAGE( "2 faces per link " );
9907               aResult = iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES;
9908               break;
9909             }
9910             face[ iSide ] = f;
9911             faceSet->erase( f );
9912             // get face nodes and find ones of a link
9913             iNode = 0;
9914             int nbl = -1;
9915             if(f->IsPoly()) {
9916               if(iSide==0) {
9917                 fnodes1.resize(f->NbNodes()+1);
9918                 notLinkNodes1.resize(f->NbNodes()-2);
9919               }
9920               else {
9921                 fnodes2.resize(f->NbNodes()+1);
9922                 notLinkNodes2.resize(f->NbNodes()-2);
9923               }
9924             }
9925             if(!f->IsQuadratic()) {
9926               SMDS_ElemIteratorPtr nIt = f->nodesIterator();
9927               while ( nIt->more() ) {
9928                 const SMDS_MeshNode* n =
9929                   static_cast<const SMDS_MeshNode*>( nIt->next() );
9930                 if ( n == n1 ) {
9931                   iLinkNode[ iSide ][ 0 ] = iNode;
9932                 }
9933                 else if ( n == n2 ) {
9934                   iLinkNode[ iSide ][ 1 ] = iNode;
9935                 }
9936                 //else if ( notLinkNodes[ iSide ][ 0 ] )
9937                 //  notLinkNodes[ iSide ][ 1 ] = n;
9938                 //else
9939                 //  notLinkNodes[ iSide ][ 0 ] = n;
9940                 else {
9941                   nbl++;
9942                   if(iSide==0)
9943                     notLinkNodes1[nbl] = n;
9944                   //notLinkNodes1.push_back(n);
9945                   else
9946                     notLinkNodes2[nbl] = n;
9947                   //notLinkNodes2.push_back(n);
9948                 }
9949                 //faceNodes[ iSide ][ iNode++ ] = n;
9950                 if(iSide==0) {
9951                   fnodes1[iNode++] = n;
9952                 }
9953                 else {
9954                   fnodes2[iNode++] = n;
9955                 }
9956               }
9957             }
9958             else { // f->IsQuadratic()
9959               const SMDS_VtkFace* F =
9960                 dynamic_cast<const SMDS_VtkFace*>(f);
9961               if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9962               // use special nodes iterator
9963               SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9964               while ( anIter->more() ) {
9965                 const SMDS_MeshNode* n =
9966                   static_cast<const SMDS_MeshNode*>( anIter->next() );
9967                 if ( n == n1 ) {
9968                   iLinkNode[ iSide ][ 0 ] = iNode;
9969                 }
9970                 else if ( n == n2 ) {
9971                   iLinkNode[ iSide ][ 1 ] = iNode;
9972                 }
9973                 else {
9974                   nbl++;
9975                   if(iSide==0) {
9976                     notLinkNodes1[nbl] = n;
9977                   }
9978                   else {
9979                     notLinkNodes2[nbl] = n;
9980                   }
9981                 }
9982                 if(iSide==0) {
9983                   fnodes1[iNode++] = n;
9984                 }
9985                 else {
9986                   fnodes2[iNode++] = n;
9987                 }
9988               }
9989             }
9990             //faceNodes[ iSide ][ iNode ] = faceNodes[ iSide ][ 0 ];
9991             if(iSide==0) {
9992               fnodes1[iNode] = fnodes1[0];
9993             }
9994             else {
9995               fnodes2[iNode] = fnodes1[0];
9996             }
9997           }
9998         }
9999       }
10000     }
10001
10002     // check similarity of elements of the sides
10003     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10004       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10005       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10006         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10007       }
10008       else {
10009         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10010       }
10011       break; // do not return because it s necessary to remove tmp faces
10012     }
10013
10014     // set nodes to merge
10015     // -------------------
10016
10017     if ( face[0] && face[1] )  {
10018       int nbNodes = face[0]->NbNodes();
10019       if ( nbNodes != face[1]->NbNodes() ) {
10020         MESSAGE("Diff nb of face nodes");
10021         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10022         break; // do not return because it s necessary to remove tmp faces
10023       }
10024       bool reverse[] = { false, false }; // order of notLinkNodes of quadrangle
10025       if ( nbNodes == 3 ) {
10026         //nReplaceMap.insert( TNodeNodeMap::value_type
10027         //                   ( notLinkNodes[0][0], notLinkNodes[1][0] ));
10028         nReplaceMap.insert( TNodeNodeMap::value_type
10029                             ( notLinkNodes1[0], notLinkNodes2[0] ));
10030       }
10031       else {
10032         for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10033           // analyse link orientation in faces
10034           int i1 = iLinkNode[ iSide ][ 0 ];
10035           int i2 = iLinkNode[ iSide ][ 1 ];
10036           reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10037           // if notLinkNodes are the first and the last ones, then
10038           // their order does not correspond to the link orientation
10039           if (( i1 == 1 && i2 == 2 ) ||
10040               ( i1 == 2 && i2 == 1 ))
10041             reverse[ iSide ] = !reverse[ iSide ];
10042         }
10043         if ( reverse[0] == reverse[1] ) {
10044           //nReplaceMap.insert( TNodeNodeMap::value_type
10045           //                   ( notLinkNodes[0][0], notLinkNodes[1][0] ));
10046           //nReplaceMap.insert( TNodeNodeMap::value_type
10047           //                   ( notLinkNodes[0][1], notLinkNodes[1][1] ));
10048           for(int nn=0; nn<nbNodes-2; nn++) {
10049             nReplaceMap.insert( TNodeNodeMap::value_type
10050                                 ( notLinkNodes1[nn], notLinkNodes2[nn] ));
10051           }
10052         }
10053         else {
10054           //nReplaceMap.insert( TNodeNodeMap::value_type
10055           //                   ( notLinkNodes[0][0], notLinkNodes[1][1] ));
10056           //nReplaceMap.insert( TNodeNodeMap::value_type
10057           //                   ( notLinkNodes[0][1], notLinkNodes[1][0] ));
10058           for(int nn=0; nn<nbNodes-2; nn++) {
10059             nReplaceMap.insert( TNodeNodeMap::value_type
10060                                 ( notLinkNodes1[nn], notLinkNodes2[nbNodes-3-nn] ));
10061           }
10062         }
10063       }
10064
10065       // add other links of the faces to linkList
10066       // -----------------------------------------
10067
10068       //const SMDS_MeshNode** nodes = faceNodes[ 0 ];
10069       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10070         //linkID = aLinkID_Gen.GetLinkID( nodes[iNode], nodes[iNode+1] );
10071         linkID = aLinkID_Gen.GetLinkID( fnodes1[iNode], fnodes1[iNode+1] );
10072         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10073         if ( !iter_isnew.second ) { // already in a set: no need to process
10074           linkIdSet.erase( iter_isnew.first );
10075         }
10076         else // new in set == encountered for the first time: add
10077         {
10078           //const SMDS_MeshNode* n1 = nodes[ iNode ];
10079           //const SMDS_MeshNode* n2 = nodes[ iNode + 1];
10080           const SMDS_MeshNode* n1 = fnodes1[ iNode ];
10081           const SMDS_MeshNode* n2 = fnodes1[ iNode + 1];
10082           linkList[0].push_back ( NLink( n1, n2 ));
10083           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10084         }
10085       }
10086     } // 2 faces found
10087   } // loop on link lists
10088
10089   if ( aResult == SEW_OK &&
10090        ( linkIt[0] != linkList[0].end() ||
10091          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10092     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10093              " " << (faceSetPtr[1]->empty()));
10094     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10095   }
10096
10097   // ====================================================================
10098   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10099   // ====================================================================
10100
10101   // delete temporary faces: they are in reverseElements of actual nodes
10102 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10103 //  while ( tmpFaceIt->more() )
10104 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10105 //  list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10106 //  for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10107 //    aMesh->RemoveElement(*tmpFaceIt);
10108
10109   if ( aResult != SEW_OK)
10110     return aResult;
10111
10112   list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10113   // loop on nodes replacement map
10114   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10115   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10116     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10117       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10118       nodeIDsToRemove.push_back( nToRemove->GetID() );
10119       // loop on elements sharing nToRemove
10120       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10121       while ( invElemIt->more() ) {
10122         const SMDS_MeshElement* e = invElemIt->next();
10123         // get a new suite of nodes: make replacement
10124         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10125         vector< const SMDS_MeshNode*> nodes( nbNodes );
10126         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10127         while ( nIt->more() ) {
10128           const SMDS_MeshNode* n =
10129             static_cast<const SMDS_MeshNode*>( nIt->next() );
10130           nnIt = nReplaceMap.find( n );
10131           if ( nnIt != nReplaceMap.end() ) {
10132             nbReplaced++;
10133             n = (*nnIt).second;
10134           }
10135           nodes[ i++ ] = n;
10136         }
10137         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10138         //         elemIDsToRemove.push_back( e->GetID() );
10139         //       else
10140         if ( nbReplaced )
10141           {
10142             SMDSAbs_ElementType etyp = e->GetType();
10143             SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10144             if (newElem)
10145               {
10146                 myLastCreatedElems.Append(newElem);
10147                 AddToSameGroups(newElem, e, aMesh);
10148                 int aShapeId = e->getshapeId();
10149                 if ( aShapeId )
10150                   {
10151                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
10152                   }
10153               }
10154             aMesh->RemoveElement(e);
10155           }
10156       }
10157     }
10158
10159   Remove( nodeIDsToRemove, true );
10160
10161   return aResult;
10162 }
10163
10164 //================================================================================
10165 /*!
10166  * \brief Find corresponding nodes in two sets of faces
10167  * \param theSide1 - first face set
10168  * \param theSide2 - second first face
10169  * \param theFirstNode1 - a boundary node of set 1
10170  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10171  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10172  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10173  * \param nReplaceMap - output map of corresponding nodes
10174  * \return bool  - is a success or not
10175  */
10176 //================================================================================
10177
10178 #ifdef _DEBUG_
10179 //#define DEBUG_MATCHING_NODES
10180 #endif
10181
10182 SMESH_MeshEditor::Sew_Error
10183 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10184                                     set<const SMDS_MeshElement*>& theSide2,
10185                                     const SMDS_MeshNode*          theFirstNode1,
10186                                     const SMDS_MeshNode*          theFirstNode2,
10187                                     const SMDS_MeshNode*          theSecondNode1,
10188                                     const SMDS_MeshNode*          theSecondNode2,
10189                                     TNodeNodeMap &                nReplaceMap)
10190 {
10191   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10192
10193   nReplaceMap.clear();
10194   if ( theFirstNode1 != theFirstNode2 )
10195     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10196   if ( theSecondNode1 != theSecondNode2 )
10197     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10198
10199   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10200   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10201
10202   list< NLink > linkList[2];
10203   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10204   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10205
10206   // loop on links in linkList; find faces by links and append links
10207   // of the found faces to linkList
10208   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10209   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10210     NLink link[] = { *linkIt[0], *linkIt[1] };
10211     if ( linkSet.find( link[0] ) == linkSet.end() )
10212       continue;
10213
10214     // by links, find faces in the face sets,
10215     // and find indices of link nodes in the found faces;
10216     // in a face set, there is only one or no face sharing a link
10217     // ---------------------------------------------------------------
10218
10219     const SMDS_MeshElement* face[] = { 0, 0 };
10220     list<const SMDS_MeshNode*> notLinkNodes[2];
10221     //bool reverse[] = { false, false }; // order of notLinkNodes
10222     int nbNodes[2];
10223     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10224     {
10225       const SMDS_MeshNode* n1 = link[iSide].first;
10226       const SMDS_MeshNode* n2 = link[iSide].second;
10227       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10228       set< const SMDS_MeshElement* > facesOfNode1;
10229       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10230       {
10231         // during a loop of the first node, we find all faces around n1,
10232         // during a loop of the second node, we find one face sharing both n1 and n2
10233         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10234         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10235         while ( fIt->more() ) { // loop on faces sharing a node
10236           const SMDS_MeshElement* f = fIt->next();
10237           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10238               ! facesOfNode1.insert( f ).second ) // f encounters twice
10239           {
10240             if ( face[ iSide ] ) {
10241               MESSAGE( "2 faces per link " );
10242               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10243             }
10244             face[ iSide ] = f;
10245             faceSet->erase( f );
10246
10247             // get not link nodes
10248             int nbN = f->NbNodes();
10249             if ( f->IsQuadratic() )
10250               nbN /= 2;
10251             nbNodes[ iSide ] = nbN;
10252             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10253             int i1 = f->GetNodeIndex( n1 );
10254             int i2 = f->GetNodeIndex( n2 );
10255             int iEnd = nbN, iBeg = -1, iDelta = 1;
10256             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10257             if ( reverse ) {
10258               std::swap( iEnd, iBeg ); iDelta = -1;
10259             }
10260             int i = i2;
10261             while ( true ) {
10262               i += iDelta;
10263               if ( i == iEnd ) i = iBeg + iDelta;
10264               if ( i == i1 ) break;
10265               nodes.push_back ( f->GetNode( i ) );
10266             }
10267           }
10268         }
10269       }
10270     }
10271     // check similarity of elements of the sides
10272     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10273       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10274       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10275         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10276       }
10277       else {
10278         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10279       }
10280     }
10281
10282     // set nodes to merge
10283     // -------------------
10284
10285     if ( face[0] && face[1] )  {
10286       if ( nbNodes[0] != nbNodes[1] ) {
10287         MESSAGE("Diff nb of face nodes");
10288         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10289       }
10290 #ifdef DEBUG_MATCHING_NODES
10291       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10292                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10293                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10294 #endif
10295       int nbN = nbNodes[0];
10296       {
10297         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10298         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10299         for ( int i = 0 ; i < nbN - 2; ++i ) {
10300 #ifdef DEBUG_MATCHING_NODES
10301           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10302 #endif
10303           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10304         }
10305       }
10306
10307       // add other links of the face 1 to linkList
10308       // -----------------------------------------
10309
10310       const SMDS_MeshElement* f0 = face[0];
10311       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10312       for ( int i = 0; i < nbN; i++ )
10313       {
10314         const SMDS_MeshNode* n2 = f0->GetNode( i );
10315         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10316           linkSet.insert( SMESH_TLink( n1, n2 ));
10317         if ( !iter_isnew.second ) { // already in a set: no need to process
10318           linkSet.erase( iter_isnew.first );
10319         }
10320         else // new in set == encountered for the first time: add
10321         {
10322 #ifdef DEBUG_MATCHING_NODES
10323           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10324                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10325 #endif
10326           linkList[0].push_back ( NLink( n1, n2 ));
10327           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10328         }
10329         n1 = n2;
10330       }
10331     } // 2 faces found
10332   } // loop on link lists
10333
10334   return SEW_OK;
10335 }
10336
10337 //================================================================================
10338 /*!
10339   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10340   \param theElems - the list of elements (edges or faces) to be replicated
10341   The nodes for duplication could be found from these elements
10342   \param theNodesNot - list of nodes to NOT replicate
10343   \param theAffectedElems - the list of elements (cells and edges) to which the 
10344   replicated nodes should be associated to.
10345   \return TRUE if operation has been completed successfully, FALSE otherwise
10346 */
10347 //================================================================================
10348
10349 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10350                                     const TIDSortedElemSet& theNodesNot,
10351                                     const TIDSortedElemSet& theAffectedElems )
10352 {
10353   myLastCreatedElems.Clear();
10354   myLastCreatedNodes.Clear();
10355
10356   if ( theElems.size() == 0 )
10357     return false;
10358
10359   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10360   if ( !aMeshDS )
10361     return false;
10362
10363   bool res = false;
10364   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10365   // duplicate elements and nodes
10366   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10367   // replce nodes by duplications
10368   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10369   return res;
10370 }
10371
10372 //================================================================================
10373 /*!
10374   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10375   \param theMeshDS - mesh instance
10376   \param theElems - the elements replicated or modified (nodes should be changed)
10377   \param theNodesNot - nodes to NOT replicate
10378   \param theNodeNodeMap - relation of old node to new created node
10379   \param theIsDoubleElem - flag os to replicate element or modify
10380   \return TRUE if operation has been completed successfully, FALSE otherwise
10381 */
10382 //================================================================================
10383
10384 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh*     theMeshDS,
10385                                     const TIDSortedElemSet& theElems,
10386                                     const TIDSortedElemSet& theNodesNot,
10387                                     std::map< const SMDS_MeshNode*,
10388                                     const SMDS_MeshNode* >& theNodeNodeMap,
10389                                     const bool theIsDoubleElem )
10390 {
10391   MESSAGE("doubleNodes");
10392   // iterate on through element and duplicate them (by nodes duplication)
10393   bool res = false;
10394   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10395   for ( ;  elemItr != theElems.end(); ++elemItr )
10396   {
10397     const SMDS_MeshElement* anElem = *elemItr;
10398     if (!anElem)
10399       continue;
10400
10401     bool isDuplicate = false;
10402     // duplicate nodes to duplicate element
10403     std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10404     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10405     int ind = 0;
10406     while ( anIter->more() ) 
10407     { 
10408
10409       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10410       SMDS_MeshNode* aNewNode = aCurrNode;
10411       if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10412         aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10413       else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10414       {
10415         // duplicate node
10416         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10417         theNodeNodeMap[ aCurrNode ] = aNewNode;
10418         myLastCreatedNodes.Append( aNewNode );
10419       }
10420       isDuplicate |= (aCurrNode != aNewNode);
10421       newNodes[ ind++ ] = aNewNode;
10422     }
10423     if ( !isDuplicate )
10424       continue;
10425
10426     if ( theIsDoubleElem )
10427       AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10428     else
10429       {
10430       MESSAGE("ChangeElementNodes");
10431       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10432       }
10433     res = true;
10434   }
10435   return res;
10436 }
10437
10438 //================================================================================
10439 /*!
10440   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10441   \param theNodes - identifiers of nodes to be doubled
10442   \param theModifiedElems - identifiers of elements to be updated by the new (doubled) 
10443          nodes. If list of element identifiers is empty then nodes are doubled but 
10444          they not assigned to elements
10445   \return TRUE if operation has been completed successfully, FALSE otherwise
10446 */
10447 //================================================================================
10448
10449 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes, 
10450                                     const std::list< int >& theListOfModifiedElems )
10451 {
10452   MESSAGE("DoubleNodes");
10453   myLastCreatedElems.Clear();
10454   myLastCreatedNodes.Clear();
10455
10456   if ( theListOfNodes.size() == 0 )
10457     return false;
10458
10459   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10460   if ( !aMeshDS )
10461     return false;
10462
10463   // iterate through nodes and duplicate them
10464
10465   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10466
10467   std::list< int >::const_iterator aNodeIter;
10468   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10469   {
10470     int aCurr = *aNodeIter;
10471     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10472     if ( !aNode )
10473       continue;
10474
10475     // duplicate node
10476
10477     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10478     if ( aNewNode )
10479     {
10480       anOldNodeToNewNode[ aNode ] = aNewNode;
10481       myLastCreatedNodes.Append( aNewNode );
10482     }
10483   }
10484
10485   // Create map of new nodes for modified elements
10486
10487   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10488
10489   std::list< int >::const_iterator anElemIter;
10490   for ( anElemIter = theListOfModifiedElems.begin(); 
10491         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10492   {
10493     int aCurr = *anElemIter;
10494     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10495     if ( !anElem )
10496       continue;
10497
10498     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10499
10500     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10501     int ind = 0;
10502     while ( anIter->more() ) 
10503     { 
10504       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10505       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10506       {
10507         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10508         aNodeArr[ ind++ ] = aNewNode;
10509       }
10510       else
10511         aNodeArr[ ind++ ] = aCurrNode;
10512     }
10513     anElemToNodes[ anElem ] = aNodeArr;
10514   }
10515
10516   // Change nodes of elements  
10517
10518   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10519     anElemToNodesIter = anElemToNodes.begin();
10520   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10521   {
10522     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10523     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10524     if ( anElem )
10525       {
10526       MESSAGE("ChangeElementNodes");
10527       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10528       }
10529   }
10530
10531   return true;
10532 }
10533
10534 namespace {
10535
10536   //================================================================================
10537   /*!
10538   \brief Check if element located inside shape
10539   \return TRUE if IN or ON shape, FALSE otherwise
10540   */
10541   //================================================================================
10542
10543   template<class Classifier>
10544   bool isInside(const SMDS_MeshElement* theElem,
10545                 Classifier&             theClassifier,
10546                 const double            theTol)
10547   {
10548     gp_XYZ centerXYZ (0, 0, 0);
10549     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10550     while (aNodeItr->more())
10551       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10552
10553     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10554     theClassifier.Perform(aPnt, theTol);
10555     TopAbs_State aState = theClassifier.State();
10556     return (aState == TopAbs_IN || aState == TopAbs_ON );
10557   }
10558
10559   //================================================================================
10560   /*!
10561    * \brief Classifier of the 3D point on the TopoDS_Face
10562    *        with interaface suitable for isInside()
10563    */
10564   //================================================================================
10565
10566   struct _FaceClassifier
10567   {
10568     Extrema_ExtPS       _extremum;
10569     BRepAdaptor_Surface _surface;
10570     TopAbs_State        _state;
10571
10572     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10573     {
10574       _extremum.Initialize( _surface,
10575                             _surface.FirstUParameter(), _surface.LastUParameter(),
10576                             _surface.FirstVParameter(), _surface.LastVParameter(),
10577                             _surface.Tolerance(), _surface.Tolerance() );
10578     }
10579     void Perform(const gp_Pnt& aPnt, double theTol)
10580     {
10581       _state = TopAbs_OUT;
10582       _extremum.Perform(aPnt);
10583       if ( _extremum.IsDone() )
10584         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10585 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10586           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10587 #else
10588           _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10589 #endif
10590     }
10591     TopAbs_State State() const
10592     {
10593       return _state;
10594     }
10595   };
10596 }
10597
10598 //================================================================================
10599 /*!
10600   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10601   \param theElems - group of of elements (edges or faces) to be replicated
10602   \param theNodesNot - group of nodes not to replicate
10603   \param theShape - shape to detect affected elements (element which geometric center
10604   located on or inside shape).
10605   The replicated nodes should be associated to affected elements.
10606   \return TRUE if operation has been completed successfully, FALSE otherwise
10607 */
10608 //================================================================================
10609
10610 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10611                                             const TIDSortedElemSet& theNodesNot,
10612                                             const TopoDS_Shape&     theShape )
10613 {
10614   if ( theShape.IsNull() )
10615     return false;
10616
10617   const double aTol = Precision::Confusion();
10618   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10619   auto_ptr<_FaceClassifier>              aFaceClassifier;
10620   if ( theShape.ShapeType() == TopAbs_SOLID )
10621   {
10622     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10623     bsc3d->PerformInfinitePoint(aTol);
10624   }
10625   else if (theShape.ShapeType() == TopAbs_FACE )
10626   {
10627     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10628   }
10629
10630   // iterates on indicated elements and get elements by back references from their nodes
10631   TIDSortedElemSet anAffected;
10632   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10633   for ( ;  elemItr != theElems.end(); ++elemItr )
10634   {
10635     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10636     if (!anElem)
10637       continue;
10638
10639     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10640     while ( nodeItr->more() )
10641     {
10642       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10643       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10644         continue;
10645       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10646       while ( backElemItr->more() )
10647       {
10648         const SMDS_MeshElement* curElem = backElemItr->next();
10649         if ( curElem && theElems.find(curElem) == theElems.end() &&
10650              ( bsc3d.get() ?
10651                isInside( curElem, *bsc3d, aTol ) :
10652                isInside( curElem, *aFaceClassifier, aTol )))
10653           anAffected.insert( curElem );
10654       }
10655     }
10656   }
10657   return DoubleNodes( theElems, theNodesNot, anAffected );
10658 }
10659
10660 /*!
10661  *  \brief compute an oriented angle between two planes defined by four points.
10662  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10663  *  @param p0 base of the rotation axe
10664  *  @param p1 extremity of the rotation axe
10665  *  @param g1 belongs to the first plane
10666  *  @param g2 belongs to the second plane
10667  */
10668 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10669 {
10670 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10671 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10672 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10673 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10674   gp_Vec vref(p0, p1);
10675   gp_Vec v1(p0, g1);
10676   gp_Vec v2(p0, g2);
10677   gp_Vec n1 = vref.Crossed(v1);
10678   gp_Vec n2 = vref.Crossed(v2);
10679   return n2.AngleWithRef(n1, vref);
10680 }
10681
10682 /*!
10683  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10684  * The list of groups must describe a partition of the mesh volumes.
10685  * The nodes of the internal faces at the boundaries of the groups are doubled.
10686  * In option, the internal faces are replaced by flat elements.
10687  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
10688  * The flat elements are stored in groups of volumes.
10689  * @param theElems - list of groups of volumes, where a group of volume is a set of
10690  * SMDS_MeshElements sorted by Id.
10691  * @param createJointElems - if TRUE, create the elements
10692  * @return TRUE if operation has been completed successfully, FALSE otherwise
10693  */
10694 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10695                                                      bool createJointElems)
10696 {
10697   MESSAGE("----------------------------------------------");
10698   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10699   MESSAGE("----------------------------------------------");
10700
10701   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10702   meshDS->BuildDownWardConnectivity(true);
10703   CHRONO(50);
10704   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10705
10706   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10707   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10708   //     build the list of nodes shared by 2 or more domains, with their domain indexes
10709
10710   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10711   std::map<int,int>celldom; // cell vtkId --> domain
10712   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
10713   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
10714   faceDomains.clear();
10715   celldom.clear();
10716   cellDomains.clear();
10717   nodeDomains.clear();
10718   std::map<int,int> emptyMap;
10719   std::set<int> emptySet;
10720   emptyMap.clear();
10721
10722   for (int idom = 0; idom < theElems.size(); idom++)
10723     {
10724
10725       // --- build a map (face to duplicate --> volume to modify)
10726       //     with all the faces shared by 2 domains (group of elements)
10727       //     and corresponding volume of this domain, for each shared face.
10728       //     a volume has a face shared by 2 domains if it has a neighbor which is not in is domain.
10729
10730       //MESSAGE("Domain " << idom);
10731       const TIDSortedElemSet& domain = theElems[idom];
10732       TIDSortedElemSet::const_iterator elemItr = domain.begin();
10733       for (; elemItr != domain.end(); ++elemItr)
10734         {
10735           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
10736           if (!anElem)
10737             continue;
10738           int vtkId = anElem->getVtkId();
10739           //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
10740           int neighborsVtkIds[NBMAXNEIGHBORS];
10741           int downIds[NBMAXNEIGHBORS];
10742           unsigned char downTypes[NBMAXNEIGHBORS];
10743           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10744           for (int n = 0; n < nbNeighbors; n++)
10745             {
10746               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
10747               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
10748               if (! domain.count(elem)) // neighbor is in another domain : face is shared
10749                 {
10750                   DownIdType face(downIds[n], downTypes[n]);
10751                   if (!faceDomains.count(face))
10752                     faceDomains[face] = emptyMap; // create an empty entry for face
10753                   if (!faceDomains[face].count(idom))
10754                     {
10755                       faceDomains[face][idom] = vtkId; // volume associated to face in this domain
10756                       celldom[vtkId] = idom;
10757                       //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
10758                     }
10759                 }
10760             }
10761         }
10762     }
10763
10764   //MESSAGE("Number of shared faces " << faceDomains.size());
10765   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
10766
10767   // --- explore the shared faces domain by domain,
10768   //     explore the nodes of the face and see if they belong to a cell in the domain,
10769   //     which has only a node or an edge on the border (not a shared face)
10770
10771   for (int idomain = 0; idomain < theElems.size(); idomain++)
10772     {
10773       //MESSAGE("Domain " << idomain);
10774       const TIDSortedElemSet& domain = theElems[idomain];
10775       itface = faceDomains.begin();
10776       for (; itface != faceDomains.end(); ++itface)
10777         {
10778           std::map<int, int> domvol = itface->second;
10779           if (!domvol.count(idomain))
10780             continue;
10781           DownIdType face = itface->first;
10782           //MESSAGE(" --- face " << face.cellId);
10783           std::set<int> oldNodes;
10784           oldNodes.clear();
10785           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10786           std::set<int>::iterator itn = oldNodes.begin();
10787           for (; itn != oldNodes.end(); ++itn)
10788             {
10789               int oldId = *itn;
10790               //MESSAGE("     node " << oldId);
10791               std::set<int> cells;
10792               cells.clear();
10793               vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10794               for (int i=0; i<l.ncells; i++)
10795                 {
10796                   int vtkId = l.cells[i];
10797                   const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
10798                   if (!domain.count(anElem))
10799                     continue;
10800                   int vtkType = grid->GetCellType(vtkId);
10801                   int downId = grid->CellIdToDownId(vtkId);
10802                   if (downId < 0)
10803                     {
10804                       MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
10805                       continue; // not OK at this stage of the algorithm:
10806                                 //no cells created after BuildDownWardConnectivity
10807                     }
10808                   DownIdType aCell(downId, vtkType);
10809                   if (celldom.count(vtkId))
10810                     continue;
10811                   cellDomains[aCell][idomain] = vtkId;
10812                   celldom[vtkId] = idomain;
10813                   //MESSAGE("       cell " << vtkId << " domain " << idomain);
10814                 }
10815             }
10816         }
10817     }
10818
10819   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
10820   //     for each shared face, get the nodes
10821   //     for each node, for each domain of the face, create a clone of the node
10822
10823   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
10824   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
10825   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
10826
10827   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
10828   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
10829   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
10830
10831   for (int idomain = 0; idomain < theElems.size(); idomain++)
10832     {
10833       itface = faceDomains.begin();
10834       for (; itface != faceDomains.end(); ++itface)
10835         {
10836           std::map<int, int> domvol = itface->second;
10837           if (!domvol.count(idomain))
10838             continue;
10839           DownIdType face = itface->first;
10840           //MESSAGE(" --- face " << face.cellId);
10841           std::set<int> oldNodes;
10842           oldNodes.clear();
10843           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10844           bool isMultipleDetected = false;
10845           std::set<int>::iterator itn = oldNodes.begin();
10846           for (; itn != oldNodes.end(); ++itn)
10847             {
10848               int oldId = *itn;
10849               //MESSAGE("     node " << oldId);
10850               if (!nodeDomains.count(oldId))
10851                 nodeDomains[oldId] = emptyMap; // create an empty entry for node
10852               if (nodeDomains[oldId].empty())
10853                 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
10854               std::map<int, int>::iterator itdom = domvol.begin();
10855               for (; itdom != domvol.end(); ++itdom)
10856                 {
10857                   int idom = itdom->first;
10858                   //MESSAGE("         domain " << idom);
10859                   if (!nodeDomains[oldId].count(idom)) // --- node to clone
10860                     {
10861                       if (nodeDomains[oldId].size() >= 2) // a multiple node
10862                         {
10863                           vector<int> orderedDoms;
10864                           //MESSAGE("multiple node " << oldId);
10865                           isMultipleDetected =true;
10866                           if (mutipleNodes.count(oldId))
10867                             orderedDoms = mutipleNodes[oldId];
10868                           else
10869                             {
10870                               map<int,int>::iterator it = nodeDomains[oldId].begin();
10871                               for (; it != nodeDomains[oldId].end(); ++it)
10872                                 orderedDoms.push_back(it->first);
10873                             }
10874                           orderedDoms.push_back(idom); // TODO order ==> push_front or back
10875                           //stringstream txt;
10876                           //for (int i=0; i<orderedDoms.size(); i++)
10877                           //  txt << orderedDoms[i] << " ";
10878                           //MESSAGE("orderedDoms " << txt.str());
10879                           mutipleNodes[oldId] = orderedDoms;
10880                         }
10881                       double *coords = grid->GetPoint(oldId);
10882                       SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
10883                       int newId = newNode->getVtkId();
10884                       nodeDomains[oldId][idom] = newId; // cloned node for other domains
10885                       //MESSAGE("   newNode " << newId << " oldNode " << oldId << " size=" <<nodeDomains[oldId].size());
10886                     }
10887                   if (nodeDomains[oldId].size() >= 3)
10888                     {
10889                       //MESSAGE("confirm multiple node " << oldId);
10890                       isMultipleDetected =true;
10891                     }
10892                 }
10893             }
10894           if (isMultipleDetected) // check if an edge of the face is shared between 3 or more domains
10895             {
10896               //MESSAGE("multiple Nodes detected on a shared face");
10897               int downId = itface->first.cellId;
10898               unsigned char cellType = itface->first.cellType;
10899               // --- shared edge or shared face ?
10900               if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
10901                 {
10902                   int nodes[3];
10903                   int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
10904                   for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
10905                     if (mutipleNodes.count(nodes[i]))
10906                       if (!mutipleNodesToFace.count(nodes[i]))
10907                         mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
10908                }
10909               else // shared face (between two volumes)
10910                 {
10911                   int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
10912                   const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
10913                   const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
10914                   for (int ie =0; ie < nbEdges; ie++)
10915                     {
10916                       int nodes[3];
10917                       int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
10918                       if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
10919                         {
10920                           vector<int> vn0 = mutipleNodes[nodes[0]];
10921                           vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
10922                           sort( vn0.begin(), vn0.end() );
10923                           sort( vn1.begin(), vn1.end() );
10924                           if (vn0 == vn1)
10925                             {
10926                               //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
10927                               double *coords = grid->GetPoint(nodes[0]);
10928                               gp_Pnt p0(coords[0], coords[1], coords[2]);
10929                               coords = grid->GetPoint(nodes[nbNodes - 1]);
10930                               gp_Pnt p1(coords[0], coords[1], coords[2]);
10931                               gp_Pnt gref;
10932                               int vtkVolIds[1000];  // an edge can belong to a lot of volumes
10933                               map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
10934                               map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
10935                               int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
10936                               for (int id=0; id < vn0.size(); id++)
10937                                 {
10938                                   int idom = vn0[id];
10939                                   for (int ivol=0; ivol<nbvol; ivol++)
10940                                     {
10941                                       int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
10942                                       SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
10943                                       if (theElems[idom].count(elem))
10944                                         {
10945                                           SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
10946                                           domvol[idom] = svol;
10947                                           //MESSAGE("  domain " << idom << " volume " << elem->GetID());
10948                                           double values[3];
10949                                           vtkIdType npts = 0;
10950                                           vtkIdType* pts = 0;
10951                                           grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
10952                                           SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
10953                                           if (id ==0)
10954                                             {
10955                                               gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
10956                                               angleDom[idom] = 0;
10957                                             }
10958                                           else
10959                                             {
10960                                               gp_Pnt g(values[0], values[1], values[2]);
10961                                               angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
10962                                               //MESSAGE("  angle=" << angleDom[idom]);
10963                                             }
10964                                           break;
10965                                         }
10966                                     }
10967                                 }
10968                               map<double, int> sortedDom; // sort domains by angle
10969                               for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
10970                                 sortedDom[ia->second] = ia->first;
10971                               vector<int> vnodes;
10972                               vector<int> vdom;
10973                               for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
10974                                 {
10975                                   vdom.push_back(ib->second);
10976                                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
10977                                 }
10978                               for (int ino = 0; ino < nbNodes; ino++)
10979                                 vnodes.push_back(nodes[ino]);
10980                               edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
10981                             }
10982                         }
10983                     }
10984                 }
10985             }
10986         }
10987     }
10988
10989   // --- iterate on shared faces (volumes to modify, face to extrude)
10990   //     get node id's of the face (id SMDS = id VTK)
10991   //     create flat element with old and new nodes if requested
10992
10993   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
10994   //     (domain1 X domain2) = domain1 + MAXINT*domain2
10995
10996   std::map<int, std::map<long,int> > nodeQuadDomains;
10997   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
10998
10999   if (createJointElems)
11000     {
11001       itface = faceDomains.begin();
11002       for (; itface != faceDomains.end(); ++itface)
11003         {
11004           DownIdType face = itface->first;
11005           std::set<int> oldNodes;
11006           std::set<int>::iterator itn;
11007           oldNodes.clear();
11008           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11009
11010           std::map<int, int> domvol = itface->second;
11011           std::map<int, int>::iterator itdom = domvol.begin();
11012           int dom1 = itdom->first;
11013           int vtkVolId = itdom->second;
11014           itdom++;
11015           int dom2 = itdom->first;
11016           SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11017                                                              nodeQuadDomains);
11018           stringstream grpname;
11019           grpname << "j_";
11020           if (dom1 < dom2)
11021             grpname << dom1 << "_" << dom2;
11022           else
11023             grpname << dom2 << "_" << dom1;
11024           int idg;
11025           string namegrp = grpname.str();
11026           if (!mapOfJunctionGroups.count(namegrp))
11027             mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11028           SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11029           if (sgrp)
11030             sgrp->Add(vol->GetID());
11031         }
11032     }
11033
11034   // --- create volumes on multiple domain intersection if requested
11035   //     iterate on mutipleNodesToFace
11036   //     iterate on edgesMultiDomains
11037
11038   if (createJointElems)
11039     {
11040       // --- iterate on mutipleNodesToFace
11041
11042       std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11043       for (; itn != mutipleNodesToFace.end(); ++itn)
11044         {
11045           int node = itn->first;
11046           vector<int> orderDom = itn->second;
11047           vector<vtkIdType> orderedNodes;
11048           for (int idom = 0; idom <orderDom.size(); idom++)
11049             orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11050             SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11051
11052             stringstream grpname;
11053             grpname << "m2j_";
11054             grpname << 0 << "_" << 0;
11055             int idg;
11056             string namegrp = grpname.str();
11057             if (!mapOfJunctionGroups.count(namegrp))
11058               mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11059             SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11060             if (sgrp)
11061               sgrp->Add(face->GetID());
11062         }
11063
11064       // --- iterate on edgesMultiDomains
11065
11066       std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11067       for (; ite != edgesMultiDomains.end(); ++ite)
11068         {
11069           vector<int> nodes = ite->first;
11070           vector<int> orderDom = ite->second;
11071           vector<vtkIdType> orderedNodes;
11072           if (nodes.size() == 2)
11073             {
11074               //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11075               for (int ino=0; ino < nodes.size(); ino++)
11076                 if (orderDom.size() == 3)
11077                   for (int idom = 0; idom <orderDom.size(); idom++)
11078                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11079                 else
11080                   for (int idom = orderDom.size()-1; idom >=0; idom--)
11081                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11082               SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11083
11084               stringstream grpname;
11085               grpname << "mj_";
11086               grpname << 0 << "_" << 0;
11087               int idg;
11088               string namegrp = grpname.str();
11089               if (!mapOfJunctionGroups.count(namegrp))
11090                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11091               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11092               if (sgrp)
11093                 sgrp->Add(vol->GetID());
11094             }
11095           else
11096             {
11097               MESSAGE("Quadratic multiple joints not implemented");
11098               // TODO quadratic nodes
11099             }
11100         }
11101     }
11102
11103   // --- list the explicit faces and edges of the mesh that need to be modified,
11104   //     i.e. faces and edges built with one or more duplicated nodes.
11105   //     associate these faces or edges to their corresponding domain.
11106   //     only the first domain found is kept when a face or edge is shared
11107
11108   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11109   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11110   faceOrEdgeDom.clear();
11111   feDom.clear();
11112
11113   for (int idomain = 0; idomain < theElems.size(); idomain++)
11114     {
11115       std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11116       for (; itnod != nodeDomains.end(); ++itnod)
11117         {
11118           int oldId = itnod->first;
11119           //MESSAGE("     node " << oldId);
11120           vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11121           for (int i = 0; i < l.ncells; i++)
11122             {
11123               int vtkId = l.cells[i];
11124               int vtkType = grid->GetCellType(vtkId);
11125               int downId = grid->CellIdToDownId(vtkId);
11126               if (downId < 0)
11127                 continue; // new cells: not to be modified
11128               DownIdType aCell(downId, vtkType);
11129               int volParents[1000];
11130               int nbvol = grid->GetParentVolumes(volParents, vtkId);
11131               for (int j = 0; j < nbvol; j++)
11132                 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11133                   if (!feDom.count(vtkId))
11134                     {
11135                       feDom[vtkId] = idomain;
11136                       faceOrEdgeDom[aCell] = emptyMap;
11137                       faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11138                       //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11139                       //        << " type " << vtkType << " downId " << downId);
11140                     }
11141             }
11142         }
11143     }
11144
11145   // --- iterate on shared faces (volumes to modify, face to extrude)
11146   //     get node id's of the face
11147   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11148
11149   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11150   for (int m=0; m<3; m++)
11151     {
11152       std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11153       itface = (*amap).begin();
11154       for (; itface != (*amap).end(); ++itface)
11155         {
11156           DownIdType face = itface->first;
11157           std::set<int> oldNodes;
11158           std::set<int>::iterator itn;
11159           oldNodes.clear();
11160           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11161           //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11162           std::map<int, int> localClonedNodeIds;
11163
11164           std::map<int, int> domvol = itface->second;
11165           std::map<int, int>::iterator itdom = domvol.begin();
11166           for (; itdom != domvol.end(); ++itdom)
11167             {
11168               int idom = itdom->first;
11169               int vtkVolId = itdom->second;
11170               //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11171               localClonedNodeIds.clear();
11172               for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11173                 {
11174                   int oldId = *itn;
11175                   if (nodeDomains[oldId].count(idom))
11176                     {
11177                       localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11178                       //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11179                     }
11180                 }
11181               meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11182             }
11183         }
11184     }
11185
11186   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11187   grid->BuildLinks();
11188
11189   CHRONOSTOP(50);
11190   counters::stats();
11191   return true;
11192 }
11193
11194 /*!
11195  * \brief Double nodes on some external faces and create flat elements.
11196  * Flat elements are mainly used by some types of mechanic calculations.
11197  *
11198  * Each group of the list must be constituted of faces.
11199  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11200  * @param theElems - list of groups of faces, where a group of faces is a set of
11201  * SMDS_MeshElements sorted by Id.
11202  * @return TRUE if operation has been completed successfully, FALSE otherwise
11203  */
11204 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11205 {
11206   MESSAGE("-------------------------------------------------");
11207   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11208   MESSAGE("-------------------------------------------------");
11209
11210   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11211
11212   // --- For each group of faces
11213   //     duplicate the nodes, create a flat element based on the face
11214   //     replace the nodes of the faces by their clones
11215
11216   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11217   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11218   clonedNodes.clear();
11219   intermediateNodes.clear();
11220   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11221   mapOfJunctionGroups.clear();
11222
11223   for (int idom = 0; idom < theElems.size(); idom++)
11224     {
11225       const TIDSortedElemSet& domain = theElems[idom];
11226       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11227       for (; elemItr != domain.end(); ++elemItr)
11228         {
11229           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11230           SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11231           if (!aFace)
11232             continue;
11233           // MESSAGE("aFace=" << aFace->GetID());
11234           bool isQuad = aFace->IsQuadratic();
11235           vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11236
11237           // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11238
11239           SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11240           while (nodeIt->more())
11241             {
11242               const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11243               bool isMedium = isQuad && (aFace->IsMediumNode(node));
11244               if (isMedium)
11245                 ln2.push_back(node);
11246               else
11247                 ln0.push_back(node);
11248
11249               const SMDS_MeshNode* clone = 0;
11250               if (!clonedNodes.count(node))
11251                 {
11252                   clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11253                   clonedNodes[node] = clone;
11254                 }
11255               else
11256                 clone = clonedNodes[node];
11257
11258               if (isMedium)
11259                 ln3.push_back(clone);
11260               else
11261                 ln1.push_back(clone);
11262
11263               const SMDS_MeshNode* inter = 0;
11264               if (isQuad && (!isMedium))
11265                 {
11266                   if (!intermediateNodes.count(node))
11267                     {
11268                       inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11269                       intermediateNodes[node] = inter;
11270                     }
11271                   else
11272                     inter = intermediateNodes[node];
11273                   ln4.push_back(inter);
11274                 }
11275             }
11276
11277           // --- extrude the face
11278
11279           vector<const SMDS_MeshNode*> ln;
11280           SMDS_MeshVolume* vol = 0;
11281           vtkIdType aType = aFace->GetVtkType();
11282           switch (aType)
11283           {
11284             case VTK_TRIANGLE:
11285               vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11286               // MESSAGE("vol prism " << vol->GetID());
11287               ln.push_back(ln1[0]);
11288               ln.push_back(ln1[1]);
11289               ln.push_back(ln1[2]);
11290               break;
11291             case VTK_QUAD:
11292               vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11293               // MESSAGE("vol hexa " << vol->GetID());
11294               ln.push_back(ln1[0]);
11295               ln.push_back(ln1[1]);
11296               ln.push_back(ln1[2]);
11297               ln.push_back(ln1[3]);
11298               break;
11299             case VTK_QUADRATIC_TRIANGLE:
11300               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11301                                       ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11302               // MESSAGE("vol quad prism " << vol->GetID());
11303               ln.push_back(ln1[0]);
11304               ln.push_back(ln1[1]);
11305               ln.push_back(ln1[2]);
11306               ln.push_back(ln3[0]);
11307               ln.push_back(ln3[1]);
11308               ln.push_back(ln3[2]);
11309               break;
11310             case VTK_QUADRATIC_QUAD:
11311 //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11312 //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11313 //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11314               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11315                                       ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11316                                       ln4[0], ln4[1], ln4[2], ln4[3]);
11317               // MESSAGE("vol quad hexa " << vol->GetID());
11318               ln.push_back(ln1[0]);
11319               ln.push_back(ln1[1]);
11320               ln.push_back(ln1[2]);
11321               ln.push_back(ln1[3]);
11322               ln.push_back(ln3[0]);
11323               ln.push_back(ln3[1]);
11324               ln.push_back(ln3[2]);
11325               ln.push_back(ln3[3]);
11326               break;
11327             case VTK_POLYGON:
11328               break;
11329             default:
11330               break;
11331           }
11332
11333           if (vol)
11334             {
11335               stringstream grpname;
11336               grpname << "jf_";
11337               grpname << idom;
11338               int idg;
11339               string namegrp = grpname.str();
11340               if (!mapOfJunctionGroups.count(namegrp))
11341                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11342               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11343               if (sgrp)
11344                 sgrp->Add(vol->GetID());
11345             }
11346
11347           // --- modify the face
11348
11349           aFace->ChangeNodes(&ln[0], ln.size());
11350         }
11351     }
11352   return true;
11353 }
11354
11355 //================================================================================
11356 /*!
11357  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
11358  * The created 2D mesh elements based on nodes of free faces of boundary volumes
11359  * \return TRUE if operation has been completed successfully, FALSE otherwise
11360  */
11361 //================================================================================
11362
11363 bool SMESH_MeshEditor::Make2DMeshFrom3D()
11364 {
11365   // iterates on volume elements and detect all free faces on them
11366   SMESHDS_Mesh* aMesh = GetMeshDS();
11367   if (!aMesh)
11368     return false;
11369   //bool res = false;
11370   int nbFree = 0, nbExisted = 0, nbCreated = 0;
11371   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
11372   while(vIt->more())
11373   {
11374     const SMDS_MeshVolume* volume = vIt->next();
11375     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
11376     vTool.SetExternalNormal();
11377     //const bool isPoly = volume->IsPoly();
11378     const int iQuad = volume->IsQuadratic();
11379     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11380     {
11381       if (!vTool.IsFreeFace(iface))
11382         continue;
11383       nbFree++;
11384       vector<const SMDS_MeshNode *> nodes;
11385       int nbFaceNodes = vTool.NbFaceNodes(iface);
11386       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
11387       int inode = 0;
11388       for ( ; inode < nbFaceNodes; inode += iQuad+1)
11389         nodes.push_back(faceNodes[inode]);
11390       if (iQuad) { // add medium nodes
11391         for ( inode = 1; inode < nbFaceNodes; inode += 2)
11392           nodes.push_back(faceNodes[inode]);
11393         if ( nbFaceNodes == 9 ) // bi-quadratic quad
11394           nodes.push_back(faceNodes[8]);
11395       }
11396       // add new face based on volume nodes
11397       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
11398         nbExisted++;
11399         continue; // face already exsist
11400       }
11401       AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
11402       nbCreated++;
11403     }
11404   }
11405   return ( nbFree==(nbExisted+nbCreated) );
11406 }
11407
11408 namespace
11409 {
11410   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
11411   {
11412     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
11413       return n;
11414     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
11415   }
11416 }
11417 //================================================================================
11418 /*!
11419  * \brief Creates missing boundary elements
11420  *  \param elements - elements whose boundary is to be checked
11421  *  \param dimension - defines type of boundary elements to create
11422  *  \param group - a group to store created boundary elements in
11423  *  \param targetMesh - a mesh to store created boundary elements in
11424  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
11425  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
11426  *                                boundary elements will be copied into the targetMesh
11427  *  \param toAddExistingBondary - if true, not only new but also pre-existing
11428  *                                boundary elements will be added into the new group
11429  *  \param aroundElements - if true, elements will be created on boundary of given
11430  *                          elements else, on boundary of the whole mesh.
11431  * \return nb of added boundary elements
11432  */
11433 //================================================================================
11434
11435 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
11436                                        Bnd_Dimension           dimension,
11437                                        SMESH_Group*            group/*=0*/,
11438                                        SMESH_Mesh*             targetMesh/*=0*/,
11439                                        bool                    toCopyElements/*=false*/,
11440                                        bool                    toCopyExistingBoundary/*=false*/,
11441                                        bool                    toAddExistingBondary/*= false*/,
11442                                        bool                    aroundElements/*= false*/)
11443 {
11444   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
11445   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
11446   // hope that all elements are of the same type, do not check them all
11447   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
11448     throw SALOME_Exception(LOCALIZED("wrong element type"));
11449
11450   if ( !targetMesh )
11451     toCopyElements = toCopyExistingBoundary = false;
11452
11453   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
11454   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
11455   int nbAddedBnd = 0;
11456
11457   // editor adding present bnd elements and optionally holding elements to add to the group
11458   SMESH_MeshEditor* presentEditor;
11459   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
11460   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
11461
11462   SMDS_VolumeTool vTool;
11463   TIDSortedElemSet avoidSet;
11464   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
11465   int inode;
11466
11467   typedef vector<const SMDS_MeshNode*> TConnectivity;
11468
11469   SMDS_ElemIteratorPtr eIt;
11470   if (elements.empty())
11471     eIt = aMesh->elementsIterator(elemType);
11472   else
11473     eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
11474
11475   while (eIt->more())
11476   {
11477     const SMDS_MeshElement* elem = eIt->next();
11478     const int iQuad = elem->IsQuadratic();
11479
11480     // ------------------------------------------------------------------------------------
11481     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
11482     // ------------------------------------------------------------------------------------
11483     vector<const SMDS_MeshElement*> presentBndElems;
11484     vector<TConnectivity>           missingBndElems;
11485     TConnectivity nodes;
11486     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
11487     {
11488       vTool.SetExternalNormal();
11489       const SMDS_MeshElement* otherVol = 0;
11490       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11491       {
11492         if ( !vTool.IsFreeFace(iface, &otherVol) &&
11493              ( !aroundElements || elements.count( otherVol )))
11494           continue;
11495         const int nbFaceNodes = vTool.NbFaceNodes(iface);
11496         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
11497         if ( missType == SMDSAbs_Edge ) // boundary edges
11498         {
11499           nodes.resize( 2+iQuad );
11500           for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
11501           {
11502             for ( int j = 0; j < nodes.size(); ++j )
11503               nodes[j] =nn[i+j];
11504             if ( const SMDS_MeshElement* edge =
11505                  aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
11506               presentBndElems.push_back( edge );
11507             else
11508               missingBndElems.push_back( nodes );
11509           }
11510         }
11511         else // boundary face
11512         {
11513           nodes.clear();
11514           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11515             nodes.push_back( nn[inode] );
11516           if (iQuad) // add medium nodes
11517             for ( inode = 1; inode < nbFaceNodes; inode += 2)
11518               nodes.push_back( nn[inode] );
11519           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
11520           if ( iCenter > 0 )
11521             nodes.push_back( vTool.GetNodes()[ iCenter ] );
11522
11523           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
11524                                                                SMDSAbs_Face, /*noMedium=*/false ))
11525             presentBndElems.push_back( f );
11526           else
11527             missingBndElems.push_back( nodes );
11528
11529           if ( targetMesh != myMesh )
11530           {
11531             // add 1D elements on face boundary to be added to a new mesh
11532             const SMDS_MeshElement* edge;
11533             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11534             {
11535               if ( iQuad )
11536                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
11537               else
11538                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
11539               if ( edge && avoidSet.insert( edge ).second )
11540                 presentBndElems.push_back( edge );
11541             }
11542           }
11543         }
11544       }
11545     }
11546     else                     // elem is a face ------------------------------------------
11547     {
11548       avoidSet.clear(), avoidSet.insert( elem );
11549       int nbNodes = elem->NbCornerNodes();
11550       nodes.resize( 2 /*+ iQuad*/);
11551       for ( int i = 0; i < nbNodes; i++ )
11552       {
11553         nodes[0] = elem->GetNode(i);
11554         nodes[1] = elem->GetNode((i+1)%nbNodes);
11555         if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
11556           continue; // not free link
11557
11558         //if ( iQuad )
11559         //nodes[2] = elem->GetNode( i + nbNodes );
11560         if ( const SMDS_MeshElement* edge =
11561              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
11562           presentBndElems.push_back( edge );
11563         else
11564           missingBndElems.push_back( nodes );
11565       }
11566     }
11567
11568     // ---------------------------------
11569     // 2. Add missing boundary elements
11570     // ---------------------------------
11571     if ( targetMesh != myMesh )
11572       // instead of making a map of nodes in this mesh and targetMesh,
11573       // we create nodes with same IDs.
11574       for ( int i = 0; i < missingBndElems.size(); ++i )
11575       {
11576         TConnectivity& srcNodes = missingBndElems[i];
11577         TConnectivity  nodes( srcNodes.size() );
11578         for ( inode = 0; inode < nodes.size(); ++inode )
11579           nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
11580         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11581                                                                    missType,
11582                                                                    /*noMedium=*/false))
11583           continue;
11584         tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
11585         ++nbAddedBnd;
11586       }
11587     else
11588       for ( int i = 0; i < missingBndElems.size(); ++i )
11589       {
11590         TConnectivity& nodes = missingBndElems[i];
11591         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11592                                                                    missType,
11593                                                                    /*noMedium=*/false))
11594           continue;
11595         tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
11596         ++nbAddedBnd;
11597       }
11598
11599     // ----------------------------------
11600     // 3. Copy present boundary elements
11601     // ----------------------------------
11602     if ( toCopyExistingBoundary )
11603       for ( int i = 0 ; i < presentBndElems.size(); ++i )
11604       {
11605         const SMDS_MeshElement* e = presentBndElems[i];
11606         TConnectivity nodes( e->NbNodes() );
11607         for ( inode = 0; inode < nodes.size(); ++inode )
11608           nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
11609         presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
11610       }
11611     else // store present elements to add them to a group
11612       for ( int i = 0 ; i < presentBndElems.size(); ++i )
11613       {
11614         presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
11615       }
11616       
11617   } // loop on given elements
11618
11619   // ---------------------------------------------
11620   // 4. Fill group with boundary elements
11621   // ---------------------------------------------
11622   if ( group )
11623   {
11624     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
11625       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
11626         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
11627   }
11628   tgtEditor.myLastCreatedElems.Clear();
11629   tgtEditor2.myLastCreatedElems.Clear();
11630
11631   // -----------------------
11632   // 5. Copy given elements
11633   // -----------------------
11634   if ( toCopyElements && targetMesh != myMesh )
11635   {
11636     if (elements.empty())
11637       eIt = aMesh->elementsIterator(elemType);
11638     else
11639       eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
11640     while (eIt->more())
11641     {
11642       const SMDS_MeshElement* elem = eIt->next();
11643       TConnectivity nodes( elem->NbNodes() );
11644       for ( inode = 0; inode < nodes.size(); ++inode )
11645         nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
11646       tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
11647
11648       tgtEditor.myLastCreatedElems.Clear();
11649     }
11650   }
11651   return nbAddedBnd;
11652 }