Salome HOME
23440: [CEA 2093] : Merge nodes failed (test case bug_1796_mergenodes)
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // 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_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.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 #include "chrono.hxx"
53
54 #include <BRepAdaptor_Surface.hxx>
55 #include <BRepBuilderAPI_MakeEdge.hxx>
56 #include <BRepClass3d_SolidClassifier.hxx>
57 #include <BRep_Tool.hxx>
58 #include <ElCLib.hxx>
59 #include <Extrema_GenExtPS.hxx>
60 #include <Extrema_POnCurv.hxx>
61 #include <Extrema_POnSurf.hxx>
62 #include <Geom2d_Curve.hxx>
63 #include <GeomAdaptor_Surface.hxx>
64 #include <Geom_Curve.hxx>
65 #include <Geom_Surface.hxx>
66 #include <Precision.hxx>
67 #include <TColStd_ListOfInteger.hxx>
68 #include <TopAbs_State.hxx>
69 #include <TopExp.hxx>
70 #include <TopExp_Explorer.hxx>
71 #include <TopTools_ListIteratorOfListOfShape.hxx>
72 #include <TopTools_ListOfShape.hxx>
73 #include <TopTools_SequenceOfShape.hxx>
74 #include <TopoDS.hxx>
75 #include <TopoDS_Edge.hxx>
76 #include <TopoDS_Face.hxx>
77 #include <TopoDS_Solid.hxx>
78 #include <gp.hxx>
79 #include <gp_Ax1.hxx>
80 #include <gp_Dir.hxx>
81 #include <gp_Lin.hxx>
82 #include <gp_Pln.hxx>
83 #include <gp_Trsf.hxx>
84 #include <gp_Vec.hxx>
85 #include <gp_XY.hxx>
86 #include <gp_XYZ.hxx>
87
88 #include <cmath>
89
90 #include <map>
91 #include <set>
92 #include <numeric>
93 #include <limits>
94 #include <algorithm>
95 #include <sstream>
96
97 #include <boost/tuple/tuple.hpp>
98
99 #include <Standard_Failure.hxx>
100 #include <Standard_ErrorHandler.hxx>
101
102 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
103
104 using namespace std;
105 using namespace SMESH::Controls;
106
107 namespace
108 {
109   template < class ELEM_SET >
110   SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
111   {
112     typedef SMDS_SetIterator
113       < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
114     return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
115   }
116 }
117
118 //=======================================================================
119 //function : SMESH_MeshEditor
120 //purpose  :
121 //=======================================================================
122
123 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
124   :myMesh( theMesh ) // theMesh may be NULL
125 {
126 }
127
128 //================================================================================
129 /*!
130  * \brief Return mesh DS
131  */
132 //================================================================================
133
134 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
135 {
136   return myMesh->GetMeshDS();
137 }
138
139
140 //================================================================================
141 /*!
142  * \brief Clears myLastCreatedNodes and myLastCreatedElems
143  */
144 //================================================================================
145
146 void SMESH_MeshEditor::ClearLastCreated()
147 {
148   myLastCreatedNodes.Clear();
149   myLastCreatedElems.Clear();
150 }
151
152 //================================================================================
153 /*!
154  * \brief Initializes members by an existing element
155  *  \param [in] elem - the source element
156  *  \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
157  */
158 //================================================================================
159
160 SMESH_MeshEditor::ElemFeatures&
161 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
162 {
163   if ( elem )
164   {
165     myType = elem->GetType();
166     if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
167     {
168       myIsPoly = elem->IsPoly();
169       if ( myIsPoly )
170       {
171         myIsQuad = elem->IsQuadratic();
172         if ( myType == SMDSAbs_Volume && !basicOnly )
173         {
174           vector<int > quant = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
175           myPolyhedQuantities.swap( quant );
176         }
177       }
178     }
179     else if ( myType == SMDSAbs_Ball && !basicOnly )
180     {
181       myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
182     }
183   }
184   return *this;
185 }
186
187 //=======================================================================
188 /*!
189  * \brief Add element
190  */
191 //=======================================================================
192
193 SMDS_MeshElement*
194 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
195                              const ElemFeatures&                  features)
196 {
197   SMDS_MeshElement* e = 0;
198   int nbnode = node.size();
199   SMESHDS_Mesh* mesh = GetMeshDS();
200   const int ID = features.myID;
201
202   switch ( features.myType ) {
203   case SMDSAbs_Face:
204     if ( !features.myIsPoly ) {
205       if      (nbnode == 3) {
206         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
207         else           e = mesh->AddFace      (node[0], node[1], node[2] );
208       }
209       else if (nbnode == 4) {
210         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
211         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
212       }
213       else if (nbnode == 6) {
214         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
215                                                node[4], node[5], ID);
216         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
217                                                node[4], node[5] );
218       }
219       else if (nbnode == 7) {
220         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
221                                                node[4], node[5], node[6], ID);
222         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
223                                                node[4], node[5], node[6] );
224       }
225       else if (nbnode == 8) {
226         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
227                                                node[4], node[5], node[6], node[7], ID);
228         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
229                                                node[4], node[5], node[6], node[7] );
230       }
231       else if (nbnode == 9) {
232         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
233                                                node[4], node[5], node[6], node[7], node[8], ID);
234         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
235                                                node[4], node[5], node[6], node[7], node[8] );
236       }
237     }
238     else if ( !features.myIsQuad )
239     {
240       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
241       else           e = mesh->AddPolygonalFace      (node    );
242     }
243     else if ( nbnode % 2 == 0 ) // just a protection
244     {
245       if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
246       else           e = mesh->AddQuadPolygonalFace      (node    );
247     }
248     break;
249
250   case SMDSAbs_Volume:
251     if ( !features.myIsPoly ) {
252       if      (nbnode == 4) {
253         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
254         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
255       }
256       else if (nbnode == 5) {
257         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
258                                                  node[4], ID);
259         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
260                                                  node[4] );
261       }
262       else if (nbnode == 6) {
263         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
264                                                  node[4], node[5], ID);
265         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
266                                                  node[4], node[5] );
267       }
268       else if (nbnode == 8) {
269         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
270                                                  node[4], node[5], node[6], node[7], ID);
271         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
272                                                  node[4], node[5], node[6], node[7] );
273       }
274       else if (nbnode == 10) {
275         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
276                                                  node[4], node[5], node[6], node[7],
277                                                  node[8], node[9], ID);
278         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
279                                                  node[4], node[5], node[6], node[7],
280                                                  node[8], node[9] );
281       }
282       else if (nbnode == 12) {
283         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
284                                                  node[4], node[5], node[6], node[7],
285                                                  node[8], node[9], node[10], node[11], ID);
286         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
287                                                  node[4], node[5], node[6], node[7],
288                                                  node[8], node[9], node[10], node[11] );
289       }
290       else if (nbnode == 13) {
291         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
292                                                  node[4], node[5], node[6], node[7],
293                                                  node[8], node[9], node[10],node[11],
294                                                  node[12],ID);
295         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
296                                                  node[4], node[5], node[6], node[7],
297                                                  node[8], node[9], node[10],node[11],
298                                                  node[12] );
299       }
300       else if (nbnode == 15) {
301         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
302                                                  node[4], node[5], node[6], node[7],
303                                                  node[8], node[9], node[10],node[11],
304                                                  node[12],node[13],node[14],ID);
305         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
306                                                  node[4], node[5], node[6], node[7],
307                                                  node[8], node[9], node[10],node[11],
308                                                  node[12],node[13],node[14] );
309       }
310       else if (nbnode == 20) {
311         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
312                                                  node[4], node[5], node[6], node[7],
313                                                  node[8], node[9], node[10],node[11],
314                                                  node[12],node[13],node[14],node[15],
315                                                  node[16],node[17],node[18],node[19],ID);
316         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
317                                                  node[4], node[5], node[6], node[7],
318                                                  node[8], node[9], node[10],node[11],
319                                                  node[12],node[13],node[14],node[15],
320                                                  node[16],node[17],node[18],node[19] );
321       }
322       else if (nbnode == 27) {
323         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
324                                                  node[4], node[5], node[6], node[7],
325                                                  node[8], node[9], node[10],node[11],
326                                                  node[12],node[13],node[14],node[15],
327                                                  node[16],node[17],node[18],node[19],
328                                                  node[20],node[21],node[22],node[23],
329                                                  node[24],node[25],node[26], ID);
330         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
331                                                  node[4], node[5], node[6], node[7],
332                                                  node[8], node[9], node[10],node[11],
333                                                  node[12],node[13],node[14],node[15],
334                                                  node[16],node[17],node[18],node[19],
335                                                  node[20],node[21],node[22],node[23],
336                                                  node[24],node[25],node[26] );
337       }
338     }
339     else if ( !features.myIsQuad )
340     {
341       if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
342       else           e = mesh->AddPolyhedralVolume      (node, features.myPolyhedQuantities    );
343     }
344     else
345     {
346       // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
347       // else           e = mesh->AddQuadPolyhedralVolume      (node, features.myPolyhedQuantities   );
348     }
349     break;
350
351   case SMDSAbs_Edge:
352     if ( nbnode == 2 ) {
353       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
354       else           e = mesh->AddEdge      (node[0], node[1] );
355     }
356     else if ( nbnode == 3 ) {
357       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
358       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
359     }
360     break;
361
362   case SMDSAbs_0DElement:
363     if ( nbnode == 1 ) {
364       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
365       else           e = mesh->Add0DElement      (node[0] );
366     }
367     break;
368
369   case SMDSAbs_Node:
370     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
371     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z()    );
372     break;
373
374   case SMDSAbs_Ball:
375     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
376     else           e = mesh->AddBall      (node[0], features.myBallDiameter    );
377     break;
378
379   default:;
380   }
381   if ( e ) myLastCreatedElems.Append( e );
382   return e;
383 }
384
385 //=======================================================================
386 /*!
387  * \brief Add element
388  */
389 //=======================================================================
390
391 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
392                                                const ElemFeatures& features)
393 {
394   vector<const SMDS_MeshNode*> nodes;
395   nodes.reserve( nodeIDs.size() );
396   vector<int>::const_iterator id = nodeIDs.begin();
397   while ( id != nodeIDs.end() ) {
398     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
399       nodes.push_back( node );
400     else
401       return 0;
402   }
403   return AddElement( nodes, features );
404 }
405
406 //=======================================================================
407 //function : Remove
408 //purpose  : Remove a node or an element.
409 //           Modify a compute state of sub-meshes which become empty
410 //=======================================================================
411
412 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
413                               const bool         isNodes )
414 {
415   myLastCreatedElems.Clear();
416   myLastCreatedNodes.Clear();
417
418   SMESHDS_Mesh* aMesh = GetMeshDS();
419   set< SMESH_subMesh *> smmap;
420
421   int removed = 0;
422   list<int>::const_iterator it = theIDs.begin();
423   for ( ; it != theIDs.end(); it++ ) {
424     const SMDS_MeshElement * elem;
425     if ( isNodes )
426       elem = aMesh->FindNode( *it );
427     else
428       elem = aMesh->FindElement( *it );
429     if ( !elem )
430       continue;
431
432     // Notify VERTEX sub-meshes about modification
433     if ( isNodes ) {
434       const SMDS_MeshNode* node = cast2Node( elem );
435       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
436         if ( int aShapeID = node->getshapeId() )
437           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
438             smmap.insert( sm );
439     }
440     // Find sub-meshes to notify about modification
441     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
442     //     while ( nodeIt->more() ) {
443     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
444     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
445     //       if ( aPosition.get() ) {
446     //         if ( int aShapeID = aPosition->GetShapeId() ) {
447     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
448     //             smmap.insert( sm );
449     //         }
450     //       }
451     //     }
452
453     // Do remove
454     if ( isNodes )
455       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
456     else
457       aMesh->RemoveElement( elem );
458     removed++;
459   }
460
461   // Notify sub-meshes about modification
462   if ( !smmap.empty() ) {
463     set< SMESH_subMesh *>::iterator smIt;
464     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
465       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
466   }
467
468   //   // Check if the whole mesh becomes empty
469   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
470   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
471
472   return removed;
473 }
474
475 //================================================================================
476 /*!
477  * \brief Create 0D elements on all nodes of the given object.
478  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
479  *                    the all mesh is treated
480  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
481  *  \param duplicateElements - to add one more 0D element to a node or not
482  */
483 //================================================================================
484
485 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
486                                                    TIDSortedElemSet&       all0DElems,
487                                                    const bool              duplicateElements )
488 {
489   SMDS_ElemIteratorPtr elemIt;
490   if ( elements.empty() )
491   {
492     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
493   }
494   else
495   {
496     elemIt = elemSetIterator( elements );
497   }
498
499   while ( elemIt->more() )
500   {
501     const SMDS_MeshElement* e = elemIt->next();
502     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
503     while ( nodeIt->more() )
504     {
505       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
506       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
507       if ( duplicateElements || !it0D->more() )
508       {
509         myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
510         all0DElems.insert( myLastCreatedElems.Last() );
511       }
512       while ( it0D->more() )
513         all0DElems.insert( it0D->next() );
514     }
515   }
516 }
517
518 //=======================================================================
519 //function : FindShape
520 //purpose  : Return an index of the shape theElem is on
521 //           or zero if a shape not found
522 //=======================================================================
523
524 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
525 {
526   myLastCreatedElems.Clear();
527   myLastCreatedNodes.Clear();
528
529   SMESHDS_Mesh * aMesh = GetMeshDS();
530   if ( aMesh->ShapeToMesh().IsNull() )
531     return 0;
532
533   int aShapeID = theElem->getshapeId();
534   if ( aShapeID < 1 )
535     return 0;
536
537   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
538     if ( sm->Contains( theElem ))
539       return aShapeID;
540
541   if ( theElem->GetType() == SMDSAbs_Node ) {
542     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
543   }
544   else {
545     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
546   }
547
548   TopoDS_Shape aShape; // the shape a node of theElem is on
549   if ( theElem->GetType() != SMDSAbs_Node )
550   {
551     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
552     while ( nodeIt->more() ) {
553       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
554       if ((aShapeID = node->getshapeId()) > 0) {
555         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
556           if ( sm->Contains( theElem ))
557             return aShapeID;
558           if ( aShape.IsNull() )
559             aShape = aMesh->IndexToShape( aShapeID );
560         }
561       }
562     }
563   }
564
565   // None of nodes is on a proper shape,
566   // find the shape among ancestors of aShape on which a node is
567   if ( !aShape.IsNull() ) {
568     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
569     for ( ; ancIt.More(); ancIt.Next() ) {
570       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
571       if ( sm && sm->Contains( theElem ))
572         return aMesh->ShapeToIndex( ancIt.Value() );
573     }
574   }
575   else
576   {
577     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
578     while ( const SMESHDS_SubMesh* sm = smIt->next() )
579       if ( sm->Contains( theElem ))
580         return sm->GetID();
581   }
582
583   return 0;
584 }
585
586 //=======================================================================
587 //function : IsMedium
588 //purpose  :
589 //=======================================================================
590
591 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
592                                 const SMDSAbs_ElementType typeToCheck)
593 {
594   bool isMedium = false;
595   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
596   while (it->more() && !isMedium ) {
597     const SMDS_MeshElement* elem = it->next();
598     isMedium = elem->IsMediumNode(node);
599   }
600   return isMedium;
601 }
602
603 //=======================================================================
604 //function : shiftNodesQuadTria
605 //purpose  : Shift nodes in the array corresponded to quadratic triangle
606 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
607 //=======================================================================
608
609 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
610 {
611   const SMDS_MeshNode* nd1 = aNodes[0];
612   aNodes[0] = aNodes[1];
613   aNodes[1] = aNodes[2];
614   aNodes[2] = nd1;
615   const SMDS_MeshNode* nd2 = aNodes[3];
616   aNodes[3] = aNodes[4];
617   aNodes[4] = aNodes[5];
618   aNodes[5] = nd2;
619 }
620
621 //=======================================================================
622 //function : nbEdgeConnectivity
623 //purpose  : return number of the edges connected with the theNode.
624 //           if theEdges has connections with the other type of the
625 //           elements, return -1
626 //=======================================================================
627
628 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
629 {
630   // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
631   // int nb=0;
632   // while(elemIt->more()) {
633   //   elemIt->next();
634   //   nb++;
635   // }
636   // return nb;
637   return theNode->NbInverseElements();
638 }
639
640 //=======================================================================
641 //function : getNodesFromTwoTria
642 //purpose  : 
643 //=======================================================================
644
645 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
646                                 const SMDS_MeshElement * theTria2,
647                                 vector< const SMDS_MeshNode*>& N1,
648                                 vector< const SMDS_MeshNode*>& N2)
649 {
650   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
651   if ( N1.size() < 6 ) return false;
652   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
653   if ( N2.size() < 6 ) return false;
654
655   int sames[3] = {-1,-1,-1};
656   int nbsames = 0;
657   int i, j;
658   for(i=0; i<3; i++) {
659     for(j=0; j<3; j++) {
660       if(N1[i]==N2[j]) {
661         sames[i] = j;
662         nbsames++;
663         break;
664       }
665     }
666   }
667   if(nbsames!=2) return false;
668   if(sames[0]>-1) {
669     shiftNodesQuadTria(N1);
670     if(sames[1]>-1) {
671       shiftNodesQuadTria(N1);
672     }
673   }
674   i = sames[0] + sames[1] + sames[2];
675   for(; i<2; i++) {
676     shiftNodesQuadTria(N2);
677   }
678   // now we receive following N1 and N2 (using numeration as in the image below)
679   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
680   // i.e. first nodes from both arrays form a new diagonal
681   return true;
682 }
683
684 //=======================================================================
685 //function : InverseDiag
686 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
687 //           but having other common link.
688 //           Return False if args are improper
689 //=======================================================================
690
691 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
692                                     const SMDS_MeshElement * theTria2 )
693 {
694   myLastCreatedElems.Clear();
695   myLastCreatedNodes.Clear();
696
697   if (!theTria1 || !theTria2)
698     return false;
699
700   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
701   if (!F1) return false;
702   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
703   if (!F2) return false;
704   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
705       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
706
707     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
708     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
709     //    |/ |                                         | \|
710     //  B +--+ 2                                     B +--+ 2
711
712     // put nodes in array and find out indices of the same ones
713     const SMDS_MeshNode* aNodes [6];
714     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
715     int i = 0;
716     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
717     while ( it->more() ) {
718       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
719
720       if ( i > 2 ) // theTria2
721         // find same node of theTria1
722         for ( int j = 0; j < 3; j++ )
723           if ( aNodes[ i ] == aNodes[ j ]) {
724             sameInd[ j ] = i;
725             sameInd[ i ] = j;
726             break;
727           }
728       // next
729       i++;
730       if ( i == 3 ) {
731         if ( it->more() )
732           return false; // theTria1 is not a triangle
733         it = theTria2->nodesIterator();
734       }
735       if ( i == 6 && it->more() )
736         return false; // theTria2 is not a triangle
737     }
738
739     // find indices of 1,2 and of A,B in theTria1
740     int iA = -1, iB = 0, i1 = 0, i2 = 0;
741     for ( i = 0; i < 6; i++ ) {
742       if ( sameInd [ i ] == -1 ) {
743         if ( i < 3 ) i1 = i;
744         else         i2 = i;
745       }
746       else if (i < 3) {
747         if ( iA >= 0) iB = i;
748         else          iA = i;
749       }
750     }
751     // nodes 1 and 2 should not be the same
752     if ( aNodes[ i1 ] == aNodes[ i2 ] )
753       return false;
754
755     // theTria1: A->2
756     aNodes[ iA ] = aNodes[ i2 ];
757     // theTria2: B->1
758     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
759
760     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
761     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
762
763     return true;
764
765   } // end if(F1 && F2)
766
767   // check case of quadratic faces
768   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
769       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
770     return false;
771   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
772       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
773     return false;
774
775   //       5
776   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
777   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
778   //    |   / |
779   //  7 +  +  + 6
780   //    | /9  |
781   //    |/    |
782   //  4 +--+--+ 3
783   //       8
784
785   vector< const SMDS_MeshNode* > N1;
786   vector< const SMDS_MeshNode* > N2;
787   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
788     return false;
789   // now we receive following N1 and N2 (using numeration as above image)
790   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
791   // i.e. first nodes from both arrays determ new diagonal
792
793   vector< const SMDS_MeshNode*> N1new( N1.size() );
794   vector< const SMDS_MeshNode*> N2new( N2.size() );
795   N1new.back() = N1.back(); // central node of biquadratic
796   N2new.back() = N2.back();
797   N1new[0] = N1[0];  N2new[0] = N1[0];
798   N1new[1] = N2[0];  N2new[1] = N1[1];
799   N1new[2] = N2[1];  N2new[2] = N2[0];
800   N1new[3] = N1[4];  N2new[3] = N1[3];
801   N1new[4] = N2[3];  N2new[4] = N2[5];
802   N1new[5] = N1[5];  N2new[5] = N1[4];
803   // change nodes in faces
804   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
805   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
806
807   // move the central node of biquadratic triangle
808   SMESH_MesherHelper helper( *GetMesh() );
809   for ( int is2nd = 0; is2nd < 2; ++is2nd )
810   {
811     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
812     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
813     if ( nodes.size() < 7 )
814       continue;
815     helper.SetSubShape( tria->getshapeId() );
816     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
817     gp_Pnt xyz;
818     if ( F.IsNull() )
819     {
820       xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
821               SMESH_TNodeXYZ( nodes[4] ) +
822               SMESH_TNodeXYZ( nodes[5] )) / 3.;
823     }
824     else
825     {
826       bool checkUV;
827       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
828                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
829                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
830       TopLoc_Location loc;
831       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
832       xyz = S->Value( uv.X(), uv.Y() );
833       xyz.Transform( loc );
834       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
835            nodes[6]->getshapeId() > 0 )
836         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
837     }
838     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
839   }
840   return true;
841 }
842
843 //=======================================================================
844 //function : findTriangles
845 //purpose  : find triangles sharing theNode1-theNode2 link
846 //=======================================================================
847
848 static bool findTriangles(const SMDS_MeshNode *    theNode1,
849                           const SMDS_MeshNode *    theNode2,
850                           const SMDS_MeshElement*& theTria1,
851                           const SMDS_MeshElement*& theTria2)
852 {
853   if ( !theNode1 || !theNode2 ) return false;
854
855   theTria1 = theTria2 = 0;
856
857   set< const SMDS_MeshElement* > emap;
858   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
859   while (it->more()) {
860     const SMDS_MeshElement* elem = it->next();
861     if ( elem->NbCornerNodes() == 3 )
862       emap.insert( elem );
863   }
864   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
865   while (it->more()) {
866     const SMDS_MeshElement* elem = it->next();
867     if ( emap.count( elem )) {
868       if ( !theTria1 )
869       {
870         theTria1 = elem;
871       }
872       else  
873       {
874         theTria2 = elem;
875         // theTria1 must be element with minimum ID
876         if ( theTria2->GetID() < theTria1->GetID() )
877           std::swap( theTria2, theTria1 );
878         return true;
879       }
880     }
881   }
882   return false;
883 }
884
885 //=======================================================================
886 //function : InverseDiag
887 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
888 //           with ones built on the same 4 nodes but having other common link.
889 //           Return false if proper faces not found
890 //=======================================================================
891
892 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
893                                     const SMDS_MeshNode * theNode2)
894 {
895   myLastCreatedElems.Clear();
896   myLastCreatedNodes.Clear();
897
898   const SMDS_MeshElement *tr1, *tr2;
899   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
900     return false;
901
902   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
903   if (!F1) return false;
904   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
905   if (!F2) return false;
906   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
907       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
908
909     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
910     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
911     //    |/ |                                    | \|
912     //  B +--+ 2                                B +--+ 2
913
914     // put nodes in array
915     // and find indices of 1,2 and of A in tr1 and of B in tr2
916     int i, iA1 = 0, i1 = 0;
917     const SMDS_MeshNode* aNodes1 [3];
918     SMDS_ElemIteratorPtr it;
919     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
920       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
921       if ( aNodes1[ i ] == theNode1 )
922         iA1 = i; // node A in tr1
923       else if ( aNodes1[ i ] != theNode2 )
924         i1 = i;  // node 1
925     }
926     int iB2 = 0, i2 = 0;
927     const SMDS_MeshNode* aNodes2 [3];
928     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
929       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
930       if ( aNodes2[ i ] == theNode2 )
931         iB2 = i; // node B in tr2
932       else if ( aNodes2[ i ] != theNode1 )
933         i2 = i;  // node 2
934     }
935
936     // nodes 1 and 2 should not be the same
937     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
938       return false;
939
940     // tr1: A->2
941     aNodes1[ iA1 ] = aNodes2[ i2 ];
942     // tr2: B->1
943     aNodes2[ iB2 ] = aNodes1[ i1 ];
944
945     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
946     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
947
948     return true;
949   }
950
951   // check case of quadratic faces
952   return InverseDiag(tr1,tr2);
953 }
954
955 //=======================================================================
956 //function : getQuadrangleNodes
957 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
958 //           fusion of triangles tr1 and tr2 having shared link on
959 //           theNode1 and theNode2
960 //=======================================================================
961
962 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
963                         const SMDS_MeshNode *    theNode1,
964                         const SMDS_MeshNode *    theNode2,
965                         const SMDS_MeshElement * tr1,
966                         const SMDS_MeshElement * tr2 )
967 {
968   if( tr1->NbNodes() != tr2->NbNodes() )
969     return false;
970   // find the 4-th node to insert into tr1
971   const SMDS_MeshNode* n4 = 0;
972   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
973   int i=0;
974   while ( !n4 && i<3 ) {
975     const SMDS_MeshNode * n = cast2Node( it->next() );
976     i++;
977     bool isDiag = ( n == theNode1 || n == theNode2 );
978     if ( !isDiag )
979       n4 = n;
980   }
981   // Make an array of nodes to be in a quadrangle
982   int iNode = 0, iFirstDiag = -1;
983   it = tr1->nodesIterator();
984   i=0;
985   while ( i<3 ) {
986     const SMDS_MeshNode * n = cast2Node( it->next() );
987     i++;
988     bool isDiag = ( n == theNode1 || n == theNode2 );
989     if ( isDiag ) {
990       if ( iFirstDiag < 0 )
991         iFirstDiag = iNode;
992       else if ( iNode - iFirstDiag == 1 )
993         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
994     }
995     else if ( n == n4 ) {
996       return false; // tr1 and tr2 should not have all the same nodes
997     }
998     theQuadNodes[ iNode++ ] = n;
999   }
1000   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
1001     theQuadNodes[ iNode ] = n4;
1002
1003   return true;
1004 }
1005
1006 //=======================================================================
1007 //function : DeleteDiag
1008 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
1009 //           with a quadrangle built on the same 4 nodes.
1010 //           Return false if proper faces not found
1011 //=======================================================================
1012
1013 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1014                                    const SMDS_MeshNode * theNode2)
1015 {
1016   myLastCreatedElems.Clear();
1017   myLastCreatedNodes.Clear();
1018
1019   const SMDS_MeshElement *tr1, *tr2;
1020   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1021     return false;
1022
1023   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1024   if (!F1) return false;
1025   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1026   if (!F2) return false;
1027   SMESHDS_Mesh * aMesh = GetMeshDS();
1028
1029   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1030       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1031
1032     const SMDS_MeshNode* aNodes [ 4 ];
1033     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1034       return false;
1035
1036     const SMDS_MeshElement* newElem = 0;
1037     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1038     myLastCreatedElems.Append(newElem);
1039     AddToSameGroups( newElem, tr1, aMesh );
1040     int aShapeId = tr1->getshapeId();
1041     if ( aShapeId )
1042       {
1043         aMesh->SetMeshElementOnShape( newElem, aShapeId );
1044       }
1045     aMesh->RemoveElement( tr1 );
1046     aMesh->RemoveElement( tr2 );
1047
1048     return true;
1049   }
1050
1051   // check case of quadratic faces
1052   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1053     return false;
1054   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1055     return false;
1056
1057   //       5
1058   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1059   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1060   //    |   / |
1061   //  7 +  +  + 6
1062   //    | /9  |
1063   //    |/    |
1064   //  4 +--+--+ 3
1065   //       8
1066
1067   vector< const SMDS_MeshNode* > N1;
1068   vector< const SMDS_MeshNode* > N2;
1069   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1070     return false;
1071   // now we receive following N1 and N2 (using numeration as above image)
1072   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1073   // i.e. first nodes from both arrays determ new diagonal
1074
1075   const SMDS_MeshNode* aNodes[8];
1076   aNodes[0] = N1[0];
1077   aNodes[1] = N1[1];
1078   aNodes[2] = N2[0];
1079   aNodes[3] = N2[1];
1080   aNodes[4] = N1[3];
1081   aNodes[5] = N2[5];
1082   aNodes[6] = N2[3];
1083   aNodes[7] = N1[5];
1084
1085   const SMDS_MeshElement* newElem = 0;
1086   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1087                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1088   myLastCreatedElems.Append(newElem);
1089   AddToSameGroups( newElem, tr1, aMesh );
1090   int aShapeId = tr1->getshapeId();
1091   if ( aShapeId )
1092     {
1093       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1094     }
1095   aMesh->RemoveElement( tr1 );
1096   aMesh->RemoveElement( tr2 );
1097
1098   // remove middle node (9)
1099   GetMeshDS()->RemoveNode( N1[4] );
1100
1101   return true;
1102 }
1103
1104 //=======================================================================
1105 //function : Reorient
1106 //purpose  : Reverse theElement orientation
1107 //=======================================================================
1108
1109 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1110 {
1111   myLastCreatedElems.Clear();
1112   myLastCreatedNodes.Clear();
1113
1114   if (!theElem)
1115     return false;
1116   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1117   if ( !it || !it->more() )
1118     return false;
1119
1120   const SMDSAbs_ElementType type = theElem->GetType();
1121   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1122     return false;
1123
1124   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1125   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1126   {
1127     const SMDS_VtkVolume* aPolyedre =
1128       dynamic_cast<const SMDS_VtkVolume*>( theElem );
1129     if (!aPolyedre) {
1130       MESSAGE("Warning: bad volumic element");
1131       return false;
1132     }
1133     const int nbFaces = aPolyedre->NbFaces();
1134     vector<const SMDS_MeshNode *> poly_nodes;
1135     vector<int> quantities (nbFaces);
1136
1137     // reverse each face of the polyedre
1138     for (int iface = 1; iface <= nbFaces; iface++) {
1139       int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1140       quantities[iface - 1] = nbFaceNodes;
1141
1142       for (inode = nbFaceNodes; inode >= 1; inode--) {
1143         const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1144         poly_nodes.push_back(curNode);
1145       }
1146     }
1147     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1148   }
1149   else // other elements
1150   {
1151     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1152     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1153     if ( interlace.empty() )
1154     {
1155       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1156     }
1157     else
1158     {
1159       SMDS_MeshCell::applyInterlace( interlace, nodes );
1160     }
1161     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1162   }
1163   return false;
1164 }
1165
1166 //================================================================================
1167 /*!
1168  * \brief Reorient faces.
1169  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1170  * \param theDirection - desired direction of normal of \a theFace
1171  * \param theFace - one of \a theFaces that should be oriented according to
1172  *        \a theDirection and whose orientation defines orientation of other faces
1173  * \return number of reoriented faces.
1174  */
1175 //================================================================================
1176
1177 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1178                                   const gp_Dir&            theDirection,
1179                                   const SMDS_MeshElement * theFace)
1180 {
1181   int nbReori = 0;
1182   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1183
1184   if ( theFaces.empty() )
1185   {
1186     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1187     while ( fIt->more() )
1188       theFaces.insert( theFaces.end(), fIt->next() );
1189   }
1190
1191   // orient theFace according to theDirection
1192   gp_XYZ normal;
1193   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1194   if ( normal * theDirection.XYZ() < 0 )
1195     nbReori += Reorient( theFace );
1196
1197   // Orient other faces
1198
1199   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1200   TIDSortedElemSet avoidSet;
1201   set< SMESH_TLink > checkedLinks;
1202   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1203
1204   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1205     theFaces.erase( theFace );
1206   startFaces.insert( theFace );
1207
1208   int nodeInd1, nodeInd2;
1209   const SMDS_MeshElement*           otherFace;
1210   vector< const SMDS_MeshElement* > facesNearLink;
1211   vector< std::pair< int, int > >   nodeIndsOfFace;
1212
1213   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1214   while ( !startFaces.empty() )
1215   {
1216     startFace = startFaces.begin();
1217     theFace = *startFace;
1218     startFaces.erase( startFace );
1219     if ( !visitedFaces.insert( theFace ).second )
1220       continue;
1221
1222     avoidSet.clear();
1223     avoidSet.insert(theFace);
1224
1225     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1226
1227     const int nbNodes = theFace->NbCornerNodes();
1228     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1229     {
1230       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1231       linkIt_isNew = checkedLinks.insert( link );
1232       if ( !linkIt_isNew.second )
1233       {
1234         // link has already been checked and won't be encountered more
1235         // if the group (theFaces) is manifold
1236         //checkedLinks.erase( linkIt_isNew.first );
1237       }
1238       else
1239       {
1240         facesNearLink.clear();
1241         nodeIndsOfFace.clear();
1242         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1243                                                              theFaces, avoidSet,
1244                                                              &nodeInd1, &nodeInd2 )))
1245           if ( otherFace != theFace)
1246           {
1247             facesNearLink.push_back( otherFace );
1248             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1249             avoidSet.insert( otherFace );
1250           }
1251         if ( facesNearLink.size() > 1 )
1252         {
1253           // NON-MANIFOLD mesh shell !
1254           // select a face most co-directed with theFace,
1255           // other faces won't be visited this time
1256           gp_XYZ NF, NOF;
1257           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1258           double proj, maxProj = -1;
1259           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1260             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1261             if (( proj = Abs( NF * NOF )) > maxProj ) {
1262               maxProj = proj;
1263               otherFace = facesNearLink[i];
1264               nodeInd1  = nodeIndsOfFace[i].first;
1265               nodeInd2  = nodeIndsOfFace[i].second;
1266             }
1267           }
1268           // not to visit rejected faces
1269           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1270             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1271               visitedFaces.insert( facesNearLink[i] );
1272         }
1273         else if ( facesNearLink.size() == 1 )
1274         {
1275           otherFace = facesNearLink[0];
1276           nodeInd1  = nodeIndsOfFace.back().first;
1277           nodeInd2  = nodeIndsOfFace.back().second;
1278         }
1279         if ( otherFace && otherFace != theFace)
1280         {
1281           // link must be reverse in otherFace if orientation ot otherFace
1282           // is same as that of theFace
1283           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1284           {
1285             nbReori += Reorient( otherFace );
1286           }
1287           startFaces.insert( otherFace );
1288         }
1289       }
1290       std::swap( link.first, link.second ); // reverse the link
1291     }
1292   }
1293   return nbReori;
1294 }
1295
1296 //================================================================================
1297 /*!
1298  * \brief Reorient faces basing on orientation of adjacent volumes.
1299  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1300  * \param theVolumes - reference volumes.
1301  * \param theOutsideNormal - to orient faces to have their normal
1302  *        pointing either \a outside or \a inside the adjacent volumes.
1303  * \return number of reoriented faces.
1304  */
1305 //================================================================================
1306
1307 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1308                                       TIDSortedElemSet & theVolumes,
1309                                       const bool         theOutsideNormal)
1310 {
1311   int nbReori = 0;
1312
1313   SMDS_ElemIteratorPtr faceIt;
1314   if ( theFaces.empty() )
1315     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1316   else
1317     faceIt = elemSetIterator( theFaces );
1318
1319   vector< const SMDS_MeshNode* > faceNodes;
1320   TIDSortedElemSet checkedVolumes;
1321   set< const SMDS_MeshNode* > faceNodesSet;
1322   SMDS_VolumeTool volumeTool;
1323
1324   while ( faceIt->more() ) // loop on given faces
1325   {
1326     const SMDS_MeshElement* face = faceIt->next();
1327     if ( face->GetType() != SMDSAbs_Face )
1328       continue;
1329
1330     const size_t nbCornersNodes = face->NbCornerNodes();
1331     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1332
1333     checkedVolumes.clear();
1334     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1335     while ( vIt->more() )
1336     {
1337       const SMDS_MeshElement* volume = vIt->next();
1338
1339       if ( !checkedVolumes.insert( volume ).second )
1340         continue;
1341       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1342         continue;
1343
1344       // is volume adjacent?
1345       bool allNodesCommon = true;
1346       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1347         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1348       if ( !allNodesCommon )
1349         continue;
1350
1351       // get nodes of a corresponding volume facet
1352       faceNodesSet.clear();
1353       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1354       volumeTool.Set( volume );
1355       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1356       if ( facetID < 0 ) continue;
1357       volumeTool.SetExternalNormal();
1358       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1359
1360       // compare order of faceNodes and facetNodes
1361       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1362       int iNN[2];
1363       for ( int i = 0; i < 2; ++i )
1364       {
1365         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1366         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1367           if ( faceNodes[ iN ] == n )
1368           {
1369             iNN[ i ] = iN;
1370             break;
1371           }
1372       }
1373       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1374       if ( isOutside != theOutsideNormal )
1375         nbReori += Reorient( face );
1376     }
1377   }  // loop on given faces
1378
1379   return nbReori;
1380 }
1381
1382 //=======================================================================
1383 //function : getBadRate
1384 //purpose  :
1385 //=======================================================================
1386
1387 static double getBadRate (const SMDS_MeshElement*               theElem,
1388                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1389 {
1390   SMESH::Controls::TSequenceOfXYZ P;
1391   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1392     return 1e100;
1393   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1394   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1395 }
1396
1397 //=======================================================================
1398 //function : QuadToTri
1399 //purpose  : Cut quadrangles into triangles.
1400 //           theCrit is used to select a diagonal to cut
1401 //=======================================================================
1402
1403 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1404                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1405 {
1406   myLastCreatedElems.Clear();
1407   myLastCreatedNodes.Clear();
1408
1409   if ( !theCrit.get() )
1410     return false;
1411
1412   SMESHDS_Mesh * aMesh = GetMeshDS();
1413
1414   Handle(Geom_Surface) surface;
1415   SMESH_MesherHelper   helper( *GetMesh() );
1416
1417   TIDSortedElemSet::iterator itElem;
1418   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1419   {
1420     const SMDS_MeshElement* elem = *itElem;
1421     if ( !elem || elem->GetType() != SMDSAbs_Face )
1422       continue;
1423     if ( elem->NbCornerNodes() != 4 )
1424       continue;
1425
1426     // retrieve element nodes
1427     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1428
1429     // compare two sets of possible triangles
1430     double aBadRate1, aBadRate2; // to what extent a set is bad
1431     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1432     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1433     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1434
1435     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1436     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1437     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1438
1439     const int aShapeId = FindShape( elem );
1440     const SMDS_MeshElement* newElem1 = 0;
1441     const SMDS_MeshElement* newElem2 = 0;
1442
1443     if ( !elem->IsQuadratic() ) // split liner quadrangle
1444     {
1445       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1446       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1447       if ( aBadRate1 <= aBadRate2 ) {
1448         // tr1 + tr2 is better
1449         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1450         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1451       }
1452       else {
1453         // tr3 + tr4 is better
1454         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1455         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1456       }
1457     }
1458     else // split quadratic quadrangle
1459     {
1460       helper.SetIsQuadratic( true );
1461       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1462
1463       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1464       if ( aNodes.size() == 9 )
1465       {
1466         helper.SetIsBiQuadratic( true );
1467         if ( aBadRate1 <= aBadRate2 )
1468           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1469         else
1470           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1471       }
1472       // create a new element
1473       if ( aBadRate1 <= aBadRate2 ) {
1474         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1475         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1476       }
1477       else {
1478         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1479         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1480       }
1481     } // quadratic case
1482
1483     // care of a new element
1484
1485     myLastCreatedElems.Append(newElem1);
1486     myLastCreatedElems.Append(newElem2);
1487     AddToSameGroups( newElem1, elem, aMesh );
1488     AddToSameGroups( newElem2, elem, aMesh );
1489
1490     // put a new triangle on the same shape
1491     if ( aShapeId )
1492       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1493     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1494
1495     aMesh->RemoveElement( elem );
1496   }
1497   return true;
1498 }
1499
1500 //=======================================================================
1501 /*!
1502  * \brief Split each of given quadrangles into 4 triangles.
1503  * \param theElems - The faces to be splitted. If empty all faces are split.
1504  */
1505 //=======================================================================
1506
1507 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1508 {
1509   myLastCreatedElems.Clear();
1510   myLastCreatedNodes.Clear();
1511
1512   SMESH_MesherHelper helper( *GetMesh() );
1513   helper.SetElementsOnShape( true );
1514
1515   SMDS_ElemIteratorPtr faceIt;
1516   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1517   else                    faceIt = elemSetIterator( theElems );
1518
1519   bool   checkUV;
1520   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1521   gp_XYZ xyz[9];
1522   vector< const SMDS_MeshNode* > nodes;
1523   SMESHDS_SubMesh*               subMeshDS = 0;
1524   TopoDS_Face                    F;
1525   Handle(Geom_Surface)           surface;
1526   TopLoc_Location                loc;
1527
1528   while ( faceIt->more() )
1529   {
1530     const SMDS_MeshElement* quad = faceIt->next();
1531     if ( !quad || quad->NbCornerNodes() != 4 )
1532       continue;
1533
1534     // get a surface the quad is on
1535
1536     if ( quad->getshapeId() < 1 )
1537     {
1538       F.Nullify();
1539       helper.SetSubShape( 0 );
1540       subMeshDS = 0;
1541     }
1542     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1543     {
1544       helper.SetSubShape( quad->getshapeId() );
1545       if ( !helper.GetSubShape().IsNull() &&
1546            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1547       {
1548         F = TopoDS::Face( helper.GetSubShape() );
1549         surface = BRep_Tool::Surface( F, loc );
1550         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1551       }
1552       else
1553       {
1554         helper.SetSubShape( 0 );
1555         subMeshDS = 0;
1556       }
1557     }
1558
1559     // create a central node
1560
1561     const SMDS_MeshNode* nCentral;
1562     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1563
1564     if ( nodes.size() == 9 )
1565     {
1566       nCentral = nodes.back();
1567     }
1568     else
1569     {
1570       size_t iN = 0;
1571       if ( F.IsNull() )
1572       {
1573         for ( ; iN < nodes.size(); ++iN )
1574           xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1575
1576         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1577           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1578
1579         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1580                                    xyz[0], xyz[1], xyz[2], xyz[3],
1581                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1582       }
1583       else
1584       {
1585         for ( ; iN < nodes.size(); ++iN )
1586           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1587
1588         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1589           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1590
1591         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1592                                   uv[0], uv[1], uv[2], uv[3],
1593                                   uv[4], uv[5], uv[6], uv[7] );
1594
1595         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1596         xyz[ 8 ] = p.XYZ();
1597       }
1598
1599       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1600                                  uv[8].X(), uv[8].Y() );
1601       myLastCreatedNodes.Append( nCentral );
1602     }
1603
1604     // create 4 triangles
1605
1606     helper.SetIsQuadratic  ( nodes.size() > 4 );
1607     helper.SetIsBiQuadratic( nodes.size() == 9 );
1608     if ( helper.GetIsQuadratic() )
1609       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1610
1611     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1612
1613     for ( int i = 0; i < 4; ++i )
1614     {
1615       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1616                                                nodes[(i+1)%4],
1617                                                nCentral );
1618       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1619       myLastCreatedElems.Append( tria );
1620     }
1621   }
1622 }
1623
1624 //=======================================================================
1625 //function : BestSplit
1626 //purpose  : Find better diagonal for cutting.
1627 //=======================================================================
1628
1629 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1630                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1631 {
1632   myLastCreatedElems.Clear();
1633   myLastCreatedNodes.Clear();
1634
1635   if (!theCrit.get())
1636     return -1;
1637
1638   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1639     return -1;
1640
1641   if( theQuad->NbNodes()==4 ||
1642       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1643
1644     // retrieve element nodes
1645     const SMDS_MeshNode* aNodes [4];
1646     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1647     int i = 0;
1648     //while (itN->more())
1649     while (i<4) {
1650       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1651     }
1652     // compare two sets of possible triangles
1653     double aBadRate1, aBadRate2; // to what extent a set is bad
1654     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1655     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1656     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1657
1658     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1659     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1660     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1661     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1662     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1663     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1664       return 1; // diagonal 1-3
1665
1666     return 2; // diagonal 2-4
1667   }
1668   return -1;
1669 }
1670
1671 namespace
1672 {
1673   // Methods of splitting volumes into tetra
1674
1675   const int theHexTo5_1[5*4+1] =
1676     {
1677       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1678     };
1679   const int theHexTo5_2[5*4+1] =
1680     {
1681       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1682     };
1683   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1684
1685   const int theHexTo6_1[6*4+1] =
1686     {
1687       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
1688     };
1689   const int theHexTo6_2[6*4+1] =
1690     {
1691       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
1692     };
1693   const int theHexTo6_3[6*4+1] =
1694     {
1695       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
1696     };
1697   const int theHexTo6_4[6*4+1] =
1698     {
1699       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
1700     };
1701   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1702
1703   const int thePyraTo2_1[2*4+1] =
1704     {
1705       0, 1, 2, 4,    0, 2, 3, 4,   -1
1706     };
1707   const int thePyraTo2_2[2*4+1] =
1708     {
1709       1, 2, 3, 4,    1, 3, 0, 4,   -1
1710     };
1711   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1712
1713   const int thePentaTo3_1[3*4+1] =
1714     {
1715       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1716     };
1717   const int thePentaTo3_2[3*4+1] =
1718     {
1719       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1720     };
1721   const int thePentaTo3_3[3*4+1] =
1722     {
1723       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1724     };
1725   const int thePentaTo3_4[3*4+1] =
1726     {
1727       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1728     };
1729   const int thePentaTo3_5[3*4+1] =
1730     {
1731       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1732     };
1733   const int thePentaTo3_6[3*4+1] =
1734     {
1735       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1736     };
1737   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1738                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1739
1740   // Methods of splitting hexahedron into prisms
1741
1742   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1743     {
1744       0, 1, 8, 4, 5, 9,    1, 2, 8, 5, 6, 9,    2, 3, 8, 6, 7, 9,   3, 0, 8, 7, 4, 9,    -1
1745     };
1746   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1747     {
1748       1, 0, 8, 2, 3, 9,    0, 4, 8, 3, 7, 9,    4, 5, 8, 7, 6, 9,   5, 1, 8, 6, 2, 9,    -1
1749     };
1750   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1751     {
1752       0, 3, 9, 1, 2, 8,    3, 7, 9, 2, 6, 8,    7, 4, 9, 6, 5, 8,   4, 0, 9, 5, 1, 8,    -1
1753     };
1754
1755   const int theHexTo2Prisms_BT_1[6*2+1] =
1756     {
1757       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1758     };
1759   const int theHexTo2Prisms_BT_2[6*2+1] =
1760     {
1761       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1762     };
1763   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1764
1765   const int theHexTo2Prisms_LR_1[6*2+1] =
1766     {
1767       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1768     };
1769   const int theHexTo2Prisms_LR_2[6*2+1] =
1770     {
1771       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1772     };
1773   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1774
1775   const int theHexTo2Prisms_FB_1[6*2+1] =
1776     {
1777       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1778     };
1779   const int theHexTo2Prisms_FB_2[6*2+1] =
1780     {
1781       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1782     };
1783   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1784
1785
1786   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1787   {
1788     int _n1, _n2, _n3;
1789     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1790     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1791     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1792                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1793   };
1794   struct TSplitMethod
1795   {
1796     int        _nbSplits;
1797     int        _nbCorners;
1798     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1799     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1800     bool       _ownConn;      //!< to delete _connectivity in destructor
1801     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1802
1803     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1804       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1805     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1806     bool hasFacet( const TTriangleFacet& facet ) const
1807     {
1808       if ( _nbCorners == 4 )
1809       {
1810         const int* tetConn = _connectivity;
1811         for ( ; tetConn[0] >= 0; tetConn += 4 )
1812           if (( facet.contains( tetConn[0] ) +
1813                 facet.contains( tetConn[1] ) +
1814                 facet.contains( tetConn[2] ) +
1815                 facet.contains( tetConn[3] )) == 3 )
1816             return true;
1817       }
1818       else // prism, _nbCorners == 6
1819       {
1820         const int* prismConn = _connectivity;
1821         for ( ; prismConn[0] >= 0; prismConn += 6 )
1822         {
1823           if (( facet.contains( prismConn[0] ) &&
1824                 facet.contains( prismConn[1] ) &&
1825                 facet.contains( prismConn[2] ))
1826               ||
1827               ( facet.contains( prismConn[3] ) &&
1828                 facet.contains( prismConn[4] ) &&
1829                 facet.contains( prismConn[5] )))
1830             return true;
1831         }
1832       }
1833       return false;
1834     }
1835   };
1836
1837   //=======================================================================
1838   /*!
1839    * \brief return TSplitMethod for the given element to split into tetrahedra
1840    */
1841   //=======================================================================
1842
1843   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1844   {
1845     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1846
1847     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1848     // an edge and a face barycenter; tertaherdons are based on triangles and
1849     // a volume barycenter
1850     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1851
1852     // Find out how adjacent volumes are split
1853
1854     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1855     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1856     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1857     {
1858       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1859       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1860       if ( nbNodes < 4 ) continue;
1861
1862       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1863       const int* nInd = vol.GetFaceNodesIndices( iF );
1864       if ( nbNodes == 4 )
1865       {
1866         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1867         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1868         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1869         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1870       }
1871       else
1872       {
1873         int iCom = 0; // common node of triangle faces to split into
1874         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1875         {
1876           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1877                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1878                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1879           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1880                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1881                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1882           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1883           {
1884             triaSplits.push_back( t012 );
1885             triaSplits.push_back( t023 );
1886             break;
1887           }
1888         }
1889       }
1890       if ( !triaSplits.empty() )
1891         hasAdjacentSplits = true;
1892     }
1893
1894     // Among variants of split method select one compliant with adjacent volumes
1895
1896     TSplitMethod method;
1897     if ( !vol.Element()->IsPoly() && !is24TetMode )
1898     {
1899       int nbVariants = 2, nbTet = 0;
1900       const int** connVariants = 0;
1901       switch ( vol.Element()->GetEntityType() )
1902       {
1903       case SMDSEntity_Hexa:
1904       case SMDSEntity_Quad_Hexa:
1905       case SMDSEntity_TriQuad_Hexa:
1906         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1907           connVariants = theHexTo5, nbTet = 5;
1908         else
1909           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1910         break;
1911       case SMDSEntity_Pyramid:
1912       case SMDSEntity_Quad_Pyramid:
1913         connVariants = thePyraTo2;  nbTet = 2;
1914         break;
1915       case SMDSEntity_Penta:
1916       case SMDSEntity_Quad_Penta:
1917         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1918         break;
1919       default:
1920         nbVariants = 0;
1921       }
1922       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1923       {
1924         // check method compliancy with adjacent tetras,
1925         // all found splits must be among facets of tetras described by this method
1926         method = TSplitMethod( nbTet, connVariants[variant] );
1927         if ( hasAdjacentSplits && method._nbSplits > 0 )
1928         {
1929           bool facetCreated = true;
1930           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1931           {
1932             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1933             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1934               facetCreated = method.hasFacet( *facet );
1935           }
1936           if ( !facetCreated )
1937             method = TSplitMethod(0); // incompatible method
1938         }
1939       }
1940     }
1941     if ( method._nbSplits < 1 )
1942     {
1943       // No standard method is applicable, use a generic solution:
1944       // each facet of a volume is split into triangles and
1945       // each of triangles and a volume barycenter form a tetrahedron.
1946
1947       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1948
1949       int* connectivity = new int[ maxTetConnSize + 1 ];
1950       method._connectivity = connectivity;
1951       method._ownConn = true;
1952       method._baryNode = !isHex27; // to create central node or not
1953
1954       int connSize = 0;
1955       int baryCenInd = vol.NbNodes() - int( isHex27 );
1956       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1957       {
1958         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1959         const int*   nInd = vol.GetFaceNodesIndices( iF );
1960         // find common node of triangle facets of tetra to create
1961         int iCommon = 0; // index in linear numeration
1962         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1963         if ( !triaSplits.empty() )
1964         {
1965           // by found facets
1966           const TTriangleFacet* facet = &triaSplits.front();
1967           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1968             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1969                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1970               break;
1971         }
1972         else if ( nbNodes > 3 && !is24TetMode )
1973         {
1974           // find the best method of splitting into triangles by aspect ratio
1975           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1976           map< double, int > badness2iCommon;
1977           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1978           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1979           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1980           {
1981             double badness = 0;
1982             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1983             {
1984               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1985                                       nodes[ iQ*((iLast-1)%nbNodes)],
1986                                       nodes[ iQ*((iLast  )%nbNodes)]);
1987               badness += getBadRate( &tria, aspectRatio );
1988             }
1989             badness2iCommon.insert( make_pair( badness, iCommon ));
1990           }
1991           // use iCommon with lowest badness
1992           iCommon = badness2iCommon.begin()->second;
1993         }
1994         if ( iCommon >= nbNodes )
1995           iCommon = 0; // something wrong
1996
1997         // fill connectivity of tetrahedra based on a current face
1998         int nbTet = nbNodes - 2;
1999         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2000         {
2001           int faceBaryCenInd;
2002           if ( isHex27 )
2003           {
2004             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2005             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2006           }
2007           else
2008           {
2009             method._faceBaryNode[ iF ] = 0;
2010             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2011           }
2012           nbTet = nbNodes;
2013           for ( int i = 0; i < nbTet; ++i )
2014           {
2015             int i1 = i, i2 = (i+1) % nbNodes;
2016             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2017             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2018             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2019             connectivity[ connSize++ ] = faceBaryCenInd;
2020             connectivity[ connSize++ ] = baryCenInd;
2021           }
2022         }
2023         else
2024         {
2025           for ( int i = 0; i < nbTet; ++i )
2026           {
2027             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2028             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2029             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2030             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2031             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2032             connectivity[ connSize++ ] = baryCenInd;
2033           }
2034         }
2035         method._nbSplits += nbTet;
2036
2037       } // loop on volume faces
2038
2039       connectivity[ connSize++ ] = -1;
2040
2041     } // end of generic solution
2042
2043     return method;
2044   }
2045   //=======================================================================
2046   /*!
2047    * \brief return TSplitMethod to split haxhedron into prisms
2048    */
2049   //=======================================================================
2050
2051   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2052                                     const int        methodFlags,
2053                                     const int        facetToSplit)
2054   {
2055     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2056     // B, T, L, B, R, F
2057     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2058
2059     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2060     {
2061       static TSplitMethod to4methods[4]; // order BT, LR, FB
2062       if ( to4methods[iF]._nbSplits == 0 )
2063       {
2064         switch ( iF ) {
2065         case 0:
2066           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2067           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2068           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2069           break;
2070         case 1:
2071           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2072           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2073           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2074           break;
2075         case 2:
2076           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2077           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2078           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2079           break;
2080         default: return to4methods[3];
2081         }
2082         to4methods[iF]._nbSplits  = 4;
2083         to4methods[iF]._nbCorners = 6;
2084       }
2085       return to4methods[iF];
2086     }
2087     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2088
2089     TSplitMethod method;
2090
2091     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2092
2093     const int nbVariants = 2, nbSplits = 2;
2094     const int** connVariants = 0;
2095     switch ( iF ) {
2096     case 0: connVariants = theHexTo2Prisms_BT; break;
2097     case 1: connVariants = theHexTo2Prisms_LR; break;
2098     case 2: connVariants = theHexTo2Prisms_FB; break;
2099     default: return method;
2100     }
2101
2102     // look for prisms adjacent via facetToSplit and an opposite one
2103     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2104     {
2105       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2106       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2107       if ( nbNodes != 4 ) return method;
2108
2109       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2110       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2111       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2112       TTriangleFacet* t;
2113       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2114         t = &t012;
2115       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2116         t = &t123;
2117       else
2118         continue;
2119
2120       // there are adjacent prism
2121       for ( int variant = 0; variant < nbVariants; ++variant )
2122       {
2123         // check method compliancy with adjacent prisms,
2124         // the found prism facets must be among facets of prisms described by current method
2125         method._nbSplits     = nbSplits;
2126         method._nbCorners    = 6;
2127         method._connectivity = connVariants[ variant ];
2128         if ( method.hasFacet( *t ))
2129           return method;
2130       }
2131     }
2132
2133     // No adjacent prisms. Select a variant with a best aspect ratio.
2134
2135     double badness[2] = { 0., 0. };
2136     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2137     const SMDS_MeshNode** nodes = vol.GetNodes();
2138     for ( int variant = 0; variant < nbVariants; ++variant )
2139       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2140       {
2141         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2142         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2143
2144         method._connectivity = connVariants[ variant ];
2145         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2146         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2147         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2148
2149         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2150                                 nodes[ t->_n2 ],
2151                                 nodes[ t->_n3 ] );
2152         badness[ variant ] += getBadRate( &tria, aspectRatio );
2153       }
2154     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2155
2156     method._nbSplits     = nbSplits;
2157     method._nbCorners    = 6;
2158     method._connectivity = connVariants[ iBetter ];
2159
2160     return method;
2161   }
2162
2163   //================================================================================
2164   /*!
2165    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2166    */
2167   //================================================================================
2168
2169   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2170                                        const SMDSAbs_GeometryType geom ) const
2171   {
2172     // find the tetrahedron including the three nodes of facet
2173     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2174     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2175     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2176     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2177     while ( volIt1->more() )
2178     {
2179       const SMDS_MeshElement* v = volIt1->next();
2180       if ( v->GetGeomType() != geom )
2181         continue;
2182       const int lastCornerInd = v->NbCornerNodes() - 1;
2183       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2184         continue; // medium node not allowed
2185       const int ind2 = v->GetNodeIndex( n2 );
2186       if ( ind2 < 0 || lastCornerInd < ind2 )
2187         continue;
2188       const int ind3 = v->GetNodeIndex( n3 );
2189       if ( ind3 < 0 || lastCornerInd < ind3 )
2190         continue;
2191       return true;
2192     }
2193     return false;
2194   }
2195
2196   //=======================================================================
2197   /*!
2198    * \brief A key of a face of volume
2199    */
2200   //=======================================================================
2201
2202   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2203   {
2204     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2205     {
2206       TIDSortedNodeSet sortedNodes;
2207       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2208       int nbNodes = vol.NbFaceNodes( iF );
2209       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2210       for ( int i = 0; i < nbNodes; i += iQ )
2211         sortedNodes.insert( fNodes[i] );
2212       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2213       first.first   = (*(n++))->GetID();
2214       first.second  = (*(n++))->GetID();
2215       second.first  = (*(n++))->GetID();
2216       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2217     }
2218   };
2219 } // namespace
2220
2221 //=======================================================================
2222 //function : SplitVolumes
2223 //purpose  : Split volume elements into tetrahedra or prisms.
2224 //           If facet ID < 0, element is split into tetrahedra,
2225 //           else a hexahedron is split into prisms so that the given facet is
2226 //           split into triangles
2227 //=======================================================================
2228
2229 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2230                                      const int            theMethodFlags)
2231 {
2232   SMDS_VolumeTool    volTool;
2233   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2234   fHelper.ToFixNodeParameters( true );
2235
2236   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2237   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2238
2239   SMESH_SequenceOfElemPtr newNodes, newElems;
2240
2241   // map face of volume to it's baricenrtic node
2242   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2243   double bc[3];
2244   vector<const SMDS_MeshElement* > splitVols;
2245
2246   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2247   for ( ; elem2facet != theElems.end(); ++elem2facet )
2248   {
2249     const SMDS_MeshElement* elem = elem2facet->first;
2250     const int       facetToSplit = elem2facet->second;
2251     if ( elem->GetType() != SMDSAbs_Volume )
2252       continue;
2253     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2254     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2255       continue;
2256
2257     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2258
2259     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2260                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2261                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2262     if ( splitMethod._nbSplits < 1 ) continue;
2263
2264     // find submesh to add new tetras to
2265     if ( !subMesh || !subMesh->Contains( elem ))
2266     {
2267       int shapeID = FindShape( elem );
2268       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2269       subMesh = GetMeshDS()->MeshElements( shapeID );
2270     }
2271     int iQ;
2272     if ( elem->IsQuadratic() )
2273     {
2274       iQ = 2;
2275       // add quadratic links to the helper
2276       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2277       {
2278         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2279         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2280         for ( int iN = 0; iN < nbN; iN += iQ )
2281           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2282       }
2283       helper.SetIsQuadratic( true );
2284     }
2285     else
2286     {
2287       iQ = 1;
2288       helper.SetIsQuadratic( false );
2289     }
2290     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2291                                         volTool.GetNodes() + elem->NbNodes() );
2292     helper.SetElementsOnShape( true );
2293     if ( splitMethod._baryNode )
2294     {
2295       // make a node at barycenter
2296       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2297       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2298       nodes.push_back( gcNode );
2299       newNodes.Append( gcNode );
2300     }
2301     if ( !splitMethod._faceBaryNode.empty() )
2302     {
2303       // make or find baricentric nodes of faces
2304       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2305       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2306       {
2307         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2308           volFace2BaryNode.insert
2309           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2310         if ( !f_n->second )
2311         {
2312           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2313           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2314         }
2315         nodes.push_back( iF_n->second = f_n->second );
2316       }
2317     }
2318
2319     // make new volumes
2320     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2321     const int* volConn = splitMethod._connectivity;
2322     if ( splitMethod._nbCorners == 4 ) // tetra
2323       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2324         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2325                                                             nodes[ volConn[1] ],
2326                                                             nodes[ volConn[2] ],
2327                                                             nodes[ volConn[3] ]));
2328     else // prisms
2329       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2330         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2331                                                             nodes[ volConn[1] ],
2332                                                             nodes[ volConn[2] ],
2333                                                             nodes[ volConn[3] ],
2334                                                             nodes[ volConn[4] ],
2335                                                             nodes[ volConn[5] ]));
2336
2337     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2338
2339     // Split faces on sides of the split volume
2340
2341     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2342     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2343     {
2344       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2345       if ( nbNodes < 4 ) continue;
2346
2347       // find an existing face
2348       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2349                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2350       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2351                                                                        /*noMedium=*/false))
2352       {
2353         // make triangles
2354         helper.SetElementsOnShape( false );
2355         vector< const SMDS_MeshElement* > triangles;
2356
2357         // find submesh to add new triangles in
2358         if ( !fSubMesh || !fSubMesh->Contains( face ))
2359         {
2360           int shapeID = FindShape( face );
2361           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2362         }
2363         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2364         if ( iF_n != splitMethod._faceBaryNode.end() )
2365         {
2366           const SMDS_MeshNode *baryNode = iF_n->second;
2367           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2368           {
2369             const SMDS_MeshNode* n1 = fNodes[iN];
2370             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2371             const SMDS_MeshNode *n3 = baryNode;
2372             if ( !volTool.IsFaceExternal( iF ))
2373               swap( n2, n3 );
2374             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2375           }
2376           if ( fSubMesh ) // update position of the bary node on geometry
2377           {
2378             if ( subMesh )
2379               subMesh->RemoveNode( baryNode, false );
2380             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2381             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2382             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2383             {
2384               fHelper.SetSubShape( s );
2385               gp_XY uv( 1e100, 1e100 );
2386               double distXYZ[4];
2387               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2388                                         uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2389                    uv.X() < 1e100 )
2390               {
2391                 // node is too far from the surface
2392                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2393                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2394                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2395               }
2396             }
2397           }
2398         }
2399         else
2400         {
2401           // among possible triangles create ones described by split method
2402           const int* nInd = volTool.GetFaceNodesIndices( iF );
2403           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2404           int iCom = 0; // common node of triangle faces to split into
2405           list< TTriangleFacet > facets;
2406           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2407           {
2408             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2409                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2410                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2411             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2412                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2413                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2414             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2415             {
2416               facets.push_back( t012 );
2417               facets.push_back( t023 );
2418               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2419                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2420                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2421                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2422               break;
2423             }
2424           }
2425           list< TTriangleFacet >::iterator facet = facets.begin();
2426           if ( facet == facets.end() )
2427             break;
2428           for ( ; facet != facets.end(); ++facet )
2429           {
2430             if ( !volTool.IsFaceExternal( iF ))
2431               swap( facet->_n2, facet->_n3 );
2432             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2433                                                  volNodes[ facet->_n2 ],
2434                                                  volNodes[ facet->_n3 ]));
2435           }
2436         }
2437         for ( size_t i = 0; i < triangles.size(); ++i )
2438         {
2439           if ( !triangles[ i ]) continue;
2440           if ( fSubMesh )
2441             fSubMesh->AddElement( triangles[ i ]);
2442           newElems.Append( triangles[ i ]);
2443         }
2444         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2445         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2446
2447       } // while a face based on facet nodes exists
2448     } // loop on volume faces to split them into triangles
2449
2450     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2451
2452     if ( geomType == SMDSEntity_TriQuad_Hexa )
2453     {
2454       // remove medium nodes that could become free
2455       for ( int i = 20; i < volTool.NbNodes(); ++i )
2456         if ( volNodes[i]->NbInverseElements() == 0 )
2457           GetMeshDS()->RemoveNode( volNodes[i] );
2458     }
2459   } // loop on volumes to split
2460
2461   myLastCreatedNodes = newNodes;
2462   myLastCreatedElems = newElems;
2463 }
2464
2465 //=======================================================================
2466 //function : GetHexaFacetsToSplit
2467 //purpose  : For hexahedra that will be split into prisms, finds facets to
2468 //           split into triangles. Only hexahedra adjacent to the one closest
2469 //           to theFacetNormal.Location() are returned.
2470 //param [in,out] theHexas - the hexahedra
2471 //param [in]     theFacetNormal - facet normal
2472 //param [out]    theFacets - the hexahedra and found facet IDs
2473 //=======================================================================
2474
2475 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2476                                              const gp_Ax1&     theFacetNormal,
2477                                              TFacetOfElem &    theFacets)
2478 {
2479   #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2480
2481   // Find a hexa closest to the location of theFacetNormal
2482
2483   const SMDS_MeshElement* startHex;
2484   {
2485     // get SMDS_ElemIteratorPtr on theHexas
2486     typedef const SMDS_MeshElement*                                      TValue;
2487     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2488     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2489     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2490     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2491     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2492       ( new TElemSetIter( theHexas.begin(),
2493                           theHexas.end(),
2494                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2495
2496     SMESH_ElementSearcher* searcher =
2497       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2498
2499     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2500
2501     delete searcher;
2502
2503     if ( !startHex )
2504       throw SALOME_Exception( THIS_METHOD "startHex not found");
2505   }
2506
2507   // Select a facet of startHex by theFacetNormal
2508
2509   SMDS_VolumeTool vTool( startHex );
2510   double norm[3], dot, maxDot = 0;
2511   int facetID = -1;
2512   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2513     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2514     {
2515       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2516       if ( dot > maxDot )
2517       {
2518         facetID = iF;
2519         maxDot = dot;
2520       }
2521     }
2522   if ( facetID < 0 )
2523     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2524
2525   // Fill theFacets starting from facetID of startHex
2526
2527   // facets used for searching of volumes adjacent to already treated ones
2528   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2529   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2530   TFacetMap facetsToCheck;
2531
2532   set<const SMDS_MeshNode*> facetNodes;
2533   const SMDS_MeshElement*   curHex;
2534
2535   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2536
2537   while ( startHex )
2538   {
2539     // move in two directions from startHex via facetID
2540     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2541     {
2542       curHex       = startHex;
2543       int curFacet = facetID;
2544       if ( is2nd ) // do not treat startHex twice
2545       {
2546         vTool.Set( curHex );
2547         if ( vTool.IsFreeFace( curFacet, &curHex ))
2548         {
2549           curHex = 0;
2550         }
2551         else
2552         {
2553           vTool.GetFaceNodes( curFacet, facetNodes );
2554           vTool.Set( curHex );
2555           curFacet = vTool.GetFaceIndex( facetNodes );
2556         }
2557       }
2558       while ( curHex )
2559       {
2560         // store a facet to split
2561         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2562         {
2563           theFacets.insert( make_pair( curHex, -1 ));
2564           break;
2565         }
2566         if ( !allHex && !theHexas.count( curHex ))
2567           break;
2568
2569         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2570           theFacets.insert( make_pair( curHex, curFacet ));
2571         if ( !facetIt2isNew.second )
2572           break;
2573
2574         // remember not-to-split facets in facetsToCheck
2575         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2576         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2577         {
2578           if ( iF == curFacet && iF == oppFacet )
2579             continue;
2580           TVolumeFaceKey facetKey ( vTool, iF );
2581           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2582           pair< TFacetMap::iterator, bool > it2isnew =
2583             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2584           if ( !it2isnew.second )
2585             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2586         }
2587         // pass to a volume adjacent via oppFacet
2588         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2589         {
2590           curHex = 0;
2591         }
2592         else
2593         {
2594           // get a new curFacet
2595           vTool.GetFaceNodes( oppFacet, facetNodes );
2596           vTool.Set( curHex );
2597           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2598         }
2599       }
2600     } // move in two directions from startHex via facetID
2601
2602     // Find a new startHex by facetsToCheck
2603
2604     startHex = 0;
2605     facetID  = -1;
2606     TFacetMap::iterator fIt = facetsToCheck.begin();
2607     while ( !startHex && fIt != facetsToCheck.end() )
2608     {
2609       const TElemFacets&  elemFacets = fIt->second;
2610       const SMDS_MeshElement*    hex = elemFacets.first->first;
2611       int                 splitFacet = elemFacets.first->second;
2612       int               lateralFacet = elemFacets.second;
2613       facetsToCheck.erase( fIt );
2614       fIt = facetsToCheck.begin();
2615
2616       vTool.Set( hex );
2617       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2618            curHex->GetGeomType() != SMDSGeom_HEXA )
2619         continue;
2620       if ( !allHex && !theHexas.count( curHex ))
2621         continue;
2622
2623       startHex = curHex;
2624
2625       // find a facet of startHex to split
2626
2627       set<const SMDS_MeshNode*> lateralNodes;
2628       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2629       vTool.GetFaceNodes( splitFacet,   facetNodes );
2630       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2631       vTool.Set( startHex );
2632       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2633
2634       // look for a facet of startHex having common nodes with facetNodes
2635       // but not lateralFacet
2636       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2637       {
2638         if ( iF == lateralFacet )
2639           continue;
2640         int nbCommonNodes = 0;
2641         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2642         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2643           nbCommonNodes += facetNodes.count( nn[ iN ]);
2644
2645         if ( nbCommonNodes >= 2 )
2646         {
2647           facetID = iF;
2648           break;
2649         }
2650       }
2651       if ( facetID < 0 )
2652         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2653     }
2654   } //   while ( startHex )
2655
2656   return;
2657 }
2658
2659 namespace
2660 {
2661   //================================================================================
2662   /*!
2663    * \brief Selects nodes of several elements according to a given interlace
2664    *  \param [in] srcNodes - nodes to select from
2665    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2666    *  \param [in] interlace - indices of nodes for all elements
2667    *  \param [in] nbElems - nb of elements
2668    *  \param [in] nbNodes - nb of nodes in each element
2669    *  \param [in] mesh - the mesh
2670    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2671    *  \param [in] type - type of elements to look for
2672    */
2673   //================================================================================
2674
2675   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2676                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2677                     const int*                            interlace,
2678                     const int                             nbElems,
2679                     const int                             nbNodes,
2680                     SMESHDS_Mesh*                         mesh = 0,
2681                     list< const SMDS_MeshElement* >*      elemQueue=0,
2682                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2683   {
2684     for ( int iE = 0; iE < nbElems; ++iE )
2685     {
2686       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2687       const int*                         select = & interlace[iE*nbNodes];
2688       elemNodes.resize( nbNodes );
2689       for ( int iN = 0; iN < nbNodes; ++iN )
2690         elemNodes[iN] = srcNodes[ select[ iN ]];
2691     }
2692     const SMDS_MeshElement* e;
2693     if ( elemQueue )
2694       for ( int iE = 0; iE < nbElems; ++iE )
2695         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2696           elemQueue->push_back( e );
2697   }
2698 }
2699
2700 //=======================================================================
2701 /*
2702  * Split bi-quadratic elements into linear ones without creation of additional nodes
2703  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2704  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2705  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2706  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2707  *   will be split in order to keep the mesh conformal.
2708  *  \param elems - elements to split
2709  */
2710 //=======================================================================
2711
2712 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2713 {
2714   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2715   vector<const SMDS_MeshElement* > splitElems;
2716   list< const SMDS_MeshElement* > elemQueue;
2717   list< const SMDS_MeshElement* >::iterator elemIt;
2718
2719   SMESHDS_Mesh * mesh = GetMeshDS();
2720   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2721   int nbElems, nbNodes;
2722
2723   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2724   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2725   {
2726     elemQueue.clear();
2727     elemQueue.push_back( *elemSetIt );
2728     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2729     {
2730       const SMDS_MeshElement* elem = *elemIt;
2731       switch( elem->GetEntityType() )
2732       {
2733       case SMDSEntity_TriQuad_Hexa: // HEX27
2734       {
2735         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2736         nbElems  = nbNodes = 8;
2737         elemType = & hexaType;
2738
2739         // get nodes for new elements
2740         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2741                                  { 1,9,20,8,    17,22,26,21 },
2742                                  { 2,10,20,9,   18,23,26,22 },
2743                                  { 3,11,20,10,  19,24,26,23 },
2744                                  { 16,21,26,24, 4,12,25,15  },
2745                                  { 17,22,26,21, 5,13,25,12  },
2746                                  { 18,23,26,22, 6,14,25,13  },
2747                                  { 19,24,26,23, 7,15,25,14  }};
2748         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2749
2750         // add boundary faces to elemQueue
2751         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2752                                  { 4,5,6,7, 12,13,14,15, 25 },
2753                                  { 0,1,5,4, 8,17,12,16,  21 },
2754                                  { 1,2,6,5, 9,18,13,17,  22 },
2755                                  { 2,3,7,6, 10,19,14,18, 23 },
2756                                  { 3,0,4,7, 11,16,15,19, 24 }};
2757         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2758
2759         // add boundary segments to elemQueue
2760         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2761                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2762                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2763         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2764         break;
2765       }
2766       case SMDSEntity_BiQuad_Triangle: // TRIA7
2767       {
2768         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2769         nbElems = 3;
2770         nbNodes = 4;
2771         elemType = & quadType;
2772
2773         // get nodes for new elements
2774         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2775         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2776
2777         // add boundary segments to elemQueue
2778         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2779         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2780         break;
2781       }
2782       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2783       {
2784         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2785         nbElems = 4;
2786         nbNodes = 4;
2787         elemType = & quadType;
2788
2789         // get nodes for new elements
2790         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2791         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2792
2793         // add boundary segments to elemQueue
2794         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2795         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2796         break;
2797       }
2798       case SMDSEntity_Quad_Edge:
2799       {
2800         if ( elemIt == elemQueue.begin() )
2801           continue; // an elem is in theElems
2802         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2803         nbElems = 2;
2804         nbNodes = 2;
2805         elemType = & segType;
2806
2807         // get nodes for new elements
2808         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2809         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2810         break;
2811       }
2812       default: continue;
2813       } // switch( elem->GetEntityType() )
2814
2815       // Create new elements
2816
2817       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2818
2819       splitElems.clear();
2820
2821       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2822       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2823       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2824       //elemType->SetID( -1 );
2825
2826       for ( int iE = 0; iE < nbElems; ++iE )
2827         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2828
2829
2830       ReplaceElemInGroups( elem, splitElems, mesh );
2831
2832       if ( subMesh )
2833         for ( size_t i = 0; i < splitElems.size(); ++i )
2834           subMesh->AddElement( splitElems[i] );
2835     }
2836   }
2837 }
2838
2839 //=======================================================================
2840 //function : AddToSameGroups
2841 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2842 //=======================================================================
2843
2844 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2845                                         const SMDS_MeshElement* elemInGroups,
2846                                         SMESHDS_Mesh *          aMesh)
2847 {
2848   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2849   if (!groups.empty()) {
2850     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2851     for ( ; grIt != groups.end(); grIt++ ) {
2852       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2853       if ( group && group->Contains( elemInGroups ))
2854         group->SMDSGroup().Add( elemToAdd );
2855     }
2856   }
2857 }
2858
2859
2860 //=======================================================================
2861 //function : RemoveElemFromGroups
2862 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2863 //=======================================================================
2864 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2865                                              SMESHDS_Mesh *          aMesh)
2866 {
2867   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2868   if (!groups.empty())
2869   {
2870     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2871     for (; GrIt != groups.end(); GrIt++)
2872     {
2873       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2874       if (!grp || grp->IsEmpty()) continue;
2875       grp->SMDSGroup().Remove(removeelem);
2876     }
2877   }
2878 }
2879
2880 //================================================================================
2881 /*!
2882  * \brief Replace elemToRm by elemToAdd in the all groups
2883  */
2884 //================================================================================
2885
2886 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2887                                             const SMDS_MeshElement* elemToAdd,
2888                                             SMESHDS_Mesh *          aMesh)
2889 {
2890   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2891   if (!groups.empty()) {
2892     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2893     for ( ; grIt != groups.end(); grIt++ ) {
2894       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2895       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2896         group->SMDSGroup().Add( elemToAdd );
2897     }
2898   }
2899 }
2900
2901 //================================================================================
2902 /*!
2903  * \brief Replace elemToRm by elemToAdd in the all groups
2904  */
2905 //================================================================================
2906
2907 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2908                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2909                                             SMESHDS_Mesh *                         aMesh)
2910 {
2911   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2912   if (!groups.empty())
2913   {
2914     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2915     for ( ; grIt != groups.end(); grIt++ ) {
2916       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2917       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2918         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2919           group->SMDSGroup().Add( elemToAdd[ i ] );
2920     }
2921   }
2922 }
2923
2924 //=======================================================================
2925 //function : QuadToTri
2926 //purpose  : Cut quadrangles into triangles.
2927 //           theCrit is used to select a diagonal to cut
2928 //=======================================================================
2929
2930 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2931                                   const bool         the13Diag)
2932 {
2933   myLastCreatedElems.Clear();
2934   myLastCreatedNodes.Clear();
2935
2936   SMESHDS_Mesh * aMesh = GetMeshDS();
2937
2938   Handle(Geom_Surface) surface;
2939   SMESH_MesherHelper   helper( *GetMesh() );
2940
2941   TIDSortedElemSet::iterator itElem;
2942   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2943   {
2944     const SMDS_MeshElement* elem = *itElem;
2945     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2946       continue;
2947
2948     if ( elem->NbNodes() == 4 ) {
2949       // retrieve element nodes
2950       const SMDS_MeshNode* aNodes [4];
2951       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2952       int i = 0;
2953       while ( itN->more() )
2954         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2955
2956       int aShapeId = FindShape( elem );
2957       const SMDS_MeshElement* newElem1 = 0;
2958       const SMDS_MeshElement* newElem2 = 0;
2959       if ( the13Diag ) {
2960         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2961         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2962       }
2963       else {
2964         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2965         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2966       }
2967       myLastCreatedElems.Append(newElem1);
2968       myLastCreatedElems.Append(newElem2);
2969       // put a new triangle on the same shape and add to the same groups
2970       if ( aShapeId )
2971       {
2972         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2973         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2974       }
2975       AddToSameGroups( newElem1, elem, aMesh );
2976       AddToSameGroups( newElem2, elem, aMesh );
2977       aMesh->RemoveElement( elem );
2978     }
2979
2980     // Quadratic quadrangle
2981
2982     else if ( elem->NbNodes() >= 8 )
2983     {
2984       // get surface elem is on
2985       int aShapeId = FindShape( elem );
2986       if ( aShapeId != helper.GetSubShapeID() ) {
2987         surface.Nullify();
2988         TopoDS_Shape shape;
2989         if ( aShapeId > 0 )
2990           shape = aMesh->IndexToShape( aShapeId );
2991         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2992           TopoDS_Face face = TopoDS::Face( shape );
2993           surface = BRep_Tool::Surface( face );
2994           if ( !surface.IsNull() )
2995             helper.SetSubShape( shape );
2996         }
2997       }
2998
2999       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3000       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3001       for ( int i = 0; itN->more(); ++i )
3002         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3003
3004       const SMDS_MeshNode* centrNode = aNodes[8];
3005       if ( centrNode == 0 )
3006       {
3007         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3008                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3009                                            surface.IsNull() );
3010         myLastCreatedNodes.Append(centrNode);
3011       }
3012
3013       // create a new element
3014       const SMDS_MeshElement* newElem1 = 0;
3015       const SMDS_MeshElement* newElem2 = 0;
3016       if ( the13Diag ) {
3017         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3018                                   aNodes[6], aNodes[7], centrNode );
3019         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3020                                   centrNode, aNodes[4], aNodes[5] );
3021       }
3022       else {
3023         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3024                                   aNodes[7], aNodes[4], centrNode );
3025         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3026                                   centrNode, aNodes[5], aNodes[6] );
3027       }
3028       myLastCreatedElems.Append(newElem1);
3029       myLastCreatedElems.Append(newElem2);
3030       // put a new triangle on the same shape and add to the same groups
3031       if ( aShapeId )
3032       {
3033         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3034         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3035       }
3036       AddToSameGroups( newElem1, elem, aMesh );
3037       AddToSameGroups( newElem2, elem, aMesh );
3038       aMesh->RemoveElement( elem );
3039     }
3040   }
3041
3042   return true;
3043 }
3044
3045 //=======================================================================
3046 //function : getAngle
3047 //purpose  :
3048 //=======================================================================
3049
3050 double getAngle(const SMDS_MeshElement * tr1,
3051                 const SMDS_MeshElement * tr2,
3052                 const SMDS_MeshNode *    n1,
3053                 const SMDS_MeshNode *    n2)
3054 {
3055   double angle = 2. * M_PI; // bad angle
3056
3057   // get normals
3058   SMESH::Controls::TSequenceOfXYZ P1, P2;
3059   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3060        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3061     return angle;
3062   gp_Vec N1,N2;
3063   if(!tr1->IsQuadratic())
3064     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3065   else
3066     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3067   if ( N1.SquareMagnitude() <= gp::Resolution() )
3068     return angle;
3069   if(!tr2->IsQuadratic())
3070     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3071   else
3072     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3073   if ( N2.SquareMagnitude() <= gp::Resolution() )
3074     return angle;
3075
3076   // find the first diagonal node n1 in the triangles:
3077   // take in account a diagonal link orientation
3078   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3079   for ( int t = 0; t < 2; t++ ) {
3080     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3081     int i = 0, iDiag = -1;
3082     while ( it->more()) {
3083       const SMDS_MeshElement *n = it->next();
3084       if ( n == n1 || n == n2 ) {
3085         if ( iDiag < 0)
3086           iDiag = i;
3087         else {
3088           if ( i - iDiag == 1 )
3089             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3090           else
3091             nFirst[ t ] = n;
3092           break;
3093         }
3094       }
3095       i++;
3096     }
3097   }
3098   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3099     N2.Reverse();
3100
3101   angle = N1.Angle( N2 );
3102   //SCRUTE( angle );
3103   return angle;
3104 }
3105
3106 // =================================================
3107 // class generating a unique ID for a pair of nodes
3108 // and able to return nodes by that ID
3109 // =================================================
3110 class LinkID_Gen {
3111 public:
3112
3113   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3114     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3115   {}
3116
3117   long GetLinkID (const SMDS_MeshNode * n1,
3118                   const SMDS_MeshNode * n2) const
3119   {
3120     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3121   }
3122
3123   bool GetNodes (const long             theLinkID,
3124                  const SMDS_MeshNode* & theNode1,
3125                  const SMDS_MeshNode* & theNode2) const
3126   {
3127     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3128     if ( !theNode1 ) return false;
3129     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3130     if ( !theNode2 ) return false;
3131     return true;
3132   }
3133
3134 private:
3135   LinkID_Gen();
3136   const SMESHDS_Mesh* myMesh;
3137   long                myMaxID;
3138 };
3139
3140
3141 //=======================================================================
3142 //function : TriToQuad
3143 //purpose  : Fuse neighbour triangles into quadrangles.
3144 //           theCrit is used to select a neighbour to fuse with.
3145 //           theMaxAngle is a max angle between element normals at which
3146 //           fusion is still performed.
3147 //=======================================================================
3148
3149 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3150                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3151                                   const double                         theMaxAngle)
3152 {
3153   myLastCreatedElems.Clear();
3154   myLastCreatedNodes.Clear();
3155
3156   if ( !theCrit.get() )
3157     return false;
3158
3159   SMESHDS_Mesh * aMesh = GetMeshDS();
3160
3161   // Prepare data for algo: build
3162   // 1. map of elements with their linkIDs
3163   // 2. map of linkIDs with their elements
3164
3165   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3166   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3167   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3168   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3169
3170   TIDSortedElemSet::iterator itElem;
3171   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3172   {
3173     const SMDS_MeshElement* elem = *itElem;
3174     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3175     bool IsTria = ( elem->NbCornerNodes()==3 );
3176     if (!IsTria) continue;
3177
3178     // retrieve element nodes
3179     const SMDS_MeshNode* aNodes [4];
3180     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3181     int i = 0;
3182     while ( i < 3 )
3183       aNodes[ i++ ] = itN->next();
3184     aNodes[ 3 ] = aNodes[ 0 ];
3185
3186     // fill maps
3187     for ( i = 0; i < 3; i++ ) {
3188       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3189       // check if elements sharing a link can be fused
3190       itLE = mapLi_listEl.find( link );
3191       if ( itLE != mapLi_listEl.end() ) {
3192         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3193           continue;
3194         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3195         //if ( FindShape( elem ) != FindShape( elem2 ))
3196         //  continue; // do not fuse triangles laying on different shapes
3197         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3198           continue; // avoid making badly shaped quads
3199         (*itLE).second.push_back( elem );
3200       }
3201       else {
3202         mapLi_listEl[ link ].push_back( elem );
3203       }
3204       mapEl_setLi [ elem ].insert( link );
3205     }
3206   }
3207   // Clean the maps from the links shared by a sole element, ie
3208   // links to which only one element is bound in mapLi_listEl
3209
3210   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3211     int nbElems = (*itLE).second.size();
3212     if ( nbElems < 2  ) {
3213       const SMDS_MeshElement* elem = (*itLE).second.front();
3214       SMESH_TLink link = (*itLE).first;
3215       mapEl_setLi[ elem ].erase( link );
3216       if ( mapEl_setLi[ elem ].empty() )
3217         mapEl_setLi.erase( elem );
3218     }
3219   }
3220
3221   // Algo: fuse triangles into quadrangles
3222
3223   while ( ! mapEl_setLi.empty() ) {
3224     // Look for the start element:
3225     // the element having the least nb of shared links
3226     const SMDS_MeshElement* startElem = 0;
3227     int minNbLinks = 4;
3228     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3229       int nbLinks = (*itEL).second.size();
3230       if ( nbLinks < minNbLinks ) {
3231         startElem = (*itEL).first;
3232         minNbLinks = nbLinks;
3233         if ( minNbLinks == 1 )
3234           break;
3235       }
3236     }
3237
3238     // search elements to fuse starting from startElem or links of elements
3239     // fused earlyer - startLinks
3240     list< SMESH_TLink > startLinks;
3241     while ( startElem || !startLinks.empty() ) {
3242       while ( !startElem && !startLinks.empty() ) {
3243         // Get an element to start, by a link
3244         SMESH_TLink linkId = startLinks.front();
3245         startLinks.pop_front();
3246         itLE = mapLi_listEl.find( linkId );
3247         if ( itLE != mapLi_listEl.end() ) {
3248           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3249           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3250           for ( ; itE != listElem.end() ; itE++ )
3251             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3252               startElem = (*itE);
3253           mapLi_listEl.erase( itLE );
3254         }
3255       }
3256
3257       if ( startElem ) {
3258         // Get candidates to be fused
3259         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3260         const SMESH_TLink *link12 = 0, *link13 = 0;
3261         startElem = 0;
3262         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3263         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3264         ASSERT( !setLi.empty() );
3265         set< SMESH_TLink >::iterator itLi;
3266         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3267         {
3268           const SMESH_TLink & link = (*itLi);
3269           itLE = mapLi_listEl.find( link );
3270           if ( itLE == mapLi_listEl.end() )
3271             continue;
3272
3273           const SMDS_MeshElement* elem = (*itLE).second.front();
3274           if ( elem == tr1 )
3275             elem = (*itLE).second.back();
3276           mapLi_listEl.erase( itLE );
3277           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3278             continue;
3279           if ( tr2 ) {
3280             tr3 = elem;
3281             link13 = &link;
3282           }
3283           else {
3284             tr2 = elem;
3285             link12 = &link;
3286           }
3287
3288           // add other links of elem to list of links to re-start from
3289           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3290           set< SMESH_TLink >::iterator it;
3291           for ( it = links.begin(); it != links.end(); it++ ) {
3292             const SMESH_TLink& link2 = (*it);
3293             if ( link2 != link )
3294               startLinks.push_back( link2 );
3295           }
3296         }
3297
3298         // Get nodes of possible quadrangles
3299         const SMDS_MeshNode *n12 [4], *n13 [4];
3300         bool Ok12 = false, Ok13 = false;
3301         const SMDS_MeshNode *linkNode1, *linkNode2;
3302         if(tr2) {
3303           linkNode1 = link12->first;
3304           linkNode2 = link12->second;
3305           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3306             Ok12 = true;
3307         }
3308         if(tr3) {
3309           linkNode1 = link13->first;
3310           linkNode2 = link13->second;
3311           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3312             Ok13 = true;
3313         }
3314
3315         // Choose a pair to fuse
3316         if ( Ok12 && Ok13 ) {
3317           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3318           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3319           double aBadRate12 = getBadRate( &quad12, theCrit );
3320           double aBadRate13 = getBadRate( &quad13, theCrit );
3321           if (  aBadRate13 < aBadRate12 )
3322             Ok12 = false;
3323           else
3324             Ok13 = false;
3325         }
3326
3327         // Make quadrangles
3328         // and remove fused elems and remove links from the maps
3329         mapEl_setLi.erase( tr1 );
3330         if ( Ok12 )
3331         {
3332           mapEl_setLi.erase( tr2 );
3333           mapLi_listEl.erase( *link12 );
3334           if ( tr1->NbNodes() == 3 )
3335           {
3336             const SMDS_MeshElement* newElem = 0;
3337             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3338             myLastCreatedElems.Append(newElem);
3339             AddToSameGroups( newElem, tr1, aMesh );
3340             int aShapeId = tr1->getshapeId();
3341             if ( aShapeId )
3342               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3343             aMesh->RemoveElement( tr1 );
3344             aMesh->RemoveElement( tr2 );
3345           }
3346           else {
3347             vector< const SMDS_MeshNode* > N1;
3348             vector< const SMDS_MeshNode* > N2;
3349             getNodesFromTwoTria(tr1,tr2,N1,N2);
3350             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3351             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3352             // i.e. first nodes from both arrays form a new diagonal
3353             const SMDS_MeshNode* aNodes[8];
3354             aNodes[0] = N1[0];
3355             aNodes[1] = N1[1];
3356             aNodes[2] = N2[0];
3357             aNodes[3] = N2[1];
3358             aNodes[4] = N1[3];
3359             aNodes[5] = N2[5];
3360             aNodes[6] = N2[3];
3361             aNodes[7] = N1[5];
3362             const SMDS_MeshElement* newElem = 0;
3363             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3364               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3365                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3366             else
3367               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3368                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3369             myLastCreatedElems.Append(newElem);
3370             AddToSameGroups( newElem, tr1, aMesh );
3371             int aShapeId = tr1->getshapeId();
3372             if ( aShapeId )
3373               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3374             aMesh->RemoveElement( tr1 );
3375             aMesh->RemoveElement( tr2 );
3376             // remove middle node (9)
3377             if ( N1[4]->NbInverseElements() == 0 )
3378               aMesh->RemoveNode( N1[4] );
3379             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3380               aMesh->RemoveNode( N1[6] );
3381             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3382               aMesh->RemoveNode( N2[6] );
3383           }
3384         }
3385         else if ( Ok13 )
3386         {
3387           mapEl_setLi.erase( tr3 );
3388           mapLi_listEl.erase( *link13 );
3389           if ( tr1->NbNodes() == 3 ) {
3390             const SMDS_MeshElement* newElem = 0;
3391             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3392             myLastCreatedElems.Append(newElem);
3393             AddToSameGroups( newElem, tr1, aMesh );
3394             int aShapeId = tr1->getshapeId();
3395             if ( aShapeId )
3396               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3397             aMesh->RemoveElement( tr1 );
3398             aMesh->RemoveElement( tr3 );
3399           }
3400           else {
3401             vector< const SMDS_MeshNode* > N1;
3402             vector< const SMDS_MeshNode* > N2;
3403             getNodesFromTwoTria(tr1,tr3,N1,N2);
3404             // now we receive following N1 and N2 (using numeration as above image)
3405             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3406             // i.e. first nodes from both arrays form a new diagonal
3407             const SMDS_MeshNode* aNodes[8];
3408             aNodes[0] = N1[0];
3409             aNodes[1] = N1[1];
3410             aNodes[2] = N2[0];
3411             aNodes[3] = N2[1];
3412             aNodes[4] = N1[3];
3413             aNodes[5] = N2[5];
3414             aNodes[6] = N2[3];
3415             aNodes[7] = N1[5];
3416             const SMDS_MeshElement* newElem = 0;
3417             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3418               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3419                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3420             else
3421               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3422                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3423             myLastCreatedElems.Append(newElem);
3424             AddToSameGroups( newElem, tr1, aMesh );
3425             int aShapeId = tr1->getshapeId();
3426             if ( aShapeId )
3427               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3428             aMesh->RemoveElement( tr1 );
3429             aMesh->RemoveElement( tr3 );
3430             // remove middle node (9)
3431             if ( N1[4]->NbInverseElements() == 0 )
3432               aMesh->RemoveNode( N1[4] );
3433             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3434               aMesh->RemoveNode( N1[6] );
3435             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3436               aMesh->RemoveNode( N2[6] );
3437           }
3438         }
3439
3440         // Next element to fuse: the rejected one
3441         if ( tr3 )
3442           startElem = Ok12 ? tr3 : tr2;
3443
3444       } // if ( startElem )
3445     } // while ( startElem || !startLinks.empty() )
3446   } // while ( ! mapEl_setLi.empty() )
3447
3448   return true;
3449 }
3450
3451
3452 /*#define DUMPSO(txt) \
3453 //  cout << txt << endl;
3454 //=============================================================================
3455 //
3456 //
3457 //
3458 //=============================================================================
3459 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3460 {
3461 if ( i1 == i2 )
3462 return;
3463 int tmp = idNodes[ i1 ];
3464 idNodes[ i1 ] = idNodes[ i2 ];
3465 idNodes[ i2 ] = tmp;
3466 gp_Pnt Ptmp = P[ i1 ];
3467 P[ i1 ] = P[ i2 ];
3468 P[ i2 ] = Ptmp;
3469 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3470 }
3471
3472 //=======================================================================
3473 //function : SortQuadNodes
3474 //purpose  : Set 4 nodes of a quadrangle face in a good order.
3475 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
3476 //           1 or 2 else 0.
3477 //=======================================================================
3478
3479 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3480 int               idNodes[] )
3481 {
3482   gp_Pnt P[4];
3483   int i;
3484   for ( i = 0; i < 4; i++ ) {
3485     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3486     if ( !n ) return 0;
3487     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3488   }
3489
3490   gp_Vec V1(P[0], P[1]);
3491   gp_Vec V2(P[0], P[2]);
3492   gp_Vec V3(P[0], P[3]);
3493
3494   gp_Vec Cross1 = V1 ^ V2;
3495   gp_Vec Cross2 = V2 ^ V3;
3496
3497   i = 0;
3498   if (Cross1.Dot(Cross2) < 0)
3499   {
3500     Cross1 = V2 ^ V1;
3501     Cross2 = V1 ^ V3;
3502
3503     if (Cross1.Dot(Cross2) < 0)
3504       i = 2;
3505     else
3506       i = 1;
3507     swap ( i, i + 1, idNodes, P );
3508
3509     //     for ( int ii = 0; ii < 4; ii++ ) {
3510     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3511     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3512     //     }
3513   }
3514   return i;
3515 }
3516
3517 //=======================================================================
3518 //function : SortHexaNodes
3519 //purpose  : Set 8 nodes of a hexahedron in a good order.
3520 //           Return success status
3521 //=======================================================================
3522
3523 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3524                                       int               idNodes[] )
3525 {
3526   gp_Pnt P[8];
3527   int i;
3528   DUMPSO( "INPUT: ========================================");
3529   for ( i = 0; i < 8; i++ ) {
3530     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3531     if ( !n ) return false;
3532     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3533     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3534   }
3535   DUMPSO( "========================================");
3536
3537
3538   set<int> faceNodes;  // ids of bottom face nodes, to be found
3539   set<int> checkedId1; // ids of tried 2-nd nodes
3540   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3541   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
3542   int iMin, iLoop1 = 0;
3543
3544   // Loop to try the 2-nd nodes
3545
3546   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3547   {
3548     // Find not checked 2-nd node
3549     for ( i = 1; i < 8; i++ )
3550       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3551         int id1 = idNodes[i];
3552         swap ( 1, i, idNodes, P );
3553         checkedId1.insert ( id1 );
3554         break;
3555       }
3556
3557     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3558     // ie that all but meybe one (id3 which is on the same face) nodes
3559     // lay on the same side from the triangle plane.
3560
3561     bool manyInPlane = false; // more than 4 nodes lay in plane
3562     int iLoop2 = 0;
3563     while ( ++iLoop2 < 6 ) {
3564
3565       // get 1-2-3 plane coeffs
3566       Standard_Real A, B, C, D;
3567       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3568       if ( N.SquareMagnitude() > gp::Resolution() )
3569       {
3570         gp_Pln pln ( P[0], N );
3571         pln.Coefficients( A, B, C, D );
3572
3573         // find the node (iMin) closest to pln
3574         Standard_Real dist[ 8 ], minDist = DBL_MAX;
3575         set<int> idInPln;
3576         for ( i = 3; i < 8; i++ ) {
3577           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3578           if ( fabs( dist[i] ) < minDist ) {
3579             minDist = fabs( dist[i] );
3580             iMin = i;
3581           }
3582           if ( fabs( dist[i] ) <= tol )
3583             idInPln.insert( idNodes[i] );
3584         }
3585
3586         // there should not be more than 4 nodes in bottom plane
3587         if ( idInPln.size() > 1 )
3588         {
3589           DUMPSO( "### idInPln.size() = " << idInPln.size());
3590           // idInPlane does not contain the first 3 nodes
3591           if ( manyInPlane || idInPln.size() == 5)
3592             return false; // all nodes in one plane
3593           manyInPlane = true;
3594
3595           // set the 1-st node to be not in plane
3596           for ( i = 3; i < 8; i++ ) {
3597             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3598               DUMPSO( "### Reset 0-th node");
3599               swap( 0, i, idNodes, P );
3600               break;
3601             }
3602           }
3603
3604           // reset to re-check second nodes
3605           leastDist = DBL_MAX;
3606           faceNodes.clear();
3607           checkedId1.clear();
3608           iLoop1 = 0;
3609           break; // from iLoop2;
3610         }
3611
3612         // check that the other 4 nodes are on the same side
3613         bool sameSide = true;
3614         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3615         for ( i = 3; sameSide && i < 8; i++ ) {
3616           if ( i != iMin )
3617             sameSide = ( isNeg == dist[i] <= 0.);
3618         }
3619
3620         // keep best solution
3621         if ( sameSide && minDist < leastDist ) {
3622           leastDist = minDist;
3623           faceNodes.clear();
3624           faceNodes.insert( idNodes[ 1 ] );
3625           faceNodes.insert( idNodes[ 2 ] );
3626           faceNodes.insert( idNodes[ iMin ] );
3627           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3628                   << " leastDist = " << leastDist);
3629           if ( leastDist <= DBL_MIN )
3630             break;
3631         }
3632       }
3633
3634       // set next 3-d node to check
3635       int iNext = 2 + iLoop2;
3636       if ( iNext < 8 ) {
3637         DUMPSO( "Try 2-nd");
3638         swap ( 2, iNext, idNodes, P );
3639       }
3640     } // while ( iLoop2 < 6 )
3641   } // iLoop1
3642
3643   if ( faceNodes.empty() ) return false;
3644
3645   // Put the faceNodes in proper places
3646   for ( i = 4; i < 8; i++ ) {
3647     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3648       // find a place to put
3649       int iTo = 1;
3650       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3651         iTo++;
3652       DUMPSO( "Set faceNodes");
3653       swap ( iTo, i, idNodes, P );
3654     }
3655   }
3656
3657
3658   // Set nodes of the found bottom face in good order
3659   DUMPSO( " Found bottom face: ");
3660   i = SortQuadNodes( theMesh, idNodes );
3661   if ( i ) {
3662     gp_Pnt Ptmp = P[ i ];
3663     P[ i ] = P[ i+1 ];
3664     P[ i+1 ] = Ptmp;
3665   }
3666   //   else
3667   //     for ( int ii = 0; ii < 4; ii++ ) {
3668   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3669   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3670   //    }
3671
3672   // Gravity center of the top and bottom faces
3673   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3674   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3675
3676   // Get direction from the bottom to the top face
3677   gp_Vec upDir ( aGCb, aGCt );
3678   Standard_Real upDirSize = upDir.Magnitude();
3679   if ( upDirSize <= gp::Resolution() ) return false;
3680   upDir / upDirSize;
3681
3682   // Assure that the bottom face normal points up
3683   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3684   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3685   if ( Nb.Dot( upDir ) < 0 ) {
3686     DUMPSO( "Reverse bottom face");
3687     swap( 1, 3, idNodes, P );
3688   }
3689
3690   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3691   Standard_Real minDist = DBL_MAX;
3692   for ( i = 4; i < 8; i++ ) {
3693     // projection of P[i] to the plane defined by P[0] and upDir
3694     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3695     Standard_Real sqDist = P[0].SquareDistance( Pp );
3696     if ( sqDist < minDist ) {
3697       minDist = sqDist;
3698       iMin = i;
3699     }
3700   }
3701   DUMPSO( "Set 4-th");
3702   swap ( 4, iMin, idNodes, P );
3703
3704   // Set nodes of the top face in good order
3705   DUMPSO( "Sort top face");
3706   i = SortQuadNodes( theMesh, &idNodes[4] );
3707   if ( i ) {
3708     i += 4;
3709     gp_Pnt Ptmp = P[ i ];
3710     P[ i ] = P[ i+1 ];
3711     P[ i+1 ] = Ptmp;
3712   }
3713
3714   // Assure that direction of the top face normal is from the bottom face
3715   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3716   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3717   if ( Nt.Dot( upDir ) < 0 ) {
3718     DUMPSO( "Reverse top face");
3719     swap( 5, 7, idNodes, P );
3720   }
3721
3722   //   DUMPSO( "OUTPUT: ========================================");
3723   //   for ( i = 0; i < 8; i++ ) {
3724   //     float *p = ugrid->GetPoint(idNodes[i]);
3725   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3726   //   }
3727
3728   return true;
3729 }*/
3730
3731 //================================================================================
3732 /*!
3733  * \brief Return nodes linked to the given one
3734  * \param theNode - the node
3735  * \param linkedNodes - the found nodes
3736  * \param type - the type of elements to check
3737  *
3738  * Medium nodes are ignored
3739  */
3740 //================================================================================
3741
3742 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3743                                        TIDSortedElemSet &   linkedNodes,
3744                                        SMDSAbs_ElementType  type )
3745 {
3746   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3747   while ( elemIt->more() )
3748   {
3749     const SMDS_MeshElement* elem = elemIt->next();
3750     if(elem->GetType() == SMDSAbs_0DElement)
3751       continue;
3752
3753     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3754     if ( elem->GetType() == SMDSAbs_Volume )
3755     {
3756       SMDS_VolumeTool vol( elem );
3757       while ( nodeIt->more() ) {
3758         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3759         if ( theNode != n && vol.IsLinked( theNode, n ))
3760           linkedNodes.insert( n );
3761       }
3762     }
3763     else
3764     {
3765       for ( int i = 0; nodeIt->more(); ++i ) {
3766         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3767         if ( n == theNode ) {
3768           int iBefore = i - 1;
3769           int iAfter  = i + 1;
3770           if ( elem->IsQuadratic() ) {
3771             int nb = elem->NbNodes() / 2;
3772             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3773             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3774           }
3775           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3776           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3777         }
3778       }
3779     }
3780   }
3781 }
3782
3783 //=======================================================================
3784 //function : laplacianSmooth
3785 //purpose  : pulls theNode toward the center of surrounding nodes directly
3786 //           connected to that node along an element edge
3787 //=======================================================================
3788
3789 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3790                      const Handle(Geom_Surface)&          theSurface,
3791                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3792 {
3793   // find surrounding nodes
3794
3795   TIDSortedElemSet nodeSet;
3796   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3797
3798   // compute new coodrs
3799
3800   double coord[] = { 0., 0., 0. };
3801   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3802   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3803     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3804     if ( theSurface.IsNull() ) { // smooth in 3D
3805       coord[0] += node->X();
3806       coord[1] += node->Y();
3807       coord[2] += node->Z();
3808     }
3809     else { // smooth in 2D
3810       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3811       gp_XY* uv = theUVMap[ node ];
3812       coord[0] += uv->X();
3813       coord[1] += uv->Y();
3814     }
3815   }
3816   int nbNodes = nodeSet.size();
3817   if ( !nbNodes )
3818     return;
3819   coord[0] /= nbNodes;
3820   coord[1] /= nbNodes;
3821
3822   if ( !theSurface.IsNull() ) {
3823     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3824     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3825     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3826     coord[0] = p3d.X();
3827     coord[1] = p3d.Y();
3828     coord[2] = p3d.Z();
3829   }
3830   else
3831     coord[2] /= nbNodes;
3832
3833   // move node
3834
3835   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3836 }
3837
3838 //=======================================================================
3839 //function : centroidalSmooth
3840 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3841 //           surrounding elements
3842 //=======================================================================
3843
3844 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3845                       const Handle(Geom_Surface)&          theSurface,
3846                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3847 {
3848   gp_XYZ aNewXYZ(0.,0.,0.);
3849   SMESH::Controls::Area anAreaFunc;
3850   double totalArea = 0.;
3851   int nbElems = 0;
3852
3853   // compute new XYZ
3854
3855   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3856   while ( elemIt->more() )
3857   {
3858     const SMDS_MeshElement* elem = elemIt->next();
3859     nbElems++;
3860
3861     gp_XYZ elemCenter(0.,0.,0.);
3862     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3863     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3864     int nn = elem->NbNodes();
3865     if(elem->IsQuadratic()) nn = nn/2;
3866     int i=0;
3867     //while ( itN->more() ) {
3868     while ( i<nn ) {
3869       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3870       i++;
3871       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3872       aNodePoints.push_back( aP );
3873       if ( !theSurface.IsNull() ) { // smooth in 2D
3874         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3875         gp_XY* uv = theUVMap[ aNode ];
3876         aP.SetCoord( uv->X(), uv->Y(), 0. );
3877       }
3878       elemCenter += aP;
3879     }
3880     double elemArea = anAreaFunc.GetValue( aNodePoints );
3881     totalArea += elemArea;
3882     elemCenter /= nn;
3883     aNewXYZ += elemCenter * elemArea;
3884   }
3885   aNewXYZ /= totalArea;
3886   if ( !theSurface.IsNull() ) {
3887     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3888     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3889   }
3890
3891   // move node
3892
3893   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3894 }
3895
3896 //=======================================================================
3897 //function : getClosestUV
3898 //purpose  : return UV of closest projection
3899 //=======================================================================
3900
3901 static bool getClosestUV (Extrema_GenExtPS& projector,
3902                           const gp_Pnt&     point,
3903                           gp_XY &           result)
3904 {
3905   projector.Perform( point );
3906   if ( projector.IsDone() ) {
3907     double u, v, minVal = DBL_MAX;
3908     for ( int i = projector.NbExt(); i > 0; i-- )
3909       if ( projector.SquareDistance( i ) < minVal ) {
3910         minVal = projector.SquareDistance( i );
3911         projector.Point( i ).Parameter( u, v );
3912       }
3913     result.SetCoord( u, v );
3914     return true;
3915   }
3916   return false;
3917 }
3918
3919 //=======================================================================
3920 //function : Smooth
3921 //purpose  : Smooth theElements during theNbIterations or until a worst
3922 //           element has aspect ratio <= theTgtAspectRatio.
3923 //           Aspect Ratio varies in range [1.0, inf].
3924 //           If theElements is empty, the whole mesh is smoothed.
3925 //           theFixedNodes contains additionally fixed nodes. Nodes built
3926 //           on edges and boundary nodes are always fixed.
3927 //=======================================================================
3928
3929 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3930                                set<const SMDS_MeshNode*> & theFixedNodes,
3931                                const SmoothMethod          theSmoothMethod,
3932                                const int                   theNbIterations,
3933                                double                      theTgtAspectRatio,
3934                                const bool                  the2D)
3935 {
3936   myLastCreatedElems.Clear();
3937   myLastCreatedNodes.Clear();
3938
3939   if ( theTgtAspectRatio < 1.0 )
3940     theTgtAspectRatio = 1.0;
3941
3942   const double disttol = 1.e-16;
3943
3944   SMESH::Controls::AspectRatio aQualityFunc;
3945
3946   SMESHDS_Mesh* aMesh = GetMeshDS();
3947
3948   if ( theElems.empty() ) {
3949     // add all faces to theElems
3950     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3951     while ( fIt->more() ) {
3952       const SMDS_MeshElement* face = fIt->next();
3953       theElems.insert( theElems.end(), face );
3954     }
3955   }
3956   // get all face ids theElems are on
3957   set< int > faceIdSet;
3958   TIDSortedElemSet::iterator itElem;
3959   if ( the2D )
3960     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3961       int fId = FindShape( *itElem );
3962       // check that corresponding submesh exists and a shape is face
3963       if (fId &&
3964           faceIdSet.find( fId ) == faceIdSet.end() &&
3965           aMesh->MeshElements( fId )) {
3966         TopoDS_Shape F = aMesh->IndexToShape( fId );
3967         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3968           faceIdSet.insert( fId );
3969       }
3970     }
3971   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3972
3973   // ===============================================
3974   // smooth elements on each TopoDS_Face separately
3975   // ===============================================
3976
3977   SMESH_MesherHelper helper( *GetMesh() );
3978
3979   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3980   for ( ; fId != faceIdSet.rend(); ++fId )
3981   {
3982     // get face surface and submesh
3983     Handle(Geom_Surface) surface;
3984     SMESHDS_SubMesh* faceSubMesh = 0;
3985     TopoDS_Face face;
3986     double fToler2 = 0;
3987     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3988     bool isUPeriodic = false, isVPeriodic = false;
3989     if ( *fId )
3990     {
3991       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3992       surface = BRep_Tool::Surface( face );
3993       faceSubMesh = aMesh->MeshElements( *fId );
3994       fToler2 = BRep_Tool::Tolerance( face );
3995       fToler2 *= fToler2 * 10.;
3996       isUPeriodic = surface->IsUPeriodic();
3997       // if ( isUPeriodic )
3998       //   surface->UPeriod();
3999       isVPeriodic = surface->IsVPeriodic();
4000       // if ( isVPeriodic )
4001       //   surface->VPeriod();
4002       surface->Bounds( u1, u2, v1, v2 );
4003       helper.SetSubShape( face );
4004     }
4005     // ---------------------------------------------------------
4006     // for elements on a face, find movable and fixed nodes and
4007     // compute UV for them
4008     // ---------------------------------------------------------
4009     bool checkBoundaryNodes = false;
4010     bool isQuadratic = false;
4011     set<const SMDS_MeshNode*> setMovableNodes;
4012     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4013     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4014     list< const SMDS_MeshElement* > elemsOnFace;
4015
4016     Extrema_GenExtPS projector;
4017     GeomAdaptor_Surface surfAdaptor;
4018     if ( !surface.IsNull() ) {
4019       surfAdaptor.Load( surface );
4020       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4021     }
4022     int nbElemOnFace = 0;
4023     itElem = theElems.begin();
4024     // loop on not yet smoothed elements: look for elems on a face
4025     while ( itElem != theElems.end() )
4026     {
4027       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4028         break; // all elements found
4029
4030       const SMDS_MeshElement* elem = *itElem;
4031       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4032            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4033         ++itElem;
4034         continue;
4035       }
4036       elemsOnFace.push_back( elem );
4037       theElems.erase( itElem++ );
4038       nbElemOnFace++;
4039
4040       if ( !isQuadratic )
4041         isQuadratic = elem->IsQuadratic();
4042
4043       // get movable nodes of elem
4044       const SMDS_MeshNode* node;
4045       SMDS_TypeOfPosition posType;
4046       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4047       int nn = 0, nbn =  elem->NbNodes();
4048       if(elem->IsQuadratic())
4049         nbn = nbn/2;
4050       while ( nn++ < nbn ) {
4051         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4052         const SMDS_PositionPtr& pos = node->GetPosition();
4053         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4054         if (posType != SMDS_TOP_EDGE &&
4055             posType != SMDS_TOP_VERTEX &&
4056             theFixedNodes.find( node ) == theFixedNodes.end())
4057         {
4058           // check if all faces around the node are on faceSubMesh
4059           // because a node on edge may be bound to face
4060           bool all = true;
4061           if ( faceSubMesh ) {
4062             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4063             while ( eIt->more() && all ) {
4064               const SMDS_MeshElement* e = eIt->next();
4065               all = faceSubMesh->Contains( e );
4066             }
4067           }
4068           if ( all )
4069             setMovableNodes.insert( node );
4070           else
4071             checkBoundaryNodes = true;
4072         }
4073         if ( posType == SMDS_TOP_3DSPACE )
4074           checkBoundaryNodes = true;
4075       }
4076
4077       if ( surface.IsNull() )
4078         continue;
4079
4080       // get nodes to check UV
4081       list< const SMDS_MeshNode* > uvCheckNodes;
4082       const SMDS_MeshNode* nodeInFace = 0;
4083       itN = elem->nodesIterator();
4084       nn = 0; nbn =  elem->NbNodes();
4085       if(elem->IsQuadratic())
4086         nbn = nbn/2;
4087       while ( nn++ < nbn ) {
4088         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4089         if ( node->GetPosition()->GetDim() == 2 )
4090           nodeInFace = node;
4091         if ( uvMap.find( node ) == uvMap.end() )
4092           uvCheckNodes.push_back( node );
4093         // add nodes of elems sharing node
4094         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4095         //         while ( eIt->more() ) {
4096         //           const SMDS_MeshElement* e = eIt->next();
4097         //           if ( e != elem ) {
4098         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4099         //             while ( nIt->more() ) {
4100         //               const SMDS_MeshNode* n =
4101         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4102         //               if ( uvMap.find( n ) == uvMap.end() )
4103         //                 uvCheckNodes.push_back( n );
4104         //             }
4105         //           }
4106         //         }
4107       }
4108       // check UV on face
4109       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4110       for ( ; n != uvCheckNodes.end(); ++n ) {
4111         node = *n;
4112         gp_XY uv( 0, 0 );
4113         const SMDS_PositionPtr& pos = node->GetPosition();
4114         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4115         // get existing UV
4116         if ( pos )
4117         {
4118           bool toCheck = true;
4119           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4120         }
4121         // compute not existing UV
4122         bool project = ( posType == SMDS_TOP_3DSPACE );
4123         // double dist1 = DBL_MAX, dist2 = 0;
4124         // if ( posType != SMDS_TOP_3DSPACE ) {
4125         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4126         //   project = dist1 > fToler2;
4127         // }
4128         if ( project ) { // compute new UV
4129           gp_XY newUV;
4130           gp_Pnt pNode = SMESH_TNodeXYZ( node );
4131           if ( !getClosestUV( projector, pNode, newUV )) {
4132             MESSAGE("Node Projection Failed " << node);
4133           }
4134           else {
4135             if ( isUPeriodic )
4136               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4137             if ( isVPeriodic )
4138               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4139             // check new UV
4140             // if ( posType != SMDS_TOP_3DSPACE )
4141             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4142             // if ( dist2 < dist1 )
4143               uv = newUV;
4144           }
4145         }
4146         // store UV in the map
4147         listUV.push_back( uv );
4148         uvMap.insert( make_pair( node, &listUV.back() ));
4149       }
4150     } // loop on not yet smoothed elements
4151
4152     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4153       checkBoundaryNodes = true;
4154
4155     // fix nodes on mesh boundary
4156
4157     if ( checkBoundaryNodes ) {
4158       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4159       map< SMESH_TLink, int >::iterator link_nb;
4160       // put all elements links to linkNbMap
4161       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4162       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4163         const SMDS_MeshElement* elem = (*elemIt);
4164         int nbn =  elem->NbCornerNodes();
4165         // loop on elem links: insert them in linkNbMap
4166         for ( int iN = 0; iN < nbn; ++iN ) {
4167           const SMDS_MeshNode* n1 = elem->GetNode( iN );
4168           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4169           SMESH_TLink link( n1, n2 );
4170           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4171           link_nb->second++;
4172         }
4173       }
4174       // remove nodes that are in links encountered only once from setMovableNodes
4175       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4176         if ( link_nb->second == 1 ) {
4177           setMovableNodes.erase( link_nb->first.node1() );
4178           setMovableNodes.erase( link_nb->first.node2() );
4179         }
4180       }
4181     }
4182
4183     // -----------------------------------------------------
4184     // for nodes on seam edge, compute one more UV ( uvMap2 );
4185     // find movable nodes linked to nodes on seam and which
4186     // are to be smoothed using the second UV ( uvMap2 )
4187     // -----------------------------------------------------
4188
4189     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4190     if ( !surface.IsNull() ) {
4191       TopExp_Explorer eExp( face, TopAbs_EDGE );
4192       for ( ; eExp.More(); eExp.Next() ) {
4193         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4194         if ( !BRep_Tool::IsClosed( edge, face ))
4195           continue;
4196         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4197         if ( !sm ) continue;
4198         // find out which parameter varies for a node on seam
4199         double f,l;
4200         gp_Pnt2d uv1, uv2;
4201         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4202         if ( pcurve.IsNull() ) continue;
4203         uv1 = pcurve->Value( f );
4204         edge.Reverse();
4205         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4206         if ( pcurve.IsNull() ) continue;
4207         uv2 = pcurve->Value( f );
4208         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4209         // assure uv1 < uv2
4210         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4211           std::swap( uv1, uv2 );
4212         // get nodes on seam and its vertices
4213         list< const SMDS_MeshNode* > seamNodes;
4214         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4215         while ( nSeamIt->more() ) {
4216           const SMDS_MeshNode* node = nSeamIt->next();
4217           if ( !isQuadratic || !IsMedium( node ))
4218             seamNodes.push_back( node );
4219         }
4220         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4221         for ( ; vExp.More(); vExp.Next() ) {
4222           sm = aMesh->MeshElements( vExp.Current() );
4223           if ( sm ) {
4224             nSeamIt = sm->GetNodes();
4225             while ( nSeamIt->more() )
4226               seamNodes.push_back( nSeamIt->next() );
4227           }
4228         }
4229         // loop on nodes on seam
4230         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4231         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4232           const SMDS_MeshNode* nSeam = *noSeIt;
4233           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4234           if ( n_uv == uvMap.end() )
4235             continue;
4236           // set the first UV
4237           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4238           // set the second UV
4239           listUV.push_back( *n_uv->second );
4240           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4241           if ( uvMap2.empty() )
4242             uvMap2 = uvMap; // copy the uvMap contents
4243           uvMap2[ nSeam ] = &listUV.back();
4244
4245           // collect movable nodes linked to ones on seam in nodesNearSeam
4246           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4247           while ( eIt->more() ) {
4248             const SMDS_MeshElement* e = eIt->next();
4249             int nbUseMap1 = 0, nbUseMap2 = 0;
4250             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4251             int nn = 0, nbn =  e->NbNodes();
4252             if(e->IsQuadratic()) nbn = nbn/2;
4253             while ( nn++ < nbn )
4254             {
4255               const SMDS_MeshNode* n =
4256                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4257               if (n == nSeam ||
4258                   setMovableNodes.find( n ) == setMovableNodes.end() )
4259                 continue;
4260               // add only nodes being closer to uv2 than to uv1
4261               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4262               //              0.5 * ( n->Y() + nSeam->Y() ),
4263               //              0.5 * ( n->Z() + nSeam->Z() ));
4264               // gp_XY uv;
4265               // getClosestUV( projector, pMid, uv );
4266               double x = uvMap[ n ]->Coord( iPar );
4267               if ( Abs( uv1.Coord( iPar ) - x ) >
4268                    Abs( uv2.Coord( iPar ) - x )) {
4269                 nodesNearSeam.insert( n );
4270                 nbUseMap2++;
4271               }
4272               else
4273                 nbUseMap1++;
4274             }
4275             // for centroidalSmooth all element nodes must
4276             // be on one side of a seam
4277             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4278               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4279               nn = 0;
4280               while ( nn++ < nbn ) {
4281                 const SMDS_MeshNode* n =
4282                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4283                 setMovableNodes.erase( n );
4284               }
4285             }
4286           }
4287         } // loop on nodes on seam
4288       } // loop on edge of a face
4289     } // if ( !face.IsNull() )
4290
4291     if ( setMovableNodes.empty() ) {
4292       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4293       continue; // goto next face
4294     }
4295
4296     // -------------
4297     // SMOOTHING //
4298     // -------------
4299
4300     int it = -1;
4301     double maxRatio = -1., maxDisplacement = -1.;
4302     set<const SMDS_MeshNode*>::iterator nodeToMove;
4303     for ( it = 0; it < theNbIterations; it++ ) {
4304       maxDisplacement = 0.;
4305       nodeToMove = setMovableNodes.begin();
4306       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4307         const SMDS_MeshNode* node = (*nodeToMove);
4308         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4309
4310         // smooth
4311         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4312         if ( theSmoothMethod == LAPLACIAN )
4313           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4314         else
4315           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4316
4317         // node displacement
4318         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4319         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4320         if ( aDispl > maxDisplacement )
4321           maxDisplacement = aDispl;
4322       }
4323       // no node movement => exit
4324       //if ( maxDisplacement < 1.e-16 ) {
4325       if ( maxDisplacement < disttol ) {
4326         MESSAGE("-- no node movement --");
4327         break;
4328       }
4329
4330       // check elements quality
4331       maxRatio  = 0;
4332       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4333       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4334         const SMDS_MeshElement* elem = (*elemIt);
4335         if ( !elem || elem->GetType() != SMDSAbs_Face )
4336           continue;
4337         SMESH::Controls::TSequenceOfXYZ aPoints;
4338         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4339           double aValue = aQualityFunc.GetValue( aPoints );
4340           if ( aValue > maxRatio )
4341             maxRatio = aValue;
4342         }
4343       }
4344       if ( maxRatio <= theTgtAspectRatio ) {
4345         //MESSAGE("-- quality achieved --");
4346         break;
4347       }
4348       if (it+1 == theNbIterations) {
4349         //MESSAGE("-- Iteration limit exceeded --");
4350       }
4351     } // smoothing iterations
4352
4353     // MESSAGE(" Face id: " << *fId <<
4354     //         " Nb iterstions: " << it <<
4355     //         " Displacement: " << maxDisplacement <<
4356     //         " Aspect Ratio " << maxRatio);
4357
4358     // ---------------------------------------
4359     // new nodes positions are computed,
4360     // record movement in DS and set new UV
4361     // ---------------------------------------
4362     nodeToMove = setMovableNodes.begin();
4363     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4364       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4365       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4366       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4367       if ( node_uv != uvMap.end() ) {
4368         gp_XY* uv = node_uv->second;
4369         node->SetPosition
4370           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4371       }
4372     }
4373
4374     // move medium nodes of quadratic elements
4375     if ( isQuadratic )
4376     {
4377       vector<const SMDS_MeshNode*> nodes;
4378       bool checkUV;
4379       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4380       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4381       {
4382         const SMDS_MeshElement* QF = *elemIt;
4383         if ( QF->IsQuadratic() )
4384         {
4385           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4386                         SMDS_MeshElement::iterator() );
4387           nodes.push_back( nodes[0] );
4388           gp_Pnt xyz;
4389           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4390           {
4391             if ( !surface.IsNull() )
4392             {
4393               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4394               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4395               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4396               xyz = surface->Value( uv.X(), uv.Y() );
4397             }
4398             else {
4399               xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4400             }
4401             if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4402               // we have to move a medium node
4403               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4404           }
4405         }
4406       }
4407     }
4408
4409   } // loop on face ids
4410
4411 }
4412
4413 namespace
4414 {
4415   //=======================================================================
4416   //function : isReverse
4417   //purpose  : Return true if normal of prevNodes is not co-directied with
4418   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4419   //           iNotSame is where prevNodes and nextNodes are different.
4420   //           If result is true then future volume orientation is OK
4421   //=======================================================================
4422
4423   bool isReverse(const SMDS_MeshElement*             face,
4424                  const vector<const SMDS_MeshNode*>& prevNodes,
4425                  const vector<const SMDS_MeshNode*>& nextNodes,
4426                  const int                           iNotSame)
4427   {
4428
4429     SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4430     SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4431     gp_XYZ extrDir( pN - pP ), faceNorm;
4432     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4433
4434     return faceNorm * extrDir < 0.0;
4435   }
4436
4437   //================================================================================
4438   /*!
4439    * \brief Assure that theElemSets[0] holds elements, not nodes
4440    */
4441   //================================================================================
4442
4443   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4444   {
4445     if ( !theElemSets[0].empty() &&
4446          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4447     {
4448       std::swap( theElemSets[0], theElemSets[1] );
4449     }
4450     else if ( !theElemSets[1].empty() &&
4451               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4452     {
4453       std::swap( theElemSets[0], theElemSets[1] );
4454     }
4455   }
4456 }
4457
4458 //=======================================================================
4459 /*!
4460  * \brief Create elements by sweeping an element
4461  * \param elem - element to sweep
4462  * \param newNodesItVec - nodes generated from each node of the element
4463  * \param newElems - generated elements
4464  * \param nbSteps - number of sweeping steps
4465  * \param srcElements - to append elem for each generated element
4466  */
4467 //=======================================================================
4468
4469 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4470                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4471                                     list<const SMDS_MeshElement*>&        newElems,
4472                                     const size_t                          nbSteps,
4473                                     SMESH_SequenceOfElemPtr&              srcElements)
4474 {
4475   SMESHDS_Mesh* aMesh = GetMeshDS();
4476
4477   const int           nbNodes = elem->NbNodes();
4478   const int         nbCorners = elem->NbCornerNodes();
4479   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4480                                                           polyhedron creation !!! */
4481   // Loop on elem nodes:
4482   // find new nodes and detect same nodes indices
4483   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4484   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4485   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4486   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4487
4488   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4489   vector<int> sames(nbNodes);
4490   vector<bool> isSingleNode(nbNodes);
4491
4492   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4493     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4494     const SMDS_MeshNode*                         node = nnIt->first;
4495     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4496     if ( listNewNodes.empty() )
4497       return;
4498
4499     itNN   [ iNode ] = listNewNodes.begin();
4500     prevNod[ iNode ] = node;
4501     nextNod[ iNode ] = listNewNodes.front();
4502
4503     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4504                                                              corner node of linear */
4505     if ( prevNod[ iNode ] != nextNod [ iNode ])
4506       nbDouble += !isSingleNode[iNode];
4507
4508     if( iNode < nbCorners ) { // check corners only
4509       if ( prevNod[ iNode ] == nextNod [ iNode ])
4510         sames[nbSame++] = iNode;
4511       else
4512         iNotSameNode = iNode;
4513     }
4514   }
4515
4516   if ( nbSame == nbNodes || nbSame > 2) {
4517     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4518     return;
4519   }
4520
4521   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4522   {
4523     // fix nodes order to have bottom normal external
4524     if ( baseType == SMDSEntity_Polygon )
4525     {
4526       std::reverse( itNN.begin(), itNN.end() );
4527       std::reverse( prevNod.begin(), prevNod.end() );
4528       std::reverse( midlNod.begin(), midlNod.end() );
4529       std::reverse( nextNod.begin(), nextNod.end() );
4530       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4531     }
4532     else
4533     {
4534       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4535       SMDS_MeshCell::applyInterlace( ind, itNN );
4536       SMDS_MeshCell::applyInterlace( ind, prevNod );
4537       SMDS_MeshCell::applyInterlace( ind, nextNod );
4538       SMDS_MeshCell::applyInterlace( ind, midlNod );
4539       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4540       if ( nbSame > 0 )
4541       {
4542         sames[nbSame] = iNotSameNode;
4543         for ( int j = 0; j <= nbSame; ++j )
4544           for ( size_t i = 0; i < ind.size(); ++i )
4545             if ( ind[i] == sames[j] )
4546             {
4547               sames[j] = i;
4548               break;
4549             }
4550         iNotSameNode = sames[nbSame];
4551       }
4552     }
4553   }
4554   else if ( elem->GetType() == SMDSAbs_Edge )
4555   {
4556     // orient a new face same as adjacent one
4557     int i1, i2;
4558     const SMDS_MeshElement* e;
4559     TIDSortedElemSet dummy;
4560     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4561         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4562         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4563     {
4564       // there is an adjacent face, check order of nodes in it
4565       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4566       if ( sameOrder )
4567       {
4568         std::swap( itNN[0],    itNN[1] );
4569         std::swap( prevNod[0], prevNod[1] );
4570         std::swap( nextNod[0], nextNod[1] );
4571 #if defined(__APPLE__)
4572         std::swap( isSingleNode[0], isSingleNode[1] );
4573 #else
4574         isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4575 #endif
4576         if ( nbSame > 0 )
4577           sames[0] = 1 - sames[0];
4578         iNotSameNode = 1 - iNotSameNode;
4579       }
4580     }
4581   }
4582
4583   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4584   if ( nbSame > 0 ) {
4585     iSameNode    = sames[ nbSame-1 ];
4586     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4587     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4588     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4589   }
4590
4591   if ( baseType == SMDSEntity_Polygon )
4592   {
4593     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4594     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4595   }
4596   else if ( baseType == SMDSEntity_Quad_Polygon )
4597   {
4598     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4599     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4600   }
4601
4602   // make new elements
4603   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4604   {
4605     // get next nodes
4606     for ( iNode = 0; iNode < nbNodes; iNode++ )
4607     {
4608       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4609       nextNod[ iNode ] = *itNN[ iNode ]++;
4610     }
4611
4612     SMDS_MeshElement* aNewElem = 0;
4613     /*if(!elem->IsPoly())*/ {
4614       switch ( baseType ) {
4615       case SMDSEntity_0D:
4616       case SMDSEntity_Node: { // sweep NODE
4617         if ( nbSame == 0 ) {
4618           if ( isSingleNode[0] )
4619             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4620           else
4621             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4622         }
4623         else
4624           return;
4625         break;
4626       }
4627       case SMDSEntity_Edge: { // sweep EDGE
4628         if ( nbDouble == 0 )
4629         {
4630           if ( nbSame == 0 ) // ---> quadrangle
4631             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4632                                       nextNod[ 1 ], nextNod[ 0 ] );
4633           else               // ---> triangle
4634             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4635                                       nextNod[ iNotSameNode ] );
4636         }
4637         else                 // ---> polygon
4638         {
4639           vector<const SMDS_MeshNode*> poly_nodes;
4640           poly_nodes.push_back( prevNod[0] );
4641           poly_nodes.push_back( prevNod[1] );
4642           if ( prevNod[1] != nextNod[1] )
4643           {
4644             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4645             poly_nodes.push_back( nextNod[1] );
4646           }
4647           if ( prevNod[0] != nextNod[0] )
4648           {
4649             poly_nodes.push_back( nextNod[0] );
4650             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4651           }
4652           switch ( poly_nodes.size() ) {
4653           case 3:
4654             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4655             break;
4656           case 4:
4657             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4658                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4659             break;
4660           default:
4661             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4662           }
4663         }
4664         break;
4665       }
4666       case SMDSEntity_Triangle: // TRIANGLE --->
4667         {
4668           if ( nbDouble > 0 ) break;
4669           if ( nbSame == 0 )       // ---> pentahedron
4670             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4671                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4672
4673           else if ( nbSame == 1 )  // ---> pyramid
4674             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4675                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4676                                          nextNod[ iSameNode ]);
4677
4678           else // 2 same nodes:       ---> tetrahedron
4679             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4680                                          nextNod[ iNotSameNode ]);
4681           break;
4682         }
4683       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4684         {
4685           if ( nbSame == 2 )
4686             return;
4687           if ( nbDouble+nbSame == 2 )
4688           {
4689             if(nbSame==0) {      // ---> quadratic quadrangle
4690               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4691                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4692             }
4693             else { //(nbSame==1) // ---> quadratic triangle
4694               if(sames[0]==2) {
4695                 return; // medium node on axis
4696               }
4697               else if(sames[0]==0)
4698                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4699                                           prevNod[2], midlNod[1], nextNod[2] );
4700               else // sames[0]==1
4701                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4702                                           prevNod[2], nextNod[2], midlNod[0]);
4703             }
4704           }
4705           else if ( nbDouble == 3 )
4706           {
4707             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4708               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4709                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4710             }
4711           }
4712           else
4713             return;
4714           break;
4715         }
4716       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4717         if ( nbDouble > 0 ) break;
4718
4719         if ( nbSame == 0 )       // ---> hexahedron
4720           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4721                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4722
4723         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4724           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4725                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4726                                        nextNod[ iSameNode ]);
4727           newElems.push_back( aNewElem );
4728           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4729                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4730                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4731         }
4732         else if ( nbSame == 2 ) { // ---> pentahedron
4733           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4734             // iBeforeSame is same too
4735             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4736                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4737                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4738           else
4739             // iAfterSame is same too
4740             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4741                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4742                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4743         }
4744         break;
4745       }
4746       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4747       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4748         if ( nbDouble+nbSame != 3 ) break;
4749         if(nbSame==0) {
4750           // --->  pentahedron with 15 nodes
4751           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4752                                        nextNod[0], nextNod[1], nextNod[2],
4753                                        prevNod[3], prevNod[4], prevNod[5],
4754                                        nextNod[3], nextNod[4], nextNod[5],
4755                                        midlNod[0], midlNod[1], midlNod[2]);
4756         }
4757         else if(nbSame==1) {
4758           // --->  2d order pyramid of 13 nodes
4759           int apex = iSameNode;
4760           int i0 = ( apex + 1 ) % nbCorners;
4761           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4762           int i0a = apex + 3;
4763           int i1a = i1 + 3;
4764           int i01 = i0 + 3;
4765           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4766                                       nextNod[i0], nextNod[i1], prevNod[apex],
4767                                       prevNod[i01], midlNod[i0],
4768                                       nextNod[i01], midlNod[i1],
4769                                       prevNod[i1a], prevNod[i0a],
4770                                       nextNod[i0a], nextNod[i1a]);
4771         }
4772         else if(nbSame==2) {
4773           // --->  2d order tetrahedron of 10 nodes
4774           int n1 = iNotSameNode;
4775           int n2 = ( n1 + 1             ) % nbCorners;
4776           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4777           int n12 = n1 + 3;
4778           int n23 = n2 + 3;
4779           int n31 = n3 + 3;
4780           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4781                                        prevNod[n12], prevNod[n23], prevNod[n31],
4782                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4783         }
4784         break;
4785       }
4786       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4787         if( nbSame == 0 ) {
4788           if ( nbDouble != 4 ) break;
4789           // --->  hexahedron with 20 nodes
4790           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4791                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4792                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4793                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4794                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4795         }
4796         else if(nbSame==1) {
4797           // ---> pyramid + pentahedron - can not be created since it is needed
4798           // additional middle node at the center of face
4799           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4800           return;
4801         }
4802         else if( nbSame == 2 ) {
4803           if ( nbDouble != 2 ) break;
4804           // --->  2d order Pentahedron with 15 nodes
4805           int n1,n2,n4,n5;
4806           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4807             // iBeforeSame is same too
4808             n1 = iBeforeSame;
4809             n2 = iOpposSame;
4810             n4 = iSameNode;
4811             n5 = iAfterSame;
4812           }
4813           else {
4814             // iAfterSame is same too
4815             n1 = iSameNode;
4816             n2 = iBeforeSame;
4817             n4 = iAfterSame;
4818             n5 = iOpposSame;
4819           }
4820           int n12 = n2 + 4;
4821           int n45 = n4 + 4;
4822           int n14 = n1 + 4;
4823           int n25 = n5 + 4;
4824           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4825                                        prevNod[n4], prevNod[n5], nextNod[n5],
4826                                        prevNod[n12], midlNod[n2], nextNod[n12],
4827                                        prevNod[n45], midlNod[n5], nextNod[n45],
4828                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4829         }
4830         break;
4831       }
4832       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4833
4834         if( nbSame == 0 && nbDouble == 9 ) {
4835           // --->  tri-quadratic hexahedron with 27 nodes
4836           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4837                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4838                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4839                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4840                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4841                                        prevNod[8], // bottom center
4842                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4843                                        nextNod[8], // top center
4844                                        midlNod[8]);// elem center
4845         }
4846         else
4847         {
4848           return;
4849         }
4850         break;
4851       }
4852       case SMDSEntity_Polygon: { // sweep POLYGON
4853
4854         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4855           // --->  hexagonal prism
4856           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4857                                        prevNod[3], prevNod[4], prevNod[5],
4858                                        nextNod[0], nextNod[1], nextNod[2],
4859                                        nextNod[3], nextNod[4], nextNod[5]);
4860         }
4861         break;
4862       }
4863       case SMDSEntity_Ball:
4864         return;
4865
4866       default:
4867         break;
4868       } // switch ( baseType )
4869     } // scope
4870
4871     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4872     {
4873       if ( baseType != SMDSEntity_Polygon )
4874       {
4875         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4876         SMDS_MeshCell::applyInterlace( ind, prevNod );
4877         SMDS_MeshCell::applyInterlace( ind, nextNod );
4878         SMDS_MeshCell::applyInterlace( ind, midlNod );
4879         SMDS_MeshCell::applyInterlace( ind, itNN );
4880         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4881         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4882       }
4883       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4884       vector<int> quantities (nbNodes + 2);
4885       polyedre_nodes.clear();
4886       quantities.clear();
4887
4888       // bottom of prism
4889       for (int inode = 0; inode < nbNodes; inode++)
4890         polyedre_nodes.push_back( prevNod[inode] );
4891       quantities.push_back( nbNodes );
4892
4893       // top of prism
4894       polyedre_nodes.push_back( nextNod[0] );
4895       for (int inode = nbNodes; inode-1; --inode )
4896         polyedre_nodes.push_back( nextNod[inode-1] );
4897       quantities.push_back( nbNodes );
4898
4899       // side faces
4900       // 3--6--2
4901       // |     |
4902       // 7     5
4903       // |     |
4904       // 0--4--1
4905       const int iQuad = elem->IsQuadratic();
4906       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4907       {
4908         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4909         int inextface = (iface+1+iQuad) % nbNodes;
4910         int imid      = (iface+1) % nbNodes;
4911         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4912         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4913         polyedre_nodes.push_back( prevNod[iface] );             // 1
4914         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4915         {
4916           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4917           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4918         }
4919         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4920         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4921         {
4922           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4923           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4924         }
4925         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4926         if ( nbFaceNodes > 2 )
4927           quantities.push_back( nbFaceNodes );
4928         else // degenerated face
4929           polyedre_nodes.resize( prevNbNodes );
4930       }
4931       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4932
4933     } // try to create a polyherdal prism
4934
4935     if ( aNewElem ) {
4936       newElems.push_back( aNewElem );
4937       myLastCreatedElems.Append(aNewElem);
4938       srcElements.Append( elem );
4939     }
4940
4941     // set new prev nodes
4942     for ( iNode = 0; iNode < nbNodes; iNode++ )
4943       prevNod[ iNode ] = nextNod[ iNode ];
4944
4945   } // loop on steps
4946 }
4947
4948 //=======================================================================
4949 /*!
4950  * \brief Create 1D and 2D elements around swept elements
4951  * \param mapNewNodes - source nodes and ones generated from them
4952  * \param newElemsMap - source elements and ones generated from them
4953  * \param elemNewNodesMap - nodes generated from each node of each element
4954  * \param elemSet - all swept elements
4955  * \param nbSteps - number of sweeping steps
4956  * \param srcElements - to append elem for each generated element
4957  */
4958 //=======================================================================
4959
4960 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4961                                   TTElemOfElemListMap &    newElemsMap,
4962                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4963                                   TIDSortedElemSet&        elemSet,
4964                                   const int                nbSteps,
4965                                   SMESH_SequenceOfElemPtr& srcElements)
4966 {
4967   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4968   SMESHDS_Mesh* aMesh = GetMeshDS();
4969
4970   // Find nodes belonging to only one initial element - sweep them into edges.
4971
4972   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4973   for ( ; nList != mapNewNodes.end(); nList++ )
4974   {
4975     const SMDS_MeshNode* node =
4976       static_cast<const SMDS_MeshNode*>( nList->first );
4977     if ( newElemsMap.count( node ))
4978       continue; // node was extruded into edge
4979     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4980     int nbInitElems = 0;
4981     const SMDS_MeshElement* el = 0;
4982     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4983     while ( eIt->more() && nbInitElems < 2 ) {
4984       const SMDS_MeshElement* e = eIt->next();
4985       SMDSAbs_ElementType  type = e->GetType();
4986       if ( type == SMDSAbs_Volume ||
4987            type < highType ||
4988            !elemSet.count(e))
4989         continue;
4990       if ( type > highType ) {
4991         nbInitElems = 0;
4992         highType    = type;
4993       }
4994       el = e;
4995       ++nbInitElems;
4996     }
4997     if ( nbInitElems == 1 ) {
4998       bool NotCreateEdge = el && el->IsMediumNode(node);
4999       if(!NotCreateEdge) {
5000         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5001         list<const SMDS_MeshElement*> newEdges;
5002         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5003       }
5004     }
5005   }
5006
5007   // Make a ceiling for each element ie an equal element of last new nodes.
5008   // Find free links of faces - make edges and sweep them into faces.
5009
5010   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5011
5012   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
5013   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5014   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5015   {
5016     const SMDS_MeshElement* elem = itElem->first;
5017     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5018
5019     if(itElem->second.size()==0) continue;
5020
5021     const bool isQuadratic = elem->IsQuadratic();
5022
5023     if ( elem->GetType() == SMDSAbs_Edge ) {
5024       // create a ceiling edge
5025       if ( !isQuadratic ) {
5026         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5027                                vecNewNodes[ 1 ]->second.back())) {
5028           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5029                                                    vecNewNodes[ 1 ]->second.back()));
5030           srcElements.Append( elem );
5031         }
5032       }
5033       else {
5034         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5035                                vecNewNodes[ 1 ]->second.back(),
5036                                vecNewNodes[ 2 ]->second.back())) {
5037           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5038                                                    vecNewNodes[ 1 ]->second.back(),
5039                                                    vecNewNodes[ 2 ]->second.back()));
5040           srcElements.Append( elem );
5041         }
5042       }
5043     }
5044     if ( elem->GetType() != SMDSAbs_Face )
5045       continue;
5046
5047     bool hasFreeLinks = false;
5048
5049     TIDSortedElemSet avoidSet;
5050     avoidSet.insert( elem );
5051
5052     set<const SMDS_MeshNode*> aFaceLastNodes;
5053     int iNode, nbNodes = vecNewNodes.size();
5054     if ( !isQuadratic ) {
5055       // loop on the face nodes
5056       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5057         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5058         // look for free links of the face
5059         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5060         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5061         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5062         // check if a link n1-n2 is free
5063         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5064           hasFreeLinks = true;
5065           // make a new edge and a ceiling for a new edge
5066           const SMDS_MeshElement* edge;
5067           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5068             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5069             srcElements.Append( myLastCreatedElems.Last() );
5070           }
5071           n1 = vecNewNodes[ iNode ]->second.back();
5072           n2 = vecNewNodes[ iNext ]->second.back();
5073           if ( !aMesh->FindEdge( n1, n2 )) {
5074             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5075             srcElements.Append( edge );
5076           }
5077         }
5078       }
5079     }
5080     else { // elem is quadratic face
5081       int nbn = nbNodes/2;
5082       for ( iNode = 0; iNode < nbn; iNode++ ) {
5083         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5084         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5085         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5086         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5087         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5088         // check if a link is free
5089         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5090              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5091              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5092           hasFreeLinks = true;
5093           // make an edge and a ceiling for a new edge
5094           // find medium node
5095           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5096             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5097             srcElements.Append( elem );
5098           }
5099           n1 = vecNewNodes[ iNode ]->second.back();
5100           n2 = vecNewNodes[ iNext ]->second.back();
5101           n3 = vecNewNodes[ iNode+nbn ]->second.back();
5102           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5103             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5104             srcElements.Append( elem );
5105           }
5106         }
5107       }
5108       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5109         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5110       }
5111     }
5112
5113     // sweep free links into faces
5114
5115     if ( hasFreeLinks ) {
5116       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5117       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5118
5119       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5120       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5121       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5122         initNodeSet.insert( vecNewNodes[ iNode ]->first );
5123         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5124       }
5125       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
5126         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5127         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5128       }
5129       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5130         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5131         std::advance( v, volNb );
5132         // find indices of free faces of a volume and their source edges
5133         list< int > freeInd;
5134         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5135         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5136         int iF, nbF = vTool.NbFaces();
5137         for ( iF = 0; iF < nbF; iF ++ ) {
5138           if (vTool.IsFreeFace( iF ) &&
5139               vTool.GetFaceNodes( iF, faceNodeSet ) &&
5140               initNodeSet != faceNodeSet) // except an initial face
5141           {
5142             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5143               continue;
5144             if ( faceNodeSet == initNodeSetNoCenter )
5145               continue;
5146             freeInd.push_back( iF );
5147             // find source edge of a free face iF
5148             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5149             vector<const SMDS_MeshNode*>::iterator lastCommom;
5150             commonNodes.resize( nbNodes, 0 );
5151             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5152                                                 initNodeSet.begin(), initNodeSet.end(),
5153                                                 commonNodes.begin());
5154             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5155               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5156             else
5157               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5158 #ifdef _DEBUG_
5159             if ( !srcEdges.back() )
5160             {
5161               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5162                    << iF << " of volume #" << vTool.ID() << endl;
5163             }
5164 #endif
5165           }
5166         }
5167         if ( freeInd.empty() )
5168           continue;
5169
5170         // create wall faces for all steps;
5171         // if such a face has been already created by sweep of edge,
5172         // assure that its orientation is OK
5173         for ( int iStep = 0; iStep < nbSteps; iStep++ )
5174         {
5175           vTool.Set( *v, /*ignoreCentralNodes=*/false );
5176           vTool.SetExternalNormal();
5177           const int nextShift = vTool.IsForward() ? +1 : -1;
5178           list< int >::iterator ind = freeInd.begin();
5179           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5180           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5181           {
5182             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5183             int nbn = vTool.NbFaceNodes( *ind );
5184             const SMDS_MeshElement * f = 0;
5185             if ( nbn == 3 )              ///// triangle
5186             {
5187               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5188               if ( !f ||
5189                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5190               {
5191                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5192                                                      nodes[ 1 ],
5193                                                      nodes[ 1 + nextShift ] };
5194                 if ( f )
5195                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5196                 else
5197                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5198                                                             newOrder[ 2 ] ));
5199               }
5200             }
5201             else if ( nbn == 4 )       ///// quadrangle
5202             {
5203               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5204               if ( !f ||
5205                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5206               {
5207                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5208                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
5209                 if ( f )
5210                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5211                 else
5212                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5213                                                             newOrder[ 2 ], newOrder[ 3 ]));
5214               }
5215             }
5216             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5217             {
5218               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5219               if ( !f ||
5220                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5221               {
5222                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5223                                                      nodes[2],
5224                                                      nodes[2 + 2*nextShift],
5225                                                      nodes[3 - 2*nextShift],
5226                                                      nodes[3],
5227                                                      nodes[3 + 2*nextShift]};
5228                 if ( f )
5229                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5230                 else
5231                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5232                                                             newOrder[ 1 ],
5233                                                             newOrder[ 2 ],
5234                                                             newOrder[ 3 ],
5235                                                             newOrder[ 4 ],
5236                                                             newOrder[ 5 ] ));
5237               }
5238             }
5239             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5240             {
5241               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5242                                    nodes[1], nodes[3], nodes[5], nodes[7] );
5243               if ( !f ||
5244                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5245               {
5246                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5247                                                      nodes[4 - 2*nextShift],
5248                                                      nodes[4],
5249                                                      nodes[4 + 2*nextShift],
5250                                                      nodes[1],
5251                                                      nodes[5 - 2*nextShift],
5252                                                      nodes[5],
5253                                                      nodes[5 + 2*nextShift] };
5254                 if ( f )
5255                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5256                 else
5257                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5258                                                            newOrder[ 2 ], newOrder[ 3 ],
5259                                                            newOrder[ 4 ], newOrder[ 5 ],
5260                                                            newOrder[ 6 ], newOrder[ 7 ]));
5261               }
5262             }
5263             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5264             {
5265               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5266                                       SMDSAbs_Face, /*noMedium=*/false);
5267               if ( !f ||
5268                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5269               {
5270                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5271                                                      nodes[4 - 2*nextShift],
5272                                                      nodes[4],
5273                                                      nodes[4 + 2*nextShift],
5274                                                      nodes[1],
5275                                                      nodes[5 - 2*nextShift],
5276                                                      nodes[5],
5277                                                      nodes[5 + 2*nextShift],
5278                                                      nodes[8] };
5279                 if ( f )
5280                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5281                 else
5282                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5283                                                            newOrder[ 2 ], newOrder[ 3 ],
5284                                                            newOrder[ 4 ], newOrder[ 5 ],
5285                                                            newOrder[ 6 ], newOrder[ 7 ],
5286                                                            newOrder[ 8 ]));
5287               }
5288             }
5289             else  //////// polygon
5290             {
5291               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5292               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5293               if ( !f ||
5294                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5295               {
5296                 if ( !vTool.IsForward() )
5297                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5298                 if ( f )
5299                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5300                 else
5301                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5302               }
5303             }
5304
5305             while ( srcElements.Length() < myLastCreatedElems.Length() )
5306               srcElements.Append( *srcEdge );
5307
5308           }  // loop on free faces
5309
5310           // go to the next volume
5311           iVol = 0;
5312           while ( iVol++ < nbVolumesByStep ) v++;
5313
5314         } // loop on steps
5315       } // loop on volumes of one step
5316     } // sweep free links into faces
5317
5318     // Make a ceiling face with a normal external to a volume
5319
5320     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5321     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5322     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5323
5324     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5325       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5326       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5327     }
5328     if ( iF >= 0 )
5329     {
5330       lastVol.SetExternalNormal();
5331       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5332       const               int nbn = lastVol.NbFaceNodes( iF );
5333       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5334       if ( !hasFreeLinks ||
5335            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5336       {
5337         const vector<int>& interlace =
5338           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5339         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5340
5341         AddElement( nodeVec, anyFace.Init( elem ));
5342
5343         while ( srcElements.Length() < myLastCreatedElems.Length() )
5344           srcElements.Append( elem );
5345       }
5346     }
5347   } // loop on swept elements
5348 }
5349
5350 //=======================================================================
5351 //function : RotationSweep
5352 //purpose  :
5353 //=======================================================================
5354
5355 SMESH_MeshEditor::PGroupIDs
5356 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5357                                 const gp_Ax1&      theAxis,
5358                                 const double       theAngle,
5359                                 const int          theNbSteps,
5360                                 const double       theTol,
5361                                 const bool         theMakeGroups,
5362                                 const bool         theMakeWalls)
5363 {
5364   myLastCreatedElems.Clear();
5365   myLastCreatedNodes.Clear();
5366
5367   // source elements for each generated one
5368   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5369
5370   gp_Trsf aTrsf;
5371   aTrsf.SetRotation( theAxis, theAngle );
5372   gp_Trsf aTrsf2;
5373   aTrsf2.SetRotation( theAxis, theAngle/2. );
5374
5375   gp_Lin aLine( theAxis );
5376   double aSqTol = theTol * theTol;
5377
5378   SMESHDS_Mesh* aMesh = GetMeshDS();
5379
5380   TNodeOfNodeListMap mapNewNodes;
5381   TElemOfVecOfNnlmiMap mapElemNewNodes;
5382   TTElemOfElemListMap newElemsMap;
5383
5384   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5385                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5386                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5387   // loop on theElemSets
5388   setElemsFirst( theElemSets );
5389   TIDSortedElemSet::iterator itElem;
5390   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5391   {
5392     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5393     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5394       const SMDS_MeshElement* elem = *itElem;
5395       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5396         continue;
5397       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5398       newNodesItVec.reserve( elem->NbNodes() );
5399
5400       // loop on elem nodes
5401       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5402       while ( itN->more() )
5403       {
5404         const SMDS_MeshNode* node = cast2Node( itN->next() );
5405
5406         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5407         double coord[3];
5408         aXYZ.Coord( coord[0], coord[1], coord[2] );
5409         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5410
5411         // check if a node has been already sweeped
5412         TNodeOfNodeListMapItr nIt =
5413           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5414         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5415         if ( listNewNodes.empty() )
5416         {
5417           // check if we are to create medium nodes between corner ones
5418           bool needMediumNodes = false;
5419           if ( isQuadraticMesh )
5420           {
5421             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5422             while (it->more() && !needMediumNodes )
5423             {
5424               const SMDS_MeshElement* invElem = it->next();
5425               if ( invElem != elem && !theElems.count( invElem )) continue;
5426               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5427               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5428                 needMediumNodes = true;
5429             }
5430           }
5431
5432           // make new nodes
5433           const SMDS_MeshNode * newNode = node;
5434           for ( int i = 0; i < theNbSteps; i++ ) {
5435             if ( !isOnAxis ) {
5436               if ( needMediumNodes )  // create a medium node
5437               {
5438                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5439                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5440                 myLastCreatedNodes.Append(newNode);
5441                 srcNodes.Append( node );
5442                 listNewNodes.push_back( newNode );
5443                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5444               }
5445               else {
5446                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5447               }
5448               // create a corner node
5449               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5450               myLastCreatedNodes.Append(newNode);
5451               srcNodes.Append( node );
5452               listNewNodes.push_back( newNode );
5453             }
5454             else {
5455               listNewNodes.push_back( newNode );
5456               // if ( needMediumNodes )
5457               //   listNewNodes.push_back( newNode );
5458             }
5459           }
5460         }
5461         newNodesItVec.push_back( nIt );
5462       }
5463       // make new elements
5464       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5465     }
5466   }
5467
5468   if ( theMakeWalls )
5469     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5470
5471   PGroupIDs newGroupIDs;
5472   if ( theMakeGroups )
5473     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5474
5475   return newGroupIDs;
5476 }
5477
5478 //=======================================================================
5479 //function : ExtrusParam
5480 //purpose  : standard construction
5481 //=======================================================================
5482
5483 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5484                                             const int                theNbSteps,
5485                                             const std::list<double>& theScales,
5486                                             const gp_XYZ*            theBasePoint,
5487                                             const int                theFlags,
5488                                             const double             theTolerance):
5489   myDir( theStep ),
5490   myBaseP( Precision::Infinite(), 0, 0 ),
5491   myFlags( theFlags ),
5492   myTolerance( theTolerance ),
5493   myElemsToUse( NULL )
5494 {
5495   mySteps = new TColStd_HSequenceOfReal;
5496   const double stepSize = theStep.Magnitude();
5497   for (int i=1; i<=theNbSteps; i++ )
5498     mySteps->Append( stepSize );
5499
5500   int nbScales = theScales.size();
5501   if ( nbScales > 0 )
5502   {
5503     if ( IsLinearVariation() && nbScales < theNbSteps )
5504     {
5505       myScales.reserve( theNbSteps );
5506       std::list<double>::const_iterator scale = theScales.begin();
5507       double prevScale = 1.0;
5508       for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
5509       {
5510         int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
5511         int    stDelta = Max( 1, iStep - myScales.size());
5512         double scDelta = ( *scale - prevScale ) / stDelta;
5513         for ( int iStep = 0; iStep < stDelta; ++iStep )
5514         {
5515           myScales.push_back( prevScale + scDelta );
5516           prevScale = myScales.back();
5517         }
5518         prevScale = *scale;
5519       }
5520     }
5521     else
5522     {
5523       myScales.assign( theScales.begin(), theScales.end() );
5524     }
5525   }
5526   if ( theBasePoint )
5527   {
5528     myBaseP = *theBasePoint;
5529   }
5530
5531   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5532       ( theTolerance > 0 ))
5533   {
5534     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5535   }
5536   else
5537   {
5538     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5539   }
5540 }
5541
5542 //=======================================================================
5543 //function : ExtrusParam
5544 //purpose  : steps are given explicitly
5545 //=======================================================================
5546
5547 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5548                                             Handle(TColStd_HSequenceOfReal) theSteps,
5549                                             const int                       theFlags,
5550                                             const double                    theTolerance):
5551   myDir( theDir ),
5552   mySteps( theSteps ),
5553   myFlags( theFlags ),
5554   myTolerance( theTolerance ),
5555   myElemsToUse( NULL )
5556 {
5557   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5558       ( theTolerance > 0 ))
5559   {
5560     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5561   }
5562   else
5563   {
5564     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5565   }
5566 }
5567
5568 //=======================================================================
5569 //function : ExtrusParam
5570 //purpose  : for extrusion by normal
5571 //=======================================================================
5572
5573 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5574                                             const int    theNbSteps,
5575                                             const int    theFlags,
5576                                             const int    theDim ):
5577   myDir( 1,0,0 ),
5578   mySteps( new TColStd_HSequenceOfReal ),
5579   myFlags( theFlags ),
5580   myTolerance( 0 ),
5581   myElemsToUse( NULL )
5582 {
5583   for (int i = 0; i < theNbSteps; i++ )
5584     mySteps->Append( theStepSize );
5585
5586   if ( theDim == 1 )
5587   {
5588     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5589   }
5590   else
5591   {
5592     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5593   }
5594 }
5595
5596 //=======================================================================
5597 //function : ExtrusParam::SetElementsToUse
5598 //purpose  : stores elements to use for extrusion by normal, depending on
5599 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5600 //           define myBaseP for scaling
5601 //=======================================================================
5602
5603 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5604                                                       const TIDSortedElemSet& nodes )
5605 {
5606   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5607
5608   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5609   {
5610     myBaseP.SetCoord( 0.,0.,0. );
5611     TIDSortedElemSet newNodes;
5612
5613     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5614     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5615     {
5616       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5617       TIDSortedElemSet::const_iterator itElem = elements.begin();
5618       for ( ; itElem != elements.end(); itElem++ )
5619       {
5620         const SMDS_MeshElement* elem = *itElem;
5621         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5622         while ( itN->more() ) {
5623           const SMDS_MeshElement* node = itN->next();
5624           if ( newNodes.insert( node ).second )
5625             myBaseP += SMESH_TNodeXYZ( node );
5626         }
5627       }
5628     }
5629     myBaseP /= newNodes.size();
5630   }
5631 }
5632
5633 //=======================================================================
5634 //function : ExtrusParam::beginStepIter
5635 //purpose  : prepare iteration on steps
5636 //=======================================================================
5637
5638 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5639 {
5640   myWithMediumNodes = withMediumNodes;
5641   myNextStep = 1;
5642   myCurSteps.clear();
5643 }
5644 //=======================================================================
5645 //function : ExtrusParam::moreSteps
5646 //purpose  : are there more steps?
5647 //=======================================================================
5648
5649 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5650 {
5651   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5652 }
5653 //=======================================================================
5654 //function : ExtrusParam::nextStep
5655 //purpose  : returns the next step
5656 //=======================================================================
5657
5658 double SMESH_MeshEditor::ExtrusParam::nextStep()
5659 {
5660   double res = 0;
5661   if ( !myCurSteps.empty() )
5662   {
5663     res = myCurSteps.back();
5664     myCurSteps.pop_back();
5665   }
5666   else if ( myNextStep <= mySteps->Length() )
5667   {
5668     myCurSteps.push_back( mySteps->Value( myNextStep ));
5669     ++myNextStep;
5670     if ( myWithMediumNodes )
5671     {
5672       myCurSteps.back() /= 2.;
5673       myCurSteps.push_back( myCurSteps.back() );
5674     }
5675     res = nextStep();
5676   }
5677   return res;
5678 }
5679
5680 //=======================================================================
5681 //function : ExtrusParam::makeNodesByDir
5682 //purpose  : create nodes for standard extrusion
5683 //=======================================================================
5684
5685 int SMESH_MeshEditor::ExtrusParam::
5686 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5687                 const SMDS_MeshNode*              srcNode,
5688                 std::list<const SMDS_MeshNode*> & newNodes,
5689                 const bool                        makeMediumNodes)
5690 {
5691   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5692
5693   int nbNodes = 0;
5694   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5695   {
5696     p += myDir.XYZ() * nextStep();
5697     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5698     newNodes.push_back( newNode );
5699   }
5700
5701   if ( !myScales.empty() )
5702   {
5703     if ( makeMediumNodes && myMediumScales.empty() )
5704     {
5705       myMediumScales.resize( myScales.size() );
5706       double prevFactor = 1.;
5707       for ( size_t i = 0; i < myScales.size(); ++i )
5708       {
5709         myMediumScales[i] = 0.5 * ( prevFactor + myScales[i] );
5710         prevFactor = myScales[i];
5711       }
5712     }
5713     typedef std::vector<double>::iterator ScaleIt;
5714     ScaleIt scales[] = { myScales.begin(), myMediumScales.begin() };
5715
5716     size_t iSc = 0, nbScales = myScales.size() + myMediumScales.size();
5717
5718     gp_XYZ center = myBaseP;
5719     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5720     size_t iN  = 0;
5721     for ( beginStepIter( makeMediumNodes ); moreSteps() && ( iN < nbScales ); ++nIt, ++iN )
5722     {
5723       center += myDir.XYZ() * nextStep();
5724
5725       iSc += int( makeMediumNodes );
5726       ScaleIt& scale = scales[ iSc % 2 ];
5727       
5728       gp_XYZ xyz = SMESH_TNodeXYZ( *nIt );
5729       xyz = ( *scale * ( xyz - center )) + center;
5730       mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5731
5732       ++scale;
5733     }
5734   }
5735   return nbNodes;
5736 }
5737
5738 //=======================================================================
5739 //function : ExtrusParam::makeNodesByDirAndSew
5740 //purpose  : create nodes for standard extrusion with sewing
5741 //=======================================================================
5742
5743 int SMESH_MeshEditor::ExtrusParam::
5744 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5745                       const SMDS_MeshNode*              srcNode,
5746                       std::list<const SMDS_MeshNode*> & newNodes,
5747                       const bool                        makeMediumNodes)
5748 {
5749   gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5750
5751   int nbNodes = 0;
5752   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5753   {
5754     P1 += myDir.XYZ() * nextStep();
5755
5756     // try to search in sequence of existing nodes
5757     // if myNodes.Length()>0 we 'nave to use given sequence
5758     // else - use all nodes of mesh
5759     const SMDS_MeshNode * node = 0;
5760     if ( myNodes.Length() > 0 ) {
5761       int i;
5762       for(i=1; i<=myNodes.Length(); i++) {
5763         gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5764         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5765         {
5766           node = myNodes.Value(i);
5767           break;
5768         }
5769       }
5770     }
5771     else {
5772       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5773       while(itn->more()) {
5774         SMESH_TNodeXYZ P2( itn->next() );
5775         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5776         {
5777           node = P2._node;
5778           break;
5779         }
5780       }
5781     }
5782
5783     if ( !node )
5784       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5785
5786     newNodes.push_back( node );
5787
5788   } // loop on steps
5789
5790   return nbNodes;
5791 }
5792
5793 //=======================================================================
5794 //function : ExtrusParam::makeNodesByNormal2D
5795 //purpose  : create nodes for extrusion using normals of faces
5796 //=======================================================================
5797
5798 int SMESH_MeshEditor::ExtrusParam::
5799 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5800                      const SMDS_MeshNode*              srcNode,
5801                      std::list<const SMDS_MeshNode*> & newNodes,
5802                      const bool                        makeMediumNodes)
5803 {
5804   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5805
5806   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5807
5808   // get normals to faces sharing srcNode
5809   vector< gp_XYZ > norms, baryCenters;
5810   gp_XYZ norm, avgNorm( 0,0,0 );
5811   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5812   while ( faceIt->more() )
5813   {
5814     const SMDS_MeshElement* face = faceIt->next();
5815     if ( myElemsToUse && !myElemsToUse->count( face ))
5816       continue;
5817     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5818     {
5819       norms.push_back( norm );
5820       avgNorm += norm;
5821       if ( !alongAvgNorm )
5822       {
5823         gp_XYZ bc(0,0,0);
5824         int nbN = 0;
5825         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5826           bc += SMESH_TNodeXYZ( nIt->next() );
5827         baryCenters.push_back( bc / nbN );
5828       }
5829     }
5830   }
5831
5832   if ( norms.empty() ) return 0;
5833
5834   double normSize = avgNorm.Modulus();
5835   if ( normSize < std::numeric_limits<double>::min() )
5836     return 0;
5837
5838   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5839   {
5840     myDir = avgNorm;
5841     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5842   }
5843
5844   avgNorm /= normSize;
5845
5846   int nbNodes = 0;
5847   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5848   {
5849     gp_XYZ pNew = p;
5850     double stepSize = nextStep();
5851
5852     if ( norms.size() > 1 )
5853     {
5854       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5855       {
5856         // translate plane of a face
5857         baryCenters[ iF ] += norms[ iF ] * stepSize;
5858
5859         // find point of intersection of the face plane located at baryCenters[ iF ]
5860         // and avgNorm located at pNew
5861         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5862         double dot  = ( norms[ iF ] * avgNorm );
5863         if ( dot < std::numeric_limits<double>::min() )
5864           dot = stepSize * 1e-3;
5865         double step = -( norms[ iF ] * pNew + d ) / dot;
5866         pNew += step * avgNorm;
5867       }
5868     }
5869     else
5870     {
5871       pNew += stepSize * avgNorm;
5872     }
5873     p = pNew;
5874
5875     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5876     newNodes.push_back( newNode );
5877   }
5878   return nbNodes;
5879 }
5880
5881 //=======================================================================
5882 //function : ExtrusParam::makeNodesByNormal1D
5883 //purpose  : create nodes for extrusion using normals of edges
5884 //=======================================================================
5885
5886 int SMESH_MeshEditor::ExtrusParam::
5887 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5888                      const SMDS_MeshNode*              srcNode,
5889                      std::list<const SMDS_MeshNode*> & newNodes,
5890                      const bool                        makeMediumNodes)
5891 {
5892   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5893   return 0;
5894 }
5895
5896 //=======================================================================
5897 //function : ExtrusionSweep
5898 //purpose  :
5899 //=======================================================================
5900
5901 SMESH_MeshEditor::PGroupIDs
5902 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5903                                   const gp_Vec&        theStep,
5904                                   const int            theNbSteps,
5905                                   TTElemOfElemListMap& newElemsMap,
5906                                   const int            theFlags,
5907                                   const double         theTolerance)
5908 {
5909   ExtrusParam aParams( theStep, theNbSteps, std::list<double>(), 0, theFlags, theTolerance );
5910   return ExtrusionSweep( theElems, aParams, newElemsMap );
5911 }
5912
5913
5914 //=======================================================================
5915 //function : ExtrusionSweep
5916 //purpose  :
5917 //=======================================================================
5918
5919 SMESH_MeshEditor::PGroupIDs
5920 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5921                                   ExtrusParam&         theParams,
5922                                   TTElemOfElemListMap& newElemsMap)
5923 {
5924   myLastCreatedElems.Clear();
5925   myLastCreatedNodes.Clear();
5926
5927   // source elements for each generated one
5928   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5929
5930   setElemsFirst( theElemSets );
5931   const int nbSteps = theParams.NbSteps();
5932   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5933
5934   TNodeOfNodeListMap   mapNewNodes;
5935   TElemOfVecOfNnlmiMap mapElemNewNodes;
5936
5937   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5938                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5939                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5940   // loop on theElems
5941   TIDSortedElemSet::iterator itElem;
5942   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5943   {
5944     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5945     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5946     {
5947       // check element type
5948       const SMDS_MeshElement* elem = *itElem;
5949       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5950         continue;
5951
5952       const size_t nbNodes = elem->NbNodes();
5953       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5954       newNodesItVec.reserve( nbNodes );
5955
5956       // loop on elem nodes
5957       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5958       while ( itN->more() )
5959       {
5960         // check if a node has been already sweeped
5961         const SMDS_MeshNode* node = cast2Node( itN->next() );
5962         TNodeOfNodeListMap::iterator nIt =
5963           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5964         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5965         if ( listNewNodes.empty() )
5966         {
5967           // make new nodes
5968
5969           // check if we are to create medium nodes between corner ones
5970           bool needMediumNodes = false;
5971           if ( isQuadraticMesh )
5972           {
5973             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5974             while (it->more() && !needMediumNodes )
5975             {
5976               const SMDS_MeshElement* invElem = it->next();
5977               if ( invElem != elem && !theElems.count( invElem )) continue;
5978               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5979               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5980                 needMediumNodes = true;
5981             }
5982           }
5983           // create nodes for all steps
5984           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5985           {
5986             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5987             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5988             {
5989               myLastCreatedNodes.Append( *newNodesIt );
5990               srcNodes.Append( node );
5991             }
5992           }
5993           else
5994           {
5995             break; // newNodesItVec will be shorter than nbNodes
5996           }
5997         }
5998         newNodesItVec.push_back( nIt );
5999       }
6000       // make new elements
6001       if ( newNodesItVec.size() == nbNodes )
6002         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
6003     }
6004   }
6005
6006   if ( theParams.ToMakeBoundary() ) {
6007     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
6008   }
6009   PGroupIDs newGroupIDs;
6010   if ( theParams.ToMakeGroups() )
6011     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
6012
6013   return newGroupIDs;
6014 }
6015
6016 //=======================================================================
6017 //function : ExtrusionAlongTrack
6018 //purpose  :
6019 //=======================================================================
6020 SMESH_MeshEditor::Extrusion_Error
6021 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
6022                                        SMESH_subMesh*       theTrack,
6023                                        const SMDS_MeshNode* theN1,
6024                                        const bool           theHasAngles,
6025                                        list<double>&        theAngles,
6026                                        const bool           theLinearVariation,
6027                                        const bool           theHasRefPoint,
6028                                        const gp_Pnt&        theRefPoint,
6029                                        const bool           theMakeGroups)
6030 {
6031   myLastCreatedElems.Clear();
6032   myLastCreatedNodes.Clear();
6033
6034   int aNbE;
6035   std::list<double> aPrms;
6036   TIDSortedElemSet::iterator itElem;
6037
6038   gp_XYZ aGC;
6039   TopoDS_Edge aTrackEdge;
6040   TopoDS_Vertex aV1, aV2;
6041
6042   SMDS_ElemIteratorPtr aItE;
6043   SMDS_NodeIteratorPtr aItN;
6044   SMDSAbs_ElementType aTypeE;
6045
6046   TNodeOfNodeListMap mapNewNodes;
6047
6048   // 1. Check data
6049   aNbE = theElements[0].size() + theElements[1].size();
6050   // nothing to do
6051   if ( !aNbE )
6052     return EXTR_NO_ELEMENTS;
6053
6054   // 1.1 Track Pattern
6055   ASSERT( theTrack );
6056
6057   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
6058   if ( !pSubMeshDS )
6059     return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
6060                                 theHasAngles, theAngles, theLinearVariation,
6061                                 theHasRefPoint, theRefPoint, theMakeGroups );
6062
6063   aItE = pSubMeshDS->GetElements();
6064   while ( aItE->more() ) {
6065     const SMDS_MeshElement* pE = aItE->next();
6066     aTypeE = pE->GetType();
6067     // Pattern must contain links only
6068     if ( aTypeE != SMDSAbs_Edge )
6069       return EXTR_PATH_NOT_EDGE;
6070   }
6071
6072   list<SMESH_MeshEditor_PathPoint> fullList;
6073
6074   const TopoDS_Shape& aS = theTrack->GetSubShape();
6075   // Sub-shape for the Pattern must be an Edge or Wire
6076   if( aS.ShapeType() == TopAbs_EDGE ) {
6077     aTrackEdge = TopoDS::Edge( aS );
6078     // the Edge must not be degenerated
6079     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6080       return EXTR_BAD_PATH_SHAPE;
6081     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6082     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
6083     const SMDS_MeshNode* aN1 = aItN->next();
6084     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
6085     const SMDS_MeshNode* aN2 = aItN->next();
6086     // starting node must be aN1 or aN2
6087     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6088       return EXTR_BAD_STARTING_NODE;
6089     aItN = pSubMeshDS->GetNodes();
6090     while ( aItN->more() ) {
6091       const SMDS_MeshNode* pNode = aItN->next();
6092       const SMDS_EdgePosition* pEPos =
6093         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6094       double aT = pEPos->GetUParameter();
6095       aPrms.push_back( aT );
6096     }
6097     //Extrusion_Error err =
6098     makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6099   } else if( aS.ShapeType() == TopAbs_WIRE ) {
6100     list< SMESH_subMesh* > LSM;
6101     TopTools_SequenceOfShape Edges;
6102     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6103     while(itSM->more()) {
6104       SMESH_subMesh* SM = itSM->next();
6105       LSM.push_back(SM);
6106       const TopoDS_Shape& aS = SM->GetSubShape();
6107       Edges.Append(aS);
6108     }
6109     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6110     int startNid = theN1->GetID();
6111     TColStd_MapOfInteger UsedNums;
6112
6113     int NbEdges = Edges.Length();
6114     int i = 1;
6115     for(; i<=NbEdges; i++) {
6116       int k = 0;
6117       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6118       for(; itLSM!=LSM.end(); itLSM++) {
6119         k++;
6120         if(UsedNums.Contains(k)) continue;
6121         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6122         SMESH_subMesh* locTrack = *itLSM;
6123         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6124         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6125         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6126         const SMDS_MeshNode* aN1 = aItN->next();
6127         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6128         const SMDS_MeshNode* aN2 = aItN->next();
6129         // starting node must be aN1 or aN2
6130         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6131         // 2. Collect parameters on the track edge
6132         aPrms.clear();
6133         aItN = locMeshDS->GetNodes();
6134         while ( aItN->more() ) {
6135           const SMDS_MeshNode* pNode = aItN->next();
6136           const SMDS_EdgePosition* pEPos =
6137             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6138           double aT = pEPos->GetUParameter();
6139           aPrms.push_back( aT );
6140         }
6141         list<SMESH_MeshEditor_PathPoint> LPP;
6142         //Extrusion_Error err =
6143         makeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6144         LLPPs.push_back(LPP);
6145         UsedNums.Add(k);
6146         // update startN for search following egde
6147         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6148         else startNid = aN1->GetID();
6149         break;
6150       }
6151     }
6152     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6153     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6154     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6155     for(; itPP!=firstList.end(); itPP++) {
6156       fullList.push_back( *itPP );
6157     }
6158     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6159     fullList.pop_back();
6160     itLLPP++;
6161     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6162       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6163       itPP = currList.begin();
6164       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6165       gp_Dir D1 = PP1.Tangent();
6166       gp_Dir D2 = PP2.Tangent();
6167       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6168                            (D1.Z()+D2.Z())/2 ) );
6169       PP1.SetTangent(Dnew);
6170       fullList.push_back(PP1);
6171       itPP++;
6172       for(; itPP!=firstList.end(); itPP++) {
6173         fullList.push_back( *itPP );
6174       }
6175       PP1 = fullList.back();
6176       fullList.pop_back();
6177     }
6178     // if wire not closed
6179     fullList.push_back(PP1);
6180     // else ???
6181   }
6182   else {
6183     return EXTR_BAD_PATH_SHAPE;
6184   }
6185
6186   return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6187                           theHasRefPoint, theRefPoint, theMakeGroups);
6188 }
6189
6190
6191 //=======================================================================
6192 //function : ExtrusionAlongTrack
6193 //purpose  :
6194 //=======================================================================
6195 SMESH_MeshEditor::Extrusion_Error
6196 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
6197                                        SMESH_Mesh*          theTrack,
6198                                        const SMDS_MeshNode* theN1,
6199                                        const bool           theHasAngles,
6200                                        list<double>&        theAngles,
6201                                        const bool           theLinearVariation,
6202                                        const bool           theHasRefPoint,
6203                                        const gp_Pnt&        theRefPoint,
6204                                        const bool           theMakeGroups)
6205 {
6206   myLastCreatedElems.Clear();
6207   myLastCreatedNodes.Clear();
6208
6209   int aNbE;
6210   std::list<double> aPrms;
6211   TIDSortedElemSet::iterator itElem;
6212
6213   gp_XYZ aGC;
6214   TopoDS_Edge aTrackEdge;
6215   TopoDS_Vertex aV1, aV2;
6216
6217   SMDS_ElemIteratorPtr aItE;
6218   SMDS_NodeIteratorPtr aItN;
6219   SMDSAbs_ElementType aTypeE;
6220
6221   TNodeOfNodeListMap mapNewNodes;
6222
6223   // 1. Check data
6224   aNbE = theElements[0].size() + theElements[1].size();
6225   // nothing to do
6226   if ( !aNbE )
6227     return EXTR_NO_ELEMENTS;
6228
6229   // 1.1 Track Pattern
6230   ASSERT( theTrack );
6231
6232   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6233
6234   aItE = pMeshDS->elementsIterator();
6235   while ( aItE->more() ) {
6236     const SMDS_MeshElement* pE = aItE->next();
6237     aTypeE = pE->GetType();
6238     // Pattern must contain links only
6239     if ( aTypeE != SMDSAbs_Edge )
6240       return EXTR_PATH_NOT_EDGE;
6241   }
6242
6243   list<SMESH_MeshEditor_PathPoint> fullList;
6244
6245   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6246
6247   if ( !theTrack->HasShapeToMesh() ) {
6248     //Mesh without shape
6249     const SMDS_MeshNode* currentNode = NULL;
6250     const SMDS_MeshNode* prevNode = theN1;
6251     std::vector<const SMDS_MeshNode*> aNodesList;
6252     aNodesList.push_back(theN1);
6253     int nbEdges = 0, conn=0;
6254     const SMDS_MeshElement* prevElem = NULL;
6255     const SMDS_MeshElement* currentElem = NULL;
6256     int totalNbEdges = theTrack->NbEdges();
6257     SMDS_ElemIteratorPtr nIt;
6258
6259     //check start node
6260     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6261       return EXTR_BAD_STARTING_NODE;
6262     }
6263
6264     conn = nbEdgeConnectivity(theN1);
6265     if( conn != 1 )
6266       return EXTR_PATH_NOT_EDGE;
6267
6268     aItE = theN1->GetInverseElementIterator();
6269     prevElem = aItE->next();
6270     currentElem = prevElem;
6271     //Get all nodes
6272     if(totalNbEdges == 1 ) {
6273       nIt = currentElem->nodesIterator();
6274       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6275       if(currentNode == prevNode)
6276         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6277       aNodesList.push_back(currentNode);
6278     } else {
6279       nIt = currentElem->nodesIterator();
6280       while( nIt->more() ) {
6281         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6282         if(currentNode == prevNode)
6283           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6284         aNodesList.push_back(currentNode);
6285
6286         //case of the closed mesh
6287         if(currentNode == theN1) {
6288           nbEdges++;
6289           break;
6290         }
6291
6292         conn = nbEdgeConnectivity(currentNode);
6293         if(conn > 2) {
6294           return EXTR_PATH_NOT_EDGE;
6295         }else if( conn == 1 && nbEdges > 0 ) {
6296           //End of the path
6297           nbEdges++;
6298           break;
6299         }else {
6300           prevNode = currentNode;
6301           aItE = currentNode->GetInverseElementIterator();
6302           currentElem = aItE->next();
6303           if( currentElem  == prevElem)
6304             currentElem = aItE->next();
6305           nIt = currentElem->nodesIterator();
6306           prevElem = currentElem;
6307           nbEdges++;
6308         }
6309       }
6310     }
6311
6312     if(nbEdges != totalNbEdges)
6313       return EXTR_PATH_NOT_EDGE;
6314
6315     TopTools_SequenceOfShape Edges;
6316     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6317     int startNid = theN1->GetID();
6318     for ( size_t i = 1; i < aNodesList.size(); i++ )
6319     {
6320       gp_Pnt     p1 = SMESH_TNodeXYZ( aNodesList[i-1] );
6321       gp_Pnt     p2 = SMESH_TNodeXYZ( aNodesList[i] );
6322       TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6323       list<SMESH_MeshEditor_PathPoint> LPP;
6324       aPrms.clear();
6325       makeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6326       LLPPs.push_back(LPP);
6327       if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i  ]->GetID();
6328       else                                        startNid = aNodesList[i-1]->GetID();
6329     }
6330
6331     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6332     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6333     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6334     for(; itPP!=firstList.end(); itPP++) {
6335       fullList.push_back( *itPP );
6336     }
6337
6338     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6339     SMESH_MeshEditor_PathPoint PP2;
6340     fullList.pop_back();
6341     itLLPP++;
6342     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6343       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6344       itPP = currList.begin();
6345       PP2 = currList.front();
6346       gp_Dir D1 = PP1.Tangent();
6347       gp_Dir D2 = PP2.Tangent();
6348       gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6349       PP1.SetTangent(Dnew);
6350       fullList.push_back(PP1);
6351       itPP++;
6352       for(; itPP!=currList.end(); itPP++) {
6353         fullList.push_back( *itPP );
6354       }
6355       PP1 = fullList.back();
6356       fullList.pop_back();
6357     }
6358     fullList.push_back(PP1);
6359
6360   } // Sub-shape for the Pattern must be an Edge or Wire
6361   else if ( aS.ShapeType() == TopAbs_EDGE )
6362   {
6363     aTrackEdge = TopoDS::Edge( aS );
6364     // the Edge must not be degenerated
6365     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6366       return EXTR_BAD_PATH_SHAPE;
6367     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6368     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6369     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6370     // starting node must be aN1 or aN2
6371     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6372       return EXTR_BAD_STARTING_NODE;
6373     aItN = pMeshDS->nodesIterator();
6374     while ( aItN->more() ) {
6375       const SMDS_MeshNode* pNode = aItN->next();
6376       if( pNode==aN1 || pNode==aN2 ) continue;
6377       const SMDS_EdgePosition* pEPos =
6378         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6379       double aT = pEPos->GetUParameter();
6380       aPrms.push_back( aT );
6381     }
6382     //Extrusion_Error err =
6383     makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6384   }
6385   else if( aS.ShapeType() == TopAbs_WIRE ) {
6386     list< SMESH_subMesh* > LSM;
6387     TopTools_SequenceOfShape Edges;
6388     TopExp_Explorer eExp(aS, TopAbs_EDGE);
6389     for(; eExp.More(); eExp.Next()) {
6390       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6391       if( SMESH_Algo::isDegenerated(E) ) continue;
6392       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6393       if(SM) {
6394         LSM.push_back(SM);
6395         Edges.Append(E);
6396       }
6397     }
6398     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6399     TopoDS_Vertex aVprev;
6400     TColStd_MapOfInteger UsedNums;
6401     int NbEdges = Edges.Length();
6402     int i = 1;
6403     for(; i<=NbEdges; i++) {
6404       int k = 0;
6405       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6406       for(; itLSM!=LSM.end(); itLSM++) {
6407         k++;
6408         if(UsedNums.Contains(k)) continue;
6409         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6410         SMESH_subMesh* locTrack = *itLSM;
6411         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6412         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6413         bool aN1isOK = false, aN2isOK = false;
6414         if ( aVprev.IsNull() ) {
6415           // if previous vertex is not yet defined, it means that we in the beginning of wire
6416           // and we have to find initial vertex corresponding to starting node theN1
6417           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6418           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6419           // starting node must be aN1 or aN2
6420           aN1isOK = ( aN1 && aN1 == theN1 );
6421           aN2isOK = ( aN2 && aN2 == theN1 );
6422         }
6423         else {
6424           // we have specified ending vertex of the previous edge on the previous iteration
6425           // and we have just to check that it corresponds to any vertex in current segment
6426           aN1isOK = aVprev.IsSame( aV1 );
6427           aN2isOK = aVprev.IsSame( aV2 );
6428         }
6429         if ( !aN1isOK && !aN2isOK ) continue;
6430         // 2. Collect parameters on the track edge
6431         aPrms.clear();
6432         aItN = locMeshDS->GetNodes();
6433         while ( aItN->more() ) {
6434           const SMDS_MeshNode*     pNode = aItN->next();
6435           const SMDS_EdgePosition* pEPos =
6436             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6437           double aT = pEPos->GetUParameter();
6438           aPrms.push_back( aT );
6439         }
6440         list<SMESH_MeshEditor_PathPoint> LPP;
6441         //Extrusion_Error err =
6442         makeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6443         LLPPs.push_back(LPP);
6444         UsedNums.Add(k);
6445         // update startN for search following egde
6446         if ( aN1isOK ) aVprev = aV2;
6447         else           aVprev = aV1;
6448         break;
6449       }
6450     }
6451     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6452     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6453     fullList.splice( fullList.end(), firstList );
6454
6455     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6456     fullList.pop_back();
6457     itLLPP++;
6458     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6459       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6460       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6461       gp_Dir D1 = PP1.Tangent();
6462       gp_Dir D2 = PP2.Tangent();
6463       gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6464       PP1.SetTangent(Dnew);
6465       fullList.push_back(PP1);
6466       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6467       PP1 = fullList.back();
6468       fullList.pop_back();
6469     }
6470     // if wire not closed
6471     fullList.push_back(PP1);
6472     // else ???
6473   }
6474   else {
6475     return EXTR_BAD_PATH_SHAPE;
6476   }
6477
6478   return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6479                           theHasRefPoint, theRefPoint, theMakeGroups);
6480 }
6481
6482
6483 //=======================================================================
6484 //function : makeEdgePathPoints
6485 //purpose  : auxiliary for ExtrusionAlongTrack
6486 //=======================================================================
6487 SMESH_MeshEditor::Extrusion_Error
6488 SMESH_MeshEditor::makeEdgePathPoints(std::list<double>&                aPrms,
6489                                      const TopoDS_Edge&                aTrackEdge,
6490                                      bool                              FirstIsStart,
6491                                      list<SMESH_MeshEditor_PathPoint>& LPP)
6492 {
6493   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6494   aTolVec=1.e-7;
6495   aTolVec2=aTolVec*aTolVec;
6496   double aT1, aT2;
6497   TopoDS_Vertex aV1, aV2;
6498   TopExp::Vertices( aTrackEdge, aV1, aV2 );
6499   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6500   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6501   // 2. Collect parameters on the track edge
6502   aPrms.push_front( aT1 );
6503   aPrms.push_back( aT2 );
6504   // sort parameters
6505   aPrms.sort();
6506   if( FirstIsStart ) {
6507     if ( aT1 > aT2 ) {
6508       aPrms.reverse();
6509     }
6510   }
6511   else {
6512     if ( aT2 > aT1 ) {
6513       aPrms.reverse();
6514     }
6515   }
6516   // 3. Path Points
6517   SMESH_MeshEditor_PathPoint aPP;
6518   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6519   std::list<double>::iterator aItD = aPrms.begin();
6520   for(; aItD != aPrms.end(); ++aItD) {
6521     double aT = *aItD;
6522     gp_Pnt aP3D;
6523     gp_Vec aVec;
6524     aC3D->D1( aT, aP3D, aVec );
6525     aL2 = aVec.SquareMagnitude();
6526     if ( aL2 < aTolVec2 )
6527       return EXTR_CANT_GET_TANGENT;
6528     gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6529     aPP.SetPnt( aP3D );
6530     aPP.SetTangent( aTgt );
6531     aPP.SetParameter( aT );
6532     LPP.push_back(aPP);
6533   }
6534   return EXTR_OK;
6535 }
6536
6537
6538 //=======================================================================
6539 //function : makeExtrElements
6540 //purpose  : auxiliary for ExtrusionAlongTrack
6541 //=======================================================================
6542 SMESH_MeshEditor::Extrusion_Error
6543 SMESH_MeshEditor::makeExtrElements(TIDSortedElemSet                  theElemSets[2],
6544                                    list<SMESH_MeshEditor_PathPoint>& fullList,
6545                                    const bool                        theHasAngles,
6546                                    list<double>&                     theAngles,
6547                                    const bool                        theLinearVariation,
6548                                    const bool                        theHasRefPoint,
6549                                    const gp_Pnt&                     theRefPoint,
6550                                    const bool                        theMakeGroups)
6551 {
6552   const int aNbTP = fullList.size();
6553
6554   // Angles
6555   if( theHasAngles && !theAngles.empty() && theLinearVariation )
6556     linearAngleVariation(aNbTP-1, theAngles);
6557
6558   // fill vector of path points with angles
6559   vector<SMESH_MeshEditor_PathPoint> aPPs;
6560   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6561   list<double>::iterator                 itAngles = theAngles.begin();
6562   aPPs.push_back( *itPP++ );
6563   for( ; itPP != fullList.end(); itPP++) {
6564     aPPs.push_back( *itPP );
6565     if ( theHasAngles && itAngles != theAngles.end() )
6566       aPPs.back().SetAngle( *itAngles++ );
6567   }
6568
6569   TNodeOfNodeListMap   mapNewNodes;
6570   TElemOfVecOfNnlmiMap mapElemNewNodes;
6571   TTElemOfElemListMap  newElemsMap;
6572   TIDSortedElemSet::iterator itElem;
6573   // source elements for each generated one
6574   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6575
6576   // 3. Center of rotation aV0
6577   gp_Pnt aV0 = theRefPoint;
6578   if ( !theHasRefPoint )
6579   {
6580     gp_XYZ aGC( 0.,0.,0. );
6581     TIDSortedElemSet newNodes;
6582
6583     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6584     {
6585       TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6586       itElem = theElements.begin();
6587       for ( ; itElem != theElements.end(); itElem++ )
6588       {
6589         const SMDS_MeshElement* elem = *itElem;
6590         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6591         while ( itN->more() ) {
6592           const SMDS_MeshElement* node = itN->next();
6593           if ( newNodes.insert( node ).second )
6594             aGC += SMESH_TNodeXYZ( node );
6595         }
6596       }
6597     }
6598     aGC /= newNodes.size();
6599     aV0.SetXYZ( aGC );
6600   } // if (!theHasRefPoint) {
6601
6602   // 4. Processing the elements
6603   SMESHDS_Mesh* aMesh = GetMeshDS();
6604   list<const SMDS_MeshNode*> emptyList;
6605
6606   setElemsFirst( theElemSets );
6607   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6608   {
6609     TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6610     for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6611     {
6612       const SMDS_MeshElement* elem = *itElem;
6613
6614       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6615       newNodesItVec.reserve( elem->NbNodes() );
6616
6617       // loop on elem nodes
6618       int nodeIndex = -1;
6619       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6620       while ( itN->more() )
6621       {
6622         ++nodeIndex;
6623         // check if a node has been already processed
6624         const SMDS_MeshNode* node = cast2Node( itN->next() );
6625         TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6626         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6627         if ( listNewNodes.empty() )
6628         {
6629           // make new nodes
6630           Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6631           gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6632           gp_Ax1 anAx1, anAxT1T0;
6633           gp_Dir aDT1x, aDT0x, aDT1T0;
6634
6635           aTolAng=1.e-4;
6636
6637           aV0x = aV0;
6638           aPN0 = SMESH_TNodeXYZ( node );
6639
6640           const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6641           aP0x = aPP0.Pnt();
6642           aDT0x= aPP0.Tangent();
6643
6644           for ( int j = 1; j < aNbTP; ++j ) {
6645             const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6646             aP1x     = aPP1.Pnt();
6647             aDT1x    = aPP1.Tangent();
6648             aAngle1x = aPP1.Angle();
6649
6650             gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6651             // Translation
6652             gp_Vec aV01x( aP0x, aP1x );
6653             aTrsf.SetTranslation( aV01x );
6654
6655             // traslated point
6656             aV1x = aV0x.Transformed( aTrsf );
6657             aPN1 = aPN0.Transformed( aTrsf );
6658
6659             // rotation 1 [ T1,T0 ]
6660             aAngleT1T0=-aDT1x.Angle( aDT0x );
6661             if (fabs(aAngleT1T0) > aTolAng)
6662             {
6663               aDT1T0=aDT1x^aDT0x;
6664               anAxT1T0.SetLocation( aV1x );
6665               anAxT1T0.SetDirection( aDT1T0 );
6666               aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6667
6668               aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6669             }
6670
6671             // rotation 2
6672             if ( theHasAngles ) {
6673               anAx1.SetLocation( aV1x );
6674               anAx1.SetDirection( aDT1x );
6675               aTrsfRot.SetRotation( anAx1, aAngle1x );
6676
6677               aPN1 = aPN1.Transformed( aTrsfRot );
6678             }
6679
6680             // make new node
6681             if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6682             {
6683               // create additional node
6684               gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6685               const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6686               myLastCreatedNodes.Append(newNode);
6687               srcNodes.Append( node );
6688               listNewNodes.push_back( newNode );
6689             }
6690             const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6691             myLastCreatedNodes.Append(newNode);
6692             srcNodes.Append( node );
6693             listNewNodes.push_back( newNode );
6694
6695             aPN0 = aPN1;
6696             aP0x = aP1x;
6697             aV0x = aV1x;
6698             aDT0x = aDT1x;
6699           }
6700         }
6701         else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6702         {
6703           // if current elem is quadratic and current node is not medium
6704           // we have to check - may be it is needed to insert additional nodes
6705           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6706           if ((int) listNewNodes.size() == aNbTP-1 )
6707           {
6708             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6709             gp_XYZ P(node->X(), node->Y(), node->Z());
6710             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6711             int i;
6712             for(i=0; i<aNbTP-1; i++) {
6713               const SMDS_MeshNode* N = *it;
6714               double x = ( N->X() + P.X() )/2.;
6715               double y = ( N->Y() + P.Y() )/2.;
6716               double z = ( N->Z() + P.Z() )/2.;
6717               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6718               srcNodes.Append( node );
6719               myLastCreatedNodes.Append(newN);
6720               aNodes[2*i] = newN;
6721               aNodes[2*i+1] = N;
6722               P = gp_XYZ(N->X(),N->Y(),N->Z());
6723             }
6724             listNewNodes.clear();
6725             for(i=0; i<2*(aNbTP-1); i++) {
6726               listNewNodes.push_back(aNodes[i]);
6727             }
6728           }
6729         }
6730
6731         newNodesItVec.push_back( nIt );
6732       }
6733
6734       // make new elements
6735       sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6736     }
6737   }
6738
6739   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6740
6741   if ( theMakeGroups )
6742     generateGroups( srcNodes, srcElems, "extruded");
6743
6744   return EXTR_OK;
6745 }
6746
6747
6748 //=======================================================================
6749 //function : linearAngleVariation
6750 //purpose  : spread values over nbSteps
6751 //=======================================================================
6752
6753 void SMESH_MeshEditor::linearAngleVariation(const int     nbSteps,
6754                                             list<double>& Angles)
6755 {
6756   int nbAngles = Angles.size();
6757   if( nbSteps > nbAngles && nbAngles > 0 )
6758   {
6759     vector<double> theAngles(nbAngles);
6760     theAngles.assign( Angles.begin(), Angles.end() );
6761
6762     list<double> res;
6763     double rAn2St = double( nbAngles ) / double( nbSteps );
6764     double angPrev = 0, angle;
6765     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6766     {
6767       double angCur = rAn2St * ( iSt+1 );
6768       double angCurFloor  = floor( angCur );
6769       double angPrevFloor = floor( angPrev );
6770       if ( angPrevFloor == angCurFloor )
6771         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6772       else {
6773         int iP = int( angPrevFloor );
6774         double angPrevCeil = ceil(angPrev);
6775         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6776
6777         int iC = int( angCurFloor );
6778         if ( iC < nbAngles )
6779           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6780
6781         iP = int( angPrevCeil );
6782         while ( iC-- > iP )
6783           angle += theAngles[ iC ];
6784       }
6785       res.push_back(angle);
6786       angPrev = angCur;
6787     }
6788     Angles.swap( res );
6789   }
6790 }
6791
6792
6793 //================================================================================
6794 /*!
6795  * \brief Move or copy theElements applying theTrsf to their nodes
6796  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6797  *  \param theTrsf - transformation to apply
6798  *  \param theCopy - if true, create translated copies of theElems
6799  *  \param theMakeGroups - if true and theCopy, create translated groups
6800  *  \param theTargetMesh - mesh to copy translated elements into
6801  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6802  */
6803 //================================================================================
6804
6805 SMESH_MeshEditor::PGroupIDs
6806 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6807                              const gp_Trsf&     theTrsf,
6808                              const bool         theCopy,
6809                              const bool         theMakeGroups,
6810                              SMESH_Mesh*        theTargetMesh)
6811 {
6812   myLastCreatedElems.Clear();
6813   myLastCreatedNodes.Clear();
6814
6815   bool needReverse = false;
6816   string groupPostfix;
6817   switch ( theTrsf.Form() ) {
6818   case gp_PntMirror:
6819     needReverse = true;
6820     groupPostfix = "mirrored";
6821     break;
6822   case gp_Ax1Mirror:
6823     groupPostfix = "mirrored";
6824     break;
6825   case gp_Ax2Mirror:
6826     needReverse = true;
6827     groupPostfix = "mirrored";
6828     break;
6829   case gp_Rotation:
6830     groupPostfix = "rotated";
6831     break;
6832   case gp_Translation:
6833     groupPostfix = "translated";
6834     break;
6835   case gp_Scale:
6836     groupPostfix = "scaled";
6837     break;
6838   case gp_CompoundTrsf: // different scale by axis
6839     groupPostfix = "scaled";
6840     break;
6841   default:
6842     needReverse = false;
6843     groupPostfix = "transformed";
6844   }
6845
6846   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6847   SMESHDS_Mesh* aMesh    = GetMeshDS();
6848
6849   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6850   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6851   SMESH_MeshEditor::ElemFeatures elemType;
6852
6853   // map old node to new one
6854   TNodeNodeMap nodeMap;
6855
6856   // elements sharing moved nodes; those of them which have all
6857   // nodes mirrored but are not in theElems are to be reversed
6858   TIDSortedElemSet inverseElemSet;
6859
6860   // source elements for each generated one
6861   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6862
6863   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6864   TIDSortedElemSet orphanNode;
6865
6866   if ( theElems.empty() ) // transform the whole mesh
6867   {
6868     // add all elements
6869     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6870     while ( eIt->more() ) theElems.insert( eIt->next() );
6871     // add orphan nodes
6872     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6873     while ( nIt->more() )
6874     {
6875       const SMDS_MeshNode* node = nIt->next();
6876       if ( node->NbInverseElements() == 0)
6877         orphanNode.insert( node );
6878     }
6879   }
6880
6881   // loop on elements to transform nodes : first orphan nodes then elems
6882   TIDSortedElemSet::iterator itElem;
6883   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6884   for (int i=0; i<2; i++)
6885     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6886     {
6887       const SMDS_MeshElement* elem = *itElem;
6888       if ( !elem )
6889         continue;
6890
6891       // loop on elem nodes
6892       double coord[3];
6893       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6894       while ( itN->more() )
6895       {
6896         const SMDS_MeshNode* node = cast2Node( itN->next() );
6897         // check if a node has been already transformed
6898         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6899           nodeMap.insert( make_pair ( node, node ));
6900         if ( !n2n_isnew.second )
6901           continue;
6902
6903         node->GetXYZ( coord );
6904         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6905         if ( theTargetMesh ) {
6906           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6907           n2n_isnew.first->second = newNode;
6908           myLastCreatedNodes.Append(newNode);
6909           srcNodes.Append( node );
6910         }
6911         else if ( theCopy ) {
6912           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6913           n2n_isnew.first->second = newNode;
6914           myLastCreatedNodes.Append(newNode);
6915           srcNodes.Append( node );
6916         }
6917         else {
6918           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6919           // node position on shape becomes invalid
6920           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6921             ( SMDS_SpacePosition::originSpacePosition() );
6922         }
6923
6924         // keep inverse elements
6925         if ( !theCopy && !theTargetMesh && needReverse ) {
6926           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6927           while ( invElemIt->more() ) {
6928             const SMDS_MeshElement* iel = invElemIt->next();
6929             inverseElemSet.insert( iel );
6930           }
6931         }
6932       }
6933     } // loop on elems in { &orphanNode, &theElems };
6934
6935   // either create new elements or reverse mirrored ones
6936   if ( !theCopy && !needReverse && !theTargetMesh )
6937     return PGroupIDs();
6938
6939   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6940
6941   // Replicate or reverse elements
6942
6943   std::vector<int> iForw;
6944   vector<const SMDS_MeshNode*> nodes;
6945   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6946   {
6947     const SMDS_MeshElement* elem = *itElem;
6948     if ( !elem ) continue;
6949
6950     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6951     size_t               nbNodes  = elem->NbNodes();
6952     if ( geomType == SMDSGeom_NONE ) continue; // node
6953
6954     nodes.resize( nbNodes );
6955
6956     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6957     {
6958       const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6959       if (!aPolyedre)
6960         continue;
6961       nodes.clear();
6962       bool allTransformed = true;
6963       int nbFaces = aPolyedre->NbFaces();
6964       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6965       {
6966         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6967         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6968         {
6969           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6970           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6971           if ( nodeMapIt == nodeMap.end() )
6972             allTransformed = false; // not all nodes transformed
6973           else
6974             nodes.push_back((*nodeMapIt).second);
6975         }
6976         if ( needReverse && allTransformed )
6977           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6978       }
6979       if ( !allTransformed )
6980         continue; // not all nodes transformed
6981     }
6982     else // ----------------------- the rest element types
6983     {
6984       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6985       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6986       const vector<int>&    i = needReverse ? iRev : iForw;
6987
6988       // find transformed nodes
6989       size_t iNode = 0;
6990       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6991       while ( itN->more() ) {
6992         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6993         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6994         if ( nodeMapIt == nodeMap.end() )
6995           break; // not all nodes transformed
6996         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6997       }
6998       if ( iNode != nbNodes )
6999         continue; // not all nodes transformed
7000     }
7001
7002     if ( editor ) {
7003       // copy in this or a new mesh
7004       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
7005         srcElems.Append( elem );
7006     }
7007     else {
7008       // reverse element as it was reversed by transformation
7009       if ( nbNodes > 2 )
7010         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
7011     }
7012
7013   } // loop on elements
7014
7015   if ( editor && editor != this )
7016     myLastCreatedElems = editor->myLastCreatedElems;
7017
7018   PGroupIDs newGroupIDs;
7019
7020   if ( ( theMakeGroups && theCopy ) ||
7021        ( theMakeGroups && theTargetMesh ) )
7022     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
7023
7024   return newGroupIDs;
7025 }
7026
7027 //=======================================================================
7028 /*!
7029  * \brief Create groups of elements made during transformation
7030  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
7031  *  \param elemGens - elements making corresponding myLastCreatedElems
7032  *  \param postfix - to append to names of new groups
7033  *  \param targetMesh - mesh to create groups in
7034  *  \param topPresent - is there "top" elements that are created by sweeping
7035  */
7036 //=======================================================================
7037
7038 SMESH_MeshEditor::PGroupIDs
7039 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
7040                                  const SMESH_SequenceOfElemPtr& elemGens,
7041                                  const std::string&             postfix,
7042                                  SMESH_Mesh*                    targetMesh,
7043                                  const bool                     topPresent)
7044 {
7045   PGroupIDs newGroupIDs( new list<int> );
7046   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
7047
7048   // Sort existing groups by types and collect their names
7049
7050   // containers to store an old group and generated new ones;
7051   // 1st new group is for result elems of different type than a source one;
7052   // 2nd new group is for same type result elems ("top" group at extrusion)
7053   using boost::tuple;
7054   using boost::make_tuple;
7055   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
7056   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
7057   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
7058   // group names
7059   set< string > groupNames;
7060
7061   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7062   if ( !groupIt->more() ) return newGroupIDs;
7063
7064   int newGroupID = mesh->GetGroupIds().back()+1;
7065   while ( groupIt->more() )
7066   {
7067     SMESH_Group * group = groupIt->next();
7068     if ( !group ) continue;
7069     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7070     if ( !groupDS || groupDS->IsEmpty() ) continue;
7071     groupNames.insert    ( group->GetName() );
7072     groupDS->SetStoreName( group->GetName() );
7073     const SMDSAbs_ElementType type = groupDS->GetType();
7074     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7075     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7076     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7077     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7078   }
7079
7080   // Loop on nodes and elements to add them in new groups
7081
7082   vector< const SMDS_MeshElement* > resultElems;
7083   for ( int isNodes = 0; isNodes < 2; ++isNodes )
7084   {
7085     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
7086     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7087     if ( gens.Length() != elems.Length() )
7088       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7089
7090     // loop on created elements
7091     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7092     {
7093       const SMDS_MeshElement* sourceElem = gens( iElem );
7094       if ( !sourceElem ) {
7095         MESSAGE("generateGroups(): NULL source element");
7096         continue;
7097       }
7098       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7099       if ( groupsOldNew.empty() ) { // no groups of this type at all
7100         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7101           ++iElem; // skip all elements made by sourceElem
7102         continue;
7103       }
7104       // collect all elements made by the iElem-th sourceElem
7105       resultElems.clear();
7106       if ( const SMDS_MeshElement* resElem = elems( iElem ))
7107         if ( resElem != sourceElem )
7108           resultElems.push_back( resElem );
7109       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7110         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7111           if ( resElem != sourceElem )
7112             resultElems.push_back( resElem );
7113
7114       const SMDS_MeshElement* topElem = 0;
7115       if ( isNodes ) // there must be a top element
7116       {
7117         topElem = resultElems.back();
7118         resultElems.pop_back();
7119       }
7120       else
7121       {
7122         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7123         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7124           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7125           {
7126             topElem = *resElemIt;
7127             *resElemIt = 0; // erase *resElemIt
7128             break;
7129           }
7130       }
7131       // add resultElems to groups originted from ones the sourceElem belongs to
7132       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7133       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7134       {
7135         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7136         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7137         {
7138           // fill in a new group
7139           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7140           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7141           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7142             if ( *resElemIt )
7143               newGroup.Add( *resElemIt );
7144
7145           // fill a "top" group
7146           if ( topElem )
7147           {
7148             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7149             newTopGroup.Add( topElem );
7150          }
7151         }
7152       }
7153     } // loop on created elements
7154   }// loop on nodes and elements
7155
7156   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7157
7158   list<int> topGrouIds;
7159   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7160   {
7161     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
7162     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7163                                       orderedOldNewGroups[i]->get<2>() };
7164     for ( int is2nd = 0; is2nd < 2; ++is2nd )
7165     {
7166       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7167       if ( newGroupDS->IsEmpty() )
7168       {
7169         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7170       }
7171       else
7172       {
7173         // set group type
7174         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7175
7176         // make a name
7177         const bool isTop = ( topPresent &&
7178                              newGroupDS->GetType() == oldGroupDS->GetType() &&
7179                              is2nd );
7180
7181         string name = oldGroupDS->GetStoreName();
7182         { // remove trailing whitespaces (issue 22599)
7183           size_t size = name.size();
7184           while ( size > 1 && isspace( name[ size-1 ]))
7185             --size;
7186           if ( size != name.size() )
7187           {
7188             name.resize( size );
7189             oldGroupDS->SetStoreName( name.c_str() );
7190           }
7191         }
7192         if ( !targetMesh ) {
7193           string suffix = ( isTop ? "top": postfix.c_str() );
7194           name += "_";
7195           name += suffix;
7196           int nb = 1;
7197           while ( !groupNames.insert( name ).second ) // name exists
7198             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7199         }
7200         else if ( isTop ) {
7201           name += "_top";
7202         }
7203         newGroupDS->SetStoreName( name.c_str() );
7204
7205         // make a SMESH_Groups
7206         mesh->AddGroup( newGroupDS );
7207         if ( isTop )
7208           topGrouIds.push_back( newGroupDS->GetID() );
7209         else
7210           newGroupIDs->push_back( newGroupDS->GetID() );
7211       }
7212     }
7213   }
7214   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7215
7216   return newGroupIDs;
7217 }
7218
7219 //================================================================================
7220 /*!
7221  *  * \brief Return list of group of nodes close to each other within theTolerance
7222  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
7223  *  *        an Octree algorithm
7224  *  \param [in,out] theNodes - the nodes to treat
7225  *  \param [in]     theTolerance - the tolerance
7226  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
7227  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
7228  *         corner and medium nodes in separate groups
7229  */
7230 //================================================================================
7231
7232 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
7233                                             const double         theTolerance,
7234                                             TListOfListOfNodes & theGroupsOfNodes,
7235                                             bool                 theSeparateCornersAndMedium)
7236 {
7237   myLastCreatedElems.Clear();
7238   myLastCreatedNodes.Clear();
7239
7240   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
7241        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
7242        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7243     theSeparateCornersAndMedium = false;
7244
7245   TIDSortedNodeSet& corners = theNodes;
7246   TIDSortedNodeSet  medium;
7247
7248   if ( theNodes.empty() ) // get all nodes in the mesh
7249   {
7250     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7251     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7252     if ( theSeparateCornersAndMedium )
7253       while ( nIt->more() )
7254       {
7255         const SMDS_MeshNode* n = nIt->next();
7256         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7257         nodeSet->insert( nodeSet->end(), n );
7258       }
7259     else
7260       while ( nIt->more() )
7261         theNodes.insert( theNodes.end(), nIt->next() );
7262   }
7263   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7264   {
7265     TIDSortedNodeSet::iterator nIt = corners.begin();
7266     while ( nIt != corners.end() )
7267       if ( SMESH_MesherHelper::IsMedium( *nIt ))
7268       {
7269         medium.insert( medium.end(), *nIt );
7270         corners.erase( nIt++ );
7271       }
7272       else
7273       {
7274         ++nIt;
7275       }
7276   }
7277
7278   if ( !corners.empty() )
7279     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7280   if ( !medium.empty() )
7281     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7282 }
7283
7284 //=======================================================================
7285 //function : SimplifyFace
7286 //purpose  : split a chain of nodes into several closed chains
7287 //=======================================================================
7288
7289 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7290                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7291                                     vector<int>&                         quantities) const
7292 {
7293   int nbNodes = faceNodes.size();
7294   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7295     --nbNodes;
7296   if ( nbNodes < 3 )
7297     return 0;
7298   size_t prevNbQuant = quantities.size();
7299
7300   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7301   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7302   map< const SMDS_MeshNode*, int >::iterator nInd;
7303
7304   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7305   simpleNodes.push_back( faceNodes[0] );
7306   for ( int iCur = 1; iCur < nbNodes; iCur++ )
7307   {
7308     if ( faceNodes[ iCur ] != simpleNodes.back() )
7309     {
7310       int index = simpleNodes.size();
7311       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7312       int prevIndex = nInd->second;
7313       if ( prevIndex < index )
7314       {
7315         // a sub-loop found
7316         int loopLen = index - prevIndex;
7317         if ( loopLen > 2 )
7318         {
7319           // store the sub-loop
7320           quantities.push_back( loopLen );
7321           for ( int i = prevIndex; i < index; i++ )
7322             poly_nodes.push_back( simpleNodes[ i ]);
7323         }
7324         simpleNodes.resize( prevIndex+1 );
7325       }
7326       else
7327       {
7328         simpleNodes.push_back( faceNodes[ iCur ]);
7329       }
7330     }
7331   }
7332
7333   if ( simpleNodes.size() > 2 )
7334   {
7335     quantities.push_back( simpleNodes.size() );
7336     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7337   }
7338
7339   return quantities.size() - prevNbQuant;
7340 }
7341
7342 //=======================================================================
7343 //function : MergeNodes
7344 //purpose  : In each group, the cdr of nodes are substituted by the first one
7345 //           in all elements.
7346 //=======================================================================
7347
7348 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7349                                    const bool           theAvoidMakingHoles)
7350 {
7351   myLastCreatedElems.Clear();
7352   myLastCreatedNodes.Clear();
7353
7354   SMESHDS_Mesh* mesh = GetMeshDS();
7355
7356   TNodeNodeMap nodeNodeMap; // node to replace - new node
7357   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7358   list< int > rmElemIds, rmNodeIds;
7359   vector< ElemFeatures > newElemDefs;
7360
7361   // Fill nodeNodeMap and elems
7362
7363   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7364   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7365   {
7366     list<const SMDS_MeshNode*>& nodes = *grIt;
7367     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7368     const SMDS_MeshNode* nToKeep = *nIt;
7369     for ( ++nIt; nIt != nodes.end(); nIt++ )
7370     {
7371       const SMDS_MeshNode* nToRemove = *nIt;
7372       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7373       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7374       while ( invElemIt->more() ) {
7375         const SMDS_MeshElement* elem = invElemIt->next();
7376         elems.insert(elem);
7377       }
7378     }
7379   }
7380
7381   // Apply recursive replacements (BUG 0020185)
7382   TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7383   for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7384   {
7385     const SMDS_MeshNode* nToKeep = nnIt->second;
7386     TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7387     while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7388       nToKeep = nnIt_i->second;
7389     nnIt->second = nToKeep;
7390   }
7391
7392   if ( theAvoidMakingHoles )
7393   {
7394     // find elements whose topology changes
7395
7396     vector<const SMDS_MeshElement*> pbElems;
7397     set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7398     for ( ; eIt != elems.end(); ++eIt )
7399     {
7400       const SMDS_MeshElement* elem = *eIt;
7401       SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
7402       while ( itN->more() )
7403       {
7404         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7405         TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7406         if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7407         {
7408           // several nodes of elem stick
7409           pbElems.push_back( elem );
7410           break;
7411         }
7412       }
7413     }
7414     // exclude from merge nodes causing spoiling element
7415     for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7416     {
7417       bool nodesExcluded = false;
7418       for ( size_t i = 0; i < pbElems.size(); ++i )
7419       {
7420         size_t prevNbMergeNodes = nodeNodeMap.size();
7421         if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7422              prevNbMergeNodes < nodeNodeMap.size() )
7423           nodesExcluded = true;
7424       }
7425       if ( !nodesExcluded )
7426         break;
7427     }
7428   }
7429
7430   for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7431   {
7432     const SMDS_MeshNode* nToRemove = nnIt->first;
7433     const SMDS_MeshNode* nToKeep   = nnIt->second;
7434     if ( nToRemove != nToKeep )
7435     {
7436       rmNodeIds.push_back( nToRemove->GetID() );
7437       AddToSameGroups( nToKeep, nToRemove, mesh );
7438       // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7439       // w/o creating node in place of merged ones.
7440       const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7441       if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7442         if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7443           sm->SetIsAlwaysComputed( true );
7444     }
7445   }
7446
7447   // Change element nodes or remove an element
7448
7449   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7450   for ( ; eIt != elems.end(); eIt++ )
7451   {
7452     const SMDS_MeshElement* elem = *eIt;
7453     SMESHDS_SubMesh*          sm = mesh->MeshElements( elem->getshapeId() );
7454
7455     bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7456     if ( !keepElem )
7457       rmElemIds.push_back( elem->GetID() );
7458
7459     for ( size_t i = 0; i < newElemDefs.size(); ++i )
7460     {
7461       if ( i > 0 || !mesh->ChangeElementNodes( elem,
7462                                                & newElemDefs[i].myNodes[0],
7463                                                newElemDefs[i].myNodes.size() ))
7464       {
7465         if ( i == 0 )
7466         {
7467           newElemDefs[i].SetID( elem->GetID() );
7468           mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7469           if ( !keepElem ) rmElemIds.pop_back();
7470         }
7471         else
7472         {
7473           newElemDefs[i].SetID( -1 );
7474         }
7475         SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7476         if ( sm && newElem )
7477           sm->AddElement( newElem );
7478         if ( elem != newElem )
7479           ReplaceElemInGroups( elem, newElem, mesh );
7480       }
7481     }
7482   }
7483
7484   // Remove bad elements, then equal nodes (order important)
7485   Remove( rmElemIds, false );
7486   Remove( rmNodeIds, true );
7487
7488   return;
7489 }
7490
7491 //=======================================================================
7492 //function : applyMerge
7493 //purpose  : Compute new connectivity of an element after merging nodes
7494 //  \param [in] elems - the element
7495 //  \param [out] newElemDefs - definition(s) of result element(s)
7496 //  \param [inout] nodeNodeMap - nodes to merge
7497 //  \param [in] avoidMakingHoles - if true and and the element becomes invalid
7498 //              after merging (but not degenerated), removes nodes causing
7499 //              the invalidity from \a nodeNodeMap.
7500 //  \return bool - true if the element should be removed
7501 //=======================================================================
7502
7503 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7504                                    vector< ElemFeatures >& newElemDefs,
7505                                    TNodeNodeMap&           nodeNodeMap,
7506                                    const bool              avoidMakingHoles )
7507 {
7508   bool toRemove = false; // to remove elem
7509   int nbResElems = 1;    // nb new elements
7510
7511   newElemDefs.resize(nbResElems);
7512   newElemDefs[0].Init( elem );
7513   newElemDefs[0].myNodes.clear();
7514
7515   set<const SMDS_MeshNode*> nodeSet;
7516   vector< const SMDS_MeshNode*>   curNodes;
7517   vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7518   vector<int> iRepl;
7519
7520   const        int  nbNodes = elem->NbNodes();
7521   SMDSAbs_EntityType entity = elem->GetEntityType();
7522
7523   curNodes.resize( nbNodes );
7524   uniqueNodes.resize( nbNodes );
7525   iRepl.resize( nbNodes );
7526   int iUnique = 0, iCur = 0, nbRepl = 0;
7527
7528   // Get new seq of nodes
7529
7530   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7531   while ( itN->more() )
7532   {
7533     const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7534
7535     TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7536     if ( nnIt != nodeNodeMap.end() ) {
7537       n = (*nnIt).second;
7538     }
7539     curNodes[ iCur ] = n;
7540     bool isUnique = nodeSet.insert( n ).second;
7541     if ( isUnique )
7542       uniqueNodes[ iUnique++ ] = n;
7543     else
7544       iRepl[ nbRepl++ ] = iCur;
7545     iCur++;
7546   }
7547
7548   // Analyse element topology after replacement
7549
7550   int nbUniqueNodes = nodeSet.size();
7551   if ( nbNodes != nbUniqueNodes ) // some nodes stick
7552   {
7553     toRemove = true;
7554     nbResElems = 0;
7555
7556     if ( elem->IsQuadratic() && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7557     {
7558       // if corner nodes stick, remove medium nodes between them from uniqueNodes
7559       int nbCorners = nbNodes / 2;
7560       for ( int iCur = 0; iCur < nbCorners; ++iCur )
7561       {
7562         int iPrev = ( iCur + 1 ) % nbCorners;
7563         if ( curNodes[ iCur ] == curNodes[ iPrev ] ) // corners stick
7564         {
7565           int iMedium = iCur + nbCorners;
7566           vector< const SMDS_MeshNode* >::iterator i =
7567             std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7568                        uniqueNodes.end(),
7569                        curNodes[ iMedium ]);
7570           if ( i != uniqueNodes.end() )
7571           {
7572             --nbUniqueNodes;
7573             for ( ; i+1 != uniqueNodes.end(); ++i )
7574               *i = *(i+1);
7575           }
7576         }
7577       }
7578     }
7579
7580     switch ( entity )
7581     {
7582     case SMDSEntity_Polygon:
7583     case SMDSEntity_Quad_Polygon: // Polygon
7584     {
7585       ElemFeatures* elemType = & newElemDefs[0];
7586       const bool isQuad = elemType->myIsQuad;
7587       if ( isQuad )
7588         SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7589           ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7590
7591       // a polygon can divide into several elements
7592       vector<const SMDS_MeshNode *> polygons_nodes;
7593       vector<int> quantities;
7594       nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7595       newElemDefs.resize( nbResElems );
7596       for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7597       {
7598         ElemFeatures* elemType = & newElemDefs[iface];
7599         if ( iface ) elemType->Init( elem );
7600
7601         vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7602         int nbNewNodes = quantities[iface];
7603         face_nodes.assign( polygons_nodes.begin() + inode,
7604                            polygons_nodes.begin() + inode + nbNewNodes );
7605         inode += nbNewNodes;
7606         if ( isQuad ) // check if a result elem is a valid quadratic polygon
7607         {
7608           bool isValid = ( nbNewNodes % 2 == 0 );
7609           for ( int i = 0; i < nbNewNodes && isValid; ++i )
7610             isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7611           elemType->SetQuad( isValid );
7612           if ( isValid ) // put medium nodes after corners
7613             SMDS_MeshCell::applyInterlaceRev
7614               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7615                                                     nbNewNodes ), face_nodes );
7616         }
7617         elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7618       }
7619       nbUniqueNodes = newElemDefs[0].myNodes.size();
7620       break;
7621     } // Polygon
7622
7623     case SMDSEntity_Polyhedra: // Polyhedral volume
7624     {
7625       if ( nbUniqueNodes >= 4 )
7626       {
7627         // each face has to be analyzed in order to check volume validity
7628         if ( const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem ))
7629         {
7630           int nbFaces = aPolyedre->NbFaces();
7631
7632           vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7633           vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7634           vector<const SMDS_MeshNode *>  faceNodes;
7635           poly_nodes.clear();
7636           quantities.clear();
7637
7638           for (int iface = 1; iface <= nbFaces; iface++)
7639           {
7640             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7641             faceNodes.resize( nbFaceNodes );
7642             for (int inode = 1; inode <= nbFaceNodes; inode++)
7643             {
7644               const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7645               TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7646               if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7647                 faceNode = (*nnIt).second;
7648               faceNodes[inode - 1] = faceNode;
7649             }
7650             SimplifyFace(faceNodes, poly_nodes, quantities);
7651           }
7652
7653           if ( quantities.size() > 3 )
7654           {
7655             // TODO: remove coincident faces
7656             nbResElems = 1;
7657             nbUniqueNodes = newElemDefs[0].myNodes.size();
7658           }
7659         }
7660       }
7661     }
7662     break;
7663
7664     // Regular elements
7665     // TODO not all the possible cases are solved. Find something more generic?
7666     case SMDSEntity_Edge: //////// EDGE
7667     case SMDSEntity_Triangle: //// TRIANGLE
7668     case SMDSEntity_Quad_Triangle:
7669     case SMDSEntity_Tetra:
7670     case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7671     {
7672       break;
7673     }
7674     case SMDSEntity_Quad_Edge:
7675     {
7676       break;
7677     }
7678     case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7679     {
7680       if ( nbUniqueNodes < 3 )
7681         toRemove = true;
7682       else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7683         toRemove = true; // opposite nodes stick
7684       else
7685         toRemove = false;
7686       break;
7687     }
7688     case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7689     {
7690       //   1    5    2
7691       //    +---+---+
7692       //    |       |
7693       //   4+       +6
7694       //    |       |
7695       //    +---+---+
7696       //   0    7    3
7697       if ( nbUniqueNodes == 6 &&
7698            iRepl[0] < 4       &&
7699            ( nbRepl == 1 || iRepl[1] >= 4 ))
7700       {
7701         toRemove = false;
7702       }
7703       break;
7704     }
7705     case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7706     {
7707       //   1    5    2
7708       //    +---+---+
7709       //    |       |
7710       //   4+  8+   +6
7711       //    |       |
7712       //    +---+---+
7713       //   0    7    3
7714       if (( nbUniqueNodes == 7 && nbRepl == 2 && iRepl[1] != 8 ) &&
7715           (( iRepl[0] == 1 && iRepl[1] == 4 && curNodes[1] == curNodes[0] ) ||
7716            ( iRepl[0] == 2 && iRepl[1] == 5 && curNodes[2] == curNodes[1] ) ||
7717            ( iRepl[0] == 3 && iRepl[1] == 6 && curNodes[3] == curNodes[2] ) ||
7718            ( iRepl[0] == 3 && iRepl[1] == 7 && curNodes[3] == curNodes[0] )))
7719       {
7720         toRemove = false;
7721       }
7722       break;
7723     }
7724     case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7725     {
7726       if ( nbUniqueNodes == 4 ) {
7727         // ---------------------------------> tetrahedron
7728         if ( curNodes[3] == curNodes[4] &&
7729              curNodes[3] == curNodes[5] ) {
7730           // top nodes stick
7731           toRemove = false;
7732         }
7733         else if ( curNodes[0] == curNodes[1] &&
7734                   curNodes[0] == curNodes[2] ) {
7735           // bottom nodes stick: set a top before
7736           uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7737           uniqueNodes[ 0 ] = curNodes [ 5 ];
7738           uniqueNodes[ 1 ] = curNodes [ 4 ];
7739           uniqueNodes[ 2 ] = curNodes [ 3 ];
7740           toRemove = false;
7741         }
7742         else if (( curNodes[0] == curNodes[3] ) +
7743                  ( curNodes[1] == curNodes[4] ) +
7744                  ( curNodes[2] == curNodes[5] ) == 2 ) {
7745           // a lateral face turns into a line
7746           toRemove = false;
7747         }
7748       }
7749       else if ( nbUniqueNodes == 5 ) {
7750         // PENTAHEDRON --------------------> pyramid
7751         if ( curNodes[0] == curNodes[3] )
7752         {
7753           uniqueNodes[ 0 ] = curNodes[ 1 ];
7754           uniqueNodes[ 1 ] = curNodes[ 4 ];
7755           uniqueNodes[ 2 ] = curNodes[ 5 ];
7756           uniqueNodes[ 3 ] = curNodes[ 2 ];
7757           uniqueNodes[ 4 ] = curNodes[ 0 ];
7758           toRemove = false;
7759         }
7760         if ( curNodes[1] == curNodes[4] )
7761         {
7762           uniqueNodes[ 0 ] = curNodes[ 0 ];
7763           uniqueNodes[ 1 ] = curNodes[ 2 ];
7764           uniqueNodes[ 2 ] = curNodes[ 5 ];
7765           uniqueNodes[ 3 ] = curNodes[ 3 ];
7766           uniqueNodes[ 4 ] = curNodes[ 1 ];
7767           toRemove = false;
7768         }
7769         if ( curNodes[2] == curNodes[5] )
7770         {
7771           uniqueNodes[ 0 ] = curNodes[ 0 ];
7772           uniqueNodes[ 1 ] = curNodes[ 3 ];
7773           uniqueNodes[ 2 ] = curNodes[ 4 ];
7774           uniqueNodes[ 3 ] = curNodes[ 1 ];
7775           uniqueNodes[ 4 ] = curNodes[ 2 ];
7776           toRemove = false;
7777         }
7778       }
7779       break;
7780     }
7781     case SMDSEntity_Hexa:
7782     {
7783       //////////////////////////////////// HEXAHEDRON
7784       SMDS_VolumeTool hexa (elem);
7785       hexa.SetExternalNormal();
7786       if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7787         //////////////////////// HEX ---> tetrahedron
7788         for ( int iFace = 0; iFace < 6; iFace++ ) {
7789           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7790           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7791               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7792               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7793             // one face turns into a point ...
7794             int  pickInd = ind[ 0 ];
7795             int iOppFace = hexa.GetOppFaceIndex( iFace );
7796             ind = hexa.GetFaceNodesIndices( iOppFace );
7797             int nbStick = 0;
7798             uniqueNodes.clear();
7799             for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7800               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7801                 nbStick++;
7802               else
7803                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7804             }
7805             if ( nbStick == 1 ) {
7806               // ... and the opposite one - into a triangle.
7807               // set a top node
7808               uniqueNodes.push_back( curNodes[ pickInd ]);
7809               toRemove = false;
7810             }
7811             break;
7812           }
7813         }
7814       }
7815       else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7816         //////////////////////// HEX ---> prism
7817         int nbTria = 0, iTria[3];
7818         const int *ind; // indices of face nodes
7819         // look for triangular faces
7820         for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7821           ind = hexa.GetFaceNodesIndices( iFace );
7822           TIDSortedNodeSet faceNodes;
7823           for ( iCur = 0; iCur < 4; iCur++ )
7824             faceNodes.insert( curNodes[ind[iCur]] );
7825           if ( faceNodes.size() == 3 )
7826             iTria[ nbTria++ ] = iFace;
7827         }
7828         // check if triangles are opposite
7829         if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7830         {
7831           // set nodes of the bottom triangle
7832           ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7833           vector<int> indB;
7834           for ( iCur = 0; iCur < 4; iCur++ )
7835             if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7836               indB.push_back( ind[iCur] );
7837           if ( !hexa.IsForward() )
7838             std::swap( indB[0], indB[2] );
7839           for ( iCur = 0; iCur < 3; iCur++ )
7840             uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7841           // set nodes of the top triangle
7842           const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7843           for ( iCur = 0; iCur < 3; ++iCur )
7844             for ( int j = 0; j < 4; ++j )
7845               if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7846               {
7847                 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7848                 break;
7849               }
7850           toRemove = false;
7851           break;
7852         }
7853       }
7854       else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7855         //////////////////// HEXAHEDRON ---> pyramid
7856         for ( int iFace = 0; iFace < 6; iFace++ ) {
7857           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7858           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7859               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7860               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7861             // one face turns into a point ...
7862             int iOppFace = hexa.GetOppFaceIndex( iFace );
7863             ind = hexa.GetFaceNodesIndices( iOppFace );
7864             uniqueNodes.clear();
7865             for ( iCur = 0; iCur < 4; iCur++ ) {
7866               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7867                 break;
7868               else
7869                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7870             }
7871             if ( uniqueNodes.size() == 4 ) {
7872               // ... and the opposite one is a quadrangle
7873               // set a top node
7874               const int* indTop = hexa.GetFaceNodesIndices( iFace );
7875               uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7876               toRemove = false;
7877             }
7878             break;
7879           }
7880         }
7881       }
7882
7883       if ( toRemove && nbUniqueNodes > 4 ) {
7884         ////////////////// HEXAHEDRON ---> polyhedron
7885         hexa.SetExternalNormal();
7886         vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7887         vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7888         poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7889         quantities.reserve( 6 );     quantities.clear();
7890         for ( int iFace = 0; iFace < 6; iFace++ )
7891         {
7892           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7893           if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7894                curNodes[ind[1]] == curNodes[ind[3]] )
7895           {
7896             quantities.clear();
7897             break; // opposite nodes stick
7898           }
7899           nodeSet.clear();
7900           for ( iCur = 0; iCur < 4; iCur++ )
7901           {
7902             if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7903               poly_nodes.push_back( curNodes[ind[ iCur ]]);
7904           }
7905           if ( nodeSet.size() < 3 )
7906             poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7907           else
7908             quantities.push_back( nodeSet.size() );
7909         }
7910         if ( quantities.size() >= 4 )
7911         {
7912           nbResElems = 1;
7913           nbUniqueNodes = poly_nodes.size();
7914           newElemDefs[0].SetPoly(true);
7915         }
7916       }
7917       break;
7918     } // case HEXAHEDRON
7919
7920     default:
7921       toRemove = true;
7922
7923     } // switch ( entity )
7924
7925     if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7926     {
7927       // erase from nodeNodeMap nodes whose merge spoils elem
7928       vector< const SMDS_MeshNode* > noMergeNodes;
7929       SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7930       for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7931         nodeNodeMap.erase( noMergeNodes[i] );
7932     }
7933     
7934   } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7935
7936   uniqueNodes.resize( nbUniqueNodes );
7937
7938   if ( !toRemove && nbResElems == 0 )
7939     nbResElems = 1;
7940
7941   newElemDefs.resize( nbResElems );
7942
7943   return !toRemove;
7944 }
7945
7946
7947 // ========================================================
7948 // class   : SortableElement
7949 // purpose : allow sorting elements basing on their nodes
7950 // ========================================================
7951 class SortableElement : public set <const SMDS_MeshElement*>
7952 {
7953 public:
7954
7955   SortableElement( const SMDS_MeshElement* theElem )
7956   {
7957     myElem = theElem;
7958     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7959     while ( nodeIt->more() )
7960       this->insert( nodeIt->next() );
7961   }
7962
7963   const SMDS_MeshElement* Get() const
7964   { return myElem; }
7965
7966 private:
7967   mutable const SMDS_MeshElement* myElem;
7968 };
7969
7970 //=======================================================================
7971 //function : FindEqualElements
7972 //purpose  : Return list of group of elements built on the same nodes.
7973 //           Search among theElements or in the whole mesh if theElements is empty
7974 //=======================================================================
7975
7976 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
7977                                          TListOfListOfElementsID & theGroupsOfElementsID)
7978 {
7979   myLastCreatedElems.Clear();
7980   myLastCreatedNodes.Clear();
7981
7982   typedef map< SortableElement, int > TMapOfNodeSet;
7983   typedef list<int> TGroupOfElems;
7984
7985   if ( theElements.empty() )
7986   { // get all elements in the mesh
7987     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7988     while ( eIt->more() )
7989       theElements.insert( theElements.end(), eIt->next() );
7990   }
7991
7992   vector< TGroupOfElems > arrayOfGroups;
7993   TGroupOfElems groupOfElems;
7994   TMapOfNodeSet mapOfNodeSet;
7995
7996   TIDSortedElemSet::iterator elemIt = theElements.begin();
7997   for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7998   {
7999     const SMDS_MeshElement* curElem = *elemIt;
8000     SortableElement SE(curElem);
8001     // check uniqueness
8002     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8003     if ( !pp.second ) { // one more coincident elem
8004       TMapOfNodeSet::iterator& itSE = pp.first;
8005       int ind = (*itSE).second;
8006       arrayOfGroups[ind].push_back( curElem->GetID() );
8007     }
8008     else {
8009       arrayOfGroups.push_back( groupOfElems );
8010       arrayOfGroups.back().push_back( curElem->GetID() );
8011       i++;
8012     }
8013   }
8014
8015   groupOfElems.clear();
8016   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8017   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8018   {
8019     if ( groupIt->size() > 1 ) {
8020       //groupOfElems.sort(); -- theElements is sorted already
8021       theGroupsOfElementsID.push_back( groupOfElems );
8022       theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
8023     }
8024   }
8025 }
8026
8027 //=======================================================================
8028 //function : MergeElements
8029 //purpose  : In each given group, substitute all elements by the first one.
8030 //=======================================================================
8031
8032 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8033 {
8034   myLastCreatedElems.Clear();
8035   myLastCreatedNodes.Clear();
8036
8037   typedef list<int> TListOfIDs;
8038   TListOfIDs rmElemIds; // IDs of elems to remove
8039
8040   SMESHDS_Mesh* aMesh = GetMeshDS();
8041
8042   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8043   while ( groupsIt != theGroupsOfElementsID.end() ) {
8044     TListOfIDs& aGroupOfElemID = *groupsIt;
8045     aGroupOfElemID.sort();
8046     int elemIDToKeep = aGroupOfElemID.front();
8047     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8048     aGroupOfElemID.pop_front();
8049     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8050     while ( idIt != aGroupOfElemID.end() ) {
8051       int elemIDToRemove = *idIt;
8052       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8053       // add the kept element in groups of removed one (PAL15188)
8054       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8055       rmElemIds.push_back( elemIDToRemove );
8056       ++idIt;
8057     }
8058     ++groupsIt;
8059   }
8060
8061   Remove( rmElemIds, false );
8062 }
8063
8064 //=======================================================================
8065 //function : MergeEqualElements
8066 //purpose  : Remove all but one of elements built on the same nodes.
8067 //=======================================================================
8068
8069 void SMESH_MeshEditor::MergeEqualElements()
8070 {
8071   TIDSortedElemSet aMeshElements; /* empty input ==
8072                                      to merge equal elements in the whole mesh */
8073   TListOfListOfElementsID aGroupsOfElementsID;
8074   FindEqualElements(aMeshElements, aGroupsOfElementsID);
8075   MergeElements(aGroupsOfElementsID);
8076 }
8077
8078 //=======================================================================
8079 //function : findAdjacentFace
8080 //purpose  :
8081 //=======================================================================
8082
8083 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8084                                                 const SMDS_MeshNode* n2,
8085                                                 const SMDS_MeshElement* elem)
8086 {
8087   TIDSortedElemSet elemSet, avoidSet;
8088   if ( elem )
8089     avoidSet.insert ( elem );
8090   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8091 }
8092
8093 //=======================================================================
8094 //function : findSegment
8095 //purpose  : Return a mesh segment by two nodes one of which can be medium
8096 //=======================================================================
8097
8098 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8099                                            const SMDS_MeshNode* n2)
8100 {
8101   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8102   while ( it->more() )
8103   {
8104     const SMDS_MeshElement* seg = it->next();
8105     if ( seg->GetNodeIndex( n2 ) >= 0 )
8106       return seg;
8107   }
8108   return 0;
8109 }
8110
8111 //=======================================================================
8112 //function : FindFreeBorder
8113 //purpose  :
8114 //=======================================================================
8115
8116 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8117
8118 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
8119                                        const SMDS_MeshNode*             theSecondNode,
8120                                        const SMDS_MeshNode*             theLastNode,
8121                                        list< const SMDS_MeshNode* > &   theNodes,
8122                                        list< const SMDS_MeshElement* >& theFaces)
8123 {
8124   if ( !theFirstNode || !theSecondNode )
8125     return false;
8126   // find border face between theFirstNode and theSecondNode
8127   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8128   if ( !curElem )
8129     return false;
8130
8131   theFaces.push_back( curElem );
8132   theNodes.push_back( theFirstNode );
8133   theNodes.push_back( theSecondNode );
8134
8135   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8136   TIDSortedElemSet foundElems;
8137   bool needTheLast = ( theLastNode != 0 );
8138
8139   while ( nStart != theLastNode ) {
8140     if ( nStart == theFirstNode )
8141       return !needTheLast;
8142
8143     // find all free border faces sharing form nStart
8144
8145     list< const SMDS_MeshElement* > curElemList;
8146     list< const SMDS_MeshNode* >    nStartList;
8147     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8148     while ( invElemIt->more() ) {
8149       const SMDS_MeshElement* e = invElemIt->next();
8150       if ( e == curElem || foundElems.insert( e ).second ) {
8151         // get nodes
8152         int iNode = 0, nbNodes = e->NbNodes();
8153         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8154
8155         if ( e->IsQuadratic() ) {
8156           const SMDS_VtkFace* F =
8157             dynamic_cast<const SMDS_VtkFace*>(e);
8158           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8159           // use special nodes iterator
8160           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8161           while( anIter->more() ) {
8162             nodes[ iNode++ ] = cast2Node(anIter->next());
8163           }
8164         }
8165         else {
8166           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8167           while ( nIt->more() )
8168             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8169         }
8170         nodes[ iNode ] = nodes[ 0 ];
8171         // check 2 links
8172         for ( iNode = 0; iNode < nbNodes; iNode++ )
8173           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8174                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8175               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8176           {
8177             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8178             curElemList.push_back( e );
8179           }
8180       }
8181     }
8182     // analyse the found
8183
8184     int nbNewBorders = curElemList.size();
8185     if ( nbNewBorders == 0 ) {
8186       // no free border furthermore
8187       return !needTheLast;
8188     }
8189     else if ( nbNewBorders == 1 ) {
8190       // one more element found
8191       nIgnore = nStart;
8192       nStart = nStartList.front();
8193       curElem = curElemList.front();
8194       theFaces.push_back( curElem );
8195       theNodes.push_back( nStart );
8196     }
8197     else {
8198       // several continuations found
8199       list< const SMDS_MeshElement* >::iterator curElemIt;
8200       list< const SMDS_MeshNode* >::iterator nStartIt;
8201       // check if one of them reached the last node
8202       if ( needTheLast ) {
8203         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8204              curElemIt!= curElemList.end();
8205              curElemIt++, nStartIt++ )
8206           if ( *nStartIt == theLastNode ) {
8207             theFaces.push_back( *curElemIt );
8208             theNodes.push_back( *nStartIt );
8209             return true;
8210           }
8211       }
8212       // find the best free border by the continuations
8213       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8214       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8215       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8216            curElemIt!= curElemList.end();
8217            curElemIt++, nStartIt++ )
8218       {
8219         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8220         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8221         // find one more free border
8222         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8223           cNL->clear();
8224           cFL->clear();
8225         }
8226         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8227           // choice: clear a worse one
8228           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8229           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8230           contNodes[ iWorse ].clear();
8231           contFaces[ iWorse ].clear();
8232         }
8233       }
8234       if ( contNodes[0].empty() && contNodes[1].empty() )
8235         return false;
8236
8237       // append the best free border
8238       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8239       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8240       theNodes.pop_back(); // remove nIgnore
8241       theNodes.pop_back(); // remove nStart
8242       theFaces.pop_back(); // remove curElem
8243       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8244       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8245       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8246       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8247       return true;
8248
8249     } // several continuations found
8250   } // while ( nStart != theLastNode )
8251
8252   return true;
8253 }
8254
8255 //=======================================================================
8256 //function : CheckFreeBorderNodes
8257 //purpose  : Return true if the tree nodes are on a free border
8258 //=======================================================================
8259
8260 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8261                                             const SMDS_MeshNode* theNode2,
8262                                             const SMDS_MeshNode* theNode3)
8263 {
8264   list< const SMDS_MeshNode* > nodes;
8265   list< const SMDS_MeshElement* > faces;
8266   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8267 }
8268
8269 //=======================================================================
8270 //function : SewFreeBorder
8271 //purpose  :
8272 //warning  : for border-to-side sewing theSideSecondNode is considered as
8273 //           the last side node and theSideThirdNode is not used
8274 //=======================================================================
8275
8276 SMESH_MeshEditor::Sew_Error
8277 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8278                                  const SMDS_MeshNode* theBordSecondNode,
8279                                  const SMDS_MeshNode* theBordLastNode,
8280                                  const SMDS_MeshNode* theSideFirstNode,
8281                                  const SMDS_MeshNode* theSideSecondNode,
8282                                  const SMDS_MeshNode* theSideThirdNode,
8283                                  const bool           theSideIsFreeBorder,
8284                                  const bool           toCreatePolygons,
8285                                  const bool           toCreatePolyedrs)
8286 {
8287   myLastCreatedElems.Clear();
8288   myLastCreatedNodes.Clear();
8289
8290   Sew_Error aResult = SEW_OK;
8291
8292   // ====================================
8293   //    find side nodes and elements
8294   // ====================================
8295
8296   list< const SMDS_MeshNode* >    nSide[ 2 ];
8297   list< const SMDS_MeshElement* > eSide[ 2 ];
8298   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
8299   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8300
8301   // Free border 1
8302   // --------------
8303   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8304                       nSide[0], eSide[0])) {
8305     MESSAGE(" Free Border 1 not found " );
8306     aResult = SEW_BORDER1_NOT_FOUND;
8307   }
8308   if (theSideIsFreeBorder) {
8309     // Free border 2
8310     // --------------
8311     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8312                         nSide[1], eSide[1])) {
8313       MESSAGE(" Free Border 2 not found " );
8314       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8315     }
8316   }
8317   if ( aResult != SEW_OK )
8318     return aResult;
8319
8320   if (!theSideIsFreeBorder) {
8321     // Side 2
8322     // --------------
8323
8324     // -------------------------------------------------------------------------
8325     // Algo:
8326     // 1. If nodes to merge are not coincident, move nodes of the free border
8327     //    from the coord sys defined by the direction from the first to last
8328     //    nodes of the border to the correspondent sys of the side 2
8329     // 2. On the side 2, find the links most co-directed with the correspondent
8330     //    links of the free border
8331     // -------------------------------------------------------------------------
8332
8333     // 1. Since sewing may break if there are volumes to split on the side 2,
8334     //    we won't move nodes but just compute new coordinates for them
8335     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8336     TNodeXYZMap nBordXYZ;
8337     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8338     list< const SMDS_MeshNode* >::iterator nBordIt;
8339
8340     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8341     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8342     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8343     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8344     double tol2 = 1.e-8;
8345     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8346     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8347       // Need node movement.
8348
8349       // find X and Z axes to create trsf
8350       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8351       gp_Vec X = Zs ^ Zb;
8352       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8353         // Zb || Zs
8354         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8355
8356       // coord systems
8357       gp_Ax3 toBordAx( Pb1, Zb, X );
8358       gp_Ax3 fromSideAx( Ps1, Zs, X );
8359       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8360       // set trsf
8361       gp_Trsf toBordSys, fromSide2Sys;
8362       toBordSys.SetTransformation( toBordAx );
8363       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8364       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8365
8366       // move
8367       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8368         const SMDS_MeshNode* n = *nBordIt;
8369         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8370         toBordSys.Transforms( xyz );
8371         fromSide2Sys.Transforms( xyz );
8372         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8373       }
8374     }
8375     else {
8376       // just insert nodes XYZ in the nBordXYZ map
8377       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8378         const SMDS_MeshNode* n = *nBordIt;
8379         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8380       }
8381     }
8382
8383     // 2. On the side 2, find the links most co-directed with the correspondent
8384     //    links of the free border
8385
8386     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8387     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8388     sideNodes.push_back( theSideFirstNode );
8389
8390     bool hasVolumes = false;
8391     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8392     set<long> foundSideLinkIDs, checkedLinkIDs;
8393     SMDS_VolumeTool volume;
8394     //const SMDS_MeshNode* faceNodes[ 4 ];
8395
8396     const SMDS_MeshNode*    sideNode;
8397     const SMDS_MeshElement* sideElem  = 0;
8398     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8399     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8400     nBordIt = bordNodes.begin();
8401     nBordIt++;
8402     // border node position and border link direction to compare with
8403     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8404     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8405     // choose next side node by link direction or by closeness to
8406     // the current border node:
8407     bool searchByDir = ( *nBordIt != theBordLastNode );
8408     do {
8409       // find the next node on the Side 2
8410       sideNode = 0;
8411       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8412       long linkID;
8413       checkedLinkIDs.clear();
8414       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8415
8416       // loop on inverse elements of current node (prevSideNode) on the Side 2
8417       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8418       while ( invElemIt->more() )
8419       {
8420         const SMDS_MeshElement* elem = invElemIt->next();
8421         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8422         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8423         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8424         bool isVolume = volume.Set( elem );
8425         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8426         if ( isVolume ) // --volume
8427           hasVolumes = true;
8428         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8429           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8430           if(elem->IsQuadratic()) {
8431             const SMDS_VtkFace* F =
8432               dynamic_cast<const SMDS_VtkFace*>(elem);
8433             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8434             // use special nodes iterator
8435             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8436             while( anIter->more() ) {
8437               nodes[ iNode ] = cast2Node(anIter->next());
8438               if ( nodes[ iNode++ ] == prevSideNode )
8439                 iPrevNode = iNode - 1;
8440             }
8441           }
8442           else {
8443             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8444             while ( nIt->more() ) {
8445               nodes[ iNode ] = cast2Node( nIt->next() );
8446               if ( nodes[ iNode++ ] == prevSideNode )
8447                 iPrevNode = iNode - 1;
8448             }
8449           }
8450           // there are 2 links to check
8451           nbNodes = 2;
8452         }
8453         else // --edge
8454           continue;
8455         // loop on links, to be precise, on the second node of links
8456         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8457           const SMDS_MeshNode* n = nodes[ iNode ];
8458           if ( isVolume ) {
8459             if ( !volume.IsLinked( n, prevSideNode ))
8460               continue;
8461           }
8462           else {
8463             if ( iNode ) // a node before prevSideNode
8464               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8465             else         // a node after prevSideNode
8466               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8467           }
8468           // check if this link was already used
8469           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8470           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8471           if (!isJustChecked &&
8472               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8473           {
8474             // test a link geometrically
8475             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8476             bool linkIsBetter = false;
8477             double dot = 0.0, dist = 0.0;
8478             if ( searchByDir ) { // choose most co-directed link
8479               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8480               linkIsBetter = ( dot > maxDot );
8481             }
8482             else { // choose link with the node closest to bordPos
8483               dist = ( nextXYZ - bordPos ).SquareModulus();
8484               linkIsBetter = ( dist < minDist );
8485             }
8486             if ( linkIsBetter ) {
8487               maxDot = dot;
8488               minDist = dist;
8489               linkID = iLink;
8490               sideNode = n;
8491               sideElem = elem;
8492             }
8493           }
8494         }
8495       } // loop on inverse elements of prevSideNode
8496
8497       if ( !sideNode ) {
8498         MESSAGE(" Can't find path by links of the Side 2 ");
8499         return SEW_BAD_SIDE_NODES;
8500       }
8501       sideNodes.push_back( sideNode );
8502       sideElems.push_back( sideElem );
8503       foundSideLinkIDs.insert ( linkID );
8504       prevSideNode = sideNode;
8505
8506       if ( *nBordIt == theBordLastNode )
8507         searchByDir = false;
8508       else {
8509         // find the next border link to compare with
8510         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8511         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8512         // move to next border node if sideNode is before forward border node (bordPos)
8513         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8514           prevBordNode = *nBordIt;
8515           nBordIt++;
8516           bordPos = nBordXYZ[ *nBordIt ];
8517           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8518           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8519         }
8520       }
8521     }
8522     while ( sideNode != theSideSecondNode );
8523
8524     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8525       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8526       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8527     }
8528   } // end nodes search on the side 2
8529
8530   // ============================
8531   // sew the border to the side 2
8532   // ============================
8533
8534   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8535   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8536
8537   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8538   if ( toMergeConformal && toCreatePolygons )
8539   {
8540     // do not merge quadrangles if polygons are OK (IPAL0052824)
8541     eIt[0] = eSide[0].begin();
8542     eIt[1] = eSide[1].begin();
8543     bool allQuads[2] = { true, true };
8544     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8545       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8546         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8547     }
8548     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8549   }
8550
8551   TListOfListOfNodes nodeGroupsToMerge;
8552   if (( toMergeConformal ) ||
8553       ( theSideIsFreeBorder && !theSideThirdNode )) {
8554
8555     // all nodes are to be merged
8556
8557     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8558          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8559          nIt[0]++, nIt[1]++ )
8560     {
8561       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8562       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8563       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8564     }
8565   }
8566   else {
8567
8568     // insert new nodes into the border and the side to get equal nb of segments
8569
8570     // get normalized parameters of nodes on the borders
8571     vector< double > param[ 2 ];
8572     param[0].resize( maxNbNodes );
8573     param[1].resize( maxNbNodes );
8574     int iNode, iBord;
8575     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8576       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8577       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8578       const SMDS_MeshNode* nPrev = *nIt;
8579       double bordLength = 0;
8580       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8581         const SMDS_MeshNode* nCur = *nIt;
8582         gp_XYZ segment (nCur->X() - nPrev->X(),
8583                         nCur->Y() - nPrev->Y(),
8584                         nCur->Z() - nPrev->Z());
8585         double segmentLen = segment.Modulus();
8586         bordLength += segmentLen;
8587         param[ iBord ][ iNode ] = bordLength;
8588         nPrev = nCur;
8589       }
8590       // normalize within [0,1]
8591       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8592         param[ iBord ][ iNode ] /= bordLength;
8593       }
8594     }
8595
8596     // loop on border segments
8597     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8598     int i[ 2 ] = { 0, 0 };
8599     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8600     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8601
8602     TElemOfNodeListMap insertMap;
8603     TElemOfNodeListMap::iterator insertMapIt;
8604     // insertMap is
8605     // key:   elem to insert nodes into
8606     // value: 2 nodes to insert between + nodes to be inserted
8607     do {
8608       bool next[ 2 ] = { false, false };
8609
8610       // find min adjacent segment length after sewing
8611       double nextParam = 10., prevParam = 0;
8612       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8613         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8614           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8615         if ( i[ iBord ] > 0 )
8616           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8617       }
8618       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8619       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8620       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8621
8622       // choose to insert or to merge nodes
8623       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8624       if ( Abs( du ) <= minSegLen * 0.2 ) {
8625         // merge
8626         // ------
8627         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8628         const SMDS_MeshNode* n0 = *nIt[0];
8629         const SMDS_MeshNode* n1 = *nIt[1];
8630         nodeGroupsToMerge.back().push_back( n1 );
8631         nodeGroupsToMerge.back().push_back( n0 );
8632         // position of node of the border changes due to merge
8633         param[ 0 ][ i[0] ] += du;
8634         // move n1 for the sake of elem shape evaluation during insertion.
8635         // n1 will be removed by MergeNodes() anyway
8636         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8637         next[0] = next[1] = true;
8638       }
8639       else {
8640         // insert
8641         // ------
8642         int intoBord = ( du < 0 ) ? 0 : 1;
8643         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8644         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8645         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8646         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8647         if ( intoBord == 1 ) {
8648           // move node of the border to be on a link of elem of the side
8649           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8650           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8651           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8652           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8653           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8654         }
8655         insertMapIt = insertMap.find( elem );
8656         bool  notFound = ( insertMapIt == insertMap.end() );
8657         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8658         if ( otherLink ) {
8659           // insert into another link of the same element:
8660           // 1. perform insertion into the other link of the elem
8661           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8662           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8663           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8664           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8665           // 2. perform insertion into the link of adjacent faces
8666           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8667             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8668           }
8669           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8670             InsertNodesIntoLink( seg, n12, n22, nodeList );
8671           }
8672           if (toCreatePolyedrs) {
8673             // perform insertion into the links of adjacent volumes
8674             UpdateVolumes(n12, n22, nodeList);
8675           }
8676           // 3. find an element appeared on n1 and n2 after the insertion
8677           insertMap.erase( elem );
8678           elem = findAdjacentFace( n1, n2, 0 );
8679         }
8680         if ( notFound || otherLink ) {
8681           // add element and nodes of the side into the insertMap
8682           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8683           (*insertMapIt).second.push_back( n1 );
8684           (*insertMapIt).second.push_back( n2 );
8685         }
8686         // add node to be inserted into elem
8687         (*insertMapIt).second.push_back( nIns );
8688         next[ 1 - intoBord ] = true;
8689       }
8690
8691       // go to the next segment
8692       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8693         if ( next[ iBord ] ) {
8694           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8695             eIt[ iBord ]++;
8696           nPrev[ iBord ] = *nIt[ iBord ];
8697           nIt[ iBord ]++; i[ iBord ]++;
8698         }
8699       }
8700     }
8701     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8702
8703     // perform insertion of nodes into elements
8704
8705     for (insertMapIt = insertMap.begin();
8706          insertMapIt != insertMap.end();
8707          insertMapIt++ )
8708     {
8709       const SMDS_MeshElement* elem = (*insertMapIt).first;
8710       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8711       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8712       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8713
8714       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8715
8716       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8717         InsertNodesIntoLink( seg, n1, n2, nodeList );
8718       }
8719
8720       if ( !theSideIsFreeBorder ) {
8721         // look for and insert nodes into the faces adjacent to elem
8722         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8723           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8724         }
8725       }
8726       if (toCreatePolyedrs) {
8727         // perform insertion into the links of adjacent volumes
8728         UpdateVolumes(n1, n2, nodeList);
8729       }
8730     }
8731   } // end: insert new nodes
8732
8733   MergeNodes ( nodeGroupsToMerge );
8734
8735
8736   // Remove coincident segments
8737
8738   // get new segments
8739   TIDSortedElemSet segments;
8740   SMESH_SequenceOfElemPtr newFaces;
8741   for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8742   {
8743     if ( !myLastCreatedElems(i) ) continue;
8744     if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8745       segments.insert( segments.end(), myLastCreatedElems(i) );
8746     else
8747       newFaces.Append( myLastCreatedElems(i) );
8748   }
8749   // get segments adjacent to merged nodes
8750   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8751   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8752   {
8753     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8754     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8755     while ( segIt->more() )
8756       segments.insert( segIt->next() );
8757   }
8758
8759   // find coincident
8760   TListOfListOfElementsID equalGroups;
8761   if ( !segments.empty() )
8762     FindEqualElements( segments, equalGroups );
8763   if ( !equalGroups.empty() )
8764   {
8765     // remove from segments those that will be removed
8766     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8767     for ( ; itGroups != equalGroups.end(); ++itGroups )
8768     {
8769       list< int >& group = *itGroups;
8770       list< int >::iterator id = group.begin();
8771       for ( ++id; id != group.end(); ++id )
8772         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8773           segments.erase( seg );
8774     }
8775     // remove equal segments
8776     MergeElements( equalGroups );
8777
8778     // restore myLastCreatedElems
8779     myLastCreatedElems = newFaces;
8780     TIDSortedElemSet::iterator seg = segments.begin();
8781     for ( ; seg != segments.end(); ++seg )
8782       myLastCreatedElems.Append( *seg );
8783   }
8784
8785   return aResult;
8786 }
8787
8788 //=======================================================================
8789 //function : InsertNodesIntoLink
8790 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8791 //           and theBetweenNode2 and split theElement
8792 //=======================================================================
8793
8794 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8795                                            const SMDS_MeshNode*        theBetweenNode1,
8796                                            const SMDS_MeshNode*        theBetweenNode2,
8797                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8798                                            const bool                  toCreatePoly)
8799 {
8800   if ( !theElement ) return;
8801
8802   SMESHDS_Mesh *aMesh = GetMeshDS();
8803   vector<const SMDS_MeshElement*> newElems;
8804
8805   if ( theElement->GetType() == SMDSAbs_Edge )
8806   {
8807     theNodesToInsert.push_front( theBetweenNode1 );
8808     theNodesToInsert.push_back ( theBetweenNode2 );
8809     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8810     const SMDS_MeshNode* n1 = *n;
8811     for ( ++n; n != theNodesToInsert.end(); ++n )
8812     {
8813       const SMDS_MeshNode* n2 = *n;
8814       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8815         AddToSameGroups( seg, theElement, aMesh );
8816       else
8817         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8818       n1 = n2;
8819     }
8820     theNodesToInsert.pop_front();
8821     theNodesToInsert.pop_back();
8822
8823     if ( theElement->IsQuadratic() ) // add a not split part
8824     {
8825       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8826                                           theElement->end_nodes() );
8827       int iOther = 0, nbN = nodes.size();
8828       for ( ; iOther < nbN; ++iOther )
8829         if ( nodes[iOther] != theBetweenNode1 &&
8830              nodes[iOther] != theBetweenNode2 )
8831           break;
8832       if      ( iOther == 0 )
8833       {
8834         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8835           AddToSameGroups( seg, theElement, aMesh );
8836         else
8837           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8838       }
8839       else if ( iOther == 2 )
8840       {
8841         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8842           AddToSameGroups( seg, theElement, aMesh );
8843         else
8844           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8845       }
8846     }
8847     // treat new elements
8848     for ( size_t i = 0; i < newElems.size(); ++i )
8849       if ( newElems[i] )
8850       {
8851         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8852         myLastCreatedElems.Append( newElems[i] );
8853       }
8854     ReplaceElemInGroups( theElement, newElems, aMesh );
8855     aMesh->RemoveElement( theElement );
8856     return;
8857
8858   } // if ( theElement->GetType() == SMDSAbs_Edge )
8859
8860   const SMDS_MeshElement* theFace = theElement;
8861   if ( theFace->GetType() != SMDSAbs_Face ) return;
8862
8863   // find indices of 2 link nodes and of the rest nodes
8864   int iNode = 0, il1, il2, i3, i4;
8865   il1 = il2 = i3 = i4 = -1;
8866   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8867
8868   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8869   while ( nodeIt->more() ) {
8870     const SMDS_MeshNode* n = nodeIt->next();
8871     if ( n == theBetweenNode1 )
8872       il1 = iNode;
8873     else if ( n == theBetweenNode2 )
8874       il2 = iNode;
8875     else if ( i3 < 0 )
8876       i3 = iNode;
8877     else
8878       i4 = iNode;
8879     nodes[ iNode++ ] = n;
8880   }
8881   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8882     return ;
8883
8884   // arrange link nodes to go one after another regarding the face orientation
8885   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8886   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8887   if ( reverse ) {
8888     iNode = il1;
8889     il1 = il2;
8890     il2 = iNode;
8891     aNodesToInsert.reverse();
8892   }
8893   // check that not link nodes of a quadrangles are in good order
8894   int nbFaceNodes = theFace->NbNodes();
8895   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8896     iNode = i3;
8897     i3 = i4;
8898     i4 = iNode;
8899   }
8900
8901   if (toCreatePoly || theFace->IsPoly()) {
8902
8903     iNode = 0;
8904     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8905
8906     // add nodes of face up to first node of link
8907     bool isFLN = false;
8908
8909     if ( theFace->IsQuadratic() ) {
8910       const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8911       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8912       // use special nodes iterator
8913       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8914       while( anIter->more()  && !isFLN ) {
8915         const SMDS_MeshNode* n = cast2Node(anIter->next());
8916         poly_nodes[iNode++] = n;
8917         if (n == nodes[il1]) {
8918           isFLN = true;
8919         }
8920       }
8921       // add nodes to insert
8922       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8923       for (; nIt != aNodesToInsert.end(); nIt++) {
8924         poly_nodes[iNode++] = *nIt;
8925       }
8926       // add nodes of face starting from last node of link
8927       while ( anIter->more() ) {
8928         poly_nodes[iNode++] = cast2Node(anIter->next());
8929       }
8930     }
8931     else {
8932       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8933       while ( nodeIt->more() && !isFLN ) {
8934         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8935         poly_nodes[iNode++] = n;
8936         if (n == nodes[il1]) {
8937           isFLN = true;
8938         }
8939       }
8940       // add nodes to insert
8941       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8942       for (; nIt != aNodesToInsert.end(); nIt++) {
8943         poly_nodes[iNode++] = *nIt;
8944       }
8945       // add nodes of face starting from last node of link
8946       while ( nodeIt->more() ) {
8947         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8948         poly_nodes[iNode++] = n;
8949       }
8950     }
8951
8952     // make a new face
8953     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8954   }
8955
8956   else if ( !theFace->IsQuadratic() )
8957   {
8958     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8959     int nbLinkNodes = 2 + aNodesToInsert.size();
8960     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8961     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8962     linkNodes[ 0 ] = nodes[ il1 ];
8963     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8964     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8965     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8966       linkNodes[ iNode++ ] = *nIt;
8967     }
8968     // decide how to split a quadrangle: compare possible variants
8969     // and choose which of splits to be a quadrangle
8970     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8971     if ( nbFaceNodes == 3 ) {
8972       iBestQuad = nbSplits;
8973       i4 = i3;
8974     }
8975     else if ( nbFaceNodes == 4 ) {
8976       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8977       double aBestRate = DBL_MAX;
8978       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8979         i1 = 0; i2 = 1;
8980         double aBadRate = 0;
8981         // evaluate elements quality
8982         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8983           if ( iSplit == iQuad ) {
8984             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8985                                    linkNodes[ i2++ ],
8986                                    nodes[ i3 ],
8987                                    nodes[ i4 ]);
8988             aBadRate += getBadRate( &quad, aCrit );
8989           }
8990           else {
8991             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8992                                    linkNodes[ i2++ ],
8993                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8994             aBadRate += getBadRate( &tria, aCrit );
8995           }
8996         }
8997         // choice
8998         if ( aBadRate < aBestRate ) {
8999           iBestQuad = iQuad;
9000           aBestRate = aBadRate;
9001         }
9002       }
9003     }
9004
9005     // create new elements
9006     i1 = 0; i2 = 1;
9007     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
9008     {
9009       if ( iSplit == iBestQuad )
9010         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9011                                             linkNodes[ i2++ ],
9012                                             nodes[ i3 ],
9013                                             nodes[ i4 ]));
9014       else
9015         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9016                                             linkNodes[ i2++ ],
9017                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
9018     }
9019
9020     const SMDS_MeshNode* newNodes[ 4 ];
9021     newNodes[ 0 ] = linkNodes[ i1 ];
9022     newNodes[ 1 ] = linkNodes[ i2 ];
9023     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9024     newNodes[ 3 ] = nodes[ i4 ];
9025     if (iSplit == iBestQuad)
9026       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
9027     else
9028       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
9029
9030   } // end if(!theFace->IsQuadratic())
9031
9032   else { // theFace is quadratic
9033     // we have to split theFace on simple triangles and one simple quadrangle
9034     int tmp = il1/2;
9035     int nbshift = tmp*2;
9036     // shift nodes in nodes[] by nbshift
9037     int i,j;
9038     for(i=0; i<nbshift; i++) {
9039       const SMDS_MeshNode* n = nodes[0];
9040       for(j=0; j<nbFaceNodes-1; j++) {
9041         nodes[j] = nodes[j+1];
9042       }
9043       nodes[nbFaceNodes-1] = n;
9044     }
9045     il1 = il1 - nbshift;
9046     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9047     //   n0      n1     n2    n0      n1     n2
9048     //     +-----+-----+        +-----+-----+
9049     //      \         /         |           |
9050     //       \       /          |           |
9051     //      n5+     +n3       n7+           +n3
9052     //         \   /            |           |
9053     //          \ /             |           |
9054     //           +              +-----+-----+
9055     //           n4           n6      n5     n4
9056
9057     // create new elements
9058     int n1,n2,n3;
9059     if ( nbFaceNodes == 6 ) { // quadratic triangle
9060       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9061       if ( theFace->IsMediumNode(nodes[il1]) ) {
9062         // create quadrangle
9063         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
9064         n1 = 1;
9065         n2 = 2;
9066         n3 = 3;
9067       }
9068       else {
9069         // create quadrangle
9070         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
9071         n1 = 0;
9072         n2 = 1;
9073         n3 = 5;
9074       }
9075     }
9076     else { // nbFaceNodes==8 - quadratic quadrangle
9077       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9078       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9079       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9080       if ( theFace->IsMediumNode( nodes[ il1 ])) {
9081         // create quadrangle
9082         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9083         n1 = 1;
9084         n2 = 2;
9085         n3 = 3;
9086       }
9087       else {
9088         // create quadrangle
9089         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9090         n1 = 0;
9091         n2 = 1;
9092         n3 = 7;
9093       }
9094     }
9095     // create needed triangles using n1,n2,n3 and inserted nodes
9096     int nbn = 2 + aNodesToInsert.size();
9097     vector<const SMDS_MeshNode*> aNodes(nbn);
9098     aNodes[0    ] = nodes[n1];
9099     aNodes[nbn-1] = nodes[n2];
9100     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9101     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9102       aNodes[iNode++] = *nIt;
9103     }
9104     for ( i = 1; i < nbn; i++ )
9105       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9106   }
9107
9108   // remove the old face
9109   for ( size_t i = 0; i < newElems.size(); ++i )
9110     if ( newElems[i] )
9111     {
9112       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9113       myLastCreatedElems.Append( newElems[i] );
9114     }
9115   ReplaceElemInGroups( theFace, newElems, aMesh );
9116   aMesh->RemoveElement(theFace);
9117
9118 } // InsertNodesIntoLink()
9119
9120 //=======================================================================
9121 //function : UpdateVolumes
9122 //purpose  :
9123 //=======================================================================
9124
9125 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
9126                                       const SMDS_MeshNode*        theBetweenNode2,
9127                                       list<const SMDS_MeshNode*>& theNodesToInsert)
9128 {
9129   myLastCreatedElems.Clear();
9130   myLastCreatedNodes.Clear();
9131
9132   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9133   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9134     const SMDS_MeshElement* elem = invElemIt->next();
9135
9136     // check, if current volume has link theBetweenNode1 - theBetweenNode2
9137     SMDS_VolumeTool aVolume (elem);
9138     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9139       continue;
9140
9141     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9142     int iface, nbFaces = aVolume.NbFaces();
9143     vector<const SMDS_MeshNode *> poly_nodes;
9144     vector<int> quantities (nbFaces);
9145
9146     for (iface = 0; iface < nbFaces; iface++) {
9147       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9148       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9149       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9150
9151       for (int inode = 0; inode < nbFaceNodes; inode++) {
9152         poly_nodes.push_back(faceNodes[inode]);
9153
9154         if (nbInserted == 0) {
9155           if (faceNodes[inode] == theBetweenNode1) {
9156             if (faceNodes[inode + 1] == theBetweenNode2) {
9157               nbInserted = theNodesToInsert.size();
9158
9159               // add nodes to insert
9160               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9161               for (; nIt != theNodesToInsert.end(); nIt++) {
9162                 poly_nodes.push_back(*nIt);
9163               }
9164             }
9165           }
9166           else if (faceNodes[inode] == theBetweenNode2) {
9167             if (faceNodes[inode + 1] == theBetweenNode1) {
9168               nbInserted = theNodesToInsert.size();
9169
9170               // add nodes to insert in reversed order
9171               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9172               nIt--;
9173               for (; nIt != theNodesToInsert.begin(); nIt--) {
9174                 poly_nodes.push_back(*nIt);
9175               }
9176               poly_nodes.push_back(*nIt);
9177             }
9178           }
9179           else {
9180           }
9181         }
9182       }
9183       quantities[iface] = nbFaceNodes + nbInserted;
9184     }
9185
9186     // Replace the volume
9187     SMESHDS_Mesh *aMesh = GetMeshDS();
9188
9189     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9190     {
9191       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9192       myLastCreatedElems.Append( newElem );
9193       ReplaceElemInGroups( elem, newElem, aMesh );
9194     }
9195     aMesh->RemoveElement( elem );
9196   }
9197 }
9198
9199 namespace
9200 {
9201   //================================================================================
9202   /*!
9203    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9204    */
9205   //================================================================================
9206
9207   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9208                            vector<const SMDS_MeshNode *> & nodes,
9209                            vector<int> &                   nbNodeInFaces )
9210   {
9211     nodes.clear();
9212     nbNodeInFaces.clear();
9213     SMDS_VolumeTool vTool ( elem );
9214     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9215     {
9216       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9217       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9218       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9219     }
9220   }
9221 }
9222
9223 //=======================================================================
9224 /*!
9225  * \brief Convert elements contained in a sub-mesh to quadratic
9226  * \return int - nb of checked elements
9227  */
9228 //=======================================================================
9229
9230 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9231                                              SMESH_MesherHelper& theHelper,
9232                                              const bool          theForce3d)
9233 {
9234   int nbElem = 0;
9235   if( !theSm ) return nbElem;
9236
9237   vector<int> nbNodeInFaces;
9238   vector<const SMDS_MeshNode *> nodes;
9239   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9240   while(ElemItr->more())
9241   {
9242     nbElem++;
9243     const SMDS_MeshElement* elem = ElemItr->next();
9244     if( !elem ) continue;
9245
9246     // analyse a necessity of conversion
9247     const SMDSAbs_ElementType aType = elem->GetType();
9248     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9249       continue;
9250     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9251     bool hasCentralNodes = false;
9252     if ( elem->IsQuadratic() )
9253     {
9254       bool alreadyOK;
9255       switch ( aGeomType ) {
9256       case SMDSEntity_Quad_Triangle:
9257       case SMDSEntity_Quad_Quadrangle:
9258       case SMDSEntity_Quad_Hexa:
9259         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9260
9261       case SMDSEntity_BiQuad_Triangle:
9262       case SMDSEntity_BiQuad_Quadrangle:
9263       case SMDSEntity_TriQuad_Hexa:
9264         alreadyOK = theHelper.GetIsBiQuadratic();
9265         hasCentralNodes = true;
9266         break;
9267       default:
9268         alreadyOK = true;
9269       }
9270       // take into account already present modium nodes
9271       switch ( aType ) {
9272       case SMDSAbs_Volume:
9273         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9274       case SMDSAbs_Face:
9275         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9276       case SMDSAbs_Edge:
9277         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9278       default:;
9279       }
9280       if ( alreadyOK )
9281         continue;
9282     }
9283     // get elem data needed to re-create it
9284     //
9285     const int id      = elem->GetID();
9286     const int nbNodes = elem->NbCornerNodes();
9287     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9288     if ( aGeomType == SMDSEntity_Polyhedra )
9289       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9290     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9291       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9292
9293     // remove a linear element
9294     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9295
9296     // remove central nodes of biquadratic elements (biquad->quad conversion)
9297     if ( hasCentralNodes )
9298       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9299         if ( nodes[i]->NbInverseElements() == 0 )
9300           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9301
9302     const SMDS_MeshElement* NewElem = 0;
9303
9304     switch( aType )
9305     {
9306     case SMDSAbs_Edge :
9307       {
9308         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9309         break;
9310       }
9311     case SMDSAbs_Face :
9312       {
9313         switch(nbNodes)
9314         {
9315         case 3:
9316           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9317           break;
9318         case 4:
9319           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9320           break;
9321         default:
9322           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9323         }
9324         break;
9325       }
9326     case SMDSAbs_Volume :
9327       {
9328         switch( aGeomType )
9329         {
9330         case SMDSEntity_Tetra:
9331           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9332           break;
9333         case SMDSEntity_Pyramid:
9334           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9335           break;
9336         case SMDSEntity_Penta:
9337           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9338           break;
9339         case SMDSEntity_Hexa:
9340         case SMDSEntity_Quad_Hexa:
9341         case SMDSEntity_TriQuad_Hexa:
9342           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9343                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9344           break;
9345         case SMDSEntity_Hexagonal_Prism:
9346         default:
9347           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9348         }
9349         break;
9350       }
9351     default :
9352       continue;
9353     }
9354     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9355     if( NewElem && NewElem->getshapeId() < 1 )
9356       theSm->AddElement( NewElem );
9357   }
9358   return nbElem;
9359 }
9360 //=======================================================================
9361 //function : ConvertToQuadratic
9362 //purpose  :
9363 //=======================================================================
9364
9365 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9366 {
9367   SMESHDS_Mesh* meshDS = GetMeshDS();
9368
9369   SMESH_MesherHelper aHelper(*myMesh);
9370
9371   aHelper.SetIsQuadratic( true );
9372   aHelper.SetIsBiQuadratic( theToBiQuad );
9373   aHelper.SetElementsOnShape(true);
9374   aHelper.ToFixNodeParameters( true );
9375
9376   // convert elements assigned to sub-meshes
9377   int nbCheckedElems = 0;
9378   if ( myMesh->HasShapeToMesh() )
9379   {
9380     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9381     {
9382       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9383       while ( smIt->more() ) {
9384         SMESH_subMesh* sm = smIt->next();
9385         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9386           aHelper.SetSubShape( sm->GetSubShape() );
9387           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9388         }
9389       }
9390     }
9391   }
9392
9393   // convert elements NOT assigned to sub-meshes
9394   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9395   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9396   {
9397     aHelper.SetElementsOnShape(false);
9398     SMESHDS_SubMesh *smDS = 0;
9399
9400     // convert edges
9401     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9402     while( aEdgeItr->more() )
9403     {
9404       const SMDS_MeshEdge* edge = aEdgeItr->next();
9405       if ( !edge->IsQuadratic() )
9406       {
9407         int                  id = edge->GetID();
9408         const SMDS_MeshNode* n1 = edge->GetNode(0);
9409         const SMDS_MeshNode* n2 = edge->GetNode(1);
9410
9411         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9412
9413         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9414         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9415       }
9416       else
9417       {
9418         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9419       }
9420     }
9421
9422     // convert faces
9423     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9424     while( aFaceItr->more() )
9425     {
9426       const SMDS_MeshFace* face = aFaceItr->next();
9427       if ( !face ) continue;
9428       
9429       const SMDSAbs_EntityType type = face->GetEntityType();
9430       bool alreadyOK;
9431       switch( type )
9432       {
9433       case SMDSEntity_Quad_Triangle:
9434       case SMDSEntity_Quad_Quadrangle:
9435         alreadyOK = !theToBiQuad;
9436         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9437         break;
9438       case SMDSEntity_BiQuad_Triangle:
9439       case SMDSEntity_BiQuad_Quadrangle:
9440         alreadyOK = theToBiQuad;
9441         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9442         break;
9443       default: alreadyOK = false;
9444       }
9445       if ( alreadyOK )
9446         continue;
9447
9448       const int id = face->GetID();
9449       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9450
9451       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9452
9453       SMDS_MeshFace * NewFace = 0;
9454       switch( type )
9455       {
9456       case SMDSEntity_Triangle:
9457       case SMDSEntity_Quad_Triangle:
9458       case SMDSEntity_BiQuad_Triangle:
9459         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9460         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9461           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9462         break;
9463
9464       case SMDSEntity_Quadrangle:
9465       case SMDSEntity_Quad_Quadrangle:
9466       case SMDSEntity_BiQuad_Quadrangle:
9467         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9468         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9469           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9470         break;
9471
9472       default:;
9473         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9474       }
9475       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9476     }
9477
9478     // convert volumes
9479     vector<int> nbNodeInFaces;
9480     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9481     while(aVolumeItr->more())
9482     {
9483       const SMDS_MeshVolume* volume = aVolumeItr->next();
9484       if ( !volume ) continue;
9485
9486       const SMDSAbs_EntityType type = volume->GetEntityType();
9487       if ( volume->IsQuadratic() )
9488       {
9489         bool alreadyOK;
9490         switch ( type )
9491         {
9492         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9493         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9494         default:                      alreadyOK = true;
9495         }
9496         if ( alreadyOK )
9497         {
9498           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9499           continue;
9500         }
9501       }
9502       const int id = volume->GetID();
9503       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9504       if ( type == SMDSEntity_Polyhedra )
9505         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9506       else if ( type == SMDSEntity_Hexagonal_Prism )
9507         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9508
9509       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9510
9511       SMDS_MeshVolume * NewVolume = 0;
9512       switch ( type )
9513       {
9514       case SMDSEntity_Tetra:
9515         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9516         break;
9517       case SMDSEntity_Hexa:
9518       case SMDSEntity_Quad_Hexa:
9519       case SMDSEntity_TriQuad_Hexa:
9520         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9521                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9522         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9523           if ( nodes[i]->NbInverseElements() == 0 )
9524             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9525         break;
9526       case SMDSEntity_Pyramid:
9527         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9528                                       nodes[3], nodes[4], id, theForce3d);
9529         break;
9530       case SMDSEntity_Penta:
9531         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9532                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9533         break;
9534       case SMDSEntity_Hexagonal_Prism:
9535       default:
9536         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9537       }
9538       ReplaceElemInGroups(volume, NewVolume, meshDS);
9539     }
9540   }
9541
9542   if ( !theForce3d )
9543   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9544     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9545     // aHelper.FixQuadraticElements(myError);
9546     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9547   }
9548 }
9549
9550 //================================================================================
9551 /*!
9552  * \brief Makes given elements quadratic
9553  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9554  *  \param theElements - elements to make quadratic
9555  */
9556 //================================================================================
9557
9558 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9559                                           TIDSortedElemSet& theElements,
9560                                           const bool        theToBiQuad)
9561 {
9562   if ( theElements.empty() ) return;
9563
9564   // we believe that all theElements are of the same type
9565   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9566
9567   // get all nodes shared by theElements
9568   TIDSortedNodeSet allNodes;
9569   TIDSortedElemSet::iterator eIt = theElements.begin();
9570   for ( ; eIt != theElements.end(); ++eIt )
9571     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9572
9573   // complete theElements with elements of lower dim whose all nodes are in allNodes
9574
9575   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9576   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9577   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9578   for ( ; nIt != allNodes.end(); ++nIt )
9579   {
9580     const SMDS_MeshNode* n = *nIt;
9581     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9582     while ( invIt->more() )
9583     {
9584       const SMDS_MeshElement*      e = invIt->next();
9585       const SMDSAbs_ElementType type = e->GetType();
9586       if ( e->IsQuadratic() )
9587       {
9588         quadAdjacentElems[ type ].insert( e );
9589
9590         bool alreadyOK;
9591         switch ( e->GetEntityType() ) {
9592         case SMDSEntity_Quad_Triangle:
9593         case SMDSEntity_Quad_Quadrangle:
9594         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9595         case SMDSEntity_BiQuad_Triangle:
9596         case SMDSEntity_BiQuad_Quadrangle:
9597         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9598         default:                           alreadyOK = true;
9599         }
9600         if ( alreadyOK )
9601           continue;
9602       }
9603       if ( type >= elemType )
9604         continue; // same type or more complex linear element
9605
9606       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9607         continue; // e is already checked
9608
9609       // check nodes
9610       bool allIn = true;
9611       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9612       while ( nodeIt->more() && allIn )
9613         allIn = allNodes.count( nodeIt->next() );
9614       if ( allIn )
9615         theElements.insert(e );
9616     }
9617   }
9618
9619   SMESH_MesherHelper helper(*myMesh);
9620   helper.SetIsQuadratic( true );
9621   helper.SetIsBiQuadratic( theToBiQuad );
9622
9623   // add links of quadratic adjacent elements to the helper
9624
9625   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9626     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9627           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9628     {
9629       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9630     }
9631   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9632     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9633           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9634     {
9635       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9636     }
9637   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9638     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9639           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9640     {
9641       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9642     }
9643
9644   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9645
9646   SMESHDS_Mesh*  meshDS = GetMeshDS();
9647   SMESHDS_SubMesh* smDS = 0;
9648   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9649   {
9650     const SMDS_MeshElement* elem = *eIt;
9651
9652     bool alreadyOK;
9653     int nbCentralNodes = 0;
9654     switch ( elem->GetEntityType() ) {
9655       // linear convertible
9656     case SMDSEntity_Edge:
9657     case SMDSEntity_Triangle:
9658     case SMDSEntity_Quadrangle:
9659     case SMDSEntity_Tetra:
9660     case SMDSEntity_Pyramid:
9661     case SMDSEntity_Hexa:
9662     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9663       // quadratic that can become bi-quadratic
9664     case SMDSEntity_Quad_Triangle:
9665     case SMDSEntity_Quad_Quadrangle:
9666     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9667       // bi-quadratic
9668     case SMDSEntity_BiQuad_Triangle:
9669     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9670     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9671       // the rest
9672     default:                           alreadyOK = true;
9673     }
9674     if ( alreadyOK ) continue;
9675
9676     const SMDSAbs_ElementType type = elem->GetType();
9677     const int                   id = elem->GetID();
9678     const int              nbNodes = elem->NbCornerNodes();
9679     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9680
9681     helper.SetSubShape( elem->getshapeId() );
9682
9683     if ( !smDS || !smDS->Contains( elem ))
9684       smDS = meshDS->MeshElements( elem->getshapeId() );
9685     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9686
9687     SMDS_MeshElement * newElem = 0;
9688     switch( nbNodes )
9689     {
9690     case 4: // cases for most frequently used element types go first (for optimization)
9691       if ( type == SMDSAbs_Volume )
9692         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9693       else
9694         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9695       break;
9696     case 8:
9697       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9698                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9699       break;
9700     case 3:
9701       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9702       break;
9703     case 2:
9704       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9705       break;
9706     case 5:
9707       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9708                                  nodes[4], id, theForce3d);
9709       break;
9710     case 6:
9711       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9712                                  nodes[4], nodes[5], id, theForce3d);
9713       break;
9714     default:;
9715     }
9716     ReplaceElemInGroups( elem, newElem, meshDS);
9717     if( newElem && smDS )
9718       smDS->AddElement( newElem );
9719
9720      // remove central nodes
9721     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9722       if ( nodes[i]->NbInverseElements() == 0 )
9723         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9724
9725   } // loop on theElements
9726
9727   if ( !theForce3d )
9728   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9729     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9730     // helper.FixQuadraticElements( myError );
9731     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9732   }
9733 }
9734
9735 //=======================================================================
9736 /*!
9737  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9738  * \return int - nb of checked elements
9739  */
9740 //=======================================================================
9741
9742 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9743                                      SMDS_ElemIteratorPtr theItr,
9744                                      const int            theShapeID)
9745 {
9746   int nbElem = 0;
9747   SMESHDS_Mesh* meshDS = GetMeshDS();
9748   ElemFeatures elemType;
9749   vector<const SMDS_MeshNode *> nodes;
9750
9751   while( theItr->more() )
9752   {
9753     const SMDS_MeshElement* elem = theItr->next();
9754     nbElem++;
9755     if( elem && elem->IsQuadratic())
9756     {
9757       // get elem data
9758       int nbCornerNodes = elem->NbCornerNodes();
9759       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9760
9761       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9762
9763       //remove a quadratic element
9764       if ( !theSm || !theSm->Contains( elem ))
9765         theSm = meshDS->MeshElements( elem->getshapeId() );
9766       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9767
9768       // remove medium nodes
9769       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9770         if ( nodes[i]->NbInverseElements() == 0 )
9771           meshDS->RemoveFreeNode( nodes[i], theSm );
9772
9773       // add a linear element
9774       nodes.resize( nbCornerNodes );
9775       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9776       ReplaceElemInGroups(elem, newElem, meshDS);
9777       if( theSm && newElem )
9778         theSm->AddElement( newElem );
9779     }
9780   }
9781   return nbElem;
9782 }
9783
9784 //=======================================================================
9785 //function : ConvertFromQuadratic
9786 //purpose  :
9787 //=======================================================================
9788
9789 bool SMESH_MeshEditor::ConvertFromQuadratic()
9790 {
9791   int nbCheckedElems = 0;
9792   if ( myMesh->HasShapeToMesh() )
9793   {
9794     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9795     {
9796       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9797       while ( smIt->more() ) {
9798         SMESH_subMesh* sm = smIt->next();
9799         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9800           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9801       }
9802     }
9803   }
9804
9805   int totalNbElems =
9806     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9807   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9808   {
9809     SMESHDS_SubMesh *aSM = 0;
9810     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9811   }
9812
9813   return true;
9814 }
9815
9816 namespace
9817 {
9818   //================================================================================
9819   /*!
9820    * \brief Return true if all medium nodes of the element are in the node set
9821    */
9822   //================================================================================
9823
9824   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9825   {
9826     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9827       if ( !nodeSet.count( elem->GetNode(i) ))
9828         return false;
9829     return true;
9830   }
9831 }
9832
9833 //================================================================================
9834 /*!
9835  * \brief Makes given elements linear
9836  */
9837 //================================================================================
9838
9839 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9840 {
9841   if ( theElements.empty() ) return;
9842
9843   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9844   set<int> mediumNodeIDs;
9845   TIDSortedElemSet::iterator eIt = theElements.begin();
9846   for ( ; eIt != theElements.end(); ++eIt )
9847   {
9848     const SMDS_MeshElement* e = *eIt;
9849     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9850       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9851   }
9852
9853   // replace given elements by linear ones
9854   SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9855   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9856
9857   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9858   // except those elements sharing medium nodes of quadratic element whose medium nodes
9859   // are not all in mediumNodeIDs
9860
9861   // get remaining medium nodes
9862   TIDSortedNodeSet mediumNodes;
9863   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9864   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9865     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9866       mediumNodes.insert( mediumNodes.end(), n );
9867
9868   // find more quadratic elements to convert
9869   TIDSortedElemSet moreElemsToConvert;
9870   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9871   for ( ; nIt != mediumNodes.end(); ++nIt )
9872   {
9873     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9874     while ( invIt->more() )
9875     {
9876       const SMDS_MeshElement* e = invIt->next();
9877       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9878       {
9879         // find a more complex element including e and
9880         // whose medium nodes are not in mediumNodes
9881         bool complexFound = false;
9882         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9883         {
9884           SMDS_ElemIteratorPtr invIt2 =
9885             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9886           while ( invIt2->more() )
9887           {
9888             const SMDS_MeshElement* eComplex = invIt2->next();
9889             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9890             {
9891               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9892               if ( nbCommonNodes == e->NbNodes())
9893               {
9894                 complexFound = true;
9895                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9896                 break;
9897               }
9898             }
9899           }
9900         }
9901         if ( !complexFound )
9902           moreElemsToConvert.insert( e );
9903       }
9904     }
9905   }
9906   elemIt = elemSetIterator( moreElemsToConvert );
9907   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9908 }
9909
9910 //=======================================================================
9911 //function : SewSideElements
9912 //purpose  :
9913 //=======================================================================
9914
9915 SMESH_MeshEditor::Sew_Error
9916 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9917                                    TIDSortedElemSet&    theSide2,
9918                                    const SMDS_MeshNode* theFirstNode1,
9919                                    const SMDS_MeshNode* theFirstNode2,
9920                                    const SMDS_MeshNode* theSecondNode1,
9921                                    const SMDS_MeshNode* theSecondNode2)
9922 {
9923   myLastCreatedElems.Clear();
9924   myLastCreatedNodes.Clear();
9925
9926   if ( theSide1.size() != theSide2.size() )
9927     return SEW_DIFF_NB_OF_ELEMENTS;
9928
9929   Sew_Error aResult = SEW_OK;
9930   // Algo:
9931   // 1. Build set of faces representing each side
9932   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9933   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9934
9935   // =======================================================================
9936   // 1. Build set of faces representing each side:
9937   // =======================================================================
9938   // a. build set of nodes belonging to faces
9939   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9940   // c. create temporary faces representing side of volumes if correspondent
9941   //    face does not exist
9942
9943   SMESHDS_Mesh* aMesh = GetMeshDS();
9944   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9945   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9946   TIDSortedElemSet             faceSet1, faceSet2;
9947   set<const SMDS_MeshElement*> volSet1,  volSet2;
9948   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9949   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9950   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9951   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9952   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9953   int iSide, iFace, iNode;
9954
9955   list<const SMDS_MeshElement* > tempFaceList;
9956   for ( iSide = 0; iSide < 2; iSide++ ) {
9957     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9958     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9959     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9960     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9961     set<const SMDS_MeshElement*>::iterator vIt;
9962     TIDSortedElemSet::iterator eIt;
9963     set<const SMDS_MeshNode*>::iterator    nIt;
9964
9965     // check that given nodes belong to given elements
9966     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9967     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9968     int firstIndex = -1, secondIndex = -1;
9969     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9970       const SMDS_MeshElement* elem = *eIt;
9971       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9972       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9973       if ( firstIndex > -1 && secondIndex > -1 ) break;
9974     }
9975     if ( firstIndex < 0 || secondIndex < 0 ) {
9976       // we can simply return until temporary faces created
9977       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9978     }
9979
9980     // -----------------------------------------------------------
9981     // 1a. Collect nodes of existing faces
9982     //     and build set of face nodes in order to detect missing
9983     //     faces corresponding to sides of volumes
9984     // -----------------------------------------------------------
9985
9986     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9987
9988     // loop on the given element of a side
9989     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9990       //const SMDS_MeshElement* elem = *eIt;
9991       const SMDS_MeshElement* elem = *eIt;
9992       if ( elem->GetType() == SMDSAbs_Face ) {
9993         faceSet->insert( elem );
9994         set <const SMDS_MeshNode*> faceNodeSet;
9995         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9996         while ( nodeIt->more() ) {
9997           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9998           nodeSet->insert( n );
9999           faceNodeSet.insert( n );
10000         }
10001         setOfFaceNodeSet.insert( faceNodeSet );
10002       }
10003       else if ( elem->GetType() == SMDSAbs_Volume )
10004         volSet->insert( elem );
10005     }
10006     // ------------------------------------------------------------------------------
10007     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10008     // ------------------------------------------------------------------------------
10009
10010     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10011       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10012       while ( fIt->more() ) { // loop on faces sharing a node
10013         const SMDS_MeshElement* f = fIt->next();
10014         if ( faceSet->find( f ) == faceSet->end() ) {
10015           // check if all nodes are in nodeSet and
10016           // complete setOfFaceNodeSet if they are
10017           set <const SMDS_MeshNode*> faceNodeSet;
10018           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10019           bool allInSet = true;
10020           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10021             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10022             if ( nodeSet->find( n ) == nodeSet->end() )
10023               allInSet = false;
10024             else
10025               faceNodeSet.insert( n );
10026           }
10027           if ( allInSet ) {
10028             faceSet->insert( f );
10029             setOfFaceNodeSet.insert( faceNodeSet );
10030           }
10031         }
10032       }
10033     }
10034
10035     // -------------------------------------------------------------------------
10036     // 1c. Create temporary faces representing sides of volumes if correspondent
10037     //     face does not exist
10038     // -------------------------------------------------------------------------
10039
10040     if ( !volSet->empty() ) {
10041       //int nodeSetSize = nodeSet->size();
10042
10043       // loop on given volumes
10044       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10045         SMDS_VolumeTool vol (*vIt);
10046         // loop on volume faces: find free faces
10047         // --------------------------------------
10048         list<const SMDS_MeshElement* > freeFaceList;
10049         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10050           if ( !vol.IsFreeFace( iFace ))
10051             continue;
10052           // check if there is already a face with same nodes in a face set
10053           const SMDS_MeshElement* aFreeFace = 0;
10054           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10055           int nbNodes = vol.NbFaceNodes( iFace );
10056           set <const SMDS_MeshNode*> faceNodeSet;
10057           vol.GetFaceNodes( iFace, faceNodeSet );
10058           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10059           if ( isNewFace ) {
10060             // no such a face is given but it still can exist, check it
10061             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10062             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10063           }
10064           if ( !aFreeFace ) {
10065             // create a temporary face
10066             if ( nbNodes == 3 ) {
10067               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10068               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10069             }
10070             else if ( nbNodes == 4 ) {
10071               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10072               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10073             }
10074             else {
10075               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10076               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10077               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10078             }
10079             if ( aFreeFace )
10080               tempFaceList.push_back( aFreeFace );
10081           }
10082
10083           if ( aFreeFace )
10084             freeFaceList.push_back( aFreeFace );
10085
10086         } // loop on faces of a volume
10087
10088         // choose one of several free faces of a volume
10089         // --------------------------------------------
10090         if ( freeFaceList.size() > 1 ) {
10091           // choose a face having max nb of nodes shared by other elems of a side
10092           int maxNbNodes = -1;
10093           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10094           while ( fIt != freeFaceList.end() ) { // loop on free faces
10095             int nbSharedNodes = 0;
10096             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10097             while ( nodeIt->more() ) { // loop on free face nodes
10098               const SMDS_MeshNode* n =
10099                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10100               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10101               while ( invElemIt->more() ) {
10102                 const SMDS_MeshElement* e = invElemIt->next();
10103                 nbSharedNodes += faceSet->count( e );
10104                 nbSharedNodes += elemSet->count( e );
10105               }
10106             }
10107             if ( nbSharedNodes > maxNbNodes ) {
10108               maxNbNodes = nbSharedNodes;
10109               freeFaceList.erase( freeFaceList.begin(), fIt++ );
10110             }
10111             else if ( nbSharedNodes == maxNbNodes ) {
10112               fIt++;
10113             }
10114             else {
10115               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10116             }
10117           }
10118           if ( freeFaceList.size() > 1 )
10119           {
10120             // could not choose one face, use another way
10121             // choose a face most close to the bary center of the opposite side
10122             gp_XYZ aBC( 0., 0., 0. );
10123             set <const SMDS_MeshNode*> addedNodes;
10124             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10125             eIt = elemSet2->begin();
10126             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10127               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10128               while ( nodeIt->more() ) { // loop on free face nodes
10129                 const SMDS_MeshNode* n =
10130                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10131                 if ( addedNodes.insert( n ).second )
10132                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10133               }
10134             }
10135             aBC /= addedNodes.size();
10136             double minDist = DBL_MAX;
10137             fIt = freeFaceList.begin();
10138             while ( fIt != freeFaceList.end() ) { // loop on free faces
10139               double dist = 0;
10140               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10141               while ( nodeIt->more() ) { // loop on free face nodes
10142                 const SMDS_MeshNode* n =
10143                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10144                 gp_XYZ p( n->X(),n->Y(),n->Z() );
10145                 dist += ( aBC - p ).SquareModulus();
10146               }
10147               if ( dist < minDist ) {
10148                 minDist = dist;
10149                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10150               }
10151               else
10152                 fIt = freeFaceList.erase( fIt++ );
10153             }
10154           }
10155         } // choose one of several free faces of a volume
10156
10157         if ( freeFaceList.size() == 1 ) {
10158           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10159           faceSet->insert( aFreeFace );
10160           // complete a node set with nodes of a found free face
10161           //           for ( iNode = 0; iNode < ; iNode++ )
10162           //             nodeSet->insert( fNodes[ iNode ] );
10163         }
10164
10165       } // loop on volumes of a side
10166
10167       //       // complete a set of faces if new nodes in a nodeSet appeared
10168       //       // ----------------------------------------------------------
10169       //       if ( nodeSetSize != nodeSet->size() ) {
10170       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10171       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10172       //           while ( fIt->more() ) { // loop on faces sharing a node
10173       //             const SMDS_MeshElement* f = fIt->next();
10174       //             if ( faceSet->find( f ) == faceSet->end() ) {
10175       //               // check if all nodes are in nodeSet and
10176       //               // complete setOfFaceNodeSet if they are
10177       //               set <const SMDS_MeshNode*> faceNodeSet;
10178       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10179       //               bool allInSet = true;
10180       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10181       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10182       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10183       //                   allInSet = false;
10184       //                 else
10185       //                   faceNodeSet.insert( n );
10186       //               }
10187       //               if ( allInSet ) {
10188       //                 faceSet->insert( f );
10189       //                 setOfFaceNodeSet.insert( faceNodeSet );
10190       //               }
10191       //             }
10192       //           }
10193       //         }
10194       //       }
10195     } // Create temporary faces, if there are volumes given
10196   } // loop on sides
10197
10198   if ( faceSet1.size() != faceSet2.size() ) {
10199     // delete temporary faces: they are in reverseElements of actual nodes
10200 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10201 //    while ( tmpFaceIt->more() )
10202 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10203 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10204 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10205 //      aMesh->RemoveElement(*tmpFaceIt);
10206     MESSAGE("Diff nb of faces");
10207     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10208   }
10209
10210   // ============================================================
10211   // 2. Find nodes to merge:
10212   //              bind a node to remove to a node to put instead
10213   // ============================================================
10214
10215   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10216   if ( theFirstNode1 != theFirstNode2 )
10217     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10218   if ( theSecondNode1 != theSecondNode2 )
10219     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10220
10221   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10222   set< long > linkIdSet; // links to process
10223   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10224
10225   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10226   list< NLink > linkList[2];
10227   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10228   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10229   // loop on links in linkList; find faces by links and append links
10230   // of the found faces to linkList
10231   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10232   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10233   {
10234     NLink link[] = { *linkIt[0], *linkIt[1] };
10235     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10236     if ( !linkIdSet.count( linkID ) )
10237       continue;
10238
10239     // by links, find faces in the face sets,
10240     // and find indices of link nodes in the found faces;
10241     // in a face set, there is only one or no face sharing a link
10242     // ---------------------------------------------------------------
10243
10244     const SMDS_MeshElement* face[] = { 0, 0 };
10245     vector<const SMDS_MeshNode*> fnodes[2];
10246     int iLinkNode[2][2];
10247     TIDSortedElemSet avoidSet;
10248     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10249       const SMDS_MeshNode* n1 = link[iSide].first;
10250       const SMDS_MeshNode* n2 = link[iSide].second;
10251       //cout << "Side " << iSide << " ";
10252       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10253       // find a face by two link nodes
10254       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10255                                                       *faceSetPtr[ iSide ], avoidSet,
10256                                                       &iLinkNode[iSide][0],
10257                                                       &iLinkNode[iSide][1] );
10258       if ( face[ iSide ])
10259       {
10260         //cout << " F " << face[ iSide]->GetID() <<endl;
10261         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10262         // put face nodes to fnodes
10263         if ( face[ iSide ]->IsQuadratic() )
10264         {
10265           // use interlaced nodes iterator
10266           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10267           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10268           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10269           while ( nIter->more() )
10270             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10271         }
10272         else
10273         {
10274           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10275                                   face[ iSide ]->end_nodes() );
10276         }
10277         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10278       }
10279     }
10280
10281     // check similarity of elements of the sides
10282     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10283       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10284       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10285         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10286       }
10287       else {
10288         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10289       }
10290       break; // do not return because it's necessary to remove tmp faces
10291     }
10292
10293     // set nodes to merge
10294     // -------------------
10295
10296     if ( face[0] && face[1] )  {
10297       const int nbNodes = face[0]->NbNodes();
10298       if ( nbNodes != face[1]->NbNodes() ) {
10299         MESSAGE("Diff nb of face nodes");
10300         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10301         break; // do not return because it s necessary to remove tmp faces
10302       }
10303       bool reverse[] = { false, false }; // order of nodes in the link
10304       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10305         // analyse link orientation in faces
10306         int i1 = iLinkNode[ iSide ][ 0 ];
10307         int i2 = iLinkNode[ iSide ][ 1 ];
10308         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10309       }
10310       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10311       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10312       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10313       {
10314         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10315                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10316       }
10317
10318       // add other links of the faces to linkList
10319       // -----------------------------------------
10320
10321       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10322         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10323         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10324         if ( !iter_isnew.second ) { // already in a set: no need to process
10325           linkIdSet.erase( iter_isnew.first );
10326         }
10327         else // new in set == encountered for the first time: add
10328         {
10329           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10330           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10331           linkList[0].push_back ( NLink( n1, n2 ));
10332           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10333         }
10334       }
10335     } // 2 faces found
10336
10337     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10338       break;
10339
10340   } // loop on link lists
10341
10342   if ( aResult == SEW_OK &&
10343        ( //linkIt[0] != linkList[0].end() ||
10344          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10345     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10346              " " << (faceSetPtr[1]->empty()));
10347     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10348   }
10349
10350   // ====================================================================
10351   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10352   // ====================================================================
10353
10354   // delete temporary faces
10355 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10356 //  while ( tmpFaceIt->more() )
10357 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10358   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10359   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10360     aMesh->RemoveElement(*tmpFaceIt);
10361
10362   if ( aResult != SEW_OK)
10363     return aResult;
10364
10365   list< int > nodeIDsToRemove;
10366   vector< const SMDS_MeshNode*> nodes;
10367   ElemFeatures elemType;
10368
10369   // loop on nodes replacement map
10370   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10371   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10372     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10373     {
10374       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10375       nodeIDsToRemove.push_back( nToRemove->GetID() );
10376       // loop on elements sharing nToRemove
10377       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10378       while ( invElemIt->more() ) {
10379         const SMDS_MeshElement* e = invElemIt->next();
10380         // get a new suite of nodes: make replacement
10381         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10382         nodes.resize( nbNodes );
10383         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10384         while ( nIt->more() ) {
10385           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10386           nnIt = nReplaceMap.find( n );
10387           if ( nnIt != nReplaceMap.end() ) {
10388             nbReplaced++;
10389             n = (*nnIt).second;
10390           }
10391           nodes[ i++ ] = n;
10392         }
10393         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10394         //         elemIDsToRemove.push_back( e->GetID() );
10395         //       else
10396         if ( nbReplaced )
10397         {
10398           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10399           aMesh->RemoveElement( e );
10400
10401           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10402           {
10403             AddToSameGroups( newElem, e, aMesh );
10404             if ( int aShapeId = e->getshapeId() )
10405               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10406           }
10407         }
10408       }
10409     }
10410
10411   Remove( nodeIDsToRemove, true );
10412
10413   return aResult;
10414 }
10415
10416 //================================================================================
10417 /*!
10418  * \brief Find corresponding nodes in two sets of faces
10419  * \param theSide1 - first face set
10420  * \param theSide2 - second first face
10421  * \param theFirstNode1 - a boundary node of set 1
10422  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10423  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10424  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10425  * \param nReplaceMap - output map of corresponding nodes
10426  * \return bool  - is a success or not
10427  */
10428 //================================================================================
10429
10430 #ifdef _DEBUG_
10431 //#define DEBUG_MATCHING_NODES
10432 #endif
10433
10434 SMESH_MeshEditor::Sew_Error
10435 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10436                                     set<const SMDS_MeshElement*>& theSide2,
10437                                     const SMDS_MeshNode*          theFirstNode1,
10438                                     const SMDS_MeshNode*          theFirstNode2,
10439                                     const SMDS_MeshNode*          theSecondNode1,
10440                                     const SMDS_MeshNode*          theSecondNode2,
10441                                     TNodeNodeMap &                nReplaceMap)
10442 {
10443   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10444
10445   nReplaceMap.clear();
10446   if ( theFirstNode1 != theFirstNode2 )
10447     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10448   if ( theSecondNode1 != theSecondNode2 )
10449     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10450
10451   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10452   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10453
10454   list< NLink > linkList[2];
10455   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10456   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10457
10458   // loop on links in linkList; find faces by links and append links
10459   // of the found faces to linkList
10460   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10461   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10462     NLink link[] = { *linkIt[0], *linkIt[1] };
10463     if ( linkSet.find( link[0] ) == linkSet.end() )
10464       continue;
10465
10466     // by links, find faces in the face sets,
10467     // and find indices of link nodes in the found faces;
10468     // in a face set, there is only one or no face sharing a link
10469     // ---------------------------------------------------------------
10470
10471     const SMDS_MeshElement* face[] = { 0, 0 };
10472     list<const SMDS_MeshNode*> notLinkNodes[2];
10473     //bool reverse[] = { false, false }; // order of notLinkNodes
10474     int nbNodes[2];
10475     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10476     {
10477       const SMDS_MeshNode* n1 = link[iSide].first;
10478       const SMDS_MeshNode* n2 = link[iSide].second;
10479       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10480       set< const SMDS_MeshElement* > facesOfNode1;
10481       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10482       {
10483         // during a loop of the first node, we find all faces around n1,
10484         // during a loop of the second node, we find one face sharing both n1 and n2
10485         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10486         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10487         while ( fIt->more() ) { // loop on faces sharing a node
10488           const SMDS_MeshElement* f = fIt->next();
10489           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10490               ! facesOfNode1.insert( f ).second ) // f encounters twice
10491           {
10492             if ( face[ iSide ] ) {
10493               MESSAGE( "2 faces per link " );
10494               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10495             }
10496             face[ iSide ] = f;
10497             faceSet->erase( f );
10498
10499             // get not link nodes
10500             int nbN = f->NbNodes();
10501             if ( f->IsQuadratic() )
10502               nbN /= 2;
10503             nbNodes[ iSide ] = nbN;
10504             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10505             int i1 = f->GetNodeIndex( n1 );
10506             int i2 = f->GetNodeIndex( n2 );
10507             int iEnd = nbN, iBeg = -1, iDelta = 1;
10508             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10509             if ( reverse ) {
10510               std::swap( iEnd, iBeg ); iDelta = -1;
10511             }
10512             int i = i2;
10513             while ( true ) {
10514               i += iDelta;
10515               if ( i == iEnd ) i = iBeg + iDelta;
10516               if ( i == i1 ) break;
10517               nodes.push_back ( f->GetNode( i ) );
10518             }
10519           }
10520         }
10521       }
10522     }
10523     // check similarity of elements of the sides
10524     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10525       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10526       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10527         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10528       }
10529       else {
10530         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10531       }
10532     }
10533
10534     // set nodes to merge
10535     // -------------------
10536
10537     if ( face[0] && face[1] )  {
10538       if ( nbNodes[0] != nbNodes[1] ) {
10539         MESSAGE("Diff nb of face nodes");
10540         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10541       }
10542 #ifdef DEBUG_MATCHING_NODES
10543       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10544                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10545                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10546 #endif
10547       int nbN = nbNodes[0];
10548       {
10549         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10550         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10551         for ( int i = 0 ; i < nbN - 2; ++i ) {
10552 #ifdef DEBUG_MATCHING_NODES
10553           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10554 #endif
10555           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10556         }
10557       }
10558
10559       // add other links of the face 1 to linkList
10560       // -----------------------------------------
10561
10562       const SMDS_MeshElement* f0 = face[0];
10563       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10564       for ( int i = 0; i < nbN; i++ )
10565       {
10566         const SMDS_MeshNode* n2 = f0->GetNode( i );
10567         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10568           linkSet.insert( SMESH_TLink( n1, n2 ));
10569         if ( !iter_isnew.second ) { // already in a set: no need to process
10570           linkSet.erase( iter_isnew.first );
10571         }
10572         else // new in set == encountered for the first time: add
10573         {
10574 #ifdef DEBUG_MATCHING_NODES
10575           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10576                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10577 #endif
10578           linkList[0].push_back ( NLink( n1, n2 ));
10579           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10580         }
10581         n1 = n2;
10582       }
10583     } // 2 faces found
10584   } // loop on link lists
10585
10586   return SEW_OK;
10587 }
10588
10589 //================================================================================
10590 /*!
10591  * \brief Create elements equal (on same nodes) to given ones
10592  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10593  *              elements of the uppest dimension are duplicated.
10594  */
10595 //================================================================================
10596
10597 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10598 {
10599   ClearLastCreated();
10600   SMESHDS_Mesh* mesh = GetMeshDS();
10601
10602   // get an element type and an iterator over elements
10603
10604   SMDSAbs_ElementType type = SMDSAbs_All;
10605   SMDS_ElemIteratorPtr elemIt;
10606   vector< const SMDS_MeshElement* > allElems;
10607   if ( theElements.empty() )
10608   {
10609     if ( mesh->NbNodes() == 0 )
10610       return;
10611     // get most complex type
10612     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10613       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10614       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10615     };
10616     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10617       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10618       {
10619         type = types[i];
10620         break;
10621       }
10622     // put all elements in the vector <allElems>
10623     allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10624     elemIt = mesh->elementsIterator( type );
10625     while ( elemIt->more() )
10626       allElems.push_back( elemIt->next());
10627     elemIt = elemSetIterator( allElems );
10628   }
10629   else
10630   {
10631     type = (*theElements.begin())->GetType();
10632     elemIt = elemSetIterator( theElements );
10633   }
10634
10635   // duplicate elements
10636
10637   ElemFeatures elemType;
10638
10639   vector< const SMDS_MeshNode* > nodes;
10640   while ( elemIt->more() )
10641   {
10642     const SMDS_MeshElement* elem = elemIt->next();
10643     if ( elem->GetType() != type )
10644       continue;
10645
10646     elemType.Init( elem, /*basicOnly=*/false );
10647     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10648
10649     AddElement( nodes, elemType );
10650   }
10651 }
10652
10653 //================================================================================
10654 /*!
10655   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10656   \param theElems - the list of elements (edges or faces) to be replicated
10657   The nodes for duplication could be found from these elements
10658   \param theNodesNot - list of nodes to NOT replicate
10659   \param theAffectedElems - the list of elements (cells and edges) to which the
10660   replicated nodes should be associated to.
10661   \return TRUE if operation has been completed successfully, FALSE otherwise
10662 */
10663 //================================================================================
10664
10665 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10666                                     const TIDSortedElemSet& theNodesNot,
10667                                     const TIDSortedElemSet& theAffectedElems )
10668 {
10669   myLastCreatedElems.Clear();
10670   myLastCreatedNodes.Clear();
10671
10672   if ( theElems.size() == 0 )
10673     return false;
10674
10675   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10676   if ( !aMeshDS )
10677     return false;
10678
10679   bool res = false;
10680   TNodeNodeMap anOldNodeToNewNode;
10681   // duplicate elements and nodes
10682   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10683   // replce nodes by duplications
10684   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10685   return res;
10686 }
10687
10688 //================================================================================
10689 /*!
10690   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10691   \param theMeshDS - mesh instance
10692   \param theElems - the elements replicated or modified (nodes should be changed)
10693   \param theNodesNot - nodes to NOT replicate
10694   \param theNodeNodeMap - relation of old node to new created node
10695   \param theIsDoubleElem - flag os to replicate element or modify
10696   \return TRUE if operation has been completed successfully, FALSE otherwise
10697 */
10698 //================================================================================
10699
10700 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10701                                    const TIDSortedElemSet& theElems,
10702                                    const TIDSortedElemSet& theNodesNot,
10703                                    TNodeNodeMap&           theNodeNodeMap,
10704                                    const bool              theIsDoubleElem )
10705 {
10706   // iterate through element and duplicate them (by nodes duplication)
10707   bool res = false;
10708   std::vector<const SMDS_MeshNode*> newNodes;
10709   ElemFeatures elemType;
10710
10711   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10712   for ( ;  elemItr != theElems.end(); ++elemItr )
10713   {
10714     const SMDS_MeshElement* anElem = *elemItr;
10715     if (!anElem)
10716       continue;
10717
10718     // duplicate nodes to duplicate element
10719     bool isDuplicate = false;
10720     newNodes.resize( anElem->NbNodes() );
10721     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10722     int ind = 0;
10723     while ( anIter->more() )
10724     {
10725       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10726       const SMDS_MeshNode*  aNewNode = aCurrNode;
10727       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10728       if ( n2n != theNodeNodeMap.end() )
10729       {
10730         aNewNode = n2n->second;
10731       }
10732       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10733       {
10734         // duplicate node
10735         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10736         copyPosition( aCurrNode, aNewNode );
10737         theNodeNodeMap[ aCurrNode ] = aNewNode;
10738         myLastCreatedNodes.Append( aNewNode );
10739       }
10740       isDuplicate |= (aCurrNode != aNewNode);
10741       newNodes[ ind++ ] = aNewNode;
10742     }
10743     if ( !isDuplicate )
10744       continue;
10745
10746     if ( theIsDoubleElem )
10747       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10748     else
10749       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10750
10751     res = true;
10752   }
10753   return res;
10754 }
10755
10756 //================================================================================
10757 /*!
10758   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10759   \param theNodes - identifiers of nodes to be doubled
10760   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10761   nodes. If list of element identifiers is empty then nodes are doubled but
10762   they not assigned to elements
10763   \return TRUE if operation has been completed successfully, FALSE otherwise
10764 */
10765 //================================================================================
10766
10767 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10768                                     const std::list< int >& theListOfModifiedElems )
10769 {
10770   myLastCreatedElems.Clear();
10771   myLastCreatedNodes.Clear();
10772
10773   if ( theListOfNodes.size() == 0 )
10774     return false;
10775
10776   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10777   if ( !aMeshDS )
10778     return false;
10779
10780   // iterate through nodes and duplicate them
10781
10782   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10783
10784   std::list< int >::const_iterator aNodeIter;
10785   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10786   {
10787     int aCurr = *aNodeIter;
10788     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10789     if ( !aNode )
10790       continue;
10791
10792     // duplicate node
10793
10794     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10795     if ( aNewNode )
10796     {
10797       copyPosition( aNode, aNewNode );
10798       anOldNodeToNewNode[ aNode ] = aNewNode;
10799       myLastCreatedNodes.Append( aNewNode );
10800     }
10801   }
10802
10803   // Create map of new nodes for modified elements
10804
10805   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10806
10807   std::list< int >::const_iterator anElemIter;
10808   for ( anElemIter = theListOfModifiedElems.begin();
10809         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10810   {
10811     int aCurr = *anElemIter;
10812     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10813     if ( !anElem )
10814       continue;
10815
10816     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10817
10818     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10819     int ind = 0;
10820     while ( anIter->more() )
10821     {
10822       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10823       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10824       {
10825         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10826         aNodeArr[ ind++ ] = aNewNode;
10827       }
10828       else
10829         aNodeArr[ ind++ ] = aCurrNode;
10830     }
10831     anElemToNodes[ anElem ] = aNodeArr;
10832   }
10833
10834   // Change nodes of elements
10835
10836   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10837     anElemToNodesIter = anElemToNodes.begin();
10838   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10839   {
10840     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10841     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10842     if ( anElem )
10843     {
10844       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10845     }
10846   }
10847
10848   return true;
10849 }
10850
10851 namespace {
10852
10853   //================================================================================
10854   /*!
10855   \brief Check if element located inside shape
10856   \return TRUE if IN or ON shape, FALSE otherwise
10857   */
10858   //================================================================================
10859
10860   template<class Classifier>
10861   bool isInside(const SMDS_MeshElement* theElem,
10862                 Classifier&             theClassifier,
10863                 const double            theTol)
10864   {
10865     gp_XYZ centerXYZ (0, 0, 0);
10866     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10867     while (aNodeItr->more())
10868       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10869
10870     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10871     theClassifier.Perform(aPnt, theTol);
10872     TopAbs_State aState = theClassifier.State();
10873     return (aState == TopAbs_IN || aState == TopAbs_ON );
10874   }
10875
10876   //================================================================================
10877   /*!
10878    * \brief Classifier of the 3D point on the TopoDS_Face
10879    *        with interaface suitable for isInside()
10880    */
10881   //================================================================================
10882
10883   struct _FaceClassifier
10884   {
10885     Extrema_ExtPS       _extremum;
10886     BRepAdaptor_Surface _surface;
10887     TopAbs_State        _state;
10888
10889     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10890     {
10891       _extremum.Initialize( _surface,
10892                             _surface.FirstUParameter(), _surface.LastUParameter(),
10893                             _surface.FirstVParameter(), _surface.LastVParameter(),
10894                             _surface.Tolerance(), _surface.Tolerance() );
10895     }
10896     void Perform(const gp_Pnt& aPnt, double theTol)
10897     {
10898       theTol *= theTol;
10899       _state = TopAbs_OUT;
10900       _extremum.Perform(aPnt);
10901       if ( _extremum.IsDone() )
10902         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10903           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10904     }
10905     TopAbs_State State() const
10906     {
10907       return _state;
10908     }
10909   };
10910 }
10911
10912 //================================================================================
10913 /*!
10914   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10915   This method is the first step of DoubleNodeElemGroupsInRegion.
10916   \param theElems - list of groups of elements (edges or faces) to be replicated
10917   \param theNodesNot - list of groups of nodes not to replicated
10918   \param theShape - shape to detect affected elements (element which geometric center
10919          located on or inside shape). If the shape is null, detection is done on faces orientations
10920          (select elements with a gravity center on the side given by faces normals).
10921          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10922          The replicated nodes should be associated to affected elements.
10923   \return groups of affected elements
10924   \sa DoubleNodeElemGroupsInRegion()
10925  */
10926 //================================================================================
10927
10928 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10929                                                    const TIDSortedElemSet& theNodesNot,
10930                                                    const TopoDS_Shape&     theShape,
10931                                                    TIDSortedElemSet&       theAffectedElems)
10932 {
10933   if ( theShape.IsNull() )
10934   {
10935     std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10936     std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10937     std::set<const SMDS_MeshElement*> edgesToCheck;
10938     alreadyCheckedNodes.clear();
10939     alreadyCheckedElems.clear();
10940     edgesToCheck.clear();
10941
10942     // --- iterates on elements to be replicated and get elements by back references from their nodes
10943
10944     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10945     for ( ;  elemItr != theElems.end(); ++elemItr )
10946     {
10947       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10948       if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10949         continue;
10950       gp_XYZ normal;
10951       SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10952       std::set<const SMDS_MeshNode*> nodesElem;
10953       nodesElem.clear();
10954       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10955       while ( nodeItr->more() )
10956       {
10957         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10958         nodesElem.insert(aNode);
10959       }
10960       std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10961       for (; nodit != nodesElem.end(); nodit++)
10962       {
10963         const SMDS_MeshNode* aNode = *nodit;
10964         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10965           continue;
10966         if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10967           continue;
10968         alreadyCheckedNodes.insert(aNode);
10969         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10970         while ( backElemItr->more() )
10971         {
10972           const SMDS_MeshElement* curElem = backElemItr->next();
10973           if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10974             continue;
10975           if (theElems.find(curElem) != theElems.end())
10976             continue;
10977           alreadyCheckedElems.insert(curElem);
10978           double x=0, y=0, z=0;
10979           int nb = 0;
10980           SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10981           while ( nodeItr2->more() )
10982           {
10983             const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10984             x += anotherNode->X();
10985             y += anotherNode->Y();
10986             z += anotherNode->Z();
10987             nb++;
10988           }
10989           gp_XYZ p;
10990           p.SetCoord( x/nb -aNode->X(),
10991                       y/nb -aNode->Y(),
10992                       z/nb -aNode->Z() );
10993           if (normal*p > 0)
10994           {
10995             theAffectedElems.insert( curElem );
10996           }
10997           else if (curElem->GetType() == SMDSAbs_Edge)
10998             edgesToCheck.insert(curElem);
10999         }
11000       }
11001     }
11002     // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
11003     std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
11004     for( ; eit != edgesToCheck.end(); eit++)
11005     {
11006       bool onside = true;
11007       const SMDS_MeshElement* anEdge = *eit;
11008       SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
11009       while ( nodeItr->more() )
11010       {
11011         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11012         if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
11013         {
11014           onside = false;
11015           break;
11016         }
11017       }
11018       if (onside)
11019       {
11020         theAffectedElems.insert(anEdge);
11021       }
11022     }
11023   }
11024   else
11025   {
11026     const double aTol = Precision::Confusion();
11027     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11028     auto_ptr<_FaceClassifier>              aFaceClassifier;
11029     if ( theShape.ShapeType() == TopAbs_SOLID )
11030     {
11031       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11032       bsc3d->PerformInfinitePoint(aTol);
11033     }
11034     else if (theShape.ShapeType() == TopAbs_FACE )
11035     {
11036       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11037     }
11038
11039     // iterates on indicated elements and get elements by back references from their nodes
11040     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11041     for ( ;  elemItr != theElems.end(); ++elemItr )
11042     {
11043       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11044       if (!anElem)
11045         continue;
11046       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11047       while ( nodeItr->more() )
11048       {
11049         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11050         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11051           continue;
11052         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11053         while ( backElemItr->more() )
11054         {
11055           const SMDS_MeshElement* curElem = backElemItr->next();
11056           if ( curElem && theElems.find(curElem) == theElems.end() &&
11057               ( bsc3d.get() ?
11058                 isInside( curElem, *bsc3d, aTol ) :
11059                 isInside( curElem, *aFaceClassifier, aTol )))
11060             theAffectedElems.insert( curElem );
11061         }
11062       }
11063     }
11064   }
11065   return true;
11066 }
11067
11068 //================================================================================
11069 /*!
11070   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11071   \param theElems - group of of elements (edges or faces) to be replicated
11072   \param theNodesNot - group of nodes not to replicate
11073   \param theShape - shape to detect affected elements (element which geometric center
11074   located on or inside shape).
11075   The replicated nodes should be associated to affected elements.
11076   \return TRUE if operation has been completed successfully, FALSE otherwise
11077 */
11078 //================================================================================
11079
11080 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11081                                             const TIDSortedElemSet& theNodesNot,
11082                                             const TopoDS_Shape&     theShape )
11083 {
11084   if ( theShape.IsNull() )
11085     return false;
11086
11087   const double aTol = Precision::Confusion();
11088   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11089   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
11090   if ( theShape.ShapeType() == TopAbs_SOLID )
11091   {
11092     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11093     bsc3d->PerformInfinitePoint(aTol);
11094   }
11095   else if (theShape.ShapeType() == TopAbs_FACE )
11096   {
11097     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11098   }
11099
11100   // iterates on indicated elements and get elements by back references from their nodes
11101   TIDSortedElemSet anAffected;
11102   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11103   for ( ;  elemItr != theElems.end(); ++elemItr )
11104   {
11105     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11106     if (!anElem)
11107       continue;
11108
11109     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11110     while ( nodeItr->more() )
11111     {
11112       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11113       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11114         continue;
11115       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11116       while ( backElemItr->more() )
11117       {
11118         const SMDS_MeshElement* curElem = backElemItr->next();
11119         if ( curElem && theElems.find(curElem) == theElems.end() &&
11120              ( bsc3d ?
11121                isInside( curElem, *bsc3d, aTol ) :
11122                isInside( curElem, *aFaceClassifier, aTol )))
11123           anAffected.insert( curElem );
11124       }
11125     }
11126   }
11127   return DoubleNodes( theElems, theNodesNot, anAffected );
11128 }
11129
11130 /*!
11131  *  \brief compute an oriented angle between two planes defined by four points.
11132  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11133  *  @param p0 base of the rotation axe
11134  *  @param p1 extremity of the rotation axe
11135  *  @param g1 belongs to the first plane
11136  *  @param g2 belongs to the second plane
11137  */
11138 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11139 {
11140   gp_Vec vref(p0, p1);
11141   gp_Vec v1(p0, g1);
11142   gp_Vec v2(p0, g2);
11143   gp_Vec n1 = vref.Crossed(v1);
11144   gp_Vec n2 = vref.Crossed(v2);
11145   try {
11146     return n2.AngleWithRef(n1, vref);
11147   }
11148   catch ( Standard_Failure ) {
11149   }
11150   return Max( v1.Magnitude(), v2.Magnitude() );
11151 }
11152
11153 /*!
11154  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11155  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11156  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11157  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11158  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11159  * the group j_n_p is the group of the flat elements that are built between the group #n and the group #p in the list.
11160  * If there is no shared faces between the group #n and the group #p in the list, the group j_n_p is not created.
11161  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11162  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11163  * \param theElems - list of groups of volumes, where a group of volume is a set of
11164  *        SMDS_MeshElements sorted by Id.
11165  * \param createJointElems - if TRUE, create the elements
11166  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11167  *        the boundary between \a theDomains and the rest mesh
11168  * \return TRUE if operation has been completed successfully, FALSE otherwise
11169  */
11170 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11171                                                      bool                                 createJointElems,
11172                                                      bool                                 onAllBoundaries)
11173 {
11174   // MESSAGE("----------------------------------------------");
11175   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11176   // MESSAGE("----------------------------------------------");
11177
11178   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11179   meshDS->BuildDownWardConnectivity(true);
11180   CHRONO(50);
11181   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11182
11183   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11184   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11185   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11186
11187   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11188   std::map<int,int>celldom; // cell vtkId --> domain
11189   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11190   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11191   faceDomains.clear();
11192   celldom.clear();
11193   cellDomains.clear();
11194   nodeDomains.clear();
11195   std::map<int,int> emptyMap;
11196   std::set<int> emptySet;
11197   emptyMap.clear();
11198
11199   //MESSAGE(".. Number of domains :"<<theElems.size());
11200
11201   TIDSortedElemSet theRestDomElems;
11202   const int iRestDom  = -1;
11203   const int idom0     = onAllBoundaries ? iRestDom : 0;
11204   const int nbDomains = theElems.size();
11205
11206   // Check if the domains do not share an element
11207   for (int idom = 0; idom < nbDomains-1; idom++)
11208   {
11209     //       MESSAGE("... Check of domain #" << idom);
11210     const TIDSortedElemSet& domain = theElems[idom];
11211     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11212     for (; elemItr != domain.end(); ++elemItr)
11213     {
11214       const SMDS_MeshElement* anElem = *elemItr;
11215       int idombisdeb = idom + 1 ;
11216       // check if the element belongs to a domain further in the list
11217       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11218       {
11219         const TIDSortedElemSet& domainbis = theElems[idombis];
11220         if ( domainbis.count( anElem ))
11221         {
11222           MESSAGE(".... Domain #" << idom);
11223           MESSAGE(".... Domain #" << idombis);
11224           throw SALOME_Exception("The domains are not disjoint.");
11225           return false ;
11226         }
11227       }
11228     }
11229   }
11230
11231   for (int idom = 0; idom < nbDomains; idom++)
11232   {
11233
11234     // --- build a map (face to duplicate --> volume to modify)
11235     //     with all the faces shared by 2 domains (group of elements)
11236     //     and corresponding volume of this domain, for each shared face.
11237     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11238
11239     //MESSAGE("... Neighbors of domain #" << idom);
11240     const TIDSortedElemSet& domain = theElems[idom];
11241     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11242     for (; elemItr != domain.end(); ++elemItr)
11243     {
11244       const SMDS_MeshElement* anElem = *elemItr;
11245       if (!anElem)
11246         continue;
11247       int vtkId = anElem->getVtkId();
11248       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11249       int neighborsVtkIds[NBMAXNEIGHBORS];
11250       int downIds[NBMAXNEIGHBORS];
11251       unsigned char downTypes[NBMAXNEIGHBORS];
11252       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11253       for (int n = 0; n < nbNeighbors; n++)
11254       {
11255         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11256         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11257         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11258         {
11259           bool ok = false;
11260           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11261           {
11262             // MESSAGE("Domain " << idombis);
11263             const TIDSortedElemSet& domainbis = theElems[idombis];
11264             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11265           }
11266           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11267           {
11268             DownIdType face(downIds[n], downTypes[n]);
11269             if (!faceDomains[face].count(idom))
11270             {
11271               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11272               celldom[vtkId] = idom;
11273               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11274             }
11275             if ( !ok )
11276             {
11277               theRestDomElems.insert( elem );
11278               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11279               celldom[neighborsVtkIds[n]] = iRestDom;
11280             }
11281           }
11282         }
11283       }
11284     }
11285   }
11286
11287   //MESSAGE("Number of shared faces " << faceDomains.size());
11288   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11289
11290   // --- explore the shared faces domain by domain,
11291   //     explore the nodes of the face and see if they belong to a cell in the domain,
11292   //     which has only a node or an edge on the border (not a shared face)
11293
11294   for (int idomain = idom0; idomain < nbDomains; idomain++)
11295   {
11296     //MESSAGE("Domain " << idomain);
11297     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11298     itface = faceDomains.begin();
11299     for (; itface != faceDomains.end(); ++itface)
11300     {
11301       const std::map<int, int>& domvol = itface->second;
11302       if (!domvol.count(idomain))
11303         continue;
11304       DownIdType face = itface->first;
11305       //MESSAGE(" --- face " << face.cellId);
11306       std::set<int> oldNodes;
11307       oldNodes.clear();
11308       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11309       std::set<int>::iterator itn = oldNodes.begin();
11310       for (; itn != oldNodes.end(); ++itn)
11311       {
11312         int oldId = *itn;
11313         //MESSAGE("     node " << oldId);
11314         vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11315         for (int i=0; i<l.ncells; i++)
11316         {
11317           int vtkId = l.cells[i];
11318           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11319           if (!domain.count(anElem))
11320             continue;
11321           int vtkType = grid->GetCellType(vtkId);
11322           int downId = grid->CellIdToDownId(vtkId);
11323           if (downId < 0)
11324           {
11325             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11326             continue; // not OK at this stage of the algorithm:
11327             //no cells created after BuildDownWardConnectivity
11328           }
11329           DownIdType aCell(downId, vtkType);
11330           cellDomains[aCell][idomain] = vtkId;
11331           celldom[vtkId] = idomain;
11332           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11333         }
11334       }
11335     }
11336   }
11337
11338   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11339   //     for each shared face, get the nodes
11340   //     for each node, for each domain of the face, create a clone of the node
11341
11342   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11343   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11344   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11345
11346   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11347   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11348   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11349
11350   //MESSAGE(".. Duplication of the nodes");
11351   for (int idomain = idom0; idomain < nbDomains; idomain++)
11352   {
11353     itface = faceDomains.begin();
11354     for (; itface != faceDomains.end(); ++itface)
11355     {
11356       const std::map<int, int>& domvol = itface->second;
11357       if (!domvol.count(idomain))
11358         continue;
11359       DownIdType face = itface->first;
11360       //MESSAGE(" --- face " << face.cellId);
11361       std::set<int> oldNodes;
11362       oldNodes.clear();
11363       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11364       std::set<int>::iterator itn = oldNodes.begin();
11365       for (; itn != oldNodes.end(); ++itn)
11366       {
11367         int oldId = *itn;
11368         if (nodeDomains[oldId].empty())
11369         {
11370           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11371           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11372         }
11373         std::map<int, int>::const_iterator itdom = domvol.begin();
11374         for (; itdom != domvol.end(); ++itdom)
11375         {
11376           int idom = itdom->first;
11377           //MESSAGE("         domain " << idom);
11378           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11379           {
11380             if (nodeDomains[oldId].size() >= 2) // a multiple node
11381             {
11382               vector<int> orderedDoms;
11383               //MESSAGE("multiple node " << oldId);
11384               if (mutipleNodes.count(oldId))
11385                 orderedDoms = mutipleNodes[oldId];
11386               else
11387               {
11388                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11389                 for (; it != nodeDomains[oldId].end(); ++it)
11390                   orderedDoms.push_back(it->first);
11391               }
11392               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11393               //stringstream txt;
11394               //for (int i=0; i<orderedDoms.size(); i++)
11395               //  txt << orderedDoms[i] << " ";
11396               //MESSAGE("orderedDoms " << txt.str());
11397               mutipleNodes[oldId] = orderedDoms;
11398             }
11399             double *coords = grid->GetPoint(oldId);
11400             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11401             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11402             int newId = newNode->getVtkId();
11403             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11404             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11405           }
11406         }
11407       }
11408     }
11409   }
11410
11411   //MESSAGE(".. Creation of elements");
11412   for (int idomain = idom0; idomain < nbDomains; idomain++)
11413   {
11414     itface = faceDomains.begin();
11415     for (; itface != faceDomains.end(); ++itface)
11416     {
11417       std::map<int, int> domvol = itface->second;
11418       if (!domvol.count(idomain))
11419         continue;
11420       DownIdType face = itface->first;
11421       //MESSAGE(" --- face " << face.cellId);
11422       std::set<int> oldNodes;
11423       oldNodes.clear();
11424       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11425       int nbMultipleNodes = 0;
11426       std::set<int>::iterator itn = oldNodes.begin();
11427       for (; itn != oldNodes.end(); ++itn)
11428       {
11429         int oldId = *itn;
11430         if (mutipleNodes.count(oldId))
11431           nbMultipleNodes++;
11432       }
11433       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11434       {
11435         //MESSAGE("multiple Nodes detected on a shared face");
11436         int downId = itface->first.cellId;
11437         unsigned char cellType = itface->first.cellType;
11438         // --- shared edge or shared face ?
11439         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11440         {
11441           int nodes[3];
11442           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11443           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11444             if (mutipleNodes.count(nodes[i]))
11445               if (!mutipleNodesToFace.count(nodes[i]))
11446                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11447         }
11448         else // shared face (between two volumes)
11449         {
11450           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11451           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11452           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11453           for (int ie =0; ie < nbEdges; ie++)
11454           {
11455             int nodes[3];
11456             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11457             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11458             {
11459               vector<int> vn0 = mutipleNodes[nodes[0]];
11460               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11461               vector<int> doms;
11462               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11463                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11464                   if ( vn0[i0] == vn1[i1] )
11465                     doms.push_back( vn0[ i0 ]);
11466               if ( doms.size() > 2 )
11467               {
11468                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11469                 double *coords = grid->GetPoint(nodes[0]);
11470                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11471                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11472                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11473                 gp_Pnt gref;
11474                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11475                 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11476                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11477                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11478                 for ( size_t id = 0; id < doms.size(); id++ )
11479                 {
11480                   int idom = doms[id];
11481                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11482                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11483                   {
11484                     int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11485                     SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11486                     if (domain.count(elem))
11487                     {
11488                       SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11489                       domvol[idom] = svol;
11490                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11491                       double values[3];
11492                       vtkIdType npts = 0;
11493                       vtkIdType* pts = 0;
11494                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11495                       SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11496                       if (id ==0)
11497                       {
11498                         gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11499                         angleDom[idom] = 0;
11500                       }
11501                       else
11502                       {
11503                         gp_Pnt g(values[0], values[1], values[2]);
11504                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11505                         //MESSAGE("  angle=" << angleDom[idom]);
11506                       }
11507                       break;
11508                     }
11509                   }
11510                 }
11511                 map<double, int> sortedDom; // sort domains by angle
11512                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11513                   sortedDom[ia->second] = ia->first;
11514                 vector<int> vnodes;
11515                 vector<int> vdom;
11516                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11517                 {
11518                   vdom.push_back(ib->second);
11519                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11520                 }
11521                 for (int ino = 0; ino < nbNodes; ino++)
11522                   vnodes.push_back(nodes[ino]);
11523                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11524               }
11525             }
11526           }
11527         }
11528       }
11529     }
11530   }
11531
11532   // --- iterate on shared faces (volumes to modify, face to extrude)
11533   //     get node id's of the face (id SMDS = id VTK)
11534   //     create flat element with old and new nodes if requested
11535
11536   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11537   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11538
11539   std::map<int, std::map<long,int> > nodeQuadDomains;
11540   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11541
11542   //MESSAGE(".. Creation of elements: simple junction");
11543   if (createJointElems)
11544   {
11545     int idg;
11546     string joints2DName = "joints2D";
11547     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11548     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11549     string joints3DName = "joints3D";
11550     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11551     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11552
11553     itface = faceDomains.begin();
11554     for (; itface != faceDomains.end(); ++itface)
11555     {
11556       DownIdType face = itface->first;
11557       std::set<int> oldNodes;
11558       std::set<int>::iterator itn;
11559       oldNodes.clear();
11560       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11561
11562       std::map<int, int> domvol = itface->second;
11563       std::map<int, int>::iterator itdom = domvol.begin();
11564       int dom1 = itdom->first;
11565       int vtkVolId = itdom->second;
11566       itdom++;
11567       int dom2 = itdom->first;
11568       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11569                                                        nodeQuadDomains);
11570       stringstream grpname;
11571       grpname << "j_";
11572       if (dom1 < dom2)
11573         grpname << dom1 << "_" << dom2;
11574       else
11575         grpname << dom2 << "_" << dom1;
11576       string namegrp = grpname.str();
11577       if (!mapOfJunctionGroups.count(namegrp))
11578         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11579       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11580       if (sgrp)
11581         sgrp->Add(vol->GetID());
11582       if (vol->GetType() == SMDSAbs_Volume)
11583         joints3DGrp->Add(vol->GetID());
11584       else if (vol->GetType() == SMDSAbs_Face)
11585         joints2DGrp->Add(vol->GetID());
11586     }
11587   }
11588
11589   // --- create volumes on multiple domain intersection if requested
11590   //     iterate on mutipleNodesToFace
11591   //     iterate on edgesMultiDomains
11592
11593   //MESSAGE(".. Creation of elements: multiple junction");
11594   if (createJointElems)
11595   {
11596     // --- iterate on mutipleNodesToFace
11597
11598     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11599     for (; itn != mutipleNodesToFace.end(); ++itn)
11600     {
11601       int node = itn->first;
11602       vector<int> orderDom = itn->second;
11603       vector<vtkIdType> orderedNodes;
11604       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11605         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11606       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11607
11608       stringstream grpname;
11609       grpname << "m2j_";
11610       grpname << 0 << "_" << 0;
11611       int idg;
11612       string namegrp = grpname.str();
11613       if (!mapOfJunctionGroups.count(namegrp))
11614         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11615       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11616       if (sgrp)
11617         sgrp->Add(face->GetID());
11618     }
11619
11620     // --- iterate on edgesMultiDomains
11621
11622     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11623     for (; ite != edgesMultiDomains.end(); ++ite)
11624     {
11625       vector<int> nodes = ite->first;
11626       vector<int> orderDom = ite->second;
11627       vector<vtkIdType> orderedNodes;
11628       if (nodes.size() == 2)
11629       {
11630         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11631         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11632           if ( orderDom.size() == 3 )
11633             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11634               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11635           else
11636             for (int idom = orderDom.size()-1; idom >=0; idom--)
11637               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11638         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11639
11640         int idg;
11641         string namegrp = "jointsMultiples";
11642         if (!mapOfJunctionGroups.count(namegrp))
11643           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11644         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11645         if (sgrp)
11646           sgrp->Add(vol->GetID());
11647       }
11648       else
11649       {
11650         //INFOS("Quadratic multiple joints not implemented");
11651         // TODO quadratic nodes
11652       }
11653     }
11654   }
11655
11656   // --- list the explicit faces and edges of the mesh that need to be modified,
11657   //     i.e. faces and edges built with one or more duplicated nodes.
11658   //     associate these faces or edges to their corresponding domain.
11659   //     only the first domain found is kept when a face or edge is shared
11660
11661   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11662   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11663   faceOrEdgeDom.clear();
11664   feDom.clear();
11665
11666   //MESSAGE(".. Modification of elements");
11667   for (int idomain = idom0; idomain < nbDomains; idomain++)
11668   {
11669     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11670     for (; itnod != nodeDomains.end(); ++itnod)
11671     {
11672       int oldId = itnod->first;
11673       //MESSAGE("     node " << oldId);
11674       vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11675       for (int i = 0; i < l.ncells; i++)
11676       {
11677         int vtkId = l.cells[i];
11678         int vtkType = grid->GetCellType(vtkId);
11679         int downId = grid->CellIdToDownId(vtkId);
11680         if (downId < 0)
11681           continue; // new cells: not to be modified
11682         DownIdType aCell(downId, vtkType);
11683         int volParents[1000];
11684         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11685         for (int j = 0; j < nbvol; j++)
11686           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11687             if (!feDom.count(vtkId))
11688             {
11689               feDom[vtkId] = idomain;
11690               faceOrEdgeDom[aCell] = emptyMap;
11691               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11692               //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11693               //        << " type " << vtkType << " downId " << downId);
11694             }
11695       }
11696     }
11697   }
11698
11699   // --- iterate on shared faces (volumes to modify, face to extrude)
11700   //     get node id's of the face
11701   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11702
11703   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11704   for (int m=0; m<3; m++)
11705   {
11706     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11707     itface = (*amap).begin();
11708     for (; itface != (*amap).end(); ++itface)
11709     {
11710       DownIdType face = itface->first;
11711       std::set<int> oldNodes;
11712       std::set<int>::iterator itn;
11713       oldNodes.clear();
11714       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11715       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11716       std::map<int, int> localClonedNodeIds;
11717
11718       std::map<int, int> domvol = itface->second;
11719       std::map<int, int>::iterator itdom = domvol.begin();
11720       for (; itdom != domvol.end(); ++itdom)
11721       {
11722         int idom = itdom->first;
11723         int vtkVolId = itdom->second;
11724         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11725         localClonedNodeIds.clear();
11726         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11727         {
11728           int oldId = *itn;
11729           if (nodeDomains[oldId].count(idom))
11730           {
11731             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11732             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11733           }
11734         }
11735         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11736       }
11737     }
11738   }
11739
11740   // Remove empty groups (issue 0022812)
11741   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11742   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11743   {
11744     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11745       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11746   }
11747
11748   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11749   grid->DeleteLinks();
11750
11751   CHRONOSTOP(50);
11752   counters::stats();
11753   return true;
11754 }
11755
11756 /*!
11757  * \brief Double nodes on some external faces and create flat elements.
11758  * Flat elements are mainly used by some types of mechanic calculations.
11759  *
11760  * Each group of the list must be constituted of faces.
11761  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11762  * @param theElems - list of groups of faces, where a group of faces is a set of
11763  * SMDS_MeshElements sorted by Id.
11764  * @return TRUE if operation has been completed successfully, FALSE otherwise
11765  */
11766 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11767 {
11768   // MESSAGE("-------------------------------------------------");
11769   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11770   // MESSAGE("-------------------------------------------------");
11771
11772   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11773
11774   // --- For each group of faces
11775   //     duplicate the nodes, create a flat element based on the face
11776   //     replace the nodes of the faces by their clones
11777
11778   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11779   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11780   clonedNodes.clear();
11781   intermediateNodes.clear();
11782   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11783   mapOfJunctionGroups.clear();
11784
11785   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11786   {
11787     const TIDSortedElemSet&           domain = theElems[idom];
11788     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11789     for ( ; elemItr != domain.end(); ++elemItr )
11790     {
11791       SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11792       SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11793       if (!aFace)
11794         continue;
11795       // MESSAGE("aFace=" << aFace->GetID());
11796       bool isQuad = aFace->IsQuadratic();
11797       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11798
11799       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11800
11801       SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11802       while (nodeIt->more())
11803       {
11804         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11805         bool isMedium = isQuad && (aFace->IsMediumNode(node));
11806         if (isMedium)
11807           ln2.push_back(node);
11808         else
11809           ln0.push_back(node);
11810
11811         const SMDS_MeshNode* clone = 0;
11812         if (!clonedNodes.count(node))
11813         {
11814           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11815           copyPosition( node, clone );
11816           clonedNodes[node] = clone;
11817         }
11818         else
11819           clone = clonedNodes[node];
11820
11821         if (isMedium)
11822           ln3.push_back(clone);
11823         else
11824           ln1.push_back(clone);
11825
11826         const SMDS_MeshNode* inter = 0;
11827         if (isQuad && (!isMedium))
11828         {
11829           if (!intermediateNodes.count(node))
11830           {
11831             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11832             copyPosition( node, inter );
11833             intermediateNodes[node] = inter;
11834           }
11835           else
11836             inter = intermediateNodes[node];
11837           ln4.push_back(inter);
11838         }
11839       }
11840
11841       // --- extrude the face
11842
11843       vector<const SMDS_MeshNode*> ln;
11844       SMDS_MeshVolume* vol = 0;
11845       vtkIdType aType = aFace->GetVtkType();
11846       switch (aType)
11847       {
11848       case VTK_TRIANGLE:
11849         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11850         // MESSAGE("vol prism " << vol->GetID());
11851         ln.push_back(ln1[0]);
11852         ln.push_back(ln1[1]);
11853         ln.push_back(ln1[2]);
11854         break;
11855       case VTK_QUAD:
11856         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11857         // MESSAGE("vol hexa " << vol->GetID());
11858         ln.push_back(ln1[0]);
11859         ln.push_back(ln1[1]);
11860         ln.push_back(ln1[2]);
11861         ln.push_back(ln1[3]);
11862         break;
11863       case VTK_QUADRATIC_TRIANGLE:
11864         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11865                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11866         // MESSAGE("vol quad prism " << vol->GetID());
11867         ln.push_back(ln1[0]);
11868         ln.push_back(ln1[1]);
11869         ln.push_back(ln1[2]);
11870         ln.push_back(ln3[0]);
11871         ln.push_back(ln3[1]);
11872         ln.push_back(ln3[2]);
11873         break;
11874       case VTK_QUADRATIC_QUAD:
11875         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11876         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11877         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11878         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11879                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11880                                 ln4[0], ln4[1], ln4[2], ln4[3]);
11881         // MESSAGE("vol quad hexa " << vol->GetID());
11882         ln.push_back(ln1[0]);
11883         ln.push_back(ln1[1]);
11884         ln.push_back(ln1[2]);
11885         ln.push_back(ln1[3]);
11886         ln.push_back(ln3[0]);
11887         ln.push_back(ln3[1]);
11888         ln.push_back(ln3[2]);
11889         ln.push_back(ln3[3]);
11890         break;
11891       case VTK_POLYGON:
11892         break;
11893       default:
11894         break;
11895       }
11896
11897       if (vol)
11898       {
11899         stringstream grpname;
11900         grpname << "jf_";
11901         grpname << idom;
11902         int idg;
11903         string namegrp = grpname.str();
11904         if (!mapOfJunctionGroups.count(namegrp))
11905           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11906         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11907         if (sgrp)
11908           sgrp->Add(vol->GetID());
11909       }
11910
11911       // --- modify the face
11912
11913       aFace->ChangeNodes(&ln[0], ln.size());
11914     }
11915   }
11916   return true;
11917 }
11918
11919 /*!
11920  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11921  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11922  *  groups of faces to remove inside the object, (idem edges).
11923  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11924  */
11925 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
11926                                       const TopoDS_Shape&             theShape,
11927                                       SMESH_NodeSearcher*             theNodeSearcher,
11928                                       const char*                     groupName,
11929                                       std::vector<double>&            nodesCoords,
11930                                       std::vector<std::vector<int> >& listOfListOfNodes)
11931 {
11932   // MESSAGE("--------------------------------");
11933   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11934   // MESSAGE("--------------------------------");
11935
11936   // --- zone of volumes to remove is given :
11937   //     1 either by a geom shape (one or more vertices) and a radius,
11938   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11939   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11940   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11941   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11942   //     defined by it's name.
11943
11944   SMESHDS_GroupBase* groupDS = 0;
11945   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11946   while ( groupIt->more() )
11947   {
11948     groupDS = 0;
11949     SMESH_Group * group = groupIt->next();
11950     if ( !group ) continue;
11951     groupDS = group->GetGroupDS();
11952     if ( !groupDS || groupDS->IsEmpty() ) continue;
11953     std::string grpName = group->GetName();
11954     //MESSAGE("grpName=" << grpName);
11955     if (grpName == groupName)
11956       break;
11957     else
11958       groupDS = 0;
11959   }
11960
11961   bool isNodeGroup = false;
11962   bool isNodeCoords = false;
11963   if (groupDS)
11964   {
11965     if (groupDS->GetType() != SMDSAbs_Node)
11966       return;
11967     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11968   }
11969
11970   if (nodesCoords.size() > 0)
11971     isNodeCoords = true; // a list o nodes given by their coordinates
11972   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11973
11974   // --- define groups to build
11975
11976   int idg; // --- group of SMDS volumes
11977   string grpvName = groupName;
11978   grpvName += "_vol";
11979   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11980   if (!grp)
11981   {
11982     MESSAGE("group not created " << grpvName);
11983     return;
11984   }
11985   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11986
11987   int idgs; // --- group of SMDS faces on the skin
11988   string grpsName = groupName;
11989   grpsName += "_skin";
11990   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11991   if (!grps)
11992   {
11993     MESSAGE("group not created " << grpsName);
11994     return;
11995   }
11996   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11997
11998   int idgi; // --- group of SMDS faces internal (several shapes)
11999   string grpiName = groupName;
12000   grpiName += "_internalFaces";
12001   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12002   if (!grpi)
12003   {
12004     MESSAGE("group not created " << grpiName);
12005     return;
12006   }
12007   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12008
12009   int idgei; // --- group of SMDS faces internal (several shapes)
12010   string grpeiName = groupName;
12011   grpeiName += "_internalEdges";
12012   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12013   if (!grpei)
12014   {
12015     MESSAGE("group not created " << grpeiName);
12016     return;
12017   }
12018   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12019
12020   // --- build downward connectivity
12021
12022   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12023   meshDS->BuildDownWardConnectivity(true);
12024   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12025
12026   // --- set of volumes detected inside
12027
12028   std::set<int> setOfInsideVol;
12029   std::set<int> setOfVolToCheck;
12030
12031   std::vector<gp_Pnt> gpnts;
12032   gpnts.clear();
12033
12034   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12035   {
12036     //MESSAGE("group of nodes provided");
12037     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12038     while ( elemIt->more() )
12039     {
12040       const SMDS_MeshElement* elem = elemIt->next();
12041       if (!elem)
12042         continue;
12043       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12044       if (!node)
12045         continue;
12046       SMDS_MeshElement* vol = 0;
12047       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12048       while (volItr->more())
12049       {
12050         vol = (SMDS_MeshElement*)volItr->next();
12051         setOfInsideVol.insert(vol->getVtkId());
12052         sgrp->Add(vol->GetID());
12053       }
12054     }
12055   }
12056   else if (isNodeCoords)
12057   {
12058     //MESSAGE("list of nodes coordinates provided");
12059     size_t i = 0;
12060     int k = 0;
12061     while ( i < nodesCoords.size()-2 )
12062     {
12063       double x = nodesCoords[i++];
12064       double y = nodesCoords[i++];
12065       double z = nodesCoords[i++];
12066       gp_Pnt p = gp_Pnt(x, y ,z);
12067       gpnts.push_back(p);
12068       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12069       k++;
12070     }
12071   }
12072   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12073   {
12074     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12075     TopTools_IndexedMapOfShape vertexMap;
12076     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12077     gp_Pnt p = gp_Pnt(0,0,0);
12078     if (vertexMap.Extent() < 1)
12079       return;
12080
12081     for ( int i = 1; i <= vertexMap.Extent(); ++i )
12082     {
12083       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12084       p = BRep_Tool::Pnt(vertex);
12085       gpnts.push_back(p);
12086       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12087     }
12088   }
12089
12090   if (gpnts.size() > 0)
12091   {
12092     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12093     //MESSAGE("startNode->nodeId " << nodeId);
12094
12095     double radius2 = radius*radius;
12096     //MESSAGE("radius2 " << radius2);
12097
12098     // --- volumes on start node
12099
12100     setOfVolToCheck.clear();
12101     SMDS_MeshElement* startVol = 0;
12102     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12103     while (volItr->more())
12104     {
12105       startVol = (SMDS_MeshElement*)volItr->next();
12106       setOfVolToCheck.insert(startVol->getVtkId());
12107     }
12108     if (setOfVolToCheck.empty())
12109     {
12110       MESSAGE("No volumes found");
12111       return;
12112     }
12113
12114     // --- starting with central volumes then their neighbors, check if they are inside
12115     //     or outside the domain, until no more new neighbor volume is inside.
12116     //     Fill the group of inside volumes
12117
12118     std::map<int, double> mapOfNodeDistance2;
12119     mapOfNodeDistance2.clear();
12120     std::set<int> setOfOutsideVol;
12121     while (!setOfVolToCheck.empty())
12122     {
12123       std::set<int>::iterator it = setOfVolToCheck.begin();
12124       int vtkId = *it;
12125       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12126       bool volInside = false;
12127       vtkIdType npts = 0;
12128       vtkIdType* pts = 0;
12129       grid->GetCellPoints(vtkId, npts, pts);
12130       for (int i=0; i<npts; i++)
12131       {
12132         double distance2 = 0;
12133         if (mapOfNodeDistance2.count(pts[i]))
12134         {
12135           distance2 = mapOfNodeDistance2[pts[i]];
12136           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12137         }
12138         else
12139         {
12140           double *coords = grid->GetPoint(pts[i]);
12141           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12142           distance2 = 1.E40;
12143           for ( size_t j = 0; j < gpnts.size(); j++ )
12144           {
12145             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12146             if (d2 < distance2)
12147             {
12148               distance2 = d2;
12149               if (distance2 < radius2)
12150                 break;
12151             }
12152           }
12153           mapOfNodeDistance2[pts[i]] = distance2;
12154           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12155         }
12156         if (distance2 < radius2)
12157         {
12158           volInside = true; // one or more nodes inside the domain
12159           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12160           break;
12161         }
12162       }
12163       if (volInside)
12164       {
12165         setOfInsideVol.insert(vtkId);
12166         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12167         int neighborsVtkIds[NBMAXNEIGHBORS];
12168         int downIds[NBMAXNEIGHBORS];
12169         unsigned char downTypes[NBMAXNEIGHBORS];
12170         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12171         for (int n = 0; n < nbNeighbors; n++)
12172           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12173             setOfVolToCheck.insert(neighborsVtkIds[n]);
12174       }
12175       else
12176       {
12177         setOfOutsideVol.insert(vtkId);
12178         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12179       }
12180       setOfVolToCheck.erase(vtkId);
12181     }
12182   }
12183
12184   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12185   //     If yes, add the volume to the inside set
12186
12187   bool addedInside = true;
12188   std::set<int> setOfVolToReCheck;
12189   while (addedInside)
12190   {
12191     //MESSAGE(" --------------------------- re check");
12192     addedInside = false;
12193     std::set<int>::iterator itv = setOfInsideVol.begin();
12194     for (; itv != setOfInsideVol.end(); ++itv)
12195     {
12196       int vtkId = *itv;
12197       int neighborsVtkIds[NBMAXNEIGHBORS];
12198       int downIds[NBMAXNEIGHBORS];
12199       unsigned char downTypes[NBMAXNEIGHBORS];
12200       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12201       for (int n = 0; n < nbNeighbors; n++)
12202         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12203           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12204     }
12205     setOfVolToCheck = setOfVolToReCheck;
12206     setOfVolToReCheck.clear();
12207     while  (!setOfVolToCheck.empty())
12208     {
12209       std::set<int>::iterator it = setOfVolToCheck.begin();
12210       int vtkId = *it;
12211       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12212       {
12213         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12214         int countInside = 0;
12215         int neighborsVtkIds[NBMAXNEIGHBORS];
12216         int downIds[NBMAXNEIGHBORS];
12217         unsigned char downTypes[NBMAXNEIGHBORS];
12218         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12219         for (int n = 0; n < nbNeighbors; n++)
12220           if (setOfInsideVol.count(neighborsVtkIds[n]))
12221             countInside++;
12222         //MESSAGE("countInside " << countInside);
12223         if (countInside > 1)
12224         {
12225           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12226           setOfInsideVol.insert(vtkId);
12227           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12228           addedInside = true;
12229         }
12230         else
12231           setOfVolToReCheck.insert(vtkId);
12232       }
12233       setOfVolToCheck.erase(vtkId);
12234     }
12235   }
12236
12237   // --- map of Downward faces at the boundary, inside the global volume
12238   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12239   //     fill group of SMDS faces inside the volume (when several volume shapes)
12240   //     fill group of SMDS faces on the skin of the global volume (if skin)
12241
12242   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12243   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12244   std::set<int>::iterator it = setOfInsideVol.begin();
12245   for (; it != setOfInsideVol.end(); ++it)
12246   {
12247     int vtkId = *it;
12248     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12249     int neighborsVtkIds[NBMAXNEIGHBORS];
12250     int downIds[NBMAXNEIGHBORS];
12251     unsigned char downTypes[NBMAXNEIGHBORS];
12252     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12253     for (int n = 0; n < nbNeighbors; n++)
12254     {
12255       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12256       if (neighborDim == 3)
12257       {
12258         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12259         {
12260           DownIdType face(downIds[n], downTypes[n]);
12261           boundaryFaces[face] = vtkId;
12262         }
12263         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12264         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12265         if (vtkFaceId >= 0)
12266         {
12267           sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12268           // find also the smds edges on this face
12269           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12270           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12271           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12272           for (int i = 0; i < nbEdges; i++)
12273           {
12274             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12275             if (vtkEdgeId >= 0)
12276               sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12277           }
12278         }
12279       }
12280       else if (neighborDim == 2) // skin of the volume
12281       {
12282         DownIdType face(downIds[n], downTypes[n]);
12283         skinFaces[face] = vtkId;
12284         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12285         if (vtkFaceId >= 0)
12286           sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12287       }
12288     }
12289   }
12290
12291   // --- identify the edges constituting the wire of each subshape on the skin
12292   //     define polylines with the nodes of edges, equivalent to wires
12293   //     project polylines on subshapes, and partition, to get geom faces
12294
12295   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12296   std::set<int> emptySet;
12297   emptySet.clear();
12298   std::set<int> shapeIds;
12299
12300   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12301   while (itelem->more())
12302   {
12303     const SMDS_MeshElement *elem = itelem->next();
12304     int shapeId = elem->getshapeId();
12305     int vtkId = elem->getVtkId();
12306     if (!shapeIdToVtkIdSet.count(shapeId))
12307     {
12308       shapeIdToVtkIdSet[shapeId] = emptySet;
12309       shapeIds.insert(shapeId);
12310     }
12311     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12312   }
12313
12314   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12315   std::set<DownIdType, DownIdCompare> emptyEdges;
12316   emptyEdges.clear();
12317
12318   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12319   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12320   {
12321     int shapeId = itShape->first;
12322     //MESSAGE(" --- Shape ID --- "<< shapeId);
12323     shapeIdToEdges[shapeId] = emptyEdges;
12324
12325     std::vector<int> nodesEdges;
12326
12327     std::set<int>::iterator its = itShape->second.begin();
12328     for (; its != itShape->second.end(); ++its)
12329     {
12330       int vtkId = *its;
12331       //MESSAGE("     " << vtkId);
12332       int neighborsVtkIds[NBMAXNEIGHBORS];
12333       int downIds[NBMAXNEIGHBORS];
12334       unsigned char downTypes[NBMAXNEIGHBORS];
12335       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12336       for (int n = 0; n < nbNeighbors; n++)
12337       {
12338         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12339           continue;
12340         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12341         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12342         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12343         {
12344           DownIdType edge(downIds[n], downTypes[n]);
12345           if (!shapeIdToEdges[shapeId].count(edge))
12346           {
12347             shapeIdToEdges[shapeId].insert(edge);
12348             int vtkNodeId[3];
12349             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12350             nodesEdges.push_back(vtkNodeId[0]);
12351             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12352             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12353           }
12354         }
12355       }
12356     }
12357
12358     std::list<int> order;
12359     order.clear();
12360     if (nodesEdges.size() > 0)
12361     {
12362       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12363       nodesEdges[0] = -1;
12364       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12365       nodesEdges[1] = -1; // do not reuse this edge
12366       bool found = true;
12367       while (found)
12368       {
12369         int nodeTofind = order.back(); // try first to push back
12370         int i = 0;
12371         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12372           if (nodesEdges[i] == nodeTofind)
12373             break;
12374         if ( i == (int) nodesEdges.size() )
12375           found = false; // no follower found on back
12376         else
12377         {
12378           if (i%2) // odd ==> use the previous one
12379             if (nodesEdges[i-1] < 0)
12380               found = false;
12381             else
12382             {
12383               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12384               nodesEdges[i-1] = -1;
12385             }
12386           else // even ==> use the next one
12387             if (nodesEdges[i+1] < 0)
12388               found = false;
12389             else
12390             {
12391               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12392               nodesEdges[i+1] = -1;
12393             }
12394         }
12395         if (found)
12396           continue;
12397         // try to push front
12398         found = true;
12399         nodeTofind = order.front(); // try to push front
12400         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12401           if ( nodesEdges[i] == nodeTofind )
12402             break;
12403         if ( i == (int)nodesEdges.size() )
12404         {
12405           found = false; // no predecessor found on front
12406           continue;
12407         }
12408         if (i%2) // odd ==> use the previous one
12409           if (nodesEdges[i-1] < 0)
12410             found = false;
12411           else
12412           {
12413             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12414             nodesEdges[i-1] = -1;
12415           }
12416         else // even ==> use the next one
12417           if (nodesEdges[i+1] < 0)
12418             found = false;
12419           else
12420           {
12421             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12422             nodesEdges[i+1] = -1;
12423           }
12424       }
12425     }
12426
12427
12428     std::vector<int> nodes;
12429     nodes.push_back(shapeId);
12430     std::list<int>::iterator itl = order.begin();
12431     for (; itl != order.end(); itl++)
12432     {
12433       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12434       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12435     }
12436     listOfListOfNodes.push_back(nodes);
12437   }
12438
12439   //     partition geom faces with blocFissure
12440   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12441   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12442
12443   return;
12444 }
12445
12446
12447 //================================================================================
12448 /*!
12449  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12450  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12451  * \return TRUE if operation has been completed successfully, FALSE otherwise
12452  */
12453 //================================================================================
12454
12455 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12456 {
12457   // iterates on volume elements and detect all free faces on them
12458   SMESHDS_Mesh* aMesh = GetMeshDS();
12459   if (!aMesh)
12460     return false;
12461
12462   ElemFeatures faceType( SMDSAbs_Face );
12463   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12464   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12465   while(vIt->more())
12466   {
12467     const SMDS_MeshVolume* volume = vIt->next();
12468     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12469     vTool.SetExternalNormal();
12470     const int iQuad = volume->IsQuadratic();
12471     faceType.SetQuad( iQuad );
12472     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12473     {
12474       if (!vTool.IsFreeFace(iface))
12475         continue;
12476       nbFree++;
12477       vector<const SMDS_MeshNode *> nodes;
12478       int nbFaceNodes = vTool.NbFaceNodes(iface);
12479       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12480       int inode = 0;
12481       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12482         nodes.push_back(faceNodes[inode]);
12483
12484       if (iQuad) // add medium nodes
12485       {
12486         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12487           nodes.push_back(faceNodes[inode]);
12488         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12489           nodes.push_back(faceNodes[8]);
12490       }
12491       // add new face based on volume nodes
12492       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12493       {
12494         nbExisted++; // face already exsist
12495       }
12496       else
12497       {
12498         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12499         nbCreated++;
12500       }
12501     }
12502   }
12503   return ( nbFree == ( nbExisted + nbCreated ));
12504 }
12505
12506 namespace
12507 {
12508   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12509   {
12510     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12511       return n;
12512     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12513   }
12514 }
12515 //================================================================================
12516 /*!
12517  * \brief Creates missing boundary elements
12518  *  \param elements - elements whose boundary is to be checked
12519  *  \param dimension - defines type of boundary elements to create
12520  *  \param group - a group to store created boundary elements in
12521  *  \param targetMesh - a mesh to store created boundary elements in
12522  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12523  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12524  *                                boundary elements will be copied into the targetMesh
12525  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12526  *                                boundary elements will be added into the new group
12527  *  \param aroundElements - if true, elements will be created on boundary of given
12528  *                          elements else, on boundary of the whole mesh.
12529  * \return nb of added boundary elements
12530  */
12531 //================================================================================
12532
12533 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12534                                        Bnd_Dimension           dimension,
12535                                        SMESH_Group*            group/*=0*/,
12536                                        SMESH_Mesh*             targetMesh/*=0*/,
12537                                        bool                    toCopyElements/*=false*/,
12538                                        bool                    toCopyExistingBoundary/*=false*/,
12539                                        bool                    toAddExistingBondary/*= false*/,
12540                                        bool                    aroundElements/*= false*/)
12541 {
12542   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12543   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12544   // hope that all elements are of the same type, do not check them all
12545   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12546     throw SALOME_Exception(LOCALIZED("wrong element type"));
12547
12548   if ( !targetMesh )
12549     toCopyElements = toCopyExistingBoundary = false;
12550
12551   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12552   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12553   int nbAddedBnd = 0;
12554
12555   // editor adding present bnd elements and optionally holding elements to add to the group
12556   SMESH_MeshEditor* presentEditor;
12557   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12558   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12559
12560   SMESH_MesherHelper helper( *myMesh );
12561   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12562   SMDS_VolumeTool vTool;
12563   TIDSortedElemSet avoidSet;
12564   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12565   size_t inode;
12566
12567   typedef vector<const SMDS_MeshNode*> TConnectivity;
12568   TConnectivity tgtNodes;
12569   ElemFeatures elemKind( missType ), elemToCopy;
12570
12571   vector<const SMDS_MeshElement*> presentBndElems;
12572   vector<TConnectivity>           missingBndElems;
12573   vector<int>                     freeFacets;
12574   TConnectivity nodes, elemNodes;
12575
12576   SMDS_ElemIteratorPtr eIt;
12577   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12578   else                  eIt = elemSetIterator( elements );
12579
12580   while (eIt->more())
12581   {
12582     const SMDS_MeshElement* elem = eIt->next();
12583     const int              iQuad = elem->IsQuadratic();
12584     elemKind.SetQuad( iQuad );
12585
12586     // ------------------------------------------------------------------------------------
12587     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12588     // ------------------------------------------------------------------------------------
12589     presentBndElems.clear();
12590     missingBndElems.clear();
12591     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12592     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12593     {
12594       const SMDS_MeshElement* otherVol = 0;
12595       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12596       {
12597         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12598              ( !aroundElements || elements.count( otherVol )))
12599           continue;
12600         freeFacets.push_back( iface );
12601       }
12602       if ( missType == SMDSAbs_Face )
12603         vTool.SetExternalNormal();
12604       for ( size_t i = 0; i < freeFacets.size(); ++i )
12605       {
12606         int                iface = freeFacets[i];
12607         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12608         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12609         if ( missType == SMDSAbs_Edge ) // boundary edges
12610         {
12611           nodes.resize( 2+iQuad );
12612           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12613           {
12614             for ( size_t j = 0; j < nodes.size(); ++j )
12615               nodes[ j ] = nn[ i+j ];
12616             if ( const SMDS_MeshElement* edge =
12617                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12618               presentBndElems.push_back( edge );
12619             else
12620               missingBndElems.push_back( nodes );
12621           }
12622         }
12623         else // boundary face
12624         {
12625           nodes.clear();
12626           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12627             nodes.push_back( nn[inode] ); // add corner nodes
12628           if (iQuad)
12629             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12630               nodes.push_back( nn[inode] ); // add medium nodes
12631           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12632           if ( iCenter > 0 )
12633             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12634
12635           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12636                                                                SMDSAbs_Face, /*noMedium=*/false ))
12637             presentBndElems.push_back( f );
12638           else
12639             missingBndElems.push_back( nodes );
12640
12641           if ( targetMesh != myMesh )
12642           {
12643             // add 1D elements on face boundary to be added to a new mesh
12644             const SMDS_MeshElement* edge;
12645             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12646             {
12647               if ( iQuad )
12648                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12649               else
12650                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12651               if ( edge && avoidSet.insert( edge ).second )
12652                 presentBndElems.push_back( edge );
12653             }
12654           }
12655         }
12656       }
12657     }
12658     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12659     {
12660       avoidSet.clear(), avoidSet.insert( elem );
12661       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12662                         SMDS_MeshElement::iterator() );
12663       elemNodes.push_back( elemNodes[0] );
12664       nodes.resize( 2 + iQuad );
12665       const int nbLinks = elem->NbCornerNodes();
12666       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12667       {
12668         nodes[0] = elemNodes[iN];
12669         nodes[1] = elemNodes[iN+1+iQuad];
12670         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12671           continue; // not free link
12672
12673         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12674         if ( const SMDS_MeshElement* edge =
12675              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12676           presentBndElems.push_back( edge );
12677         else
12678           missingBndElems.push_back( nodes );
12679       }
12680     }
12681
12682     // ---------------------------------
12683     // 2. Add missing boundary elements
12684     // ---------------------------------
12685     if ( targetMesh != myMesh )
12686       // instead of making a map of nodes in this mesh and targetMesh,
12687       // we create nodes with same IDs.
12688       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12689       {
12690         TConnectivity& srcNodes = missingBndElems[i];
12691         tgtNodes.resize( srcNodes.size() );
12692         for ( inode = 0; inode < srcNodes.size(); ++inode )
12693           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12694         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12695                                                                    missType,
12696                                                                    /*noMedium=*/false))
12697           continue;
12698         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12699         ++nbAddedBnd;
12700       }
12701     else
12702       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12703       {
12704         TConnectivity& nodes = missingBndElems[ i ];
12705         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12706                                                                    missType,
12707                                                                    /*noMedium=*/false))
12708           continue;
12709         SMDS_MeshElement* newElem =
12710           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12711         nbAddedBnd += bool( newElem );
12712
12713         // try to set a new element to a shape
12714         if ( myMesh->HasShapeToMesh() )
12715         {
12716           bool ok = true;
12717           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12718           const size_t nbN = nodes.size() / (iQuad+1 );
12719           for ( inode = 0; inode < nbN && ok; ++inode )
12720           {
12721             pair<int, TopAbs_ShapeEnum> i_stype =
12722               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12723             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12724               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12725           }
12726           if ( ok && mediumShapes.size() > 1 )
12727           {
12728             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12729             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12730             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12731             {
12732               if (( ok = ( stype_i->first != stype_i_0.first )))
12733                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12734                                         aMesh->IndexToShape( stype_i_0.second ));
12735             }
12736           }
12737           if ( ok && mediumShapes.begin()->first == missShapeType )
12738             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12739         }
12740       }
12741
12742     // ----------------------------------
12743     // 3. Copy present boundary elements
12744     // ----------------------------------
12745     if ( toCopyExistingBoundary )
12746       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12747       {
12748         const SMDS_MeshElement* e = presentBndElems[i];
12749         tgtNodes.resize( e->NbNodes() );
12750         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12751           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12752         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12753       }
12754     else // store present elements to add them to a group
12755       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12756       {
12757         presentEditor->myLastCreatedElems.Append( presentBndElems[ i ]);
12758       }
12759
12760   } // loop on given elements
12761
12762   // ---------------------------------------------
12763   // 4. Fill group with boundary elements
12764   // ---------------------------------------------
12765   if ( group )
12766   {
12767     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12768       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12769         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12770   }
12771   tgtEditor.myLastCreatedElems.Clear();
12772   tgtEditor2.myLastCreatedElems.Clear();
12773
12774   // -----------------------
12775   // 5. Copy given elements
12776   // -----------------------
12777   if ( toCopyElements && targetMesh != myMesh )
12778   {
12779     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12780     else                  eIt = elemSetIterator( elements );
12781     while (eIt->more())
12782     {
12783       const SMDS_MeshElement* elem = eIt->next();
12784       tgtNodes.resize( elem->NbNodes() );
12785       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12786         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12787       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12788
12789       tgtEditor.myLastCreatedElems.Clear();
12790     }
12791   }
12792   return nbAddedBnd;
12793 }
12794
12795 //================================================================================
12796 /*!
12797  * \brief Copy node position and set \a to node on the same geometry
12798  */
12799 //================================================================================
12800
12801 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12802                                      const SMDS_MeshNode* to )
12803 {
12804   if ( !from || !to ) return;
12805
12806   SMDS_PositionPtr pos = from->GetPosition();
12807   if ( !pos || from->getshapeId() < 1 ) return;
12808
12809   switch ( pos->GetTypeOfPosition() )
12810   {
12811   case SMDS_TOP_3DSPACE: break;
12812
12813   case SMDS_TOP_FACE:
12814   {
12815     const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12816     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12817                                 fPos->GetUParameter(), fPos->GetVParameter() );
12818     break;
12819   }
12820   case SMDS_TOP_EDGE:
12821   {
12822     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12823     const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12824     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12825     break;
12826   }
12827   case SMDS_TOP_VERTEX:
12828   {
12829     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12830     break;
12831   }
12832   case SMDS_TOP_UNSPEC:
12833   default:;
12834   }
12835 }