Salome HOME
a37950e411a9a58af2f95936f903fcfbbc46bed2
[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_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
38
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
41
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MeshAlgos.hxx"
46 #include "SMESH_MesherHelper.hxx"
47 #include "SMESH_OctreeNode.hxx"
48 #include "SMESH_subMesh.hxx"
49
50 #include <Basics_OCCTVersion.hxx>
51
52 #include "utilities.h"
53 #include "chrono.hxx"
54
55 #include <BRepAdaptor_Surface.hxx>
56 #include <BRepBuilderAPI_MakeEdge.hxx>
57 #include <BRepClass3d_SolidClassifier.hxx>
58 #include <BRep_Tool.hxx>
59 #include <ElCLib.hxx>
60 #include <Extrema_GenExtPS.hxx>
61 #include <Extrema_POnCurv.hxx>
62 #include <Extrema_POnSurf.hxx>
63 #include <Geom2d_Curve.hxx>
64 #include <GeomAdaptor_Surface.hxx>
65 #include <Geom_Curve.hxx>
66 #include <Geom_Surface.hxx>
67 #include <Precision.hxx>
68 #include <TColStd_ListOfInteger.hxx>
69 #include <TopAbs_State.hxx>
70 #include <TopExp.hxx>
71 #include <TopExp_Explorer.hxx>
72 #include <TopTools_ListIteratorOfListOfShape.hxx>
73 #include <TopTools_ListOfShape.hxx>
74 #include <TopTools_SequenceOfShape.hxx>
75 #include <TopoDS.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 Clears myLastCreatedNodes and myLastCreatedElems
131  */
132 //================================================================================
133
134 void SMESH_MeshEditor::ClearLastCreated()
135 {
136   myLastCreatedNodes.Clear();
137   myLastCreatedElems.Clear();
138 }
139
140 //================================================================================
141 /*!
142  * \brief Initializes members by an existing element
143  *  \param [in] elem - the source element
144  *  \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
145  */
146 //================================================================================
147
148 SMESH_MeshEditor::ElemFeatures&
149 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
150 {
151   if ( elem )
152   {
153     myType = elem->GetType();
154     if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
155     {
156       myIsPoly = elem->IsPoly();
157       if ( myIsPoly )
158       {
159         myIsQuad = elem->IsQuadratic();
160         if ( myType == SMDSAbs_Volume && !basicOnly )
161         {
162           vector<int > quant = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
163           myPolyhedQuantities.swap( quant );
164         }
165       }
166     }
167     else if ( myType == SMDSAbs_Ball && !basicOnly )
168     {
169       myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
170     }
171   }
172   return *this;
173 }
174
175 //=======================================================================
176 /*!
177  * \brief Add element
178  */
179 //=======================================================================
180
181 SMDS_MeshElement*
182 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
183                              const ElemFeatures&                  features)
184 {
185   SMDS_MeshElement* e = 0;
186   int nbnode = node.size();
187   SMESHDS_Mesh* mesh = GetMeshDS();
188   const int ID = features.myID;
189
190   switch ( features.myType ) {
191   case SMDSAbs_Face:
192     if ( !features.myIsPoly ) {
193       if      (nbnode == 3) {
194         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
195         else           e = mesh->AddFace      (node[0], node[1], node[2] );
196       }
197       else if (nbnode == 4) {
198         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
199         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
200       }
201       else if (nbnode == 6) {
202         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
203                                                node[4], node[5], ID);
204         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
205                                                node[4], node[5] );
206       }
207       else if (nbnode == 7) {
208         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
209                                                node[4], node[5], node[6], ID);
210         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
211                                                node[4], node[5], node[6] );
212       }
213       else if (nbnode == 8) {
214         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
215                                                node[4], node[5], node[6], node[7], ID);
216         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
217                                                node[4], node[5], node[6], node[7] );
218       }
219       else if (nbnode == 9) {
220         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
221                                                node[4], node[5], node[6], node[7], node[8], ID);
222         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
223                                                node[4], node[5], node[6], node[7], node[8] );
224       }
225     }
226     else if ( !features.myIsQuad )
227     {
228       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
229       else           e = mesh->AddPolygonalFace      (node    );
230     }
231     else if ( nbnode % 2 == 0 ) // just a protection
232     {
233       if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
234       else           e = mesh->AddQuadPolygonalFace      (node    );
235     }
236     break;
237
238   case SMDSAbs_Volume:
239     if ( !features.myIsPoly ) {
240       if      (nbnode == 4) {
241         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
242         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
243       }
244       else if (nbnode == 5) {
245         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
246                                                  node[4], ID);
247         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
248                                                  node[4] );
249       }
250       else if (nbnode == 6) {
251         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
252                                                  node[4], node[5], ID);
253         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
254                                                  node[4], node[5] );
255       }
256       else if (nbnode == 8) {
257         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
258                                                  node[4], node[5], node[6], node[7], ID);
259         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
260                                                  node[4], node[5], node[6], node[7] );
261       }
262       else if (nbnode == 10) {
263         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
264                                                  node[4], node[5], node[6], node[7],
265                                                  node[8], node[9], ID);
266         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
267                                                  node[4], node[5], node[6], node[7],
268                                                  node[8], node[9] );
269       }
270       else if (nbnode == 12) {
271         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
272                                                  node[4], node[5], node[6], node[7],
273                                                  node[8], node[9], node[10], node[11], ID);
274         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
275                                                  node[4], node[5], node[6], node[7],
276                                                  node[8], node[9], node[10], node[11] );
277       }
278       else if (nbnode == 13) {
279         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
280                                                  node[4], node[5], node[6], node[7],
281                                                  node[8], node[9], node[10],node[11],
282                                                  node[12],ID);
283         else           e = mesh->AddVolume      (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],
286                                                  node[12] );
287       }
288       else if (nbnode == 15) {
289         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
290                                                  node[4], node[5], node[6], node[7],
291                                                  node[8], node[9], node[10],node[11],
292                                                  node[12],node[13],node[14],ID);
293         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
294                                                  node[4], node[5], node[6], node[7],
295                                                  node[8], node[9], node[10],node[11],
296                                                  node[12],node[13],node[14] );
297       }
298       else if (nbnode == 20) {
299         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
300                                                  node[4], node[5], node[6], node[7],
301                                                  node[8], node[9], node[10],node[11],
302                                                  node[12],node[13],node[14],node[15],
303                                                  node[16],node[17],node[18],node[19],ID);
304         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
305                                                  node[4], node[5], node[6], node[7],
306                                                  node[8], node[9], node[10],node[11],
307                                                  node[12],node[13],node[14],node[15],
308                                                  node[16],node[17],node[18],node[19] );
309       }
310       else if (nbnode == 27) {
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],
316                                                  node[20],node[21],node[22],node[23],
317                                                  node[24],node[25],node[26], ID);
318         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
319                                                  node[4], node[5], node[6], node[7],
320                                                  node[8], node[9], node[10],node[11],
321                                                  node[12],node[13],node[14],node[15],
322                                                  node[16],node[17],node[18],node[19],
323                                                  node[20],node[21],node[22],node[23],
324                                                  node[24],node[25],node[26] );
325       }
326     }
327     else if ( !features.myIsQuad )
328     {
329       if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
330       else           e = mesh->AddPolyhedralVolume      (node, features.myPolyhedQuantities    );
331     }
332     else
333     {
334       // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
335       // else           e = mesh->AddQuadPolyhedralVolume      (node, features.myPolyhedQuantities   );
336     }
337     break;
338
339   case SMDSAbs_Edge:
340     if ( nbnode == 2 ) {
341       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
342       else           e = mesh->AddEdge      (node[0], node[1] );
343     }
344     else if ( nbnode == 3 ) {
345       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
346       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
347     }
348     break;
349
350   case SMDSAbs_0DElement:
351     if ( nbnode == 1 ) {
352       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
353       else           e = mesh->Add0DElement      (node[0] );
354     }
355     break;
356
357   case SMDSAbs_Node:
358     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
359     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z()    );
360     break;
361
362   case SMDSAbs_Ball:
363     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
364     else           e = mesh->AddBall      (node[0], features.myBallDiameter    );
365     break;
366
367   default:;
368   }
369   if ( e ) myLastCreatedElems.Append( e );
370   return e;
371 }
372
373 //=======================================================================
374 /*!
375  * \brief Add element
376  */
377 //=======================================================================
378
379 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
380                                                const ElemFeatures& features)
381 {
382   vector<const SMDS_MeshNode*> nodes;
383   nodes.reserve( nodeIDs.size() );
384   vector<int>::const_iterator id = nodeIDs.begin();
385   while ( id != nodeIDs.end() ) {
386     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
387       nodes.push_back( node );
388     else
389       return 0;
390   }
391   return AddElement( nodes, features );
392 }
393
394 //=======================================================================
395 //function : Remove
396 //purpose  : Remove a node or an element.
397 //           Modify a compute state of sub-meshes which become empty
398 //=======================================================================
399
400 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
401                               const bool         isNodes )
402 {
403   myLastCreatedElems.Clear();
404   myLastCreatedNodes.Clear();
405
406   SMESHDS_Mesh* aMesh = GetMeshDS();
407   set< SMESH_subMesh *> smmap;
408
409   int removed = 0;
410   list<int>::const_iterator it = theIDs.begin();
411   for ( ; it != theIDs.end(); it++ ) {
412     const SMDS_MeshElement * elem;
413     if ( isNodes )
414       elem = aMesh->FindNode( *it );
415     else
416       elem = aMesh->FindElement( *it );
417     if ( !elem )
418       continue;
419
420     // Notify VERTEX sub-meshes about modification
421     if ( isNodes ) {
422       const SMDS_MeshNode* node = cast2Node( elem );
423       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
424         if ( int aShapeID = node->getshapeId() )
425           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
426             smmap.insert( sm );
427     }
428     // Find sub-meshes to notify about modification
429     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
430     //     while ( nodeIt->more() ) {
431     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
432     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
433     //       if ( aPosition.get() ) {
434     //         if ( int aShapeID = aPosition->GetShapeId() ) {
435     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
436     //             smmap.insert( sm );
437     //         }
438     //       }
439     //     }
440
441     // Do remove
442     if ( isNodes )
443       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
444     else
445       aMesh->RemoveElement( elem );
446     removed++;
447   }
448
449   // Notify sub-meshes about modification
450   if ( !smmap.empty() ) {
451     set< SMESH_subMesh *>::iterator smIt;
452     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
453       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
454   }
455
456   //   // Check if the whole mesh becomes empty
457   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
458   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
459
460   return removed;
461 }
462
463 //================================================================================
464 /*!
465  * \brief Create 0D elements on all nodes of the given object except those
466  *        nodes on which a 0D element already exists.
467  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
468  *                    the all mesh is treated
469  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
470  */
471 //================================================================================
472
473 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
474                                                    TIDSortedElemSet&       all0DElems )
475 {
476   SMDS_ElemIteratorPtr elemIt;
477   vector< const SMDS_MeshElement* > allNodes;
478   if ( elements.empty() )
479   {
480     allNodes.reserve( GetMeshDS()->NbNodes() );
481     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
482     while ( elemIt->more() )
483       allNodes.push_back( elemIt->next() );
484
485     elemIt = elemSetIterator( allNodes );
486   }
487   else
488   {
489     elemIt = elemSetIterator( elements );
490   }
491
492   while ( elemIt->more() )
493   {
494     const SMDS_MeshElement* e = elemIt->next();
495     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
496     while ( nodeIt->more() )
497     {
498       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
499       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
500       if ( it0D->more() )
501         all0DElems.insert( it0D->next() );
502       else {
503         myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
504         all0DElems.insert( myLastCreatedElems.Last() );
505       }
506     }
507   }
508 }
509
510 //=======================================================================
511 //function : FindShape
512 //purpose  : Return an index of the shape theElem is on
513 //           or zero if a shape not found
514 //=======================================================================
515
516 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
517 {
518   myLastCreatedElems.Clear();
519   myLastCreatedNodes.Clear();
520
521   SMESHDS_Mesh * aMesh = GetMeshDS();
522   if ( aMesh->ShapeToMesh().IsNull() )
523     return 0;
524
525   int aShapeID = theElem->getshapeId();
526   if ( aShapeID < 1 )
527     return 0;
528
529   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
530     if ( sm->Contains( theElem ))
531       return aShapeID;
532
533   if ( theElem->GetType() == SMDSAbs_Node ) {
534     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
535   }
536   else {
537     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
538   }
539
540   TopoDS_Shape aShape; // the shape a node of theElem is on
541   if ( theElem->GetType() != SMDSAbs_Node )
542   {
543     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
544     while ( nodeIt->more() ) {
545       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
546       if ((aShapeID = node->getshapeId()) > 0) {
547         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
548           if ( sm->Contains( theElem ))
549             return aShapeID;
550           if ( aShape.IsNull() )
551             aShape = aMesh->IndexToShape( aShapeID );
552         }
553       }
554     }
555   }
556
557   // None of nodes is on a proper shape,
558   // find the shape among ancestors of aShape on which a node is
559   if ( !aShape.IsNull() ) {
560     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
561     for ( ; ancIt.More(); ancIt.Next() ) {
562       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
563       if ( sm && sm->Contains( theElem ))
564         return aMesh->ShapeToIndex( ancIt.Value() );
565     }
566   }
567   else
568   {
569     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
570     while ( const SMESHDS_SubMesh* sm = smIt->next() )
571       if ( sm->Contains( theElem ))
572         return sm->GetID();
573   }
574
575   return 0;
576 }
577
578 //=======================================================================
579 //function : IsMedium
580 //purpose  :
581 //=======================================================================
582
583 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
584                                 const SMDSAbs_ElementType typeToCheck)
585 {
586   bool isMedium = false;
587   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
588   while (it->more() && !isMedium ) {
589     const SMDS_MeshElement* elem = it->next();
590     isMedium = elem->IsMediumNode(node);
591   }
592   return isMedium;
593 }
594
595 //=======================================================================
596 //function : shiftNodesQuadTria
597 //purpose  : Shift nodes in the array corresponded to quadratic triangle
598 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
599 //=======================================================================
600
601 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
602 {
603   const SMDS_MeshNode* nd1 = aNodes[0];
604   aNodes[0] = aNodes[1];
605   aNodes[1] = aNodes[2];
606   aNodes[2] = nd1;
607   const SMDS_MeshNode* nd2 = aNodes[3];
608   aNodes[3] = aNodes[4];
609   aNodes[4] = aNodes[5];
610   aNodes[5] = nd2;
611 }
612
613 //=======================================================================
614 //function : nbEdgeConnectivity
615 //purpose  : return number of the edges connected with the theNode.
616 //           if theEdges has connections with the other type of the
617 //           elements, return -1
618 //=======================================================================
619
620 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
621 {
622   // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
623   // int nb=0;
624   // while(elemIt->more()) {
625   //   elemIt->next();
626   //   nb++;
627   // }
628   // return nb;
629   return theNode->NbInverseElements();
630 }
631
632 //=======================================================================
633 //function : getNodesFromTwoTria
634 //purpose  : 
635 //=======================================================================
636
637 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
638                                 const SMDS_MeshElement * theTria2,
639                                 vector< const SMDS_MeshNode*>& N1,
640                                 vector< const SMDS_MeshNode*>& N2)
641 {
642   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
643   if ( N1.size() < 6 ) return false;
644   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
645   if ( N2.size() < 6 ) return false;
646
647   int sames[3] = {-1,-1,-1};
648   int nbsames = 0;
649   int i, j;
650   for(i=0; i<3; i++) {
651     for(j=0; j<3; j++) {
652       if(N1[i]==N2[j]) {
653         sames[i] = j;
654         nbsames++;
655         break;
656       }
657     }
658   }
659   if(nbsames!=2) return false;
660   if(sames[0]>-1) {
661     shiftNodesQuadTria(N1);
662     if(sames[1]>-1) {
663       shiftNodesQuadTria(N1);
664     }
665   }
666   i = sames[0] + sames[1] + sames[2];
667   for(; i<2; i++) {
668     shiftNodesQuadTria(N2);
669   }
670   // now we receive following N1 and N2 (using numeration as in the image below)
671   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
672   // i.e. first nodes from both arrays form a new diagonal
673   return true;
674 }
675
676 //=======================================================================
677 //function : InverseDiag
678 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
679 //           but having other common link.
680 //           Return False if args are improper
681 //=======================================================================
682
683 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
684                                     const SMDS_MeshElement * theTria2 )
685 {
686   MESSAGE("InverseDiag");
687   myLastCreatedElems.Clear();
688   myLastCreatedNodes.Clear();
689
690   if (!theTria1 || !theTria2)
691     return false;
692
693   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
694   if (!F1) return false;
695   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
696   if (!F2) return false;
697   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
698       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
699
700     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
701     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
702     //    |/ |                                         | \|
703     //  B +--+ 2                                     B +--+ 2
704
705     // put nodes in array and find out indices of the same ones
706     const SMDS_MeshNode* aNodes [6];
707     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
708     int i = 0;
709     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
710     while ( it->more() ) {
711       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
712
713       if ( i > 2 ) // theTria2
714         // find same node of theTria1
715         for ( int j = 0; j < 3; j++ )
716           if ( aNodes[ i ] == aNodes[ j ]) {
717             sameInd[ j ] = i;
718             sameInd[ i ] = j;
719             break;
720           }
721       // next
722       i++;
723       if ( i == 3 ) {
724         if ( it->more() )
725           return false; // theTria1 is not a triangle
726         it = theTria2->nodesIterator();
727       }
728       if ( i == 6 && it->more() )
729         return false; // theTria2 is not a triangle
730     }
731
732     // find indices of 1,2 and of A,B in theTria1
733     int iA = -1, iB = 0, i1 = 0, i2 = 0;
734     for ( i = 0; i < 6; i++ ) {
735       if ( sameInd [ i ] == -1 ) {
736         if ( i < 3 ) i1 = i;
737         else         i2 = i;
738       }
739       else if (i < 3) {
740         if ( iA >= 0) iB = i;
741         else          iA = i;
742       }
743     }
744     // nodes 1 and 2 should not be the same
745     if ( aNodes[ i1 ] == aNodes[ i2 ] )
746       return false;
747
748     // theTria1: A->2
749     aNodes[ iA ] = aNodes[ i2 ];
750     // theTria2: B->1
751     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
752
753     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
754     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
755
756     return true;
757
758   } // end if(F1 && F2)
759
760   // check case of quadratic faces
761   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
762       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
763     return false;
764   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
765       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
766     return false;
767
768   //       5
769   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
770   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
771   //    |   / |
772   //  7 +  +  + 6
773   //    | /9  |
774   //    |/    |
775   //  4 +--+--+ 3
776   //       8
777
778   vector< const SMDS_MeshNode* > N1;
779   vector< const SMDS_MeshNode* > N2;
780   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
781     return false;
782   // now we receive following N1 and N2 (using numeration as above image)
783   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
784   // i.e. first nodes from both arrays determ new diagonal
785
786   vector< const SMDS_MeshNode*> N1new( N1.size() );
787   vector< const SMDS_MeshNode*> N2new( N2.size() );
788   N1new.back() = N1.back(); // central node of biquadratic
789   N2new.back() = N2.back();
790   N1new[0] = N1[0];  N2new[0] = N1[0];
791   N1new[1] = N2[0];  N2new[1] = N1[1];
792   N1new[2] = N2[1];  N2new[2] = N2[0];
793   N1new[3] = N1[4];  N2new[3] = N1[3];
794   N1new[4] = N2[3];  N2new[4] = N2[5];
795   N1new[5] = N1[5];  N2new[5] = N1[4];
796   // change nodes in faces
797   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
798   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
799
800   // move the central node of biquadratic triangle
801   SMESH_MesherHelper helper( *GetMesh() );
802   for ( int is2nd = 0; is2nd < 2; ++is2nd )
803   {
804     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
805     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
806     if ( nodes.size() < 7 )
807       continue;
808     helper.SetSubShape( tria->getshapeId() );
809     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
810     gp_Pnt xyz;
811     if ( F.IsNull() )
812     {
813       xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
814               SMESH_TNodeXYZ( nodes[4] ) +
815               SMESH_TNodeXYZ( nodes[5] )) / 3.;
816     }
817     else
818     {
819       bool checkUV;
820       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
821                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
822                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
823       TopLoc_Location loc;
824       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
825       xyz = S->Value( uv.X(), uv.Y() );
826       xyz.Transform( loc );
827       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
828            nodes[6]->getshapeId() > 0 )
829         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
830     }
831     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
832   }
833   return true;
834 }
835
836 //=======================================================================
837 //function : findTriangles
838 //purpose  : find triangles sharing theNode1-theNode2 link
839 //=======================================================================
840
841 static bool findTriangles(const SMDS_MeshNode *    theNode1,
842                           const SMDS_MeshNode *    theNode2,
843                           const SMDS_MeshElement*& theTria1,
844                           const SMDS_MeshElement*& theTria2)
845 {
846   if ( !theNode1 || !theNode2 ) return false;
847
848   theTria1 = theTria2 = 0;
849
850   set< const SMDS_MeshElement* > emap;
851   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
852   while (it->more()) {
853     const SMDS_MeshElement* elem = it->next();
854     if ( elem->NbCornerNodes() == 3 )
855       emap.insert( elem );
856   }
857   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
858   while (it->more()) {
859     const SMDS_MeshElement* elem = it->next();
860     if ( emap.count( elem )) {
861       if ( !theTria1 )
862       {
863         theTria1 = elem;
864       }
865       else  
866       {
867         theTria2 = elem;
868         // theTria1 must be element with minimum ID
869         if ( theTria2->GetID() < theTria1->GetID() )
870           std::swap( theTria2, theTria1 );
871         return true;
872       }
873     }
874   }
875   return false;
876 }
877
878 //=======================================================================
879 //function : InverseDiag
880 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
881 //           with ones built on the same 4 nodes but having other common link.
882 //           Return false if proper faces not found
883 //=======================================================================
884
885 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
886                                     const SMDS_MeshNode * theNode2)
887 {
888   myLastCreatedElems.Clear();
889   myLastCreatedNodes.Clear();
890
891   MESSAGE( "::InverseDiag()" );
892
893   const SMDS_MeshElement *tr1, *tr2;
894   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
895     return false;
896
897   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
898   if (!F1) return false;
899   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
900   if (!F2) return false;
901   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
902       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
903
904     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
905     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
906     //    |/ |                                    | \|
907     //  B +--+ 2                                B +--+ 2
908
909     // put nodes in array
910     // and find indices of 1,2 and of A in tr1 and of B in tr2
911     int i, iA1 = 0, i1 = 0;
912     const SMDS_MeshNode* aNodes1 [3];
913     SMDS_ElemIteratorPtr it;
914     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
915       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
916       if ( aNodes1[ i ] == theNode1 )
917         iA1 = i; // node A in tr1
918       else if ( aNodes1[ i ] != theNode2 )
919         i1 = i;  // node 1
920     }
921     int iB2 = 0, i2 = 0;
922     const SMDS_MeshNode* aNodes2 [3];
923     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
924       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
925       if ( aNodes2[ i ] == theNode2 )
926         iB2 = i; // node B in tr2
927       else if ( aNodes2[ i ] != theNode1 )
928         i2 = i;  // node 2
929     }
930
931     // nodes 1 and 2 should not be the same
932     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
933       return false;
934
935     // tr1: A->2
936     aNodes1[ iA1 ] = aNodes2[ i2 ];
937     // tr2: B->1
938     aNodes2[ iB2 ] = aNodes1[ i1 ];
939
940     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
941     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
942
943     return true;
944   }
945
946   // check case of quadratic faces
947   return InverseDiag(tr1,tr2);
948 }
949
950 //=======================================================================
951 //function : getQuadrangleNodes
952 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
953 //           fusion of triangles tr1 and tr2 having shared link on
954 //           theNode1 and theNode2
955 //=======================================================================
956
957 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
958                         const SMDS_MeshNode *    theNode1,
959                         const SMDS_MeshNode *    theNode2,
960                         const SMDS_MeshElement * tr1,
961                         const SMDS_MeshElement * tr2 )
962 {
963   if( tr1->NbNodes() != tr2->NbNodes() )
964     return false;
965   // find the 4-th node to insert into tr1
966   const SMDS_MeshNode* n4 = 0;
967   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
968   int i=0;
969   while ( !n4 && i<3 ) {
970     const SMDS_MeshNode * n = cast2Node( it->next() );
971     i++;
972     bool isDiag = ( n == theNode1 || n == theNode2 );
973     if ( !isDiag )
974       n4 = n;
975   }
976   // Make an array of nodes to be in a quadrangle
977   int iNode = 0, iFirstDiag = -1;
978   it = tr1->nodesIterator();
979   i=0;
980   while ( i<3 ) {
981     const SMDS_MeshNode * n = cast2Node( it->next() );
982     i++;
983     bool isDiag = ( n == theNode1 || n == theNode2 );
984     if ( isDiag ) {
985       if ( iFirstDiag < 0 )
986         iFirstDiag = iNode;
987       else if ( iNode - iFirstDiag == 1 )
988         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
989     }
990     else if ( n == n4 ) {
991       return false; // tr1 and tr2 should not have all the same nodes
992     }
993     theQuadNodes[ iNode++ ] = n;
994   }
995   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
996     theQuadNodes[ iNode ] = n4;
997
998   return true;
999 }
1000
1001 //=======================================================================
1002 //function : DeleteDiag
1003 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
1004 //           with a quadrangle built on the same 4 nodes.
1005 //           Return false if proper faces not found
1006 //=======================================================================
1007
1008 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1009                                    const SMDS_MeshNode * theNode2)
1010 {
1011   myLastCreatedElems.Clear();
1012   myLastCreatedNodes.Clear();
1013
1014   MESSAGE( "::DeleteDiag()" );
1015
1016   const SMDS_MeshElement *tr1, *tr2;
1017   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1018     return false;
1019
1020   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1021   if (!F1) return false;
1022   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1023   if (!F2) return false;
1024   SMESHDS_Mesh * aMesh = GetMeshDS();
1025
1026   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1027       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1028
1029     const SMDS_MeshNode* aNodes [ 4 ];
1030     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1031       return false;
1032
1033     const SMDS_MeshElement* newElem = 0;
1034     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1035     myLastCreatedElems.Append(newElem);
1036     AddToSameGroups( newElem, tr1, aMesh );
1037     int aShapeId = tr1->getshapeId();
1038     if ( aShapeId )
1039       {
1040         aMesh->SetMeshElementOnShape( newElem, aShapeId );
1041       }
1042     aMesh->RemoveElement( tr1 );
1043     aMesh->RemoveElement( tr2 );
1044
1045     return true;
1046   }
1047
1048   // check case of quadratic faces
1049   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1050     return false;
1051   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1052     return false;
1053
1054   //       5
1055   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1056   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1057   //    |   / |
1058   //  7 +  +  + 6
1059   //    | /9  |
1060   //    |/    |
1061   //  4 +--+--+ 3
1062   //       8
1063
1064   vector< const SMDS_MeshNode* > N1;
1065   vector< const SMDS_MeshNode* > N2;
1066   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1067     return false;
1068   // now we receive following N1 and N2 (using numeration as above image)
1069   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1070   // i.e. first nodes from both arrays determ new diagonal
1071
1072   const SMDS_MeshNode* aNodes[8];
1073   aNodes[0] = N1[0];
1074   aNodes[1] = N1[1];
1075   aNodes[2] = N2[0];
1076   aNodes[3] = N2[1];
1077   aNodes[4] = N1[3];
1078   aNodes[5] = N2[5];
1079   aNodes[6] = N2[3];
1080   aNodes[7] = N1[5];
1081
1082   const SMDS_MeshElement* newElem = 0;
1083   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1084                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1085   myLastCreatedElems.Append(newElem);
1086   AddToSameGroups( newElem, tr1, aMesh );
1087   int aShapeId = tr1->getshapeId();
1088   if ( aShapeId )
1089     {
1090       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1091     }
1092   aMesh->RemoveElement( tr1 );
1093   aMesh->RemoveElement( tr2 );
1094
1095   // remove middle node (9)
1096   GetMeshDS()->RemoveNode( N1[4] );
1097
1098   return true;
1099 }
1100
1101 //=======================================================================
1102 //function : Reorient
1103 //purpose  : Reverse theElement orientation
1104 //=======================================================================
1105
1106 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1107 {
1108   MESSAGE("Reorient");
1109   myLastCreatedElems.Clear();
1110   myLastCreatedNodes.Clear();
1111
1112   if (!theElem)
1113     return false;
1114   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1115   if ( !it || !it->more() )
1116     return false;
1117
1118   const SMDSAbs_ElementType type = theElem->GetType();
1119   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1120     return false;
1121
1122   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1123   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1124   {
1125     const SMDS_VtkVolume* aPolyedre =
1126       dynamic_cast<const SMDS_VtkVolume*>( theElem );
1127     if (!aPolyedre) {
1128       MESSAGE("Warning: bad volumic element");
1129       return false;
1130     }
1131     const int nbFaces = aPolyedre->NbFaces();
1132     vector<const SMDS_MeshNode *> poly_nodes;
1133     vector<int> quantities (nbFaces);
1134
1135     // reverse each face of the polyedre
1136     for (int iface = 1; iface <= nbFaces; iface++) {
1137       int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1138       quantities[iface - 1] = nbFaceNodes;
1139
1140       for (inode = nbFaceNodes; inode >= 1; inode--) {
1141         const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1142         poly_nodes.push_back(curNode);
1143       }
1144     }
1145     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1146   }
1147   else // other elements
1148   {
1149     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1150     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1151     if ( interlace.empty() )
1152     {
1153       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1154     }
1155     else
1156     {
1157       SMDS_MeshCell::applyInterlace( interlace, nodes );
1158     }
1159     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1160   }
1161   return false;
1162 }
1163
1164 //================================================================================
1165 /*!
1166  * \brief Reorient faces.
1167  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1168  * \param theDirection - desired direction of normal of \a theFace
1169  * \param theFace - one of \a theFaces that sould be oriented according to
1170  *        \a theDirection and whose orientation defines orientation of other faces
1171  * \return number of reoriented faces.
1172  */
1173 //================================================================================
1174
1175 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1176                                   const gp_Dir&            theDirection,
1177                                   const SMDS_MeshElement * theFace)
1178 {
1179   int nbReori = 0;
1180   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1181
1182   if ( theFaces.empty() )
1183   {
1184     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1185     while ( fIt->more() )
1186       theFaces.insert( theFaces.end(), fIt->next() );
1187   }
1188
1189   // orient theFace according to theDirection
1190   gp_XYZ normal;
1191   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1192   if ( normal * theDirection.XYZ() < 0 )
1193     nbReori += Reorient( theFace );
1194
1195   // Orient other faces
1196
1197   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1198   TIDSortedElemSet avoidSet;
1199   set< SMESH_TLink > checkedLinks;
1200   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1201
1202   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1203     theFaces.erase( theFace );
1204   startFaces.insert( theFace );
1205
1206   int nodeInd1, nodeInd2;
1207   const SMDS_MeshElement*           otherFace;
1208   vector< const SMDS_MeshElement* > facesNearLink;
1209   vector< std::pair< int, int > >   nodeIndsOfFace;
1210
1211   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1212   while ( !startFaces.empty() )
1213   {
1214     startFace = startFaces.begin();
1215     theFace = *startFace;
1216     startFaces.erase( startFace );
1217     if ( !visitedFaces.insert( theFace ).second )
1218       continue;
1219
1220     avoidSet.clear();
1221     avoidSet.insert(theFace);
1222
1223     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1224
1225     const int nbNodes = theFace->NbCornerNodes();
1226     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1227     {
1228       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1229       linkIt_isNew = checkedLinks.insert( link );
1230       if ( !linkIt_isNew.second )
1231       {
1232         // link has already been checked and won't be encountered more
1233         // if the group (theFaces) is manifold
1234         //checkedLinks.erase( linkIt_isNew.first );
1235       }
1236       else
1237       {
1238         facesNearLink.clear();
1239         nodeIndsOfFace.clear();
1240         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1241                                                              theFaces, avoidSet,
1242                                                              &nodeInd1, &nodeInd2 )))
1243           if ( otherFace != theFace)
1244           {
1245             facesNearLink.push_back( otherFace );
1246             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1247             avoidSet.insert( otherFace );
1248           }
1249         if ( facesNearLink.size() > 1 )
1250         {
1251           // NON-MANIFOLD mesh shell !
1252           // select a face most co-directed with theFace,
1253           // other faces won't be visited this time
1254           gp_XYZ NF, NOF;
1255           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1256           double proj, maxProj = -1;
1257           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1258             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1259             if (( proj = Abs( NF * NOF )) > maxProj ) {
1260               maxProj = proj;
1261               otherFace = facesNearLink[i];
1262               nodeInd1  = nodeIndsOfFace[i].first;
1263               nodeInd2  = nodeIndsOfFace[i].second;
1264             }
1265           }
1266           // not to visit rejected faces
1267           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1268             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1269               visitedFaces.insert( facesNearLink[i] );
1270         }
1271         else if ( facesNearLink.size() == 1 )
1272         {
1273           otherFace = facesNearLink[0];
1274           nodeInd1  = nodeIndsOfFace.back().first;
1275           nodeInd2  = nodeIndsOfFace.back().second;
1276         }
1277         if ( otherFace && otherFace != theFace)
1278         {
1279           // link must be reverse in otherFace if orientation ot otherFace
1280           // is same as that of theFace
1281           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1282           {
1283             nbReori += Reorient( otherFace );
1284           }
1285           startFaces.insert( otherFace );
1286         }
1287       }
1288       std::swap( link.first, link.second ); // reverse the link
1289     }
1290   }
1291   return nbReori;
1292 }
1293
1294 //================================================================================
1295 /*!
1296  * \brief Reorient faces basing on orientation of adjacent volumes.
1297  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1298  * \param theVolumes - reference volumes.
1299  * \param theOutsideNormal - to orient faces to have their normal
1300  *        pointing either \a outside or \a inside the adjacent volumes.
1301  * \return number of reoriented faces.
1302  */
1303 //================================================================================
1304
1305 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1306                                       TIDSortedElemSet & theVolumes,
1307                                       const bool         theOutsideNormal)
1308 {
1309   int nbReori = 0;
1310
1311   SMDS_ElemIteratorPtr faceIt;
1312   if ( theFaces.empty() )
1313     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1314   else
1315     faceIt = elemSetIterator( theFaces );
1316
1317   vector< const SMDS_MeshNode* > faceNodes;
1318   TIDSortedElemSet checkedVolumes;
1319   set< const SMDS_MeshNode* > faceNodesSet;
1320   SMDS_VolumeTool volumeTool;
1321
1322   while ( faceIt->more() ) // loop on given faces
1323   {
1324     const SMDS_MeshElement* face = faceIt->next();
1325     if ( face->GetType() != SMDSAbs_Face )
1326       continue;
1327
1328     const size_t nbCornersNodes = face->NbCornerNodes();
1329     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1330
1331     checkedVolumes.clear();
1332     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1333     while ( vIt->more() )
1334     {
1335       const SMDS_MeshElement* volume = vIt->next();
1336
1337       if ( !checkedVolumes.insert( volume ).second )
1338         continue;
1339       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1340         continue;
1341
1342       // is volume adjacent?
1343       bool allNodesCommon = true;
1344       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1345         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1346       if ( !allNodesCommon )
1347         continue;
1348
1349       // get nodes of a corresponding volume facet
1350       faceNodesSet.clear();
1351       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1352       volumeTool.Set( volume );
1353       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1354       if ( facetID < 0 ) continue;
1355       volumeTool.SetExternalNormal();
1356       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1357
1358       // compare order of faceNodes and facetNodes
1359       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1360       int iNN[2];
1361       for ( int i = 0; i < 2; ++i )
1362       {
1363         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1364         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1365           if ( faceNodes[ iN ] == n )
1366           {
1367             iNN[ i ] = iN;
1368             break;
1369           }
1370       }
1371       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1372       if ( isOutside != theOutsideNormal )
1373         nbReori += Reorient( face );
1374     }
1375   }  // loop on given faces
1376
1377   return nbReori;
1378 }
1379
1380 //=======================================================================
1381 //function : getBadRate
1382 //purpose  :
1383 //=======================================================================
1384
1385 static double getBadRate (const SMDS_MeshElement*               theElem,
1386                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1387 {
1388   SMESH::Controls::TSequenceOfXYZ P;
1389   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1390     return 1e100;
1391   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1392   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1393 }
1394
1395 //=======================================================================
1396 //function : QuadToTri
1397 //purpose  : Cut quadrangles into triangles.
1398 //           theCrit is used to select a diagonal to cut
1399 //=======================================================================
1400
1401 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1402                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1403 {
1404   myLastCreatedElems.Clear();
1405   myLastCreatedNodes.Clear();
1406
1407   if ( !theCrit.get() )
1408     return false;
1409
1410   SMESHDS_Mesh * aMesh = GetMeshDS();
1411
1412   Handle(Geom_Surface) surface;
1413   SMESH_MesherHelper   helper( *GetMesh() );
1414
1415   TIDSortedElemSet::iterator itElem;
1416   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1417   {
1418     const SMDS_MeshElement* elem = *itElem;
1419     if ( !elem || elem->GetType() != SMDSAbs_Face )
1420       continue;
1421     if ( elem->NbCornerNodes() != 4 )
1422       continue;
1423
1424     // retrieve element nodes
1425     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1426
1427     // compare two sets of possible triangles
1428     double aBadRate1, aBadRate2; // to what extent a set is bad
1429     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1430     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1431     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1432
1433     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1434     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1435     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1436
1437     const int aShapeId = FindShape( elem );
1438     const SMDS_MeshElement* newElem1 = 0;
1439     const SMDS_MeshElement* newElem2 = 0;
1440
1441     if ( !elem->IsQuadratic() ) // split liner quadrangle
1442     {
1443       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1444       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1445       if ( aBadRate1 <= aBadRate2 ) {
1446         // tr1 + tr2 is better
1447         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1448         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1449       }
1450       else {
1451         // tr3 + tr4 is better
1452         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1453         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1454       }
1455     }
1456     else // split quadratic quadrangle
1457     {
1458       helper.SetIsQuadratic( true );
1459       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1460
1461       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1462       if ( aNodes.size() == 9 )
1463       {
1464         helper.SetIsBiQuadratic( true );
1465         if ( aBadRate1 <= aBadRate2 )
1466           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1467         else
1468           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1469       }
1470       // create a new element
1471       if ( aBadRate1 <= aBadRate2 ) {
1472         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1473         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1474       }
1475       else {
1476         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1477         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1478       }
1479     } // quadratic case
1480
1481     // care of a new element
1482
1483     myLastCreatedElems.Append(newElem1);
1484     myLastCreatedElems.Append(newElem2);
1485     AddToSameGroups( newElem1, elem, aMesh );
1486     AddToSameGroups( newElem2, elem, aMesh );
1487
1488     // put a new triangle on the same shape
1489     if ( aShapeId )
1490       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1491     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1492
1493     aMesh->RemoveElement( elem );
1494   }
1495   return true;
1496 }
1497
1498 //=======================================================================
1499 /*!
1500  * \brief Split each of given quadrangles into 4 triangles.
1501  * \param theElems - The faces to be splitted. If empty all faces are split.
1502  */
1503 //=======================================================================
1504
1505 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1506 {
1507   myLastCreatedElems.Clear();
1508   myLastCreatedNodes.Clear();
1509
1510   SMESH_MesherHelper helper( *GetMesh() );
1511   helper.SetElementsOnShape( true );
1512
1513   SMDS_ElemIteratorPtr faceIt;
1514   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1515   else                    faceIt = elemSetIterator( theElems );
1516
1517   bool   checkUV;
1518   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1519   gp_XYZ xyz[9];
1520   vector< const SMDS_MeshNode* > nodes;
1521   SMESHDS_SubMesh*               subMeshDS;
1522   TopoDS_Face                    F;
1523   Handle(Geom_Surface)           surface;
1524   TopLoc_Location                loc;
1525
1526   while ( faceIt->more() )
1527   {
1528     const SMDS_MeshElement* quad = faceIt->next();
1529     if ( !quad || quad->NbCornerNodes() != 4 )
1530       continue;
1531
1532     // get a surface the quad is on
1533
1534     if ( quad->getshapeId() < 1 )
1535     {
1536       F.Nullify();
1537       helper.SetSubShape( 0 );
1538       subMeshDS = 0;
1539     }
1540     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1541     {
1542       helper.SetSubShape( quad->getshapeId() );
1543       if ( !helper.GetSubShape().IsNull() &&
1544            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1545       {
1546         F = TopoDS::Face( helper.GetSubShape() );
1547         surface = BRep_Tool::Surface( F, loc );
1548         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1549       }
1550       else
1551       {
1552         helper.SetSubShape( 0 );
1553         subMeshDS = 0;
1554       }
1555     }
1556
1557     // create a central node
1558
1559     const SMDS_MeshNode* nCentral;
1560     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1561
1562     if ( nodes.size() == 9 )
1563     {
1564       nCentral = nodes.back();
1565     }
1566     else
1567     {
1568       size_t iN = 0;
1569       if ( F.IsNull() )
1570       {
1571         for ( ; iN < nodes.size(); ++iN )
1572           xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1573
1574         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1575           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1576
1577         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1578                                    xyz[0], xyz[1], xyz[2], xyz[3],
1579                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1580       }
1581       else
1582       {
1583         for ( ; iN < nodes.size(); ++iN )
1584           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1585
1586         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1587           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1588
1589         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1590                                   uv[0], uv[1], uv[2], uv[3],
1591                                   uv[4], uv[5], uv[6], uv[7] );
1592
1593         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1594         xyz[ 8 ] = p.XYZ();
1595       }
1596
1597       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1598                                  uv[8].X(), uv[8].Y() );
1599       myLastCreatedNodes.Append( nCentral );
1600     }
1601
1602     // create 4 triangles
1603
1604     helper.SetIsQuadratic  ( nodes.size() > 4 );
1605     helper.SetIsBiQuadratic( nodes.size() == 9 );
1606     if ( helper.GetIsQuadratic() )
1607       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1608
1609     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1610
1611     for ( int i = 0; i < 4; ++i )
1612     {
1613       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1614                                                nodes[(i+1)%4],
1615                                                nCentral );
1616       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1617       myLastCreatedElems.Append( tria );
1618     }
1619   }
1620 }
1621
1622 //=======================================================================
1623 //function : BestSplit
1624 //purpose  : Find better diagonal for cutting.
1625 //=======================================================================
1626
1627 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1628                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1629 {
1630   myLastCreatedElems.Clear();
1631   myLastCreatedNodes.Clear();
1632
1633   if (!theCrit.get())
1634     return -1;
1635
1636   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1637     return -1;
1638
1639   if( theQuad->NbNodes()==4 ||
1640       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1641
1642     // retrieve element nodes
1643     const SMDS_MeshNode* aNodes [4];
1644     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1645     int i = 0;
1646     //while (itN->more())
1647     while (i<4) {
1648       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1649     }
1650     // compare two sets of possible triangles
1651     double aBadRate1, aBadRate2; // to what extent a set is bad
1652     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1653     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1654     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1655
1656     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1657     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1658     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1659     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1660     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1661     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1662       return 1; // diagonal 1-3
1663
1664     return 2; // diagonal 2-4
1665   }
1666   return -1;
1667 }
1668
1669 namespace
1670 {
1671   // Methods of splitting volumes into tetra
1672
1673   const int theHexTo5_1[5*4+1] =
1674     {
1675       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1676     };
1677   const int theHexTo5_2[5*4+1] =
1678     {
1679       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1680     };
1681   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1682
1683   const int theHexTo6_1[6*4+1] =
1684     {
1685       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
1686     };
1687   const int theHexTo6_2[6*4+1] =
1688     {
1689       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
1690     };
1691   const int theHexTo6_3[6*4+1] =
1692     {
1693       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
1694     };
1695   const int theHexTo6_4[6*4+1] =
1696     {
1697       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
1698     };
1699   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1700
1701   const int thePyraTo2_1[2*4+1] =
1702     {
1703       0, 1, 2, 4,    0, 2, 3, 4,   -1
1704     };
1705   const int thePyraTo2_2[2*4+1] =
1706     {
1707       1, 2, 3, 4,    1, 3, 0, 4,   -1
1708     };
1709   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1710
1711   const int thePentaTo3_1[3*4+1] =
1712     {
1713       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1714     };
1715   const int thePentaTo3_2[3*4+1] =
1716     {
1717       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1718     };
1719   const int thePentaTo3_3[3*4+1] =
1720     {
1721       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1722     };
1723   const int thePentaTo3_4[3*4+1] =
1724     {
1725       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1726     };
1727   const int thePentaTo3_5[3*4+1] =
1728     {
1729       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1730     };
1731   const int thePentaTo3_6[3*4+1] =
1732     {
1733       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1734     };
1735   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1736                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1737
1738   // Methods of splitting hexahedron into prisms
1739
1740   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1741     {
1742       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
1743     };
1744   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1745     {
1746       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
1747     };
1748   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1749     {
1750       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
1751     };
1752
1753   const int theHexTo2Prisms_BT_1[6*2+1] =
1754     {
1755       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1756     };
1757   const int theHexTo2Prisms_BT_2[6*2+1] =
1758     {
1759       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1760     };
1761   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1762
1763   const int theHexTo2Prisms_LR_1[6*2+1] =
1764     {
1765       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1766     };
1767   const int theHexTo2Prisms_LR_2[6*2+1] =
1768     {
1769       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1770     };
1771   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1772
1773   const int theHexTo2Prisms_FB_1[6*2+1] =
1774     {
1775       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1776     };
1777   const int theHexTo2Prisms_FB_2[6*2+1] =
1778     {
1779       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1780     };
1781   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1782
1783
1784   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1785   {
1786     int _n1, _n2, _n3;
1787     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1788     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1789     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1790                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1791   };
1792   struct TSplitMethod
1793   {
1794     int        _nbSplits;
1795     int        _nbCorners;
1796     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1797     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1798     bool       _ownConn;      //!< to delete _connectivity in destructor
1799     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1800
1801     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1802       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1803     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1804     bool hasFacet( const TTriangleFacet& facet ) const
1805     {
1806       if ( _nbCorners == 4 )
1807       {
1808         const int* tetConn = _connectivity;
1809         for ( ; tetConn[0] >= 0; tetConn += 4 )
1810           if (( facet.contains( tetConn[0] ) +
1811                 facet.contains( tetConn[1] ) +
1812                 facet.contains( tetConn[2] ) +
1813                 facet.contains( tetConn[3] )) == 3 )
1814             return true;
1815       }
1816       else // prism, _nbCorners == 6
1817       {
1818         const int* prismConn = _connectivity;
1819         for ( ; prismConn[0] >= 0; prismConn += 6 )
1820         {
1821           if (( facet.contains( prismConn[0] ) &&
1822                 facet.contains( prismConn[1] ) &&
1823                 facet.contains( prismConn[2] ))
1824               ||
1825               ( facet.contains( prismConn[3] ) &&
1826                 facet.contains( prismConn[4] ) &&
1827                 facet.contains( prismConn[5] )))
1828             return true;
1829         }
1830       }
1831       return false;
1832     }
1833   };
1834
1835   //=======================================================================
1836   /*!
1837    * \brief return TSplitMethod for the given element to split into tetrahedra
1838    */
1839   //=======================================================================
1840
1841   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1842   {
1843     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1844
1845     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1846     // an edge and a face barycenter; tertaherdons are based on triangles and
1847     // a volume barycenter
1848     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1849
1850     // Find out how adjacent volumes are split
1851
1852     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1853     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1854     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1855     {
1856       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1857       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1858       if ( nbNodes < 4 ) continue;
1859
1860       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1861       const int* nInd = vol.GetFaceNodesIndices( iF );
1862       if ( nbNodes == 4 )
1863       {
1864         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1865         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1866         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1867         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1868       }
1869       else
1870       {
1871         int iCom = 0; // common node of triangle faces to split into
1872         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1873         {
1874           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1875                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1876                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1877           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1878                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1879                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1880           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1881           {
1882             triaSplits.push_back( t012 );
1883             triaSplits.push_back( t023 );
1884             break;
1885           }
1886         }
1887       }
1888       if ( !triaSplits.empty() )
1889         hasAdjacentSplits = true;
1890     }
1891
1892     // Among variants of split method select one compliant with adjacent volumes
1893
1894     TSplitMethod method;
1895     if ( !vol.Element()->IsPoly() && !is24TetMode )
1896     {
1897       int nbVariants = 2, nbTet = 0;
1898       const int** connVariants = 0;
1899       switch ( vol.Element()->GetEntityType() )
1900       {
1901       case SMDSEntity_Hexa:
1902       case SMDSEntity_Quad_Hexa:
1903       case SMDSEntity_TriQuad_Hexa:
1904         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1905           connVariants = theHexTo5, nbTet = 5;
1906         else
1907           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1908         break;
1909       case SMDSEntity_Pyramid:
1910       case SMDSEntity_Quad_Pyramid:
1911         connVariants = thePyraTo2;  nbTet = 2;
1912         break;
1913       case SMDSEntity_Penta:
1914       case SMDSEntity_Quad_Penta:
1915         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1916         break;
1917       default:
1918         nbVariants = 0;
1919       }
1920       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1921       {
1922         // check method compliancy with adjacent tetras,
1923         // all found splits must be among facets of tetras described by this method
1924         method = TSplitMethod( nbTet, connVariants[variant] );
1925         if ( hasAdjacentSplits && method._nbSplits > 0 )
1926         {
1927           bool facetCreated = true;
1928           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1929           {
1930             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1931             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1932               facetCreated = method.hasFacet( *facet );
1933           }
1934           if ( !facetCreated )
1935             method = TSplitMethod(0); // incompatible method
1936         }
1937       }
1938     }
1939     if ( method._nbSplits < 1 )
1940     {
1941       // No standard method is applicable, use a generic solution:
1942       // each facet of a volume is split into triangles and
1943       // each of triangles and a volume barycenter form a tetrahedron.
1944
1945       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1946
1947       int* connectivity = new int[ maxTetConnSize + 1 ];
1948       method._connectivity = connectivity;
1949       method._ownConn = true;
1950       method._baryNode = !isHex27; // to create central node or not
1951
1952       int connSize = 0;
1953       int baryCenInd = vol.NbNodes() - int( isHex27 );
1954       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1955       {
1956         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1957         const int*   nInd = vol.GetFaceNodesIndices( iF );
1958         // find common node of triangle facets of tetra to create
1959         int iCommon = 0; // index in linear numeration
1960         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1961         if ( !triaSplits.empty() )
1962         {
1963           // by found facets
1964           const TTriangleFacet* facet = &triaSplits.front();
1965           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1966             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1967                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1968               break;
1969         }
1970         else if ( nbNodes > 3 && !is24TetMode )
1971         {
1972           // find the best method of splitting into triangles by aspect ratio
1973           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1974           map< double, int > badness2iCommon;
1975           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1976           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1977           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1978           {
1979             double badness = 0;
1980             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1981             {
1982               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1983                                       nodes[ iQ*((iLast-1)%nbNodes)],
1984                                       nodes[ iQ*((iLast  )%nbNodes)]);
1985               badness += getBadRate( &tria, aspectRatio );
1986             }
1987             badness2iCommon.insert( make_pair( badness, iCommon ));
1988           }
1989           // use iCommon with lowest badness
1990           iCommon = badness2iCommon.begin()->second;
1991         }
1992         if ( iCommon >= nbNodes )
1993           iCommon = 0; // something wrong
1994
1995         // fill connectivity of tetrahedra based on a current face
1996         int nbTet = nbNodes - 2;
1997         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1998         {
1999           int faceBaryCenInd;
2000           if ( isHex27 )
2001           {
2002             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2003             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2004           }
2005           else
2006           {
2007             method._faceBaryNode[ iF ] = 0;
2008             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2009           }
2010           nbTet = nbNodes;
2011           for ( int i = 0; i < nbTet; ++i )
2012           {
2013             int i1 = i, i2 = (i+1) % nbNodes;
2014             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2015             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2016             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2017             connectivity[ connSize++ ] = faceBaryCenInd;
2018             connectivity[ connSize++ ] = baryCenInd;
2019           }
2020         }
2021         else
2022         {
2023           for ( int i = 0; i < nbTet; ++i )
2024           {
2025             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2026             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2027             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2028             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2029             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2030             connectivity[ connSize++ ] = baryCenInd;
2031           }
2032         }
2033         method._nbSplits += nbTet;
2034
2035       } // loop on volume faces
2036
2037       connectivity[ connSize++ ] = -1;
2038
2039     } // end of generic solution
2040
2041     return method;
2042   }
2043   //=======================================================================
2044   /*!
2045    * \brief return TSplitMethod to split haxhedron into prisms
2046    */
2047   //=======================================================================
2048
2049   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2050                                     const int        methodFlags,
2051                                     const int        facetToSplit)
2052   {
2053     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2054     // B, T, L, B, R, F
2055     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2056
2057     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2058     {
2059       static TSplitMethod to4methods[4]; // order BT, LR, FB
2060       if ( to4methods[iF]._nbSplits == 0 )
2061       {
2062         switch ( iF ) {
2063         case 0:
2064           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2065           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2066           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2067           break;
2068         case 1:
2069           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2070           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2071           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2072           break;
2073         case 2:
2074           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2075           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2076           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2077           break;
2078         default: return to4methods[3];
2079         }
2080         to4methods[iF]._nbSplits  = 4;
2081         to4methods[iF]._nbCorners = 6;
2082       }
2083       return to4methods[iF];
2084     }
2085     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2086
2087     TSplitMethod method;
2088
2089     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2090
2091     const int nbVariants = 2, nbSplits = 2;
2092     const int** connVariants = 0;
2093     switch ( iF ) {
2094     case 0: connVariants = theHexTo2Prisms_BT; break;
2095     case 1: connVariants = theHexTo2Prisms_LR; break;
2096     case 2: connVariants = theHexTo2Prisms_FB; break;
2097     default: return method;
2098     }
2099
2100     // look for prisms adjacent via facetToSplit and an opposite one
2101     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2102     {
2103       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2104       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2105       if ( nbNodes != 4 ) return method;
2106
2107       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2108       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2109       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2110       TTriangleFacet* t;
2111       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2112         t = &t012;
2113       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2114         t = &t123;
2115       else
2116         continue;
2117
2118       // there are adjacent prism
2119       for ( int variant = 0; variant < nbVariants; ++variant )
2120       {
2121         // check method compliancy with adjacent prisms,
2122         // the found prism facets must be among facets of prisms described by current method
2123         method._nbSplits     = nbSplits;
2124         method._nbCorners    = 6;
2125         method._connectivity = connVariants[ variant ];
2126         if ( method.hasFacet( *t ))
2127           return method;
2128       }
2129     }
2130
2131     // No adjacent prisms. Select a variant with a best aspect ratio.
2132
2133     double badness[2] = { 0, 0 };
2134     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2135     const SMDS_MeshNode** nodes = vol.GetNodes();
2136     for ( int variant = 0; variant < nbVariants; ++variant )
2137       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2138       {
2139         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2140         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2141
2142         method._connectivity = connVariants[ variant ];
2143         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2144         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2145         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2146
2147         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2148                                 nodes[ t->_n2 ],
2149                                 nodes[ t->_n3 ] );
2150         badness[ variant ] += getBadRate( &tria, aspectRatio );
2151       }
2152     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2153
2154     method._nbSplits     = nbSplits;
2155     method._nbCorners    = 6;
2156     method._connectivity = connVariants[ iBetter ];
2157
2158     return method;
2159   }
2160
2161   //================================================================================
2162   /*!
2163    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2164    */
2165   //================================================================================
2166
2167   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2168                                        const SMDSAbs_GeometryType geom ) const
2169   {
2170     // find the tetrahedron including the three nodes of facet
2171     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2172     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2173     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2174     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2175     while ( volIt1->more() )
2176     {
2177       const SMDS_MeshElement* v = volIt1->next();
2178       if ( v->GetGeomType() != geom )
2179         continue;
2180       const int lastCornerInd = v->NbCornerNodes() - 1;
2181       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2182         continue; // medium node not allowed
2183       const int ind2 = v->GetNodeIndex( n2 );
2184       if ( ind2 < 0 || lastCornerInd < ind2 )
2185         continue;
2186       const int ind3 = v->GetNodeIndex( n3 );
2187       if ( ind3 < 0 || lastCornerInd < ind3 )
2188         continue;
2189       return true;
2190     }
2191     return false;
2192   }
2193
2194   //=======================================================================
2195   /*!
2196    * \brief A key of a face of volume
2197    */
2198   //=======================================================================
2199
2200   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2201   {
2202     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2203     {
2204       TIDSortedNodeSet sortedNodes;
2205       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2206       int nbNodes = vol.NbFaceNodes( iF );
2207       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2208       for ( int i = 0; i < nbNodes; i += iQ )
2209         sortedNodes.insert( fNodes[i] );
2210       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2211       first.first   = (*(n++))->GetID();
2212       first.second  = (*(n++))->GetID();
2213       second.first  = (*(n++))->GetID();
2214       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2215     }
2216   };
2217 } // namespace
2218
2219 //=======================================================================
2220 //function : SplitVolumes
2221 //purpose  : Split volume elements into tetrahedra or prisms.
2222 //           If facet ID < 0, element is split into tetrahedra,
2223 //           else a hexahedron is split into prisms so that the given facet is
2224 //           split into triangles
2225 //=======================================================================
2226
2227 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2228                                      const int            theMethodFlags)
2229 {
2230   SMDS_VolumeTool    volTool;
2231   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2232   fHelper.ToFixNodeParameters( true );
2233
2234   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2235   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2236
2237   SMESH_SequenceOfElemPtr newNodes, newElems;
2238
2239   // map face of volume to it's baricenrtic node
2240   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2241   double bc[3];
2242   vector<const SMDS_MeshElement* > splitVols;
2243
2244   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2245   for ( ; elem2facet != theElems.end(); ++elem2facet )
2246   {
2247     const SMDS_MeshElement* elem = elem2facet->first;
2248     const int       facetToSplit = elem2facet->second;
2249     if ( elem->GetType() != SMDSAbs_Volume )
2250       continue;
2251     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2252     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2253       continue;
2254
2255     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2256
2257     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2258                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2259                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2260     if ( splitMethod._nbSplits < 1 ) continue;
2261
2262     // find submesh to add new tetras to
2263     if ( !subMesh || !subMesh->Contains( elem ))
2264     {
2265       int shapeID = FindShape( elem );
2266       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2267       subMesh = GetMeshDS()->MeshElements( shapeID );
2268     }
2269     int iQ;
2270     if ( elem->IsQuadratic() )
2271     {
2272       iQ = 2;
2273       // add quadratic links to the helper
2274       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2275       {
2276         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2277         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2278         for ( int iN = 0; iN < nbN; iN += iQ )
2279           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2280       }
2281       helper.SetIsQuadratic( true );
2282     }
2283     else
2284     {
2285       iQ = 1;
2286       helper.SetIsQuadratic( false );
2287     }
2288     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2289                                         volTool.GetNodes() + elem->NbNodes() );
2290     helper.SetElementsOnShape( true );
2291     if ( splitMethod._baryNode )
2292     {
2293       // make a node at barycenter
2294       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2295       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2296       nodes.push_back( gcNode );
2297       newNodes.Append( gcNode );
2298     }
2299     if ( !splitMethod._faceBaryNode.empty() )
2300     {
2301       // make or find baricentric nodes of faces
2302       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2303       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2304       {
2305         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2306           volFace2BaryNode.insert
2307           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2308         if ( !f_n->second )
2309         {
2310           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2311           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2312         }
2313         nodes.push_back( iF_n->second = f_n->second );
2314       }
2315     }
2316
2317     // make new volumes
2318     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2319     const int* volConn = splitMethod._connectivity;
2320     if ( splitMethod._nbCorners == 4 ) // tetra
2321       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2322         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2323                                                             nodes[ volConn[1] ],
2324                                                             nodes[ volConn[2] ],
2325                                                             nodes[ volConn[3] ]));
2326     else // prisms
2327       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2328         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2329                                                             nodes[ volConn[1] ],
2330                                                             nodes[ volConn[2] ],
2331                                                             nodes[ volConn[3] ],
2332                                                             nodes[ volConn[4] ],
2333                                                             nodes[ volConn[5] ]));
2334
2335     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2336
2337     // Split faces on sides of the split volume
2338
2339     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2340     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2341     {
2342       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2343       if ( nbNodes < 4 ) continue;
2344
2345       // find an existing face
2346       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2347                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2348       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2349                                                                        /*noMedium=*/false))
2350       {
2351         // make triangles
2352         helper.SetElementsOnShape( false );
2353         vector< const SMDS_MeshElement* > triangles;
2354
2355         // find submesh to add new triangles in
2356         if ( !fSubMesh || !fSubMesh->Contains( face ))
2357         {
2358           int shapeID = FindShape( face );
2359           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2360         }
2361         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2362         if ( iF_n != splitMethod._faceBaryNode.end() )
2363         {
2364           const SMDS_MeshNode *baryNode = iF_n->second;
2365           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2366           {
2367             const SMDS_MeshNode* n1 = fNodes[iN];
2368             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2369             const SMDS_MeshNode *n3 = baryNode;
2370             if ( !volTool.IsFaceExternal( iF ))
2371               swap( n2, n3 );
2372             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2373           }
2374           if ( fSubMesh ) // update position of the bary node on geometry
2375           {
2376             if ( subMesh )
2377               subMesh->RemoveNode( baryNode, false );
2378             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2379             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2380             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2381             {
2382               fHelper.SetSubShape( s );
2383               gp_XY uv( 1e100, 1e100 );
2384               double distXYZ[4];
2385               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2386                                         uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2387                    uv.X() < 1e100 )
2388               {
2389                 // node is too far from the surface
2390                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2391                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2392                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2393               }
2394             }
2395           }
2396         }
2397         else
2398         {
2399           // among possible triangles create ones discribed by split method
2400           const int* nInd = volTool.GetFaceNodesIndices( iF );
2401           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2402           int iCom = 0; // common node of triangle faces to split into
2403           list< TTriangleFacet > facets;
2404           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2405           {
2406             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2407                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2408                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2409             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2410                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2411                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2412             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2413             {
2414               facets.push_back( t012 );
2415               facets.push_back( t023 );
2416               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2417                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2418                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2419                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2420               break;
2421             }
2422           }
2423           list< TTriangleFacet >::iterator facet = facets.begin();
2424           if ( facet == facets.end() )
2425             break;
2426           for ( ; facet != facets.end(); ++facet )
2427           {
2428             if ( !volTool.IsFaceExternal( iF ))
2429               swap( facet->_n2, facet->_n3 );
2430             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2431                                                  volNodes[ facet->_n2 ],
2432                                                  volNodes[ facet->_n3 ]));
2433           }
2434         }
2435         for ( size_t i = 0; i < triangles.size(); ++i )
2436         {
2437           if ( !triangles[ i ]) continue;
2438           if ( fSubMesh )
2439             fSubMesh->AddElement( triangles[ i ]);
2440           newElems.Append( triangles[ i ]);
2441         }
2442         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2443         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2444
2445       } // while a face based on facet nodes exists
2446     } // loop on volume faces to split them into triangles
2447
2448     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2449
2450     if ( geomType == SMDSEntity_TriQuad_Hexa )
2451     {
2452       // remove medium nodes that could become free
2453       for ( int i = 20; i < volTool.NbNodes(); ++i )
2454         if ( volNodes[i]->NbInverseElements() == 0 )
2455           GetMeshDS()->RemoveNode( volNodes[i] );
2456     }
2457   } // loop on volumes to split
2458
2459   myLastCreatedNodes = newNodes;
2460   myLastCreatedElems = newElems;
2461 }
2462
2463 //=======================================================================
2464 //function : GetHexaFacetsToSplit
2465 //purpose  : For hexahedra that will be split into prisms, finds facets to
2466 //           split into triangles. Only hexahedra adjacent to the one closest
2467 //           to theFacetNormal.Location() are returned.
2468 //param [in,out] theHexas - the hexahedra
2469 //param [in]     theFacetNormal - facet normal
2470 //param [out]    theFacets - the hexahedra and found facet IDs
2471 //=======================================================================
2472
2473 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2474                                              const gp_Ax1&     theFacetNormal,
2475                                              TFacetOfElem &    theFacets)
2476 {
2477   #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2478
2479   // Find a hexa closest to the location of theFacetNormal
2480
2481   const SMDS_MeshElement* startHex;
2482   {
2483     // get SMDS_ElemIteratorPtr on theHexas
2484     typedef const SMDS_MeshElement*                                      TValue;
2485     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2486     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2487     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2488     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2489     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2490       ( new TElemSetIter( theHexas.begin(),
2491                           theHexas.end(),
2492                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2493
2494     SMESH_ElementSearcher* searcher =
2495       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2496
2497     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2498
2499     delete searcher;
2500
2501     if ( !startHex )
2502       throw SALOME_Exception( THIS_METHOD "startHex not found");
2503   }
2504
2505   // Select a facet of startHex by theFacetNormal
2506
2507   SMDS_VolumeTool vTool( startHex );
2508   double norm[3], dot, maxDot = 0;
2509   int facetID = -1;
2510   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2511     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2512     {
2513       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2514       if ( dot > maxDot )
2515       {
2516         facetID = iF;
2517         maxDot = dot;
2518       }
2519     }
2520   if ( facetID < 0 )
2521     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2522
2523   // Fill theFacets starting from facetID of startHex
2524
2525   // facets used for seach of volumes adjacent to already treated ones
2526   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2527   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2528   TFacetMap facetsToCheck;
2529
2530   set<const SMDS_MeshNode*> facetNodes;
2531   const SMDS_MeshElement*   curHex;
2532
2533   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2534
2535   while ( startHex )
2536   {
2537     // move in two directions from startHex via facetID
2538     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2539     {
2540       curHex       = startHex;
2541       int curFacet = facetID;
2542       if ( is2nd ) // do not treat startHex twice
2543       {
2544         vTool.Set( curHex );
2545         if ( vTool.IsFreeFace( curFacet, &curHex ))
2546         {
2547           curHex = 0;
2548         }
2549         else
2550         {
2551           vTool.GetFaceNodes( curFacet, facetNodes );
2552           vTool.Set( curHex );
2553           curFacet = vTool.GetFaceIndex( facetNodes );
2554         }
2555       }
2556       while ( curHex )
2557       {
2558         // store a facet to split
2559         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2560         {
2561           theFacets.insert( make_pair( curHex, -1 ));
2562           break;
2563         }
2564         if ( !allHex && !theHexas.count( curHex ))
2565           break;
2566
2567         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2568           theFacets.insert( make_pair( curHex, curFacet ));
2569         if ( !facetIt2isNew.second )
2570           break;
2571
2572         // remember not-to-split facets in facetsToCheck
2573         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2574         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2575         {
2576           if ( iF == curFacet && iF == oppFacet )
2577             continue;
2578           TVolumeFaceKey facetKey ( vTool, iF );
2579           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2580           pair< TFacetMap::iterator, bool > it2isnew =
2581             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2582           if ( !it2isnew.second )
2583             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2584         }
2585         // pass to a volume adjacent via oppFacet
2586         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2587         {
2588           curHex = 0;
2589         }
2590         else
2591         {
2592           // get a new curFacet
2593           vTool.GetFaceNodes( oppFacet, facetNodes );
2594           vTool.Set( curHex );
2595           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2596         }
2597       }
2598     } // move in two directions from startHex via facetID
2599
2600     // Find a new startHex by facetsToCheck
2601
2602     startHex = 0;
2603     facetID  = -1;
2604     TFacetMap::iterator fIt = facetsToCheck.begin();
2605     while ( !startHex && fIt != facetsToCheck.end() )
2606     {
2607       const TElemFacets&  elemFacets = fIt->second;
2608       const SMDS_MeshElement*    hex = elemFacets.first->first;
2609       int                 splitFacet = elemFacets.first->second;
2610       int               lateralFacet = elemFacets.second;
2611       facetsToCheck.erase( fIt );
2612       fIt = facetsToCheck.begin();
2613
2614       vTool.Set( hex );
2615       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2616            curHex->GetGeomType() != SMDSGeom_HEXA )
2617         continue;
2618       if ( !allHex && !theHexas.count( curHex ))
2619         continue;
2620
2621       startHex = curHex;
2622
2623       // find a facet of startHex to split
2624
2625       set<const SMDS_MeshNode*> lateralNodes;
2626       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2627       vTool.GetFaceNodes( splitFacet,   facetNodes );
2628       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2629       vTool.Set( startHex );
2630       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2631
2632       // look for a facet of startHex having common nodes with facetNodes
2633       // but not lateralFacet
2634       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2635       {
2636         if ( iF == lateralFacet )
2637           continue;
2638         int nbCommonNodes = 0;
2639         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2640         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2641           nbCommonNodes += facetNodes.count( nn[ iN ]);
2642
2643         if ( nbCommonNodes >= 2 )
2644         {
2645           facetID = iF;
2646           break;
2647         }
2648       }
2649       if ( facetID < 0 )
2650         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2651     }
2652   } //   while ( startHex )
2653
2654   return;
2655 }
2656
2657 namespace
2658 {
2659   //================================================================================
2660   /*!
2661    * \brief Selects nodes of several elements according to a given interlace
2662    *  \param [in] srcNodes - nodes to select from
2663    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2664    *  \param [in] interlace - indices of nodes for all elements
2665    *  \param [in] nbElems - nb of elements
2666    *  \param [in] nbNodes - nb of nodes in each element
2667    *  \param [in] mesh - the mesh
2668    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2669    *  \param [in] type - type of elements to look for
2670    */
2671   //================================================================================
2672
2673   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2674                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2675                     const int*                            interlace,
2676                     const int                             nbElems,
2677                     const int                             nbNodes,
2678                     SMESHDS_Mesh*                         mesh = 0,
2679                     list< const SMDS_MeshElement* >*      elemQueue=0,
2680                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2681   {
2682     for ( int iE = 0; iE < nbElems; ++iE )
2683     {
2684       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2685       const int*                         select = & interlace[iE*nbNodes];
2686       elemNodes.resize( nbNodes );
2687       for ( int iN = 0; iN < nbNodes; ++iN )
2688         elemNodes[iN] = srcNodes[ select[ iN ]];
2689     }
2690     const SMDS_MeshElement* e;
2691     if ( elemQueue )
2692       for ( int iE = 0; iE < nbElems; ++iE )
2693         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2694           elemQueue->push_back( e );
2695   }
2696 }
2697
2698 //=======================================================================
2699 /*
2700  * Split bi-quadratic elements into linear ones without creation of additional nodes
2701  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2702  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2703  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2704  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2705  *   will be split in order to keep the mesh conformal.
2706  *  \param elems - elements to split
2707  */
2708 //=======================================================================
2709
2710 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2711 {
2712   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2713   vector<const SMDS_MeshElement* > splitElems;
2714   list< const SMDS_MeshElement* > elemQueue;
2715   list< const SMDS_MeshElement* >::iterator elemIt;
2716
2717   SMESHDS_Mesh * mesh = GetMeshDS();
2718   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2719   int nbElems, nbNodes;
2720
2721   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2722   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2723   {
2724     elemQueue.clear();
2725     elemQueue.push_back( *elemSetIt );
2726     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2727     {
2728       const SMDS_MeshElement* elem = *elemIt;
2729       switch( elem->GetEntityType() )
2730       {
2731       case SMDSEntity_TriQuad_Hexa: // HEX27
2732       {
2733         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2734         nbElems  = nbNodes = 8;
2735         elemType = & hexaType;
2736
2737         // get nodes for new elements
2738         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2739                                  { 1,9,20,8,    17,22,26,21 },
2740                                  { 2,10,20,9,   18,23,26,22 },
2741                                  { 3,11,20,10,  19,24,26,23 },
2742                                  { 16,21,26,24, 4,12,25,15  },
2743                                  { 17,22,26,21, 5,13,25,12  },
2744                                  { 18,23,26,22, 6,14,25,13  },
2745                                  { 19,24,26,23, 7,15,25,14  }};
2746         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2747
2748         // add boundary faces to elemQueue
2749         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2750                                  { 4,5,6,7, 12,13,14,15, 25 },
2751                                  { 0,1,5,4, 8,17,12,16,  21 },
2752                                  { 1,2,6,5, 9,18,13,17,  22 },
2753                                  { 2,3,7,6, 10,19,14,18, 23 },
2754                                  { 3,0,4,7, 11,16,15,19, 24 }};
2755         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2756
2757         // add boundary segments to elemQueue
2758         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2759                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2760                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2761         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2762         break;
2763       }
2764       case SMDSEntity_BiQuad_Triangle: // TRIA7
2765       {
2766         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2767         nbElems = 3;
2768         nbNodes = 4;
2769         elemType = & quadType;
2770
2771         // get nodes for new elements
2772         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2773         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2774
2775         // add boundary segments to elemQueue
2776         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2777         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2778         break;
2779       }
2780       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2781       {
2782         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2783         nbElems = 4;
2784         nbNodes = 4;
2785         elemType = & quadType;
2786
2787         // get nodes for new elements
2788         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2789         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2790
2791         // add boundary segments to elemQueue
2792         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2793         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2794         break;
2795       }
2796       case SMDSEntity_Quad_Edge:
2797       {
2798         if ( elemIt == elemQueue.begin() )
2799           continue; // an elem is in theElems
2800         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2801         nbElems = 2;
2802         nbNodes = 2;
2803         elemType = & segType;
2804
2805         // get nodes for new elements
2806         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2807         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2808         break;
2809       }
2810       default: continue;
2811       } // switch( elem->GetEntityType() )
2812
2813       // Create new elements
2814
2815       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2816
2817       splitElems.clear();
2818
2819       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2820       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2821       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2822       //elemType->SetID( -1 );
2823
2824       for ( int iE = 0; iE < nbElems; ++iE )
2825         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2826
2827
2828       ReplaceElemInGroups( elem, splitElems, mesh );
2829
2830       if ( subMesh )
2831         for ( size_t i = 0; i < splitElems.size(); ++i )
2832           subMesh->AddElement( splitElems[i] );
2833     }
2834   }
2835 }
2836
2837 //=======================================================================
2838 //function : AddToSameGroups
2839 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2840 //=======================================================================
2841
2842 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2843                                         const SMDS_MeshElement* elemInGroups,
2844                                         SMESHDS_Mesh *          aMesh)
2845 {
2846   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2847   if (!groups.empty()) {
2848     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2849     for ( ; grIt != groups.end(); grIt++ ) {
2850       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2851       if ( group && group->Contains( elemInGroups ))
2852         group->SMDSGroup().Add( elemToAdd );
2853     }
2854   }
2855 }
2856
2857
2858 //=======================================================================
2859 //function : RemoveElemFromGroups
2860 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2861 //=======================================================================
2862 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2863                                              SMESHDS_Mesh *          aMesh)
2864 {
2865   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2866   if (!groups.empty())
2867   {
2868     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2869     for (; GrIt != groups.end(); GrIt++)
2870     {
2871       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2872       if (!grp || grp->IsEmpty()) continue;
2873       grp->SMDSGroup().Remove(removeelem);
2874     }
2875   }
2876 }
2877
2878 //================================================================================
2879 /*!
2880  * \brief Replace elemToRm by elemToAdd in the all groups
2881  */
2882 //================================================================================
2883
2884 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2885                                             const SMDS_MeshElement* elemToAdd,
2886                                             SMESHDS_Mesh *          aMesh)
2887 {
2888   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2889   if (!groups.empty()) {
2890     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2891     for ( ; grIt != groups.end(); grIt++ ) {
2892       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2893       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2894         group->SMDSGroup().Add( elemToAdd );
2895     }
2896   }
2897 }
2898
2899 //================================================================================
2900 /*!
2901  * \brief Replace elemToRm by elemToAdd in the all groups
2902  */
2903 //================================================================================
2904
2905 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2906                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2907                                             SMESHDS_Mesh *                         aMesh)
2908 {
2909   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2910   if (!groups.empty())
2911   {
2912     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2913     for ( ; grIt != groups.end(); grIt++ ) {
2914       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2915       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2916         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2917           group->SMDSGroup().Add( elemToAdd[ i ] );
2918     }
2919   }
2920 }
2921
2922 //=======================================================================
2923 //function : QuadToTri
2924 //purpose  : Cut quadrangles into triangles.
2925 //           theCrit is used to select a diagonal to cut
2926 //=======================================================================
2927
2928 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2929                                   const bool         the13Diag)
2930 {
2931   myLastCreatedElems.Clear();
2932   myLastCreatedNodes.Clear();
2933
2934   MESSAGE( "::QuadToTri()" );
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   MESSAGE( "::TriToQuad()" );
3157
3158   if ( !theCrit.get() )
3159     return false;
3160
3161   SMESHDS_Mesh * aMesh = GetMeshDS();
3162
3163   // Prepare data for algo: build
3164   // 1. map of elements with their linkIDs
3165   // 2. map of linkIDs with their elements
3166
3167   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3168   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3169   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3170   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3171
3172   TIDSortedElemSet::iterator itElem;
3173   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3174   {
3175     const SMDS_MeshElement* elem = *itElem;
3176     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3177     bool IsTria = ( elem->NbCornerNodes()==3 );
3178     if (!IsTria) continue;
3179
3180     // retrieve element nodes
3181     const SMDS_MeshNode* aNodes [4];
3182     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3183     int i = 0;
3184     while ( i < 3 )
3185       aNodes[ i++ ] = itN->next();
3186     aNodes[ 3 ] = aNodes[ 0 ];
3187
3188     // fill maps
3189     for ( i = 0; i < 3; i++ ) {
3190       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3191       // check if elements sharing a link can be fused
3192       itLE = mapLi_listEl.find( link );
3193       if ( itLE != mapLi_listEl.end() ) {
3194         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3195           continue;
3196         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3197         //if ( FindShape( elem ) != FindShape( elem2 ))
3198         //  continue; // do not fuse triangles laying on different shapes
3199         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3200           continue; // avoid making badly shaped quads
3201         (*itLE).second.push_back( elem );
3202       }
3203       else {
3204         mapLi_listEl[ link ].push_back( elem );
3205       }
3206       mapEl_setLi [ elem ].insert( link );
3207     }
3208   }
3209   // Clean the maps from the links shared by a sole element, ie
3210   // links to which only one element is bound in mapLi_listEl
3211
3212   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3213     int nbElems = (*itLE).second.size();
3214     if ( nbElems < 2  ) {
3215       const SMDS_MeshElement* elem = (*itLE).second.front();
3216       SMESH_TLink link = (*itLE).first;
3217       mapEl_setLi[ elem ].erase( link );
3218       if ( mapEl_setLi[ elem ].empty() )
3219         mapEl_setLi.erase( elem );
3220     }
3221   }
3222
3223   // Algo: fuse triangles into quadrangles
3224
3225   while ( ! mapEl_setLi.empty() ) {
3226     // Look for the start element:
3227     // the element having the least nb of shared links
3228     const SMDS_MeshElement* startElem = 0;
3229     int minNbLinks = 4;
3230     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3231       int nbLinks = (*itEL).second.size();
3232       if ( nbLinks < minNbLinks ) {
3233         startElem = (*itEL).first;
3234         minNbLinks = nbLinks;
3235         if ( minNbLinks == 1 )
3236           break;
3237       }
3238     }
3239
3240     // search elements to fuse starting from startElem or links of elements
3241     // fused earlyer - startLinks
3242     list< SMESH_TLink > startLinks;
3243     while ( startElem || !startLinks.empty() ) {
3244       while ( !startElem && !startLinks.empty() ) {
3245         // Get an element to start, by a link
3246         SMESH_TLink linkId = startLinks.front();
3247         startLinks.pop_front();
3248         itLE = mapLi_listEl.find( linkId );
3249         if ( itLE != mapLi_listEl.end() ) {
3250           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3251           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3252           for ( ; itE != listElem.end() ; itE++ )
3253             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3254               startElem = (*itE);
3255           mapLi_listEl.erase( itLE );
3256         }
3257       }
3258
3259       if ( startElem ) {
3260         // Get candidates to be fused
3261         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3262         const SMESH_TLink *link12, *link13;
3263         startElem = 0;
3264         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3265         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3266         ASSERT( !setLi.empty() );
3267         set< SMESH_TLink >::iterator itLi;
3268         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3269         {
3270           const SMESH_TLink & link = (*itLi);
3271           itLE = mapLi_listEl.find( link );
3272           if ( itLE == mapLi_listEl.end() )
3273             continue;
3274
3275           const SMDS_MeshElement* elem = (*itLE).second.front();
3276           if ( elem == tr1 )
3277             elem = (*itLE).second.back();
3278           mapLi_listEl.erase( itLE );
3279           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3280             continue;
3281           if ( tr2 ) {
3282             tr3 = elem;
3283             link13 = &link;
3284           }
3285           else {
3286             tr2 = elem;
3287             link12 = &link;
3288           }
3289
3290           // add other links of elem to list of links to re-start from
3291           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3292           set< SMESH_TLink >::iterator it;
3293           for ( it = links.begin(); it != links.end(); it++ ) {
3294             const SMESH_TLink& link2 = (*it);
3295             if ( link2 != link )
3296               startLinks.push_back( link2 );
3297           }
3298         }
3299
3300         // Get nodes of possible quadrangles
3301         const SMDS_MeshNode *n12 [4], *n13 [4];
3302         bool Ok12 = false, Ok13 = false;
3303         const SMDS_MeshNode *linkNode1, *linkNode2;
3304         if(tr2) {
3305           linkNode1 = link12->first;
3306           linkNode2 = link12->second;
3307           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3308             Ok12 = true;
3309         }
3310         if(tr3) {
3311           linkNode1 = link13->first;
3312           linkNode2 = link13->second;
3313           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3314             Ok13 = true;
3315         }
3316
3317         // Choose a pair to fuse
3318         if ( Ok12 && Ok13 ) {
3319           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3320           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3321           double aBadRate12 = getBadRate( &quad12, theCrit );
3322           double aBadRate13 = getBadRate( &quad13, theCrit );
3323           if (  aBadRate13 < aBadRate12 )
3324             Ok12 = false;
3325           else
3326             Ok13 = false;
3327         }
3328
3329         // Make quadrangles
3330         // and remove fused elems and remove links from the maps
3331         mapEl_setLi.erase( tr1 );
3332         if ( Ok12 )
3333         {
3334           mapEl_setLi.erase( tr2 );
3335           mapLi_listEl.erase( *link12 );
3336           if ( tr1->NbNodes() == 3 )
3337           {
3338             const SMDS_MeshElement* newElem = 0;
3339             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3340             myLastCreatedElems.Append(newElem);
3341             AddToSameGroups( newElem, tr1, aMesh );
3342             int aShapeId = tr1->getshapeId();
3343             if ( aShapeId )
3344               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3345             aMesh->RemoveElement( tr1 );
3346             aMesh->RemoveElement( tr2 );
3347           }
3348           else {
3349             vector< const SMDS_MeshNode* > N1;
3350             vector< const SMDS_MeshNode* > N2;
3351             getNodesFromTwoTria(tr1,tr2,N1,N2);
3352             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3353             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3354             // i.e. first nodes from both arrays form a new diagonal
3355             const SMDS_MeshNode* aNodes[8];
3356             aNodes[0] = N1[0];
3357             aNodes[1] = N1[1];
3358             aNodes[2] = N2[0];
3359             aNodes[3] = N2[1];
3360             aNodes[4] = N1[3];
3361             aNodes[5] = N2[5];
3362             aNodes[6] = N2[3];
3363             aNodes[7] = N1[5];
3364             const SMDS_MeshElement* newElem = 0;
3365             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3366               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3367                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3368             else
3369               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3370                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3371             myLastCreatedElems.Append(newElem);
3372             AddToSameGroups( newElem, tr1, aMesh );
3373             int aShapeId = tr1->getshapeId();
3374             if ( aShapeId )
3375               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3376             aMesh->RemoveElement( tr1 );
3377             aMesh->RemoveElement( tr2 );
3378             // remove middle node (9)
3379             if ( N1[4]->NbInverseElements() == 0 )
3380               aMesh->RemoveNode( N1[4] );
3381             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3382               aMesh->RemoveNode( N1[6] );
3383             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3384               aMesh->RemoveNode( N2[6] );
3385           }
3386         }
3387         else if ( Ok13 )
3388         {
3389           mapEl_setLi.erase( tr3 );
3390           mapLi_listEl.erase( *link13 );
3391           if ( tr1->NbNodes() == 3 ) {
3392             const SMDS_MeshElement* newElem = 0;
3393             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3394             myLastCreatedElems.Append(newElem);
3395             AddToSameGroups( newElem, tr1, aMesh );
3396             int aShapeId = tr1->getshapeId();
3397             if ( aShapeId )
3398               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3399             aMesh->RemoveElement( tr1 );
3400             aMesh->RemoveElement( tr3 );
3401           }
3402           else {
3403             vector< const SMDS_MeshNode* > N1;
3404             vector< const SMDS_MeshNode* > N2;
3405             getNodesFromTwoTria(tr1,tr3,N1,N2);
3406             // now we receive following N1 and N2 (using numeration as above image)
3407             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3408             // i.e. first nodes from both arrays form a new diagonal
3409             const SMDS_MeshNode* aNodes[8];
3410             aNodes[0] = N1[0];
3411             aNodes[1] = N1[1];
3412             aNodes[2] = N2[0];
3413             aNodes[3] = N2[1];
3414             aNodes[4] = N1[3];
3415             aNodes[5] = N2[5];
3416             aNodes[6] = N2[3];
3417             aNodes[7] = N1[5];
3418             const SMDS_MeshElement* newElem = 0;
3419             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3420               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3421                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3422             else
3423               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3424                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3425             myLastCreatedElems.Append(newElem);
3426             AddToSameGroups( newElem, tr1, aMesh );
3427             int aShapeId = tr1->getshapeId();
3428             if ( aShapeId )
3429               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3430             aMesh->RemoveElement( tr1 );
3431             aMesh->RemoveElement( tr3 );
3432             // remove middle node (9)
3433             if ( N1[4]->NbInverseElements() == 0 )
3434               aMesh->RemoveNode( N1[4] );
3435             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3436               aMesh->RemoveNode( N1[6] );
3437             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3438               aMesh->RemoveNode( N2[6] );
3439           }
3440         }
3441
3442         // Next element to fuse: the rejected one
3443         if ( tr3 )
3444           startElem = Ok12 ? tr3 : tr2;
3445
3446       } // if ( startElem )
3447     } // while ( startElem || !startLinks.empty() )
3448   } // while ( ! mapEl_setLi.empty() )
3449
3450   return true;
3451 }
3452
3453
3454 /*#define DUMPSO(txt) \
3455 //  cout << txt << endl;
3456 //=============================================================================
3457 //
3458 //
3459 //
3460 //=============================================================================
3461 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3462 {
3463 if ( i1 == i2 )
3464 return;
3465 int tmp = idNodes[ i1 ];
3466 idNodes[ i1 ] = idNodes[ i2 ];
3467 idNodes[ i2 ] = tmp;
3468 gp_Pnt Ptmp = P[ i1 ];
3469 P[ i1 ] = P[ i2 ];
3470 P[ i2 ] = Ptmp;
3471 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3472 }
3473
3474 //=======================================================================
3475 //function : SortQuadNodes
3476 //purpose  : Set 4 nodes of a quadrangle face in a good order.
3477 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
3478 //           1 or 2 else 0.
3479 //=======================================================================
3480
3481 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3482 int               idNodes[] )
3483 {
3484   gp_Pnt P[4];
3485   int i;
3486   for ( i = 0; i < 4; i++ ) {
3487     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3488     if ( !n ) return 0;
3489     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3490   }
3491
3492   gp_Vec V1(P[0], P[1]);
3493   gp_Vec V2(P[0], P[2]);
3494   gp_Vec V3(P[0], P[3]);
3495
3496   gp_Vec Cross1 = V1 ^ V2;
3497   gp_Vec Cross2 = V2 ^ V3;
3498
3499   i = 0;
3500   if (Cross1.Dot(Cross2) < 0)
3501   {
3502     Cross1 = V2 ^ V1;
3503     Cross2 = V1 ^ V3;
3504
3505     if (Cross1.Dot(Cross2) < 0)
3506       i = 2;
3507     else
3508       i = 1;
3509     swap ( i, i + 1, idNodes, P );
3510
3511     //     for ( int ii = 0; ii < 4; ii++ ) {
3512     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3513     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3514     //     }
3515   }
3516   return i;
3517 }
3518
3519 //=======================================================================
3520 //function : SortHexaNodes
3521 //purpose  : Set 8 nodes of a hexahedron in a good order.
3522 //           Return success status
3523 //=======================================================================
3524
3525 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3526                                       int               idNodes[] )
3527 {
3528   gp_Pnt P[8];
3529   int i;
3530   DUMPSO( "INPUT: ========================================");
3531   for ( i = 0; i < 8; i++ ) {
3532     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3533     if ( !n ) return false;
3534     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3535     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3536   }
3537   DUMPSO( "========================================");
3538
3539
3540   set<int> faceNodes;  // ids of bottom face nodes, to be found
3541   set<int> checkedId1; // ids of tried 2-nd nodes
3542   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3543   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
3544   int iMin, iLoop1 = 0;
3545
3546   // Loop to try the 2-nd nodes
3547
3548   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3549   {
3550     // Find not checked 2-nd node
3551     for ( i = 1; i < 8; i++ )
3552       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3553         int id1 = idNodes[i];
3554         swap ( 1, i, idNodes, P );
3555         checkedId1.insert ( id1 );
3556         break;
3557       }
3558
3559     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3560     // ie that all but meybe one (id3 which is on the same face) nodes
3561     // lay on the same side from the triangle plane.
3562
3563     bool manyInPlane = false; // more than 4 nodes lay in plane
3564     int iLoop2 = 0;
3565     while ( ++iLoop2 < 6 ) {
3566
3567       // get 1-2-3 plane coeffs
3568       Standard_Real A, B, C, D;
3569       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3570       if ( N.SquareMagnitude() > gp::Resolution() )
3571       {
3572         gp_Pln pln ( P[0], N );
3573         pln.Coefficients( A, B, C, D );
3574
3575         // find the node (iMin) closest to pln
3576         Standard_Real dist[ 8 ], minDist = DBL_MAX;
3577         set<int> idInPln;
3578         for ( i = 3; i < 8; i++ ) {
3579           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3580           if ( fabs( dist[i] ) < minDist ) {
3581             minDist = fabs( dist[i] );
3582             iMin = i;
3583           }
3584           if ( fabs( dist[i] ) <= tol )
3585             idInPln.insert( idNodes[i] );
3586         }
3587
3588         // there should not be more than 4 nodes in bottom plane
3589         if ( idInPln.size() > 1 )
3590         {
3591           DUMPSO( "### idInPln.size() = " << idInPln.size());
3592           // idInPlane does not contain the first 3 nodes
3593           if ( manyInPlane || idInPln.size() == 5)
3594             return false; // all nodes in one plane
3595           manyInPlane = true;
3596
3597           // set the 1-st node to be not in plane
3598           for ( i = 3; i < 8; i++ ) {
3599             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3600               DUMPSO( "### Reset 0-th node");
3601               swap( 0, i, idNodes, P );
3602               break;
3603             }
3604           }
3605
3606           // reset to re-check second nodes
3607           leastDist = DBL_MAX;
3608           faceNodes.clear();
3609           checkedId1.clear();
3610           iLoop1 = 0;
3611           break; // from iLoop2;
3612         }
3613
3614         // check that the other 4 nodes are on the same side
3615         bool sameSide = true;
3616         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3617         for ( i = 3; sameSide && i < 8; i++ ) {
3618           if ( i != iMin )
3619             sameSide = ( isNeg == dist[i] <= 0.);
3620         }
3621
3622         // keep best solution
3623         if ( sameSide && minDist < leastDist ) {
3624           leastDist = minDist;
3625           faceNodes.clear();
3626           faceNodes.insert( idNodes[ 1 ] );
3627           faceNodes.insert( idNodes[ 2 ] );
3628           faceNodes.insert( idNodes[ iMin ] );
3629           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3630                   << " leastDist = " << leastDist);
3631           if ( leastDist <= DBL_MIN )
3632             break;
3633         }
3634       }
3635
3636       // set next 3-d node to check
3637       int iNext = 2 + iLoop2;
3638       if ( iNext < 8 ) {
3639         DUMPSO( "Try 2-nd");
3640         swap ( 2, iNext, idNodes, P );
3641       }
3642     } // while ( iLoop2 < 6 )
3643   } // iLoop1
3644
3645   if ( faceNodes.empty() ) return false;
3646
3647   // Put the faceNodes in proper places
3648   for ( i = 4; i < 8; i++ ) {
3649     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3650       // find a place to put
3651       int iTo = 1;
3652       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3653         iTo++;
3654       DUMPSO( "Set faceNodes");
3655       swap ( iTo, i, idNodes, P );
3656     }
3657   }
3658
3659
3660   // Set nodes of the found bottom face in good order
3661   DUMPSO( " Found bottom face: ");
3662   i = SortQuadNodes( theMesh, idNodes );
3663   if ( i ) {
3664     gp_Pnt Ptmp = P[ i ];
3665     P[ i ] = P[ i+1 ];
3666     P[ i+1 ] = Ptmp;
3667   }
3668   //   else
3669   //     for ( int ii = 0; ii < 4; ii++ ) {
3670   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3671   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3672   //    }
3673
3674   // Gravity center of the top and bottom faces
3675   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3676   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3677
3678   // Get direction from the bottom to the top face
3679   gp_Vec upDir ( aGCb, aGCt );
3680   Standard_Real upDirSize = upDir.Magnitude();
3681   if ( upDirSize <= gp::Resolution() ) return false;
3682   upDir / upDirSize;
3683
3684   // Assure that the bottom face normal points up
3685   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3686   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3687   if ( Nb.Dot( upDir ) < 0 ) {
3688     DUMPSO( "Reverse bottom face");
3689     swap( 1, 3, idNodes, P );
3690   }
3691
3692   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3693   Standard_Real minDist = DBL_MAX;
3694   for ( i = 4; i < 8; i++ ) {
3695     // projection of P[i] to the plane defined by P[0] and upDir
3696     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3697     Standard_Real sqDist = P[0].SquareDistance( Pp );
3698     if ( sqDist < minDist ) {
3699       minDist = sqDist;
3700       iMin = i;
3701     }
3702   }
3703   DUMPSO( "Set 4-th");
3704   swap ( 4, iMin, idNodes, P );
3705
3706   // Set nodes of the top face in good order
3707   DUMPSO( "Sort top face");
3708   i = SortQuadNodes( theMesh, &idNodes[4] );
3709   if ( i ) {
3710     i += 4;
3711     gp_Pnt Ptmp = P[ i ];
3712     P[ i ] = P[ i+1 ];
3713     P[ i+1 ] = Ptmp;
3714   }
3715
3716   // Assure that direction of the top face normal is from the bottom face
3717   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3718   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3719   if ( Nt.Dot( upDir ) < 0 ) {
3720     DUMPSO( "Reverse top face");
3721     swap( 5, 7, idNodes, P );
3722   }
3723
3724   //   DUMPSO( "OUTPUT: ========================================");
3725   //   for ( i = 0; i < 8; i++ ) {
3726   //     float *p = ugrid->GetPoint(idNodes[i]);
3727   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3728   //   }
3729
3730   return true;
3731 }*/
3732
3733 //================================================================================
3734 /*!
3735  * \brief Return nodes linked to the given one
3736  * \param theNode - the node
3737  * \param linkedNodes - the found nodes
3738  * \param type - the type of elements to check
3739  *
3740  * Medium nodes are ignored
3741  */
3742 //================================================================================
3743
3744 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3745                                        TIDSortedElemSet &   linkedNodes,
3746                                        SMDSAbs_ElementType  type )
3747 {
3748   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3749   while ( elemIt->more() )
3750   {
3751     const SMDS_MeshElement* elem = elemIt->next();
3752     if(elem->GetType() == SMDSAbs_0DElement)
3753       continue;
3754
3755     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3756     if ( elem->GetType() == SMDSAbs_Volume )
3757     {
3758       SMDS_VolumeTool vol( elem );
3759       while ( nodeIt->more() ) {
3760         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3761         if ( theNode != n && vol.IsLinked( theNode, n ))
3762           linkedNodes.insert( n );
3763       }
3764     }
3765     else
3766     {
3767       for ( int i = 0; nodeIt->more(); ++i ) {
3768         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3769         if ( n == theNode ) {
3770           int iBefore = i - 1;
3771           int iAfter  = i + 1;
3772           if ( elem->IsQuadratic() ) {
3773             int nb = elem->NbNodes() / 2;
3774             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3775             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3776           }
3777           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3778           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3779         }
3780       }
3781     }
3782   }
3783 }
3784
3785 //=======================================================================
3786 //function : laplacianSmooth
3787 //purpose  : pulls theNode toward the center of surrounding nodes directly
3788 //           connected to that node along an element edge
3789 //=======================================================================
3790
3791 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3792                      const Handle(Geom_Surface)&          theSurface,
3793                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3794 {
3795   // find surrounding nodes
3796
3797   TIDSortedElemSet nodeSet;
3798   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3799
3800   // compute new coodrs
3801
3802   double coord[] = { 0., 0., 0. };
3803   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3804   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3805     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3806     if ( theSurface.IsNull() ) { // smooth in 3D
3807       coord[0] += node->X();
3808       coord[1] += node->Y();
3809       coord[2] += node->Z();
3810     }
3811     else { // smooth in 2D
3812       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3813       gp_XY* uv = theUVMap[ node ];
3814       coord[0] += uv->X();
3815       coord[1] += uv->Y();
3816     }
3817   }
3818   int nbNodes = nodeSet.size();
3819   if ( !nbNodes )
3820     return;
3821   coord[0] /= nbNodes;
3822   coord[1] /= nbNodes;
3823
3824   if ( !theSurface.IsNull() ) {
3825     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3826     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3827     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3828     coord[0] = p3d.X();
3829     coord[1] = p3d.Y();
3830     coord[2] = p3d.Z();
3831   }
3832   else
3833     coord[2] /= nbNodes;
3834
3835   // move node
3836
3837   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3838 }
3839
3840 //=======================================================================
3841 //function : centroidalSmooth
3842 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3843 //           surrounding elements
3844 //=======================================================================
3845
3846 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3847                       const Handle(Geom_Surface)&          theSurface,
3848                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3849 {
3850   gp_XYZ aNewXYZ(0.,0.,0.);
3851   SMESH::Controls::Area anAreaFunc;
3852   double totalArea = 0.;
3853   int nbElems = 0;
3854
3855   // compute new XYZ
3856
3857   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3858   while ( elemIt->more() )
3859   {
3860     const SMDS_MeshElement* elem = elemIt->next();
3861     nbElems++;
3862
3863     gp_XYZ elemCenter(0.,0.,0.);
3864     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3865     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3866     int nn = elem->NbNodes();
3867     if(elem->IsQuadratic()) nn = nn/2;
3868     int i=0;
3869     //while ( itN->more() ) {
3870     while ( i<nn ) {
3871       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3872       i++;
3873       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3874       aNodePoints.push_back( aP );
3875       if ( !theSurface.IsNull() ) { // smooth in 2D
3876         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3877         gp_XY* uv = theUVMap[ aNode ];
3878         aP.SetCoord( uv->X(), uv->Y(), 0. );
3879       }
3880       elemCenter += aP;
3881     }
3882     double elemArea = anAreaFunc.GetValue( aNodePoints );
3883     totalArea += elemArea;
3884     elemCenter /= nn;
3885     aNewXYZ += elemCenter * elemArea;
3886   }
3887   aNewXYZ /= totalArea;
3888   if ( !theSurface.IsNull() ) {
3889     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3890     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3891   }
3892
3893   // move node
3894
3895   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3896 }
3897
3898 //=======================================================================
3899 //function : getClosestUV
3900 //purpose  : return UV of closest projection
3901 //=======================================================================
3902
3903 static bool getClosestUV (Extrema_GenExtPS& projector,
3904                           const gp_Pnt&     point,
3905                           gp_XY &           result)
3906 {
3907   projector.Perform( point );
3908   if ( projector.IsDone() ) {
3909     double u, v, minVal = DBL_MAX;
3910     for ( int i = projector.NbExt(); i > 0; i-- )
3911       if ( projector.SquareDistance( i ) < minVal ) {
3912         minVal = projector.SquareDistance( i );
3913         projector.Point( i ).Parameter( u, v );
3914       }
3915     result.SetCoord( u, v );
3916     return true;
3917   }
3918   return false;
3919 }
3920
3921 //=======================================================================
3922 //function : Smooth
3923 //purpose  : Smooth theElements during theNbIterations or until a worst
3924 //           element has aspect ratio <= theTgtAspectRatio.
3925 //           Aspect Ratio varies in range [1.0, inf].
3926 //           If theElements is empty, the whole mesh is smoothed.
3927 //           theFixedNodes contains additionally fixed nodes. Nodes built
3928 //           on edges and boundary nodes are always fixed.
3929 //=======================================================================
3930
3931 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3932                                set<const SMDS_MeshNode*> & theFixedNodes,
3933                                const SmoothMethod          theSmoothMethod,
3934                                const int                   theNbIterations,
3935                                double                      theTgtAspectRatio,
3936                                const bool                  the2D)
3937 {
3938   myLastCreatedElems.Clear();
3939   myLastCreatedNodes.Clear();
3940
3941   MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3942
3943   if ( theTgtAspectRatio < 1.0 )
3944     theTgtAspectRatio = 1.0;
3945
3946   const double disttol = 1.e-16;
3947
3948   SMESH::Controls::AspectRatio aQualityFunc;
3949
3950   SMESHDS_Mesh* aMesh = GetMeshDS();
3951
3952   if ( theElems.empty() ) {
3953     // add all faces to theElems
3954     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3955     while ( fIt->more() ) {
3956       const SMDS_MeshElement* face = fIt->next();
3957       theElems.insert( theElems.end(), face );
3958     }
3959   }
3960   // get all face ids theElems are on
3961   set< int > faceIdSet;
3962   TIDSortedElemSet::iterator itElem;
3963   if ( the2D )
3964     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3965       int fId = FindShape( *itElem );
3966       // check that corresponding submesh exists and a shape is face
3967       if (fId &&
3968           faceIdSet.find( fId ) == faceIdSet.end() &&
3969           aMesh->MeshElements( fId )) {
3970         TopoDS_Shape F = aMesh->IndexToShape( fId );
3971         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3972           faceIdSet.insert( fId );
3973       }
3974     }
3975   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3976
3977   // ===============================================
3978   // smooth elements on each TopoDS_Face separately
3979   // ===============================================
3980
3981   SMESH_MesherHelper helper( *GetMesh() );
3982
3983   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3984   for ( ; fId != faceIdSet.rend(); ++fId )
3985   {
3986     // get face surface and submesh
3987     Handle(Geom_Surface) surface;
3988     SMESHDS_SubMesh* faceSubMesh = 0;
3989     TopoDS_Face face;
3990     double fToler2 = 0;
3991     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3992     bool isUPeriodic = false, isVPeriodic = false;
3993     if ( *fId )
3994     {
3995       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3996       surface = BRep_Tool::Surface( face );
3997       faceSubMesh = aMesh->MeshElements( *fId );
3998       fToler2 = BRep_Tool::Tolerance( face );
3999       fToler2 *= fToler2 * 10.;
4000       isUPeriodic = surface->IsUPeriodic();
4001       if ( isUPeriodic )
4002         surface->UPeriod();
4003       isVPeriodic = surface->IsVPeriodic();
4004       if ( isVPeriodic )
4005         surface->VPeriod();
4006       surface->Bounds( u1, u2, v1, v2 );
4007       helper.SetSubShape( face );
4008     }
4009     // ---------------------------------------------------------
4010     // for elements on a face, find movable and fixed nodes and
4011     // compute UV for them
4012     // ---------------------------------------------------------
4013     bool checkBoundaryNodes = false;
4014     bool isQuadratic = false;
4015     set<const SMDS_MeshNode*> setMovableNodes;
4016     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4017     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4018     list< const SMDS_MeshElement* > elemsOnFace;
4019
4020     Extrema_GenExtPS projector;
4021     GeomAdaptor_Surface surfAdaptor;
4022     if ( !surface.IsNull() ) {
4023       surfAdaptor.Load( surface );
4024       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4025     }
4026     int nbElemOnFace = 0;
4027     itElem = theElems.begin();
4028     // loop on not yet smoothed elements: look for elems on a face
4029     while ( itElem != theElems.end() )
4030     {
4031       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4032         break; // all elements found
4033
4034       const SMDS_MeshElement* elem = *itElem;
4035       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4036            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4037         ++itElem;
4038         continue;
4039       }
4040       elemsOnFace.push_back( elem );
4041       theElems.erase( itElem++ );
4042       nbElemOnFace++;
4043
4044       if ( !isQuadratic )
4045         isQuadratic = elem->IsQuadratic();
4046
4047       // get movable nodes of elem
4048       const SMDS_MeshNode* node;
4049       SMDS_TypeOfPosition posType;
4050       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4051       int nn = 0, nbn =  elem->NbNodes();
4052       if(elem->IsQuadratic())
4053         nbn = nbn/2;
4054       while ( nn++ < nbn ) {
4055         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4056         const SMDS_PositionPtr& pos = node->GetPosition();
4057         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4058         if (posType != SMDS_TOP_EDGE &&
4059             posType != SMDS_TOP_VERTEX &&
4060             theFixedNodes.find( node ) == theFixedNodes.end())
4061         {
4062           // check if all faces around the node are on faceSubMesh
4063           // because a node on edge may be bound to face
4064           SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4065           bool all = true;
4066           if ( faceSubMesh ) {
4067             while ( eIt->more() && all ) {
4068               const SMDS_MeshElement* e = eIt->next();
4069               all = faceSubMesh->Contains( e );
4070             }
4071           }
4072           if ( all )
4073             setMovableNodes.insert( node );
4074           else
4075             checkBoundaryNodes = true;
4076         }
4077         if ( posType == SMDS_TOP_3DSPACE )
4078           checkBoundaryNodes = true;
4079       }
4080
4081       if ( surface.IsNull() )
4082         continue;
4083
4084       // get nodes to check UV
4085       list< const SMDS_MeshNode* > uvCheckNodes;
4086       const SMDS_MeshNode* nodeInFace = 0;
4087       itN = elem->nodesIterator();
4088       nn = 0; nbn =  elem->NbNodes();
4089       if(elem->IsQuadratic())
4090         nbn = nbn/2;
4091       while ( nn++ < nbn ) {
4092         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4093         if ( node->GetPosition()->GetDim() == 2 )
4094           nodeInFace = node;
4095         if ( uvMap.find( node ) == uvMap.end() )
4096           uvCheckNodes.push_back( node );
4097         // add nodes of elems sharing node
4098         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4099         //         while ( eIt->more() ) {
4100         //           const SMDS_MeshElement* e = eIt->next();
4101         //           if ( e != elem ) {
4102         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4103         //             while ( nIt->more() ) {
4104         //               const SMDS_MeshNode* n =
4105         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4106         //               if ( uvMap.find( n ) == uvMap.end() )
4107         //                 uvCheckNodes.push_back( n );
4108         //             }
4109         //           }
4110         //         }
4111       }
4112       // check UV on face
4113       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4114       for ( ; n != uvCheckNodes.end(); ++n ) {
4115         node = *n;
4116         gp_XY uv( 0, 0 );
4117         const SMDS_PositionPtr& pos = node->GetPosition();
4118         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4119         // get existing UV
4120         if ( pos )
4121         {
4122           bool toCheck = true;
4123           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4124         }
4125         // compute not existing UV
4126         bool project = ( posType == SMDS_TOP_3DSPACE );
4127         // double dist1 = DBL_MAX, dist2 = 0;
4128         // if ( posType != SMDS_TOP_3DSPACE ) {
4129         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4130         //   project = dist1 > fToler2;
4131         // }
4132         if ( project ) { // compute new UV
4133           gp_XY newUV;
4134           gp_Pnt pNode = SMESH_TNodeXYZ( node );
4135           if ( !getClosestUV( projector, pNode, newUV )) {
4136             MESSAGE("Node Projection Failed " << node);
4137           }
4138           else {
4139             if ( isUPeriodic )
4140               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4141             if ( isVPeriodic )
4142               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4143             // check new UV
4144             // if ( posType != SMDS_TOP_3DSPACE )
4145             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4146             // if ( dist2 < dist1 )
4147               uv = newUV;
4148           }
4149         }
4150         // store UV in the map
4151         listUV.push_back( uv );
4152         uvMap.insert( make_pair( node, &listUV.back() ));
4153       }
4154     } // loop on not yet smoothed elements
4155
4156     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4157       checkBoundaryNodes = true;
4158
4159     // fix nodes on mesh boundary
4160
4161     if ( checkBoundaryNodes ) {
4162       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4163       map< SMESH_TLink, int >::iterator link_nb;
4164       // put all elements links to linkNbMap
4165       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4166       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4167         const SMDS_MeshElement* elem = (*elemIt);
4168         int nbn =  elem->NbCornerNodes();
4169         // loop on elem links: insert them in linkNbMap
4170         for ( int iN = 0; iN < nbn; ++iN ) {
4171           const SMDS_MeshNode* n1 = elem->GetNode( iN );
4172           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4173           SMESH_TLink link( n1, n2 );
4174           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4175           link_nb->second++;
4176         }
4177       }
4178       // remove nodes that are in links encountered only once from setMovableNodes
4179       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4180         if ( link_nb->second == 1 ) {
4181           setMovableNodes.erase( link_nb->first.node1() );
4182           setMovableNodes.erase( link_nb->first.node2() );
4183         }
4184       }
4185     }
4186
4187     // -----------------------------------------------------
4188     // for nodes on seam edge, compute one more UV ( uvMap2 );
4189     // find movable nodes linked to nodes on seam and which
4190     // are to be smoothed using the second UV ( uvMap2 )
4191     // -----------------------------------------------------
4192
4193     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4194     if ( !surface.IsNull() ) {
4195       TopExp_Explorer eExp( face, TopAbs_EDGE );
4196       for ( ; eExp.More(); eExp.Next() ) {
4197         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4198         if ( !BRep_Tool::IsClosed( edge, face ))
4199           continue;
4200         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4201         if ( !sm ) continue;
4202         // find out which parameter varies for a node on seam
4203         double f,l;
4204         gp_Pnt2d uv1, uv2;
4205         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4206         if ( pcurve.IsNull() ) continue;
4207         uv1 = pcurve->Value( f );
4208         edge.Reverse();
4209         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4210         if ( pcurve.IsNull() ) continue;
4211         uv2 = pcurve->Value( f );
4212         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4213         // assure uv1 < uv2
4214         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4215           std::swap( uv1, uv2 );
4216         // get nodes on seam and its vertices
4217         list< const SMDS_MeshNode* > seamNodes;
4218         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4219         while ( nSeamIt->more() ) {
4220           const SMDS_MeshNode* node = nSeamIt->next();
4221           if ( !isQuadratic || !IsMedium( node ))
4222             seamNodes.push_back( node );
4223         }
4224         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4225         for ( ; vExp.More(); vExp.Next() ) {
4226           sm = aMesh->MeshElements( vExp.Current() );
4227           if ( sm ) {
4228             nSeamIt = sm->GetNodes();
4229             while ( nSeamIt->more() )
4230               seamNodes.push_back( nSeamIt->next() );
4231           }
4232         }
4233         // loop on nodes on seam
4234         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4235         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4236           const SMDS_MeshNode* nSeam = *noSeIt;
4237           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4238           if ( n_uv == uvMap.end() )
4239             continue;
4240           // set the first UV
4241           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4242           // set the second UV
4243           listUV.push_back( *n_uv->second );
4244           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4245           if ( uvMap2.empty() )
4246             uvMap2 = uvMap; // copy the uvMap contents
4247           uvMap2[ nSeam ] = &listUV.back();
4248
4249           // collect movable nodes linked to ones on seam in nodesNearSeam
4250           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4251           while ( eIt->more() ) {
4252             const SMDS_MeshElement* e = eIt->next();
4253             int nbUseMap1 = 0, nbUseMap2 = 0;
4254             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4255             int nn = 0, nbn =  e->NbNodes();
4256             if(e->IsQuadratic()) nbn = nbn/2;
4257             while ( nn++ < nbn )
4258             {
4259               const SMDS_MeshNode* n =
4260                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4261               if (n == nSeam ||
4262                   setMovableNodes.find( n ) == setMovableNodes.end() )
4263                 continue;
4264               // add only nodes being closer to uv2 than to uv1
4265               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4266               //              0.5 * ( n->Y() + nSeam->Y() ),
4267               //              0.5 * ( n->Z() + nSeam->Z() ));
4268               // gp_XY uv;
4269               // getClosestUV( projector, pMid, uv );
4270               double x = uvMap[ n ]->Coord( iPar );
4271               if ( Abs( uv1.Coord( iPar ) - x ) >
4272                    Abs( uv2.Coord( iPar ) - x )) {
4273                 nodesNearSeam.insert( n );
4274                 nbUseMap2++;
4275               }
4276               else
4277                 nbUseMap1++;
4278             }
4279             // for centroidalSmooth all element nodes must
4280             // be on one side of a seam
4281             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4282               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4283               nn = 0;
4284               while ( nn++ < nbn ) {
4285                 const SMDS_MeshNode* n =
4286                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4287                 setMovableNodes.erase( n );
4288               }
4289             }
4290           }
4291         } // loop on nodes on seam
4292       } // loop on edge of a face
4293     } // if ( !face.IsNull() )
4294
4295     if ( setMovableNodes.empty() ) {
4296       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4297       continue; // goto next face
4298     }
4299
4300     // -------------
4301     // SMOOTHING //
4302     // -------------
4303
4304     int it = -1;
4305     double maxRatio = -1., maxDisplacement = -1.;
4306     set<const SMDS_MeshNode*>::iterator nodeToMove;
4307     for ( it = 0; it < theNbIterations; it++ ) {
4308       maxDisplacement = 0.;
4309       nodeToMove = setMovableNodes.begin();
4310       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4311         const SMDS_MeshNode* node = (*nodeToMove);
4312         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4313
4314         // smooth
4315         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4316         if ( theSmoothMethod == LAPLACIAN )
4317           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4318         else
4319           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4320
4321         // node displacement
4322         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4323         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4324         if ( aDispl > maxDisplacement )
4325           maxDisplacement = aDispl;
4326       }
4327       // no node movement => exit
4328       //if ( maxDisplacement < 1.e-16 ) {
4329       if ( maxDisplacement < disttol ) {
4330         MESSAGE("-- no node movement --");
4331         break;
4332       }
4333
4334       // check elements quality
4335       maxRatio  = 0;
4336       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4337       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4338         const SMDS_MeshElement* elem = (*elemIt);
4339         if ( !elem || elem->GetType() != SMDSAbs_Face )
4340           continue;
4341         SMESH::Controls::TSequenceOfXYZ aPoints;
4342         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4343           double aValue = aQualityFunc.GetValue( aPoints );
4344           if ( aValue > maxRatio )
4345             maxRatio = aValue;
4346         }
4347       }
4348       if ( maxRatio <= theTgtAspectRatio ) {
4349         MESSAGE("-- quality achived --");
4350         break;
4351       }
4352       if (it+1 == theNbIterations) {
4353         MESSAGE("-- Iteration limit exceeded --");
4354       }
4355     } // smoothing iterations
4356
4357     MESSAGE(" Face id: " << *fId <<
4358             " Nb iterstions: " << it <<
4359             " Displacement: " << maxDisplacement <<
4360             " Aspect Ratio " << maxRatio);
4361
4362     // ---------------------------------------
4363     // new nodes positions are computed,
4364     // record movement in DS and set new UV
4365     // ---------------------------------------
4366     nodeToMove = setMovableNodes.begin();
4367     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4368       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4369       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4370       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4371       if ( node_uv != uvMap.end() ) {
4372         gp_XY* uv = node_uv->second;
4373         node->SetPosition
4374           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4375       }
4376     }
4377
4378     // move medium nodes of quadratic elements
4379     if ( isQuadratic )
4380     {
4381       vector<const SMDS_MeshNode*> nodes;
4382       bool checkUV;
4383       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4384       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4385       {
4386         const SMDS_MeshElement* QF = *elemIt;
4387         if ( QF->IsQuadratic() )
4388         {
4389           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4390                         SMDS_MeshElement::iterator() );
4391           nodes.push_back( nodes[0] );
4392           gp_Pnt xyz;
4393           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4394           {
4395             if ( !surface.IsNull() )
4396             {
4397               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4398               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4399               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4400               xyz = surface->Value( uv.X(), uv.Y() );
4401             }
4402             else {
4403               xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4404             }
4405             if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4406               // we have to move a medium node
4407               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4408           }
4409         }
4410       }
4411     }
4412
4413   } // loop on face ids
4414
4415 }
4416
4417 namespace
4418 {
4419   //=======================================================================
4420   //function : isReverse
4421   //purpose  : Return true if normal of prevNodes is not co-directied with
4422   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4423   //           iNotSame is where prevNodes and nextNodes are different.
4424   //           If result is true then future volume orientation is OK
4425   //=======================================================================
4426
4427   bool isReverse(const SMDS_MeshElement*             face,
4428                  const vector<const SMDS_MeshNode*>& prevNodes,
4429                  const vector<const SMDS_MeshNode*>& nextNodes,
4430                  const int                           iNotSame)
4431   {
4432
4433     SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4434     SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4435     gp_XYZ extrDir( pN - pP ), faceNorm;
4436     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4437
4438     return faceNorm * extrDir < 0.0;
4439   }
4440
4441   //================================================================================
4442   /*!
4443    * \brief Assure that theElemSets[0] holds elements, not nodes
4444    */
4445   //================================================================================
4446
4447   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4448   {
4449     if ( !theElemSets[0].empty() &&
4450          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4451     {
4452       std::swap( theElemSets[0], theElemSets[1] );
4453     }
4454     else if ( !theElemSets[1].empty() &&
4455               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4456     {
4457       std::swap( theElemSets[0], theElemSets[1] );
4458     }
4459   }
4460 }
4461
4462 //=======================================================================
4463 /*!
4464  * \brief Create elements by sweeping an element
4465  * \param elem - element to sweep
4466  * \param newNodesItVec - nodes generated from each node of the element
4467  * \param newElems - generated elements
4468  * \param nbSteps - number of sweeping steps
4469  * \param srcElements - to append elem for each generated element
4470  */
4471 //=======================================================================
4472
4473 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4474                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4475                                     list<const SMDS_MeshElement*>&        newElems,
4476                                     const size_t                          nbSteps,
4477                                     SMESH_SequenceOfElemPtr&              srcElements)
4478 {
4479   //MESSAGE("sweepElement " << nbSteps);
4480   SMESHDS_Mesh* aMesh = GetMeshDS();
4481
4482   const int           nbNodes = elem->NbNodes();
4483   const int         nbCorners = elem->NbCornerNodes();
4484   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4485                                                           polyhedron creation !!! */
4486   // Loop on elem nodes:
4487   // find new nodes and detect same nodes indices
4488   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4489   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4490   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4491   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4492
4493   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4494   vector<int> sames(nbNodes);
4495   vector<bool> isSingleNode(nbNodes);
4496
4497   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4498     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4499     const SMDS_MeshNode*                         node = nnIt->first;
4500     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4501     if ( listNewNodes.empty() )
4502       return;
4503
4504     itNN   [ iNode ] = listNewNodes.begin();
4505     prevNod[ iNode ] = node;
4506     nextNod[ iNode ] = listNewNodes.front();
4507
4508     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4509                                                              corner node of linear */
4510     if ( prevNod[ iNode ] != nextNod [ iNode ])
4511       nbDouble += !isSingleNode[iNode];
4512
4513     if( iNode < nbCorners ) { // check corners only
4514       if ( prevNod[ iNode ] == nextNod [ iNode ])
4515         sames[nbSame++] = iNode;
4516       else
4517         iNotSameNode = iNode;
4518     }
4519   }
4520
4521   if ( nbSame == nbNodes || nbSame > 2) {
4522     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4523     return;
4524   }
4525
4526   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4527   {
4528     // fix nodes order to have bottom normal external
4529     if ( baseType == SMDSEntity_Polygon )
4530     {
4531       std::reverse( itNN.begin(), itNN.end() );
4532       std::reverse( prevNod.begin(), prevNod.end() );
4533       std::reverse( midlNod.begin(), midlNod.end() );
4534       std::reverse( nextNod.begin(), nextNod.end() );
4535       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4536     }
4537     else
4538     {
4539       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4540       SMDS_MeshCell::applyInterlace( ind, itNN );
4541       SMDS_MeshCell::applyInterlace( ind, prevNod );
4542       SMDS_MeshCell::applyInterlace( ind, nextNod );
4543       SMDS_MeshCell::applyInterlace( ind, midlNod );
4544       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4545       if ( nbSame > 0 )
4546       {
4547         sames[nbSame] = iNotSameNode;
4548         for ( int j = 0; j <= nbSame; ++j )
4549           for ( size_t i = 0; i < ind.size(); ++i )
4550             if ( ind[i] == sames[j] )
4551             {
4552               sames[j] = i;
4553               break;
4554             }
4555         iNotSameNode = sames[nbSame];
4556       }
4557     }
4558   }
4559   else if ( elem->GetType() == SMDSAbs_Edge )
4560   {
4561     // orient a new face same as adjacent one
4562     int i1, i2;
4563     const SMDS_MeshElement* e;
4564     TIDSortedElemSet dummy;
4565     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4566         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4567         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4568     {
4569       // there is an adjacent face, check order of nodes in it
4570       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4571       if ( sameOrder )
4572       {
4573         std::swap( itNN[0],    itNN[1] );
4574         std::swap( prevNod[0], prevNod[1] );
4575         std::swap( nextNod[0], nextNod[1] );
4576         isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4577         if ( nbSame > 0 )
4578           sames[0] = 1 - sames[0];
4579         iNotSameNode = 1 - iNotSameNode;
4580       }
4581     }
4582   }
4583
4584   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4585   if ( nbSame > 0 ) {
4586     iSameNode    = sames[ nbSame-1 ];
4587     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4588     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4589     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4590   }
4591
4592   if ( baseType == SMDSEntity_Polygon )
4593   {
4594     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4595     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4596   }
4597   else if ( baseType == SMDSEntity_Quad_Polygon )
4598   {
4599     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4600     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4601   }
4602
4603   // make new elements
4604   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4605   {
4606     // get next nodes
4607     for ( iNode = 0; iNode < nbNodes; iNode++ )
4608     {
4609       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4610       nextNod[ iNode ] = *itNN[ iNode ]++;
4611     }
4612
4613     SMDS_MeshElement* aNewElem = 0;
4614     /*if(!elem->IsPoly())*/ {
4615       switch ( baseType ) {
4616       case SMDSEntity_0D:
4617       case SMDSEntity_Node: { // sweep NODE
4618         if ( nbSame == 0 ) {
4619           if ( isSingleNode[0] )
4620             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4621           else
4622             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4623         }
4624         else
4625           return;
4626         break;
4627       }
4628       case SMDSEntity_Edge: { // sweep EDGE
4629         if ( nbDouble == 0 )
4630         {
4631           if ( nbSame == 0 ) // ---> quadrangle
4632             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4633                                       nextNod[ 1 ], nextNod[ 0 ] );
4634           else               // ---> triangle
4635             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4636                                       nextNod[ iNotSameNode ] );
4637         }
4638         else                 // ---> polygon
4639         {
4640           vector<const SMDS_MeshNode*> poly_nodes;
4641           poly_nodes.push_back( prevNod[0] );
4642           poly_nodes.push_back( prevNod[1] );
4643           if ( prevNod[1] != nextNod[1] )
4644           {
4645             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4646             poly_nodes.push_back( nextNod[1] );
4647           }
4648           if ( prevNod[0] != nextNod[0] )
4649           {
4650             poly_nodes.push_back( nextNod[0] );
4651             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4652           }
4653           switch ( poly_nodes.size() ) {
4654           case 3:
4655             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4656             break;
4657           case 4:
4658             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4659                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4660             break;
4661           default:
4662             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4663           }
4664         }
4665         break;
4666       }
4667       case SMDSEntity_Triangle: // TRIANGLE --->
4668         {
4669           if ( nbDouble > 0 ) break;
4670           if ( nbSame == 0 )       // ---> pentahedron
4671             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4672                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4673
4674           else if ( nbSame == 1 )  // ---> pyramid
4675             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4676                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4677                                          nextNod[ iSameNode ]);
4678
4679           else // 2 same nodes:       ---> tetrahedron
4680             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4681                                          nextNod[ iNotSameNode ]);
4682           break;
4683         }
4684       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4685         {
4686           if ( nbSame == 2 )
4687             return;
4688           if ( nbDouble+nbSame == 2 )
4689           {
4690             if(nbSame==0) {      // ---> quadratic quadrangle
4691               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4692                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4693             }
4694             else { //(nbSame==1) // ---> quadratic triangle
4695               if(sames[0]==2) {
4696                 return; // medium node on axis
4697               }
4698               else if(sames[0]==0)
4699                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4700                                           prevNod[2], midlNod[1], nextNod[2] );
4701               else // sames[0]==1
4702                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4703                                           prevNod[2], nextNod[2], midlNod[0]);
4704             }
4705           }
4706           else if ( nbDouble == 3 )
4707           {
4708             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4709               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4710                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4711             }
4712           }
4713           else
4714             return;
4715           break;
4716         }
4717       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4718         if ( nbDouble > 0 ) break;
4719
4720         if ( nbSame == 0 )       // ---> hexahedron
4721           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4722                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4723
4724         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4725           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4726                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4727                                        nextNod[ iSameNode ]);
4728           newElems.push_back( aNewElem );
4729           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4730                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4731                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4732         }
4733         else if ( nbSame == 2 ) { // ---> pentahedron
4734           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4735             // iBeforeSame is same too
4736             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4737                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4738                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4739           else
4740             // iAfterSame is same too
4741             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4742                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4743                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4744         }
4745         break;
4746       }
4747       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4748       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4749         if ( nbDouble+nbSame != 3 ) break;
4750         if(nbSame==0) {
4751           // --->  pentahedron with 15 nodes
4752           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4753                                        nextNod[0], nextNod[1], nextNod[2],
4754                                        prevNod[3], prevNod[4], prevNod[5],
4755                                        nextNod[3], nextNod[4], nextNod[5],
4756                                        midlNod[0], midlNod[1], midlNod[2]);
4757         }
4758         else if(nbSame==1) {
4759           // --->  2d order pyramid of 13 nodes
4760           int apex = iSameNode;
4761           int i0 = ( apex + 1 ) % nbCorners;
4762           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4763           int i0a = apex + 3;
4764           int i1a = i1 + 3;
4765           int i01 = i0 + 3;
4766           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4767                                       nextNod[i0], nextNod[i1], prevNod[apex],
4768                                       prevNod[i01], midlNod[i0],
4769                                       nextNod[i01], midlNod[i1],
4770                                       prevNod[i1a], prevNod[i0a],
4771                                       nextNod[i0a], nextNod[i1a]);
4772         }
4773         else if(nbSame==2) {
4774           // --->  2d order tetrahedron of 10 nodes
4775           int n1 = iNotSameNode;
4776           int n2 = ( n1 + 1             ) % nbCorners;
4777           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4778           int n12 = n1 + 3;
4779           int n23 = n2 + 3;
4780           int n31 = n3 + 3;
4781           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4782                                        prevNod[n12], prevNod[n23], prevNod[n31],
4783                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4784         }
4785         break;
4786       }
4787       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4788         if( nbSame == 0 ) {
4789           if ( nbDouble != 4 ) break;
4790           // --->  hexahedron with 20 nodes
4791           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4792                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4793                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4794                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4795                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4796         }
4797         else if(nbSame==1) {
4798           // ---> pyramid + pentahedron - can not be created since it is needed
4799           // additional middle node at the center of face
4800           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4801           return;
4802         }
4803         else if( nbSame == 2 ) {
4804           if ( nbDouble != 2 ) break;
4805           // --->  2d order Pentahedron with 15 nodes
4806           int n1,n2,n4,n5;
4807           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4808             // iBeforeSame is same too
4809             n1 = iBeforeSame;
4810             n2 = iOpposSame;
4811             n4 = iSameNode;
4812             n5 = iAfterSame;
4813           }
4814           else {
4815             // iAfterSame is same too
4816             n1 = iSameNode;
4817             n2 = iBeforeSame;
4818             n4 = iAfterSame;
4819             n5 = iOpposSame;
4820           }
4821           int n12 = n2 + 4;
4822           int n45 = n4 + 4;
4823           int n14 = n1 + 4;
4824           int n25 = n5 + 4;
4825           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4826                                        prevNod[n4], prevNod[n5], nextNod[n5],
4827                                        prevNod[n12], midlNod[n2], nextNod[n12],
4828                                        prevNod[n45], midlNod[n5], nextNod[n45],
4829                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4830         }
4831         break;
4832       }
4833       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4834
4835         if( nbSame == 0 && nbDouble == 9 ) {
4836           // --->  tri-quadratic hexahedron with 27 nodes
4837           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4838                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4839                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4840                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4841                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4842                                        prevNod[8], // bottom center
4843                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4844                                        nextNod[8], // top center
4845                                        midlNod[8]);// elem center
4846         }
4847         else
4848         {
4849           return;
4850         }
4851         break;
4852       }
4853       case SMDSEntity_Polygon: { // sweep POLYGON
4854
4855         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4856           // --->  hexagonal prism
4857           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4858                                        prevNod[3], prevNod[4], prevNod[5],
4859                                        nextNod[0], nextNod[1], nextNod[2],
4860                                        nextNod[3], nextNod[4], nextNod[5]);
4861         }
4862         break;
4863       }
4864       case SMDSEntity_Ball:
4865         return;
4866
4867       default:
4868         break;
4869       } // switch ( baseType )
4870     } // scope
4871
4872     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4873     {
4874       if ( baseType != SMDSEntity_Polygon )
4875       {
4876         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4877         SMDS_MeshCell::applyInterlace( ind, prevNod );
4878         SMDS_MeshCell::applyInterlace( ind, nextNod );
4879         SMDS_MeshCell::applyInterlace( ind, midlNod );
4880         SMDS_MeshCell::applyInterlace( ind, itNN );
4881         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4882         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4883       }
4884       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4885       vector<int> quantities (nbNodes + 2);
4886       polyedre_nodes.clear();
4887       quantities.clear();
4888
4889       // bottom of prism
4890       for (int inode = 0; inode < nbNodes; inode++)
4891         polyedre_nodes.push_back( prevNod[inode] );
4892       quantities.push_back( nbNodes );
4893
4894       // top of prism
4895       polyedre_nodes.push_back( nextNod[0] );
4896       for (int inode = nbNodes; inode-1; --inode )
4897         polyedre_nodes.push_back( nextNod[inode-1] );
4898       quantities.push_back( nbNodes );
4899
4900       // side faces
4901       // 3--6--2
4902       // |     |
4903       // 7     5
4904       // |     |
4905       // 0--4--1
4906       const int iQuad = elem->IsQuadratic();
4907       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4908       {
4909         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4910         int inextface = (iface+1+iQuad) % nbNodes;
4911         int imid      = (iface+1) % nbNodes;
4912         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4913         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4914         polyedre_nodes.push_back( prevNod[iface] );             // 1
4915         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4916         {
4917           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4918           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4919         }
4920         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4921         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4922         {
4923           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4924           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4925         }
4926         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4927         if ( nbFaceNodes > 2 )
4928           quantities.push_back( nbFaceNodes );
4929         else // degenerated face
4930           polyedre_nodes.resize( prevNbNodes );
4931       }
4932       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4933
4934     } // try to create a polyherdal prism
4935
4936     if ( aNewElem ) {
4937       newElems.push_back( aNewElem );
4938       myLastCreatedElems.Append(aNewElem);
4939       srcElements.Append( elem );
4940     }
4941
4942     // set new prev nodes
4943     for ( iNode = 0; iNode < nbNodes; iNode++ )
4944       prevNod[ iNode ] = nextNod[ iNode ];
4945
4946   } // loop on steps
4947 }
4948
4949 //=======================================================================
4950 /*!
4951  * \brief Create 1D and 2D elements around swept elements
4952  * \param mapNewNodes - source nodes and ones generated from them
4953  * \param newElemsMap - source elements and ones generated from them
4954  * \param elemNewNodesMap - nodes generated from each node of each element
4955  * \param elemSet - all swept elements
4956  * \param nbSteps - number of sweeping steps
4957  * \param srcElements - to append elem for each generated element
4958  */
4959 //=======================================================================
4960
4961 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4962                                   TTElemOfElemListMap &    newElemsMap,
4963                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4964                                   TIDSortedElemSet&        elemSet,
4965                                   const int                nbSteps,
4966                                   SMESH_SequenceOfElemPtr& srcElements)
4967 {
4968   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4969   SMESHDS_Mesh* aMesh = GetMeshDS();
4970
4971   // Find nodes belonging to only one initial element - sweep them into edges.
4972
4973   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4974   for ( ; nList != mapNewNodes.end(); nList++ )
4975   {
4976     const SMDS_MeshNode* node =
4977       static_cast<const SMDS_MeshNode*>( nList->first );
4978     if ( newElemsMap.count( node ))
4979       continue; // node was extruded into edge
4980     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4981     int nbInitElems = 0;
4982     const SMDS_MeshElement* el = 0;
4983     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4984     while ( eIt->more() && nbInitElems < 2 ) {
4985       const SMDS_MeshElement* e = eIt->next();
4986       SMDSAbs_ElementType  type = e->GetType();
4987       if ( type == SMDSAbs_Volume ||
4988            type < highType ||
4989            !elemSet.count(e))
4990         continue;
4991       if ( type > highType ) {
4992         nbInitElems = 0;
4993         highType    = type;
4994       }
4995       el = e;
4996       ++nbInitElems;
4997     }
4998     if ( nbInitElems == 1 ) {
4999       bool NotCreateEdge = el && el->IsMediumNode(node);
5000       if(!NotCreateEdge) {
5001         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5002         list<const SMDS_MeshElement*> newEdges;
5003         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5004       }
5005     }
5006   }
5007
5008   // Make a ceiling for each element ie an equal element of last new nodes.
5009   // Find free links of faces - make edges and sweep them into faces.
5010
5011   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5012
5013   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
5014   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5015   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5016   {
5017     const SMDS_MeshElement* elem = itElem->first;
5018     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5019
5020     if(itElem->second.size()==0) continue;
5021
5022     const bool isQuadratic = elem->IsQuadratic();
5023
5024     if ( elem->GetType() == SMDSAbs_Edge ) {
5025       // create a ceiling edge
5026       if ( !isQuadratic ) {
5027         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5028                                vecNewNodes[ 1 ]->second.back())) {
5029           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5030                                                    vecNewNodes[ 1 ]->second.back()));
5031           srcElements.Append( elem );
5032         }
5033       }
5034       else {
5035         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5036                                vecNewNodes[ 1 ]->second.back(),
5037                                vecNewNodes[ 2 ]->second.back())) {
5038           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5039                                                    vecNewNodes[ 1 ]->second.back(),
5040                                                    vecNewNodes[ 2 ]->second.back()));
5041           srcElements.Append( elem );
5042         }
5043       }
5044     }
5045     if ( elem->GetType() != SMDSAbs_Face )
5046       continue;
5047
5048     bool hasFreeLinks = false;
5049
5050     TIDSortedElemSet avoidSet;
5051     avoidSet.insert( elem );
5052
5053     set<const SMDS_MeshNode*> aFaceLastNodes;
5054     int iNode, nbNodes = vecNewNodes.size();
5055     if ( !isQuadratic ) {
5056       // loop on the face nodes
5057       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5058         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5059         // look for free links of the face
5060         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5061         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5062         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5063         // check if a link n1-n2 is free
5064         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5065           hasFreeLinks = true;
5066           // make a new edge and a ceiling for a new edge
5067           const SMDS_MeshElement* edge;
5068           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5069             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5070             srcElements.Append( myLastCreatedElems.Last() );
5071           }
5072           n1 = vecNewNodes[ iNode ]->second.back();
5073           n2 = vecNewNodes[ iNext ]->second.back();
5074           if ( !aMesh->FindEdge( n1, n2 )) {
5075             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5076             srcElements.Append( edge );
5077           }
5078         }
5079       }
5080     }
5081     else { // elem is quadratic face
5082       int nbn = nbNodes/2;
5083       for ( iNode = 0; iNode < nbn; iNode++ ) {
5084         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5085         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5086         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5087         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5088         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5089         // check if a link is free
5090         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5091              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5092              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5093           hasFreeLinks = true;
5094           // make an edge and a ceiling for a new edge
5095           // find medium node
5096           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5097             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5098             srcElements.Append( elem );
5099           }
5100           n1 = vecNewNodes[ iNode ]->second.back();
5101           n2 = vecNewNodes[ iNext ]->second.back();
5102           n3 = vecNewNodes[ iNode+nbn ]->second.back();
5103           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5104             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5105             srcElements.Append( elem );
5106           }
5107         }
5108       }
5109       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5110         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5111       }
5112     }
5113
5114     // sweep free links into faces
5115
5116     if ( hasFreeLinks ) {
5117       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5118       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5119
5120       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5121       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5122       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5123         initNodeSet.insert( vecNewNodes[ iNode ]->first );
5124         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5125       }
5126       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
5127         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5128         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5129       }
5130       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5131         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5132         std::advance( v, volNb );
5133         // find indices of free faces of a volume and their source edges
5134         list< int > freeInd;
5135         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5136         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5137         int iF, nbF = vTool.NbFaces();
5138         for ( iF = 0; iF < nbF; iF ++ ) {
5139           if (vTool.IsFreeFace( iF ) &&
5140               vTool.GetFaceNodes( iF, faceNodeSet ) &&
5141               initNodeSet != faceNodeSet) // except an initial face
5142           {
5143             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5144               continue;
5145             if ( faceNodeSet == initNodeSetNoCenter )
5146               continue;
5147             freeInd.push_back( iF );
5148             // find source edge of a free face iF
5149             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5150             vector<const SMDS_MeshNode*>::iterator lastCommom;
5151             commonNodes.resize( nbNodes, 0 );
5152             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5153                                                 initNodeSet.begin(), initNodeSet.end(),
5154                                                 commonNodes.begin());
5155             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5156               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5157             else
5158               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5159 #ifdef _DEBUG_
5160             if ( !srcEdges.back() )
5161             {
5162               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5163                    << iF << " of volume #" << vTool.ID() << endl;
5164             }
5165 #endif
5166           }
5167         }
5168         if ( freeInd.empty() )
5169           continue;
5170
5171         // create wall faces for all steps;
5172         // if such a face has been already created by sweep of edge,
5173         // assure that its orientation is OK
5174         for ( int iStep = 0; iStep < nbSteps; iStep++ )
5175         {
5176           vTool.Set( *v, /*ignoreCentralNodes=*/false );
5177           vTool.SetExternalNormal();
5178           const int nextShift = vTool.IsForward() ? +1 : -1;
5179           list< int >::iterator ind = freeInd.begin();
5180           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5181           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5182           {
5183             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5184             int nbn = vTool.NbFaceNodes( *ind );
5185             const SMDS_MeshElement * f = 0;
5186             if ( nbn == 3 )              ///// triangle
5187             {
5188               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5189               if ( !f ||
5190                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5191               {
5192                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5193                                                      nodes[ 1 ],
5194                                                      nodes[ 1 + nextShift ] };
5195                 if ( f )
5196                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5197                 else
5198                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5199                                                             newOrder[ 2 ] ));
5200               }
5201             }
5202             else if ( nbn == 4 )       ///// quadrangle
5203             {
5204               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5205               if ( !f ||
5206                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5207               {
5208                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5209                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
5210                 if ( f )
5211                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5212                 else
5213                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5214                                                             newOrder[ 2 ], newOrder[ 3 ]));
5215               }
5216             }
5217             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5218             {
5219               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5220               if ( !f ||
5221                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5222               {
5223                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5224                                                      nodes[2],
5225                                                      nodes[2 + 2*nextShift],
5226                                                      nodes[3 - 2*nextShift],
5227                                                      nodes[3],
5228                                                      nodes[3 + 2*nextShift]};
5229                 if ( f )
5230                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5231                 else
5232                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5233                                                             newOrder[ 1 ],
5234                                                             newOrder[ 2 ],
5235                                                             newOrder[ 3 ],
5236                                                             newOrder[ 4 ],
5237                                                             newOrder[ 5 ] ));
5238               }
5239             }
5240             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5241             {
5242               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5243                                    nodes[1], nodes[3], nodes[5], nodes[7] );
5244               if ( !f ||
5245                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5246               {
5247                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5248                                                      nodes[4 - 2*nextShift],
5249                                                      nodes[4],
5250                                                      nodes[4 + 2*nextShift],
5251                                                      nodes[1],
5252                                                      nodes[5 - 2*nextShift],
5253                                                      nodes[5],
5254                                                      nodes[5 + 2*nextShift] };
5255                 if ( f )
5256                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5257                 else
5258                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5259                                                            newOrder[ 2 ], newOrder[ 3 ],
5260                                                            newOrder[ 4 ], newOrder[ 5 ],
5261                                                            newOrder[ 6 ], newOrder[ 7 ]));
5262               }
5263             }
5264             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5265             {
5266               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5267                                       SMDSAbs_Face, /*noMedium=*/false);
5268               if ( !f ||
5269                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5270               {
5271                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5272                                                      nodes[4 - 2*nextShift],
5273                                                      nodes[4],
5274                                                      nodes[4 + 2*nextShift],
5275                                                      nodes[1],
5276                                                      nodes[5 - 2*nextShift],
5277                                                      nodes[5],
5278                                                      nodes[5 + 2*nextShift],
5279                                                      nodes[8] };
5280                 if ( f )
5281                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5282                 else
5283                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5284                                                            newOrder[ 2 ], newOrder[ 3 ],
5285                                                            newOrder[ 4 ], newOrder[ 5 ],
5286                                                            newOrder[ 6 ], newOrder[ 7 ],
5287                                                            newOrder[ 8 ]));
5288               }
5289             }
5290             else  //////// polygon
5291             {
5292               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5293               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5294               if ( !f ||
5295                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5296               {
5297                 if ( !vTool.IsForward() )
5298                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5299                 if ( f )
5300                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5301                 else
5302                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5303               }
5304             }
5305
5306             while ( srcElements.Length() < myLastCreatedElems.Length() )
5307               srcElements.Append( *srcEdge );
5308
5309           }  // loop on free faces
5310
5311           // go to the next volume
5312           iVol = 0;
5313           while ( iVol++ < nbVolumesByStep ) v++;
5314
5315         } // loop on steps
5316       } // loop on volumes of one step
5317     } // sweep free links into faces
5318
5319     // Make a ceiling face with a normal external to a volume
5320
5321     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5322     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5323     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5324
5325     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5326       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5327       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5328     }
5329     if ( iF >= 0 )
5330     {
5331       lastVol.SetExternalNormal();
5332       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5333       const               int nbn = lastVol.NbFaceNodes( iF );
5334       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5335       if ( !hasFreeLinks ||
5336            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5337       {
5338         const vector<int>& interlace =
5339           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5340         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5341
5342         AddElement( nodeVec, anyFace.Init( elem ));
5343
5344         while ( srcElements.Length() < myLastCreatedElems.Length() )
5345           srcElements.Append( elem );
5346       }
5347     }
5348   } // loop on swept elements
5349 }
5350
5351 //=======================================================================
5352 //function : RotationSweep
5353 //purpose  :
5354 //=======================================================================
5355
5356 SMESH_MeshEditor::PGroupIDs
5357 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5358                                 const gp_Ax1&      theAxis,
5359                                 const double       theAngle,
5360                                 const int          theNbSteps,
5361                                 const double       theTol,
5362                                 const bool         theMakeGroups,
5363                                 const bool         theMakeWalls)
5364 {
5365   myLastCreatedElems.Clear();
5366   myLastCreatedNodes.Clear();
5367
5368   // source elements for each generated one
5369   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5370
5371   MESSAGE( "RotationSweep()");
5372   gp_Trsf aTrsf;
5373   aTrsf.SetRotation( theAxis, theAngle );
5374   gp_Trsf aTrsf2;
5375   aTrsf2.SetRotation( theAxis, theAngle/2. );
5376
5377   gp_Lin aLine( theAxis );
5378   double aSqTol = theTol * theTol;
5379
5380   SMESHDS_Mesh* aMesh = GetMeshDS();
5381
5382   TNodeOfNodeListMap mapNewNodes;
5383   TElemOfVecOfNnlmiMap mapElemNewNodes;
5384   TTElemOfElemListMap newElemsMap;
5385
5386   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5387                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5388                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5389   // loop on theElemSets
5390   setElemsFirst( theElemSets );
5391   TIDSortedElemSet::iterator itElem;
5392   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5393   {
5394     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5395     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5396       const SMDS_MeshElement* elem = *itElem;
5397       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5398         continue;
5399       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5400       newNodesItVec.reserve( elem->NbNodes() );
5401
5402       // loop on elem nodes
5403       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5404       while ( itN->more() )
5405       {
5406         const SMDS_MeshNode* node = cast2Node( itN->next() );
5407
5408         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5409         double coord[3];
5410         aXYZ.Coord( coord[0], coord[1], coord[2] );
5411         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5412
5413         // check if a node has been already sweeped
5414         TNodeOfNodeListMapItr nIt =
5415           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5416         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5417         if ( listNewNodes.empty() )
5418         {
5419           // check if we are to create medium nodes between corner ones
5420           bool needMediumNodes = false;
5421           if ( isQuadraticMesh )
5422           {
5423             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5424             while (it->more() && !needMediumNodes )
5425             {
5426               const SMDS_MeshElement* invElem = it->next();
5427               if ( invElem != elem && !theElems.count( invElem )) continue;
5428               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5429               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5430                 needMediumNodes = true;
5431             }
5432           }
5433
5434           // make new nodes
5435           const SMDS_MeshNode * newNode = node;
5436           for ( int i = 0; i < theNbSteps; i++ ) {
5437             if ( !isOnAxis ) {
5438               if ( needMediumNodes )  // create a medium node
5439               {
5440                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5441                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5442                 myLastCreatedNodes.Append(newNode);
5443                 srcNodes.Append( node );
5444                 listNewNodes.push_back( newNode );
5445                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5446               }
5447               else {
5448                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5449               }
5450               // create a corner node
5451               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5452               myLastCreatedNodes.Append(newNode);
5453               srcNodes.Append( node );
5454               listNewNodes.push_back( newNode );
5455             }
5456             else {
5457               listNewNodes.push_back( newNode );
5458               // if ( needMediumNodes )
5459               //   listNewNodes.push_back( newNode );
5460             }
5461           }
5462         }
5463         newNodesItVec.push_back( nIt );
5464       }
5465       // make new elements
5466       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5467     }
5468   }
5469
5470   if ( theMakeWalls )
5471     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5472
5473   PGroupIDs newGroupIDs;
5474   if ( theMakeGroups )
5475     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5476
5477   return newGroupIDs;
5478 }
5479
5480 //=======================================================================
5481 //function : ExtrusParam
5482 //purpose  : standard construction
5483 //=======================================================================
5484
5485 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&  theStep,
5486                                             const int      theNbSteps,
5487                                             const int      theFlags,
5488                                             const double   theTolerance):
5489   myDir( theStep ),
5490   myFlags( theFlags ),
5491   myTolerance( theTolerance ),
5492   myElemsToUse( NULL )
5493 {
5494   mySteps = new TColStd_HSequenceOfReal;
5495   const double stepSize = theStep.Magnitude();
5496   for (int i=1; i<=theNbSteps; i++ )
5497     mySteps->Append( stepSize );
5498
5499   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5500       ( theTolerance > 0 ))
5501   {
5502     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5503   }
5504   else
5505   {
5506     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5507   }
5508 }
5509
5510 //=======================================================================
5511 //function : ExtrusParam
5512 //purpose  : steps are given explicitly
5513 //=======================================================================
5514
5515 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5516                                             Handle(TColStd_HSequenceOfReal) theSteps,
5517                                             const int                       theFlags,
5518                                             const double                    theTolerance):
5519   myDir( theDir ),
5520   mySteps( theSteps ),
5521   myFlags( theFlags ),
5522   myTolerance( theTolerance ),
5523   myElemsToUse( NULL )
5524 {
5525   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5526       ( theTolerance > 0 ))
5527   {
5528     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5529   }
5530   else
5531   {
5532     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5533   }
5534 }
5535
5536 //=======================================================================
5537 //function : ExtrusParam
5538 //purpose  : for extrusion by normal
5539 //=======================================================================
5540
5541 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5542                                             const int    theNbSteps,
5543                                             const int    theFlags,
5544                                             const int    theDim ):
5545   myDir( 1,0,0 ),
5546   mySteps( new TColStd_HSequenceOfReal ),
5547   myFlags( theFlags ),
5548   myTolerance( 0 ),
5549   myElemsToUse( NULL )
5550 {
5551   for (int i = 0; i < theNbSteps; i++ )
5552     mySteps->Append( theStepSize );
5553
5554   if ( theDim == 1 )
5555   {
5556     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5557   }
5558   else
5559   {
5560     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5561   }
5562 }
5563
5564 //=======================================================================
5565 //function : ExtrusParam::SetElementsToUse
5566 //purpose  : stores elements to use for extrusion by normal, depending on
5567 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag
5568 //=======================================================================
5569
5570 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems )
5571 {
5572   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5573 }
5574
5575 //=======================================================================
5576 //function : ExtrusParam::beginStepIter
5577 //purpose  : prepare iteration on steps
5578 //=======================================================================
5579
5580 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5581 {
5582   myWithMediumNodes = withMediumNodes;
5583   myNextStep = 1;
5584   myCurSteps.clear();
5585 }
5586 //=======================================================================
5587 //function : ExtrusParam::moreSteps
5588 //purpose  : are there more steps?
5589 //=======================================================================
5590
5591 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5592 {
5593   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5594 }
5595 //=======================================================================
5596 //function : ExtrusParam::nextStep
5597 //purpose  : returns the next step
5598 //=======================================================================
5599
5600 double SMESH_MeshEditor::ExtrusParam::nextStep()
5601 {
5602   double res = 0;
5603   if ( !myCurSteps.empty() )
5604   {
5605     res = myCurSteps.back();
5606     myCurSteps.pop_back();
5607   }
5608   else if ( myNextStep <= mySteps->Length() )
5609   {
5610     myCurSteps.push_back( mySteps->Value( myNextStep ));
5611     ++myNextStep;
5612     if ( myWithMediumNodes )
5613     {
5614       myCurSteps.back() /= 2.;
5615       myCurSteps.push_back( myCurSteps.back() );
5616     }
5617     res = nextStep();
5618   }
5619   return res;
5620 }
5621
5622 //=======================================================================
5623 //function : ExtrusParam::makeNodesByDir
5624 //purpose  : create nodes for standard extrusion
5625 //=======================================================================
5626
5627 int SMESH_MeshEditor::ExtrusParam::
5628 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5629                 const SMDS_MeshNode*              srcNode,
5630                 std::list<const SMDS_MeshNode*> & newNodes,
5631                 const bool                        makeMediumNodes)
5632 {
5633   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5634
5635   int nbNodes = 0;
5636   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5637   {
5638     p += myDir.XYZ() * nextStep();
5639     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5640     newNodes.push_back( newNode );
5641   }
5642   return nbNodes;
5643 }
5644
5645 //=======================================================================
5646 //function : ExtrusParam::makeNodesByDirAndSew
5647 //purpose  : create nodes for standard extrusion with sewing
5648 //=======================================================================
5649
5650 int SMESH_MeshEditor::ExtrusParam::
5651 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5652                       const SMDS_MeshNode*              srcNode,
5653                       std::list<const SMDS_MeshNode*> & newNodes,
5654                       const bool                        makeMediumNodes)
5655 {
5656   gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5657
5658   int nbNodes = 0;
5659   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5660   {
5661     P1 += myDir.XYZ() * nextStep();
5662
5663     // try to search in sequence of existing nodes
5664     // if myNodes.Length()>0 we 'nave to use given sequence
5665     // else - use all nodes of mesh
5666     const SMDS_MeshNode * node = 0;
5667     if ( myNodes.Length() > 0 ) {
5668       int i;
5669       for(i=1; i<=myNodes.Length(); i++) {
5670         gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5671         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5672         {
5673           node = myNodes.Value(i);
5674           break;
5675         }
5676       }
5677     }
5678     else {
5679       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5680       while(itn->more()) {
5681         SMESH_TNodeXYZ P2( itn->next() );
5682         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5683         {
5684           node = P2._node;
5685           break;
5686         }
5687       }
5688     }
5689
5690     if ( !node )
5691       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5692
5693     newNodes.push_back( node );
5694
5695   } // loop on steps
5696
5697   return nbNodes;
5698 }
5699
5700 //=======================================================================
5701 //function : ExtrusParam::makeNodesByNormal2D
5702 //purpose  : create nodes for extrusion using normals of faces
5703 //=======================================================================
5704
5705 int SMESH_MeshEditor::ExtrusParam::
5706 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5707                      const SMDS_MeshNode*              srcNode,
5708                      std::list<const SMDS_MeshNode*> & newNodes,
5709                      const bool                        makeMediumNodes)
5710 {
5711   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5712
5713   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5714
5715   // get normals to faces sharing srcNode
5716   vector< gp_XYZ > norms, baryCenters;
5717   gp_XYZ norm, avgNorm( 0,0,0 );
5718   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5719   while ( faceIt->more() )
5720   {
5721     const SMDS_MeshElement* face = faceIt->next();
5722     if ( myElemsToUse && !myElemsToUse->count( face ))
5723       continue;
5724     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5725     {
5726       norms.push_back( norm );
5727       avgNorm += norm;
5728       if ( !alongAvgNorm )
5729       {
5730         gp_XYZ bc(0,0,0);
5731         int nbN = 0;
5732         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5733           bc += SMESH_TNodeXYZ( nIt->next() );
5734         baryCenters.push_back( bc / nbN );
5735       }
5736     }
5737   }
5738
5739   if ( norms.empty() ) return 0;
5740
5741   double normSize = avgNorm.Modulus();
5742   if ( normSize < std::numeric_limits<double>::min() )
5743     return 0;
5744
5745   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5746   {
5747     myDir = avgNorm;
5748     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5749   }
5750
5751   avgNorm /= normSize;
5752
5753   int nbNodes = 0;
5754   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5755   {
5756     gp_XYZ pNew = p;
5757     double stepSize = nextStep();
5758
5759     if ( norms.size() > 1 )
5760     {
5761       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5762       {
5763         // translate plane of a face
5764         baryCenters[ iF ] += norms[ iF ] * stepSize;
5765
5766         // find point of intersection of the face plane located at baryCenters[ iF ]
5767         // and avgNorm located at pNew
5768         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5769         double dot  = ( norms[ iF ] * avgNorm );
5770         if ( dot < std::numeric_limits<double>::min() )
5771           dot = stepSize * 1e-3;
5772         double step = -( norms[ iF ] * pNew + d ) / dot;
5773         pNew += step * avgNorm;
5774       }
5775     }
5776     else
5777     {
5778       pNew += stepSize * avgNorm;
5779     }
5780     p = pNew;
5781
5782     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5783     newNodes.push_back( newNode );
5784   }
5785   return nbNodes;
5786 }
5787
5788 //=======================================================================
5789 //function : ExtrusParam::makeNodesByNormal1D
5790 //purpose  : create nodes for extrusion using normals of edges
5791 //=======================================================================
5792
5793 int SMESH_MeshEditor::ExtrusParam::
5794 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5795                      const SMDS_MeshNode*              srcNode,
5796                      std::list<const SMDS_MeshNode*> & newNodes,
5797                      const bool                        makeMediumNodes)
5798 {
5799   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5800   return 0;
5801 }
5802
5803 //=======================================================================
5804 //function : ExtrusionSweep
5805 //purpose  :
5806 //=======================================================================
5807
5808 SMESH_MeshEditor::PGroupIDs
5809 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5810                                   const gp_Vec&        theStep,
5811                                   const int            theNbSteps,
5812                                   TTElemOfElemListMap& newElemsMap,
5813                                   const int            theFlags,
5814                                   const double         theTolerance)
5815 {
5816   ExtrusParam aParams( theStep, theNbSteps, theFlags, theTolerance );
5817   return ExtrusionSweep( theElems, aParams, newElemsMap );
5818 }
5819
5820
5821 //=======================================================================
5822 //function : ExtrusionSweep
5823 //purpose  :
5824 //=======================================================================
5825
5826 SMESH_MeshEditor::PGroupIDs
5827 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5828                                   ExtrusParam&         theParams,
5829                                   TTElemOfElemListMap& newElemsMap)
5830 {
5831   myLastCreatedElems.Clear();
5832   myLastCreatedNodes.Clear();
5833
5834   // source elements for each generated one
5835   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5836
5837   //SMESHDS_Mesh* aMesh = GetMeshDS();
5838
5839   setElemsFirst( theElemSets );
5840   const int nbSteps = theParams.NbSteps();
5841   theParams.SetElementsToUse( theElemSets[0] );
5842
5843   TNodeOfNodeListMap mapNewNodes;
5844   //TNodeOfNodeVecMap mapNewNodes;
5845   TElemOfVecOfNnlmiMap mapElemNewNodes;
5846   //TElemOfVecOfMapNodesMap mapElemNewNodes;
5847
5848   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5849                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5850                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5851   // loop on theElems
5852   TIDSortedElemSet::iterator itElem;
5853   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5854   {
5855     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5856     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5857     {
5858       // check element type
5859       const SMDS_MeshElement* elem = *itElem;
5860       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5861         continue;
5862
5863       const size_t nbNodes = elem->NbNodes();
5864       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5865       newNodesItVec.reserve( nbNodes );
5866
5867       // loop on elem nodes
5868       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5869       while ( itN->more() )
5870       {
5871         // check if a node has been already sweeped
5872         const SMDS_MeshNode* node = cast2Node( itN->next() );
5873         TNodeOfNodeListMap::iterator nIt =
5874           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5875         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5876         if ( listNewNodes.empty() )
5877         {
5878           // make new nodes
5879
5880           // check if we are to create medium nodes between corner ones
5881           bool needMediumNodes = false;
5882           if ( isQuadraticMesh )
5883           {
5884             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5885             while (it->more() && !needMediumNodes )
5886             {
5887               const SMDS_MeshElement* invElem = it->next();
5888               if ( invElem != elem && !theElems.count( invElem )) continue;
5889               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5890               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5891                 needMediumNodes = true;
5892             }
5893           }
5894           // create nodes for all steps
5895           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5896           {
5897             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5898             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5899             {
5900               myLastCreatedNodes.Append( *newNodesIt );
5901               srcNodes.Append( node );
5902             }
5903           }
5904           else
5905           {
5906             break; // newNodesItVec will be shorter than nbNodes
5907           }
5908         }
5909         newNodesItVec.push_back( nIt );
5910       }
5911       // make new elements
5912       if ( newNodesItVec.size() == nbNodes )
5913         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5914     }
5915   }
5916
5917   if ( theParams.ToMakeBoundary() ) {
5918     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5919   }
5920   PGroupIDs newGroupIDs;
5921   if ( theParams.ToMakeGroups() )
5922     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5923
5924   return newGroupIDs;
5925 }
5926
5927 //=======================================================================
5928 //function : ExtrusionAlongTrack
5929 //purpose  :
5930 //=======================================================================
5931 SMESH_MeshEditor::Extrusion_Error
5932 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5933                                        SMESH_subMesh*       theTrack,
5934                                        const SMDS_MeshNode* theN1,
5935                                        const bool           theHasAngles,
5936                                        list<double>&        theAngles,
5937                                        const bool           theLinearVariation,
5938                                        const bool           theHasRefPoint,
5939                                        const gp_Pnt&        theRefPoint,
5940                                        const bool           theMakeGroups)
5941 {
5942   MESSAGE("ExtrusionAlongTrack");
5943   myLastCreatedElems.Clear();
5944   myLastCreatedNodes.Clear();
5945
5946   int aNbE;
5947   std::list<double> aPrms;
5948   TIDSortedElemSet::iterator itElem;
5949
5950   gp_XYZ aGC;
5951   TopoDS_Edge aTrackEdge;
5952   TopoDS_Vertex aV1, aV2;
5953
5954   SMDS_ElemIteratorPtr aItE;
5955   SMDS_NodeIteratorPtr aItN;
5956   SMDSAbs_ElementType aTypeE;
5957
5958   TNodeOfNodeListMap mapNewNodes;
5959
5960   // 1. Check data
5961   aNbE = theElements[0].size() + theElements[1].size();
5962   // nothing to do
5963   if ( !aNbE )
5964     return EXTR_NO_ELEMENTS;
5965
5966   // 1.1 Track Pattern
5967   ASSERT( theTrack );
5968
5969   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5970   if ( !pSubMeshDS )
5971     return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
5972                                 theHasAngles, theAngles, theLinearVariation,
5973                                 theHasRefPoint, theRefPoint, theMakeGroups );
5974
5975   aItE = pSubMeshDS->GetElements();
5976   while ( aItE->more() ) {
5977     const SMDS_MeshElement* pE = aItE->next();
5978     aTypeE = pE->GetType();
5979     // Pattern must contain links only
5980     if ( aTypeE != SMDSAbs_Edge )
5981       return EXTR_PATH_NOT_EDGE;
5982   }
5983
5984   list<SMESH_MeshEditor_PathPoint> fullList;
5985
5986   const TopoDS_Shape& aS = theTrack->GetSubShape();
5987   // Sub-shape for the Pattern must be an Edge or Wire
5988   if( aS.ShapeType() == TopAbs_EDGE ) {
5989     aTrackEdge = TopoDS::Edge( aS );
5990     // the Edge must not be degenerated
5991     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5992       return EXTR_BAD_PATH_SHAPE;
5993     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5994     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5995     const SMDS_MeshNode* aN1 = aItN->next();
5996     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5997     const SMDS_MeshNode* aN2 = aItN->next();
5998     // starting node must be aN1 or aN2
5999     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6000       return EXTR_BAD_STARTING_NODE;
6001     aItN = pSubMeshDS->GetNodes();
6002     while ( aItN->more() ) {
6003       const SMDS_MeshNode* pNode = aItN->next();
6004       const SMDS_EdgePosition* pEPos =
6005         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6006       double aT = pEPos->GetUParameter();
6007       aPrms.push_back( aT );
6008     }
6009     //Extrusion_Error err =
6010     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6011   } else if( aS.ShapeType() == TopAbs_WIRE ) {
6012     list< SMESH_subMesh* > LSM;
6013     TopTools_SequenceOfShape Edges;
6014     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6015     while(itSM->more()) {
6016       SMESH_subMesh* SM = itSM->next();
6017       LSM.push_back(SM);
6018       const TopoDS_Shape& aS = SM->GetSubShape();
6019       Edges.Append(aS);
6020     }
6021     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6022     int startNid = theN1->GetID();
6023     TColStd_MapOfInteger UsedNums;
6024
6025     int NbEdges = Edges.Length();
6026     int i = 1;
6027     for(; i<=NbEdges; i++) {
6028       int k = 0;
6029       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6030       for(; itLSM!=LSM.end(); itLSM++) {
6031         k++;
6032         if(UsedNums.Contains(k)) continue;
6033         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6034         SMESH_subMesh* locTrack = *itLSM;
6035         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6036         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6037         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6038         const SMDS_MeshNode* aN1 = aItN->next();
6039         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6040         const SMDS_MeshNode* aN2 = aItN->next();
6041         // starting node must be aN1 or aN2
6042         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6043         // 2. Collect parameters on the track edge
6044         aPrms.clear();
6045         aItN = locMeshDS->GetNodes();
6046         while ( aItN->more() ) {
6047           const SMDS_MeshNode* pNode = aItN->next();
6048           const SMDS_EdgePosition* pEPos =
6049             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6050           double aT = pEPos->GetUParameter();
6051           aPrms.push_back( aT );
6052         }
6053         list<SMESH_MeshEditor_PathPoint> LPP;
6054         //Extrusion_Error err =
6055         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6056         LLPPs.push_back(LPP);
6057         UsedNums.Add(k);
6058         // update startN for search following egde
6059         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6060         else startNid = aN1->GetID();
6061         break;
6062       }
6063     }
6064     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6065     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6066     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6067     for(; itPP!=firstList.end(); itPP++) {
6068       fullList.push_back( *itPP );
6069     }
6070     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6071     fullList.pop_back();
6072     itLLPP++;
6073     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6074       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6075       itPP = currList.begin();
6076       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6077       gp_Dir D1 = PP1.Tangent();
6078       gp_Dir D2 = PP2.Tangent();
6079       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6080                            (D1.Z()+D2.Z())/2 ) );
6081       PP1.SetTangent(Dnew);
6082       fullList.push_back(PP1);
6083       itPP++;
6084       for(; itPP!=firstList.end(); itPP++) {
6085         fullList.push_back( *itPP );
6086       }
6087       PP1 = fullList.back();
6088       fullList.pop_back();
6089     }
6090     // if wire not closed
6091     fullList.push_back(PP1);
6092     // else ???
6093   }
6094   else {
6095     return EXTR_BAD_PATH_SHAPE;
6096   }
6097
6098   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6099                           theHasRefPoint, theRefPoint, theMakeGroups);
6100 }
6101
6102
6103 //=======================================================================
6104 //function : ExtrusionAlongTrack
6105 //purpose  :
6106 //=======================================================================
6107 SMESH_MeshEditor::Extrusion_Error
6108 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
6109                                        SMESH_Mesh*          theTrack,
6110                                        const SMDS_MeshNode* theN1,
6111                                        const bool           theHasAngles,
6112                                        list<double>&        theAngles,
6113                                        const bool           theLinearVariation,
6114                                        const bool           theHasRefPoint,
6115                                        const gp_Pnt&        theRefPoint,
6116                                        const bool           theMakeGroups)
6117 {
6118   myLastCreatedElems.Clear();
6119   myLastCreatedNodes.Clear();
6120
6121   int aNbE;
6122   std::list<double> aPrms;
6123   TIDSortedElemSet::iterator itElem;
6124
6125   gp_XYZ aGC;
6126   TopoDS_Edge aTrackEdge;
6127   TopoDS_Vertex aV1, aV2;
6128
6129   SMDS_ElemIteratorPtr aItE;
6130   SMDS_NodeIteratorPtr aItN;
6131   SMDSAbs_ElementType aTypeE;
6132
6133   TNodeOfNodeListMap mapNewNodes;
6134
6135   // 1. Check data
6136   aNbE = theElements[0].size() + theElements[1].size();
6137   // nothing to do
6138   if ( !aNbE )
6139     return EXTR_NO_ELEMENTS;
6140
6141   // 1.1 Track Pattern
6142   ASSERT( theTrack );
6143
6144   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6145
6146   aItE = pMeshDS->elementsIterator();
6147   while ( aItE->more() ) {
6148     const SMDS_MeshElement* pE = aItE->next();
6149     aTypeE = pE->GetType();
6150     // Pattern must contain links only
6151     if ( aTypeE != SMDSAbs_Edge )
6152       return EXTR_PATH_NOT_EDGE;
6153   }
6154
6155   list<SMESH_MeshEditor_PathPoint> fullList;
6156
6157   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6158
6159   if ( !theTrack->HasShapeToMesh() ) {
6160     //Mesh without shape
6161     const SMDS_MeshNode* currentNode = NULL;
6162     const SMDS_MeshNode* prevNode = theN1;
6163     std::vector<const SMDS_MeshNode*> aNodesList;
6164     aNodesList.push_back(theN1);
6165     int nbEdges = 0, conn=0;
6166     const SMDS_MeshElement* prevElem = NULL;
6167     const SMDS_MeshElement* currentElem = NULL;
6168     int totalNbEdges = theTrack->NbEdges();
6169     SMDS_ElemIteratorPtr nIt;
6170
6171     //check start node
6172     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6173       return EXTR_BAD_STARTING_NODE;
6174     }
6175
6176     conn = nbEdgeConnectivity(theN1);
6177     if( conn != 1 )
6178       return EXTR_PATH_NOT_EDGE;
6179
6180     aItE = theN1->GetInverseElementIterator();
6181     prevElem = aItE->next();
6182     currentElem = prevElem;
6183     //Get all nodes
6184     if(totalNbEdges == 1 ) {
6185       nIt = currentElem->nodesIterator();
6186       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6187       if(currentNode == prevNode)
6188         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6189       aNodesList.push_back(currentNode);
6190     } else {
6191       nIt = currentElem->nodesIterator();
6192       while( nIt->more() ) {
6193         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6194         if(currentNode == prevNode)
6195           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6196         aNodesList.push_back(currentNode);
6197
6198         //case of the closed mesh
6199         if(currentNode == theN1) {
6200           nbEdges++;
6201           break;
6202         }
6203
6204         conn = nbEdgeConnectivity(currentNode);
6205         if(conn > 2) {
6206           return EXTR_PATH_NOT_EDGE;
6207         }else if( conn == 1 && nbEdges > 0 ) {
6208           //End of the path
6209           nbEdges++;
6210           break;
6211         }else {
6212           prevNode = currentNode;
6213           aItE = currentNode->GetInverseElementIterator();
6214           currentElem = aItE->next();
6215           if( currentElem  == prevElem)
6216             currentElem = aItE->next();
6217           nIt = currentElem->nodesIterator();
6218           prevElem = currentElem;
6219           nbEdges++;
6220         }
6221       }
6222     }
6223
6224     if(nbEdges != totalNbEdges)
6225       return EXTR_PATH_NOT_EDGE;
6226
6227     TopTools_SequenceOfShape Edges;
6228     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6229     int startNid = theN1->GetID();
6230     for ( size_t i = 1; i < aNodesList.size(); i++ )
6231     {
6232       gp_Pnt     p1 = SMESH_TNodeXYZ( aNodesList[i-1] );
6233       gp_Pnt     p2 = SMESH_TNodeXYZ( aNodesList[i] );
6234       TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6235       list<SMESH_MeshEditor_PathPoint> LPP;
6236       aPrms.clear();
6237       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6238       LLPPs.push_back(LPP);
6239       if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i  ]->GetID();
6240       else                                        startNid = aNodesList[i-1]->GetID();
6241     }
6242
6243     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6244     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6245     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6246     for(; itPP!=firstList.end(); itPP++) {
6247       fullList.push_back( *itPP );
6248     }
6249
6250     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6251     SMESH_MeshEditor_PathPoint PP2;
6252     fullList.pop_back();
6253     itLLPP++;
6254     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6255       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6256       itPP = currList.begin();
6257       PP2 = currList.front();
6258       gp_Dir D1 = PP1.Tangent();
6259       gp_Dir D2 = PP2.Tangent();
6260       gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6261       PP1.SetTangent(Dnew);
6262       fullList.push_back(PP1);
6263       itPP++;
6264       for(; itPP!=currList.end(); itPP++) {
6265         fullList.push_back( *itPP );
6266       }
6267       PP1 = fullList.back();
6268       fullList.pop_back();
6269     }
6270     fullList.push_back(PP1);
6271
6272   } // Sub-shape for the Pattern must be an Edge or Wire
6273   else if ( aS.ShapeType() == TopAbs_EDGE )
6274   {
6275     aTrackEdge = TopoDS::Edge( aS );
6276     // the Edge must not be degenerated
6277     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6278       return EXTR_BAD_PATH_SHAPE;
6279     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6280     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6281     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6282     // starting node must be aN1 or aN2
6283     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6284       return EXTR_BAD_STARTING_NODE;
6285     aItN = pMeshDS->nodesIterator();
6286     while ( aItN->more() ) {
6287       const SMDS_MeshNode* pNode = aItN->next();
6288       if( pNode==aN1 || pNode==aN2 ) continue;
6289       const SMDS_EdgePosition* pEPos =
6290         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6291       double aT = pEPos->GetUParameter();
6292       aPrms.push_back( aT );
6293     }
6294     //Extrusion_Error err =
6295     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6296   }
6297   else if( aS.ShapeType() == TopAbs_WIRE ) {
6298     list< SMESH_subMesh* > LSM;
6299     TopTools_SequenceOfShape Edges;
6300     TopExp_Explorer eExp(aS, TopAbs_EDGE);
6301     for(; eExp.More(); eExp.Next()) {
6302       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6303       if( SMESH_Algo::isDegenerated(E) ) continue;
6304       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6305       if(SM) {
6306         LSM.push_back(SM);
6307         Edges.Append(E);
6308       }
6309     }
6310     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6311     TopoDS_Vertex aVprev;
6312     TColStd_MapOfInteger UsedNums;
6313     int NbEdges = Edges.Length();
6314     int i = 1;
6315     for(; i<=NbEdges; i++) {
6316       int k = 0;
6317       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6318       for(; itLSM!=LSM.end(); itLSM++) {
6319         k++;
6320         if(UsedNums.Contains(k)) continue;
6321         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6322         SMESH_subMesh* locTrack = *itLSM;
6323         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6324         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6325         bool aN1isOK = false, aN2isOK = false;
6326         if ( aVprev.IsNull() ) {
6327           // if previous vertex is not yet defined, it means that we in the beginning of wire
6328           // and we have to find initial vertex corresponding to starting node theN1
6329           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6330           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6331           // starting node must be aN1 or aN2
6332           aN1isOK = ( aN1 && aN1 == theN1 );
6333           aN2isOK = ( aN2 && aN2 == theN1 );
6334         }
6335         else {
6336           // we have specified ending vertex of the previous edge on the previous iteration
6337           // and we have just to check that it corresponds to any vertex in current segment
6338           aN1isOK = aVprev.IsSame( aV1 );
6339           aN2isOK = aVprev.IsSame( aV2 );
6340         }
6341         if ( !aN1isOK && !aN2isOK ) continue;
6342         // 2. Collect parameters on the track edge
6343         aPrms.clear();
6344         aItN = locMeshDS->GetNodes();
6345         while ( aItN->more() ) {
6346           const SMDS_MeshNode*     pNode = aItN->next();
6347           const SMDS_EdgePosition* pEPos =
6348             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6349           double aT = pEPos->GetUParameter();
6350           aPrms.push_back( aT );
6351         }
6352         list<SMESH_MeshEditor_PathPoint> LPP;
6353         //Extrusion_Error err =
6354         MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6355         LLPPs.push_back(LPP);
6356         UsedNums.Add(k);
6357         // update startN for search following egde
6358         if ( aN1isOK ) aVprev = aV2;
6359         else           aVprev = aV1;
6360         break;
6361       }
6362     }
6363     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6364     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6365     fullList.splice( fullList.end(), firstList );
6366
6367     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6368     fullList.pop_back();
6369     itLLPP++;
6370     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6371       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6372       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6373       gp_Dir D1 = PP1.Tangent();
6374       gp_Dir D2 = PP2.Tangent();
6375       gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6376       PP1.SetTangent(Dnew);
6377       fullList.push_back(PP1);
6378       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6379       PP1 = fullList.back();
6380       fullList.pop_back();
6381     }
6382     // if wire not closed
6383     fullList.push_back(PP1);
6384     // else ???
6385   }
6386   else {
6387     return EXTR_BAD_PATH_SHAPE;
6388   }
6389
6390   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6391                           theHasRefPoint, theRefPoint, theMakeGroups);
6392 }
6393
6394
6395 //=======================================================================
6396 //function : MakeEdgePathPoints
6397 //purpose  : auxilary for ExtrusionAlongTrack
6398 //=======================================================================
6399 SMESH_MeshEditor::Extrusion_Error
6400 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>&                aPrms,
6401                                      const TopoDS_Edge&                aTrackEdge,
6402                                      bool                              FirstIsStart,
6403                                      list<SMESH_MeshEditor_PathPoint>& LPP)
6404 {
6405   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6406   aTolVec=1.e-7;
6407   aTolVec2=aTolVec*aTolVec;
6408   double aT1, aT2;
6409   TopoDS_Vertex aV1, aV2;
6410   TopExp::Vertices( aTrackEdge, aV1, aV2 );
6411   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6412   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6413   // 2. Collect parameters on the track edge
6414   aPrms.push_front( aT1 );
6415   aPrms.push_back( aT2 );
6416   // sort parameters
6417   aPrms.sort();
6418   if( FirstIsStart ) {
6419     if ( aT1 > aT2 ) {
6420       aPrms.reverse();
6421     }
6422   }
6423   else {
6424     if ( aT2 > aT1 ) {
6425       aPrms.reverse();
6426     }
6427   }
6428   // 3. Path Points
6429   SMESH_MeshEditor_PathPoint aPP;
6430   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6431   std::list<double>::iterator aItD = aPrms.begin();
6432   for(; aItD != aPrms.end(); ++aItD) {
6433     double aT = *aItD;
6434     gp_Pnt aP3D;
6435     gp_Vec aVec;
6436     aC3D->D1( aT, aP3D, aVec );
6437     aL2 = aVec.SquareMagnitude();
6438     if ( aL2 < aTolVec2 )
6439       return EXTR_CANT_GET_TANGENT;
6440     gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6441     aPP.SetPnt( aP3D );
6442     aPP.SetTangent( aTgt );
6443     aPP.SetParameter( aT );
6444     LPP.push_back(aPP);
6445   }
6446   return EXTR_OK;
6447 }
6448
6449
6450 //=======================================================================
6451 //function : MakeExtrElements
6452 //purpose  : auxilary for ExtrusionAlongTrack
6453 //=======================================================================
6454 SMESH_MeshEditor::Extrusion_Error
6455 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet                  theElemSets[2],
6456                                    list<SMESH_MeshEditor_PathPoint>& fullList,
6457                                    const bool                        theHasAngles,
6458                                    list<double>&                     theAngles,
6459                                    const bool                        theLinearVariation,
6460                                    const bool                        theHasRefPoint,
6461                                    const gp_Pnt&                     theRefPoint,
6462                                    const bool                        theMakeGroups)
6463 {
6464   const int aNbTP = fullList.size();
6465
6466   // Angles
6467   if( theHasAngles && !theAngles.empty() && theLinearVariation )
6468     LinearAngleVariation(aNbTP-1, theAngles);
6469
6470   // fill vector of path points with angles
6471   vector<SMESH_MeshEditor_PathPoint> aPPs;
6472   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6473   list<double>::iterator                 itAngles = theAngles.begin();
6474   aPPs.push_back( *itPP++ );
6475   for( ; itPP != fullList.end(); itPP++) {
6476     aPPs.push_back( *itPP );
6477     if ( theHasAngles && itAngles != theAngles.end() )
6478       aPPs.back().SetAngle( *itAngles++ );
6479   }
6480
6481   TNodeOfNodeListMap   mapNewNodes;
6482   TElemOfVecOfNnlmiMap mapElemNewNodes;
6483   TTElemOfElemListMap  newElemsMap;
6484   TIDSortedElemSet::iterator itElem;
6485   // source elements for each generated one
6486   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6487
6488   // 3. Center of rotation aV0
6489   gp_Pnt aV0 = theRefPoint;
6490   if ( !theHasRefPoint )
6491   {
6492     gp_XYZ aGC( 0.,0.,0. );
6493     TIDSortedElemSet newNodes;
6494
6495     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6496     {
6497       TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6498       itElem = theElements.begin();
6499       for ( ; itElem != theElements.end(); itElem++ )
6500       {
6501         const SMDS_MeshElement* elem = *itElem;
6502         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6503         while ( itN->more() ) {
6504           const SMDS_MeshElement* node = itN->next();
6505           if ( newNodes.insert( node ).second )
6506             aGC += SMESH_TNodeXYZ( node );
6507         }
6508       }
6509     }
6510     aGC /= newNodes.size();
6511     aV0.SetXYZ( aGC );
6512   } // if (!theHasRefPoint) {
6513
6514   // 4. Processing the elements
6515   SMESHDS_Mesh* aMesh = GetMeshDS();
6516   list<const SMDS_MeshNode*> emptyList;
6517
6518   setElemsFirst( theElemSets );
6519   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6520   {
6521     TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6522     for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6523     {
6524       const SMDS_MeshElement* elem = *itElem;
6525
6526       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6527       newNodesItVec.reserve( elem->NbNodes() );
6528
6529       // loop on elem nodes
6530       int nodeIndex = -1;
6531       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6532       while ( itN->more() )
6533       {
6534         ++nodeIndex;
6535         // check if a node has been already processed
6536         const SMDS_MeshNode* node = cast2Node( itN->next() );
6537         TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6538         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6539         if ( listNewNodes.empty() )
6540         {
6541           // make new nodes
6542           Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6543           gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6544           gp_Ax1 anAx1, anAxT1T0;
6545           gp_Dir aDT1x, aDT0x, aDT1T0;
6546
6547           aTolAng=1.e-4;
6548
6549           aV0x = aV0;
6550           aPN0 = SMESH_TNodeXYZ( node );
6551
6552           const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6553           aP0x = aPP0.Pnt();
6554           aDT0x= aPP0.Tangent();
6555
6556           for ( int j = 1; j < aNbTP; ++j ) {
6557             const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6558             aP1x     = aPP1.Pnt();
6559             aDT1x    = aPP1.Tangent();
6560             aAngle1x = aPP1.Angle();
6561
6562             gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6563             // Translation
6564             gp_Vec aV01x( aP0x, aP1x );
6565             aTrsf.SetTranslation( aV01x );
6566
6567             // traslated point
6568             aV1x = aV0x.Transformed( aTrsf );
6569             aPN1 = aPN0.Transformed( aTrsf );
6570
6571             // rotation 1 [ T1,T0 ]
6572             aAngleT1T0=-aDT1x.Angle( aDT0x );
6573             if (fabs(aAngleT1T0) > aTolAng)
6574             {
6575               aDT1T0=aDT1x^aDT0x;
6576               anAxT1T0.SetLocation( aV1x );
6577               anAxT1T0.SetDirection( aDT1T0 );
6578               aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6579
6580               aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6581             }
6582
6583             // rotation 2
6584             if ( theHasAngles ) {
6585               anAx1.SetLocation( aV1x );
6586               anAx1.SetDirection( aDT1x );
6587               aTrsfRot.SetRotation( anAx1, aAngle1x );
6588
6589               aPN1 = aPN1.Transformed( aTrsfRot );
6590             }
6591
6592             // make new node
6593             if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6594             {
6595               // create additional node
6596               gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6597               const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6598               myLastCreatedNodes.Append(newNode);
6599               srcNodes.Append( node );
6600               listNewNodes.push_back( newNode );
6601             }
6602             const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6603             myLastCreatedNodes.Append(newNode);
6604             srcNodes.Append( node );
6605             listNewNodes.push_back( newNode );
6606
6607             aPN0 = aPN1;
6608             aP0x = aP1x;
6609             aV0x = aV1x;
6610             aDT0x = aDT1x;
6611           }
6612         }
6613         else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6614         {
6615           // if current elem is quadratic and current node is not medium
6616           // we have to check - may be it is needed to insert additional nodes
6617           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6618           if ((int) listNewNodes.size() == aNbTP-1 )
6619           {
6620             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6621             gp_XYZ P(node->X(), node->Y(), node->Z());
6622             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6623             int i;
6624             for(i=0; i<aNbTP-1; i++) {
6625               const SMDS_MeshNode* N = *it;
6626               double x = ( N->X() + P.X() )/2.;
6627               double y = ( N->Y() + P.Y() )/2.;
6628               double z = ( N->Z() + P.Z() )/2.;
6629               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6630               srcNodes.Append( node );
6631               myLastCreatedNodes.Append(newN);
6632               aNodes[2*i] = newN;
6633               aNodes[2*i+1] = N;
6634               P = gp_XYZ(N->X(),N->Y(),N->Z());
6635             }
6636             listNewNodes.clear();
6637             for(i=0; i<2*(aNbTP-1); i++) {
6638               listNewNodes.push_back(aNodes[i]);
6639             }
6640           }
6641         }
6642
6643         newNodesItVec.push_back( nIt );
6644       }
6645
6646       // make new elements
6647       sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6648     }
6649   }
6650
6651   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6652
6653   if ( theMakeGroups )
6654     generateGroups( srcNodes, srcElems, "extruded");
6655
6656   return EXTR_OK;
6657 }
6658
6659
6660 //=======================================================================
6661 //function : LinearAngleVariation
6662 //purpose  : auxilary for ExtrusionAlongTrack
6663 //=======================================================================
6664 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6665                                             list<double>& Angles)
6666 {
6667   int nbAngles = Angles.size();
6668   if( nbSteps > nbAngles ) {
6669     vector<double> theAngles(nbAngles);
6670     list<double>::iterator it = Angles.begin();
6671     int i = -1;
6672     for(; it!=Angles.end(); it++) {
6673       i++;
6674       theAngles[i] = (*it);
6675     }
6676     list<double> res;
6677     double rAn2St = double( nbAngles ) / double( nbSteps );
6678     double angPrev = 0, angle;
6679     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6680       double angCur = rAn2St * ( iSt+1 );
6681       double angCurFloor  = floor( angCur );
6682       double angPrevFloor = floor( angPrev );
6683       if ( angPrevFloor == angCurFloor )
6684         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6685       else {
6686         int iP = int( angPrevFloor );
6687         double angPrevCeil = ceil(angPrev);
6688         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6689
6690         int iC = int( angCurFloor );
6691         if ( iC < nbAngles )
6692           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6693
6694         iP = int( angPrevCeil );
6695         while ( iC-- > iP )
6696           angle += theAngles[ iC ];
6697       }
6698       res.push_back(angle);
6699       angPrev = angCur;
6700     }
6701     Angles.clear();
6702     it = res.begin();
6703     for(; it!=res.end(); it++)
6704       Angles.push_back( *it );
6705   }
6706 }
6707
6708
6709 //================================================================================
6710 /*!
6711  * \brief Move or copy theElements applying theTrsf to their nodes
6712  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6713  *  \param theTrsf - transformation to apply
6714  *  \param theCopy - if true, create translated copies of theElems
6715  *  \param theMakeGroups - if true and theCopy, create translated groups
6716  *  \param theTargetMesh - mesh to copy translated elements into
6717  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6718  */
6719 //================================================================================
6720
6721 SMESH_MeshEditor::PGroupIDs
6722 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6723                              const gp_Trsf&     theTrsf,
6724                              const bool         theCopy,
6725                              const bool         theMakeGroups,
6726                              SMESH_Mesh*        theTargetMesh)
6727 {
6728   myLastCreatedElems.Clear();
6729   myLastCreatedNodes.Clear();
6730
6731   bool needReverse = false;
6732   string groupPostfix;
6733   switch ( theTrsf.Form() ) {
6734   case gp_PntMirror:
6735     MESSAGE("gp_PntMirror");
6736     needReverse = true;
6737     groupPostfix = "mirrored";
6738     break;
6739   case gp_Ax1Mirror:
6740     MESSAGE("gp_Ax1Mirror");
6741     groupPostfix = "mirrored";
6742     break;
6743   case gp_Ax2Mirror:
6744     MESSAGE("gp_Ax2Mirror");
6745     needReverse = true;
6746     groupPostfix = "mirrored";
6747     break;
6748   case gp_Rotation:
6749     MESSAGE("gp_Rotation");
6750     groupPostfix = "rotated";
6751     break;
6752   case gp_Translation:
6753     MESSAGE("gp_Translation");
6754     groupPostfix = "translated";
6755     break;
6756   case gp_Scale:
6757     MESSAGE("gp_Scale");
6758     groupPostfix = "scaled";
6759     break;
6760   case gp_CompoundTrsf: // different scale by axis
6761     MESSAGE("gp_CompoundTrsf");
6762     groupPostfix = "scaled";
6763     break;
6764   default:
6765     MESSAGE("default");
6766     needReverse = false;
6767     groupPostfix = "transformed";
6768   }
6769
6770   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6771   SMESHDS_Mesh* aMesh    = GetMeshDS();
6772
6773   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6774   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6775   SMESH_MeshEditor::ElemFeatures elemType;
6776
6777   // map old node to new one
6778   TNodeNodeMap nodeMap;
6779
6780   // elements sharing moved nodes; those of them which have all
6781   // nodes mirrored but are not in theElems are to be reversed
6782   TIDSortedElemSet inverseElemSet;
6783
6784   // source elements for each generated one
6785   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6786
6787   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6788   TIDSortedElemSet orphanNode;
6789
6790   if ( theElems.empty() ) // transform the whole mesh
6791   {
6792     // add all elements
6793     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6794     while ( eIt->more() ) theElems.insert( eIt->next() );
6795     // add orphan nodes
6796     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6797     while ( nIt->more() )
6798     {
6799       const SMDS_MeshNode* node = nIt->next();
6800       if ( node->NbInverseElements() == 0)
6801         orphanNode.insert( node );
6802     }
6803   }
6804
6805   // loop on elements to transform nodes : first orphan nodes then elems
6806   TIDSortedElemSet::iterator itElem;
6807   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6808   for (int i=0; i<2; i++)
6809     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6810     {
6811       const SMDS_MeshElement* elem = *itElem;
6812       if ( !elem )
6813         continue;
6814
6815       // loop on elem nodes
6816       double coord[3];
6817       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6818       while ( itN->more() )
6819       {
6820         const SMDS_MeshNode* node = cast2Node( itN->next() );
6821         // check if a node has been already transformed
6822         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6823           nodeMap.insert( make_pair ( node, node ));
6824         if ( !n2n_isnew.second )
6825           continue;
6826
6827         node->GetXYZ( coord );
6828         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6829         if ( theTargetMesh ) {
6830           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6831           n2n_isnew.first->second = newNode;
6832           myLastCreatedNodes.Append(newNode);
6833           srcNodes.Append( node );
6834         }
6835         else if ( theCopy ) {
6836           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6837           n2n_isnew.first->second = newNode;
6838           myLastCreatedNodes.Append(newNode);
6839           srcNodes.Append( node );
6840         }
6841         else {
6842           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6843           // node position on shape becomes invalid
6844           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6845             ( SMDS_SpacePosition::originSpacePosition() );
6846         }
6847
6848         // keep inverse elements
6849         if ( !theCopy && !theTargetMesh && needReverse ) {
6850           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6851           while ( invElemIt->more() ) {
6852             const SMDS_MeshElement* iel = invElemIt->next();
6853             inverseElemSet.insert( iel );
6854           }
6855         }
6856       }
6857     } // loop on elems in { &orphanNode, &theElems };
6858
6859   // either create new elements or reverse mirrored ones
6860   if ( !theCopy && !needReverse && !theTargetMesh )
6861     return PGroupIDs();
6862
6863   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6864
6865   // Replicate or reverse elements
6866
6867   std::vector<int> iForw;
6868   vector<const SMDS_MeshNode*> nodes;
6869   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6870   {
6871     const SMDS_MeshElement* elem = *itElem;
6872     if ( !elem ) continue;
6873
6874     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6875     size_t               nbNodes  = elem->NbNodes();
6876     if ( geomType == SMDSGeom_NONE ) continue; // node
6877
6878     nodes.resize( nbNodes );
6879
6880     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6881     {
6882       const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6883       if (!aPolyedre)
6884         continue;
6885       nodes.clear();
6886       bool allTransformed = true;
6887       int nbFaces = aPolyedre->NbFaces();
6888       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6889       {
6890         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6891         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6892         {
6893           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6894           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6895           if ( nodeMapIt == nodeMap.end() )
6896             allTransformed = false; // not all nodes transformed
6897           else
6898             nodes.push_back((*nodeMapIt).second);
6899         }
6900         if ( needReverse && allTransformed )
6901           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6902       }
6903       if ( !allTransformed )
6904         continue; // not all nodes transformed
6905     }
6906     else // ----------------------- the rest element types
6907     {
6908       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6909       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6910       const vector<int>&    i = needReverse ? iRev : iForw;
6911
6912       // find transformed nodes
6913       size_t iNode = 0;
6914       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6915       while ( itN->more() ) {
6916         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6917         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6918         if ( nodeMapIt == nodeMap.end() )
6919           break; // not all nodes transformed
6920         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6921       }
6922       if ( iNode != nbNodes )
6923         continue; // not all nodes transformed
6924     }
6925
6926     if ( editor ) {
6927       // copy in this or a new mesh
6928       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6929         srcElems.Append( elem );
6930     }
6931     else {
6932       // reverse element as it was reversed by transformation
6933       if ( nbNodes > 2 )
6934         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6935     }
6936
6937   } // loop on elements
6938
6939   if ( editor && editor != this )
6940     myLastCreatedElems = editor->myLastCreatedElems;
6941
6942   PGroupIDs newGroupIDs;
6943
6944   if ( ( theMakeGroups && theCopy ) ||
6945        ( theMakeGroups && theTargetMesh ) )
6946     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6947
6948   return newGroupIDs;
6949 }
6950
6951 //=======================================================================
6952 /*!
6953  * \brief Create groups of elements made during transformation
6954  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6955  *  \param elemGens - elements making corresponding myLastCreatedElems
6956  *  \param postfix - to append to names of new groups
6957  *  \param targetMesh - mesh to create groups in
6958  *  \param topPresent - is there "top" elements that are created by sweeping
6959  */
6960 //=======================================================================
6961
6962 SMESH_MeshEditor::PGroupIDs
6963 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6964                                  const SMESH_SequenceOfElemPtr& elemGens,
6965                                  const std::string&             postfix,
6966                                  SMESH_Mesh*                    targetMesh,
6967                                  const bool                     topPresent)
6968 {
6969   PGroupIDs newGroupIDs( new list<int> );
6970   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6971
6972   // Sort existing groups by types and collect their names
6973
6974   // containers to store an old group and generated new ones;
6975   // 1st new group is for result elems of different type than a source one;
6976   // 2nd new group is for same type result elems ("top" group at extrusion)
6977   using boost::tuple;
6978   using boost::make_tuple;
6979   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6980   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6981   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6982   // group names
6983   set< string > groupNames;
6984
6985   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6986   if ( !groupIt->more() ) return newGroupIDs;
6987
6988   int newGroupID = mesh->GetGroupIds().back()+1;
6989   while ( groupIt->more() )
6990   {
6991     SMESH_Group * group = groupIt->next();
6992     if ( !group ) continue;
6993     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6994     if ( !groupDS || groupDS->IsEmpty() ) continue;
6995     groupNames.insert    ( group->GetName() );
6996     groupDS->SetStoreName( group->GetName() );
6997     const SMDSAbs_ElementType type = groupDS->GetType();
6998     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6999     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7000     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7001     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7002   }
7003
7004   // Loop on nodes and elements to add them in new groups
7005
7006   vector< const SMDS_MeshElement* > resultElems;
7007   for ( int isNodes = 0; isNodes < 2; ++isNodes )
7008   {
7009     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
7010     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7011     if ( gens.Length() != elems.Length() )
7012       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7013
7014     // loop on created elements
7015     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7016     {
7017       const SMDS_MeshElement* sourceElem = gens( iElem );
7018       if ( !sourceElem ) {
7019         MESSAGE("generateGroups(): NULL source element");
7020         continue;
7021       }
7022       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7023       if ( groupsOldNew.empty() ) { // no groups of this type at all
7024         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7025           ++iElem; // skip all elements made by sourceElem
7026         continue;
7027       }
7028       // collect all elements made by the iElem-th sourceElem
7029       resultElems.clear();
7030       if ( const SMDS_MeshElement* resElem = elems( iElem ))
7031         if ( resElem != sourceElem )
7032           resultElems.push_back( resElem );
7033       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7034         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7035           if ( resElem != sourceElem )
7036             resultElems.push_back( resElem );
7037
7038       const SMDS_MeshElement* topElem = 0;
7039       if ( isNodes ) // there must be a top element
7040       {
7041         topElem = resultElems.back();
7042         resultElems.pop_back();
7043       }
7044       else
7045       {
7046         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7047         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7048           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7049           {
7050             topElem = *resElemIt;
7051             *resElemIt = 0; // erase *resElemIt
7052             break;
7053           }
7054       }
7055       // add resultElems to groups originted from ones the sourceElem belongs to
7056       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7057       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7058       {
7059         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7060         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7061         {
7062           // fill in a new group
7063           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7064           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7065           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7066             if ( *resElemIt )
7067               newGroup.Add( *resElemIt );
7068
7069           // fill a "top" group
7070           if ( topElem )
7071           {
7072             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7073             newTopGroup.Add( topElem );
7074          }
7075         }
7076       }
7077     } // loop on created elements
7078   }// loop on nodes and elements
7079
7080   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7081
7082   list<int> topGrouIds;
7083   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7084   {
7085     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
7086     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7087                                       orderedOldNewGroups[i]->get<2>() };
7088     for ( int is2nd = 0; is2nd < 2; ++is2nd )
7089     {
7090       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7091       if ( newGroupDS->IsEmpty() )
7092       {
7093         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7094       }
7095       else
7096       {
7097         // set group type
7098         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7099
7100         // make a name
7101         const bool isTop = ( topPresent &&
7102                              newGroupDS->GetType() == oldGroupDS->GetType() &&
7103                              is2nd );
7104
7105         string name = oldGroupDS->GetStoreName();
7106         { // remove trailing whitespaces (issue 22599)
7107           size_t size = name.size();
7108           while ( size > 1 && isspace( name[ size-1 ]))
7109             --size;
7110           if ( size != name.size() )
7111           {
7112             name.resize( size );
7113             oldGroupDS->SetStoreName( name.c_str() );
7114           }
7115         }
7116         if ( !targetMesh ) {
7117           string suffix = ( isTop ? "top": postfix.c_str() );
7118           name += "_";
7119           name += suffix;
7120           int nb = 1;
7121           while ( !groupNames.insert( name ).second ) // name exists
7122             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7123         }
7124         else if ( isTop ) {
7125           name += "_top";
7126         }
7127         newGroupDS->SetStoreName( name.c_str() );
7128
7129         // make a SMESH_Groups
7130         mesh->AddGroup( newGroupDS );
7131         if ( isTop )
7132           topGrouIds.push_back( newGroupDS->GetID() );
7133         else
7134           newGroupIDs->push_back( newGroupDS->GetID() );
7135       }
7136     }
7137   }
7138   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7139
7140   return newGroupIDs;
7141 }
7142
7143 //================================================================================
7144 /*!
7145  *  * \brief Return list of group of nodes close to each other within theTolerance
7146  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
7147  *  *        an Octree algorithm
7148  *  \param [in,out] theNodes - the nodes to treat
7149  *  \param [in]     theTolerance - the tolerance
7150  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
7151  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
7152  *         corner and medium nodes in separate groups
7153  */
7154 //================================================================================
7155
7156 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
7157                                             const double         theTolerance,
7158                                             TListOfListOfNodes & theGroupsOfNodes,
7159                                             bool                 theSeparateCornersAndMedium)
7160 {
7161   myLastCreatedElems.Clear();
7162   myLastCreatedNodes.Clear();
7163
7164   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
7165        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
7166        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7167     theSeparateCornersAndMedium = false;
7168
7169   TIDSortedNodeSet& corners = theNodes;
7170   TIDSortedNodeSet  medium;
7171
7172   if ( theNodes.empty() ) // get all nodes in the mesh
7173   {
7174     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7175     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7176     if ( theSeparateCornersAndMedium )
7177       while ( nIt->more() )
7178       {
7179         const SMDS_MeshNode* n = nIt->next();
7180         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7181         nodeSet->insert( nodeSet->end(), n );
7182       }
7183     else
7184       while ( nIt->more() )
7185         theNodes.insert( theNodes.end(),nIt->next() );
7186   }
7187   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7188   {
7189     TIDSortedNodeSet::iterator nIt = corners.begin();
7190     while ( nIt != corners.end() )
7191       if ( SMESH_MesherHelper::IsMedium( *nIt ))
7192       {
7193         medium.insert( medium.end(), *nIt );
7194         corners.erase( nIt++ );
7195       }
7196       else
7197       {
7198         ++nIt;
7199       }
7200   }
7201
7202   if ( !corners.empty() )
7203     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7204   if ( !medium.empty() )
7205     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7206 }
7207
7208 //=======================================================================
7209 //function : SimplifyFace
7210 //purpose  : split a chain of nodes into several closed chains
7211 //=======================================================================
7212
7213 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7214                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7215                                     vector<int>&                         quantities) const
7216 {
7217   int nbNodes = faceNodes.size();
7218   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7219     --nbNodes;
7220   if ( nbNodes < 3 )
7221     return 0;
7222   size_t prevNbQuant = quantities.size();
7223
7224   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7225   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7226   map< const SMDS_MeshNode*, int >::iterator nInd;
7227
7228   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7229   simpleNodes.push_back( faceNodes[0] );
7230   for ( int iCur = 1; iCur < nbNodes; iCur++ )
7231   {
7232     if ( faceNodes[ iCur ] != simpleNodes.back() )
7233     {
7234       int index = simpleNodes.size();
7235       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7236       int prevIndex = nInd->second;
7237       if ( prevIndex < index )
7238       {
7239         // a sub-loop found
7240         int loopLen = index - prevIndex;
7241         if ( loopLen > 2 )
7242         {
7243           // store the sub-loop
7244           quantities.push_back( loopLen );
7245           for ( int i = prevIndex; i < index; i++ )
7246             poly_nodes.push_back( simpleNodes[ i ]);
7247         }
7248         simpleNodes.resize( prevIndex+1 );
7249       }
7250       else
7251       {
7252         simpleNodes.push_back( faceNodes[ iCur ]);
7253       }
7254     }
7255   }
7256
7257   if ( simpleNodes.size() > 2 )
7258   {
7259     quantities.push_back( simpleNodes.size() );
7260     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7261   }
7262
7263   return quantities.size() - prevNbQuant;
7264 }
7265
7266 //=======================================================================
7267 //function : MergeNodes
7268 //purpose  : In each group, the cdr of nodes are substituted by the first one
7269 //           in all elements.
7270 //=======================================================================
7271
7272 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7273 {
7274   MESSAGE("MergeNodes");
7275   myLastCreatedElems.Clear();
7276   myLastCreatedNodes.Clear();
7277
7278   SMESHDS_Mesh* aMesh = GetMeshDS();
7279
7280   TNodeNodeMap nodeNodeMap; // node to replace - new node
7281   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7282   list< int > rmElemIds, rmNodeIds;
7283
7284   // Fill nodeNodeMap and elems
7285
7286   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7287   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7288   {
7289     list<const SMDS_MeshNode*>& nodes = *grIt;
7290     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7291     const SMDS_MeshNode* nToKeep = *nIt;
7292     for ( ++nIt; nIt != nodes.end(); nIt++ )
7293     {
7294       const SMDS_MeshNode* nToRemove = *nIt;
7295       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7296       if ( nToRemove != nToKeep )
7297       {
7298         rmNodeIds.push_back( nToRemove->GetID() );
7299         AddToSameGroups( nToKeep, nToRemove, aMesh );
7300         // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7301         // after MergeNodes() w/o creating node in place of merged ones.
7302         const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7303         if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7304           if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7305             sm->SetIsAlwaysComputed( true );
7306       }
7307       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7308       while ( invElemIt->more() ) {
7309         const SMDS_MeshElement* elem = invElemIt->next();
7310         elems.insert(elem);
7311       }
7312     }
7313   }
7314   // Change element nodes or remove an element
7315
7316   set<const SMDS_MeshNode*> nodeSet;
7317   vector< const SMDS_MeshNode*> curNodes, uniqueNodes;
7318   vector<int> iRepl;
7319   ElemFeatures elemType;
7320
7321   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7322   for ( ; eIt != elems.end(); eIt++ )
7323   {
7324     const SMDS_MeshElement* elem = *eIt;
7325     const           int  nbNodes = elem->NbNodes();
7326     const           int aShapeId = FindShape( elem );
7327     SMDSAbs_EntityType    entity = elem->GetEntityType();
7328
7329     nodeSet.clear();
7330     curNodes.resize( nbNodes );
7331     uniqueNodes.resize( nbNodes );
7332     iRepl.resize( nbNodes );
7333     int iUnique = 0, iCur = 0, nbRepl = 0;
7334
7335     // get new seq of nodes
7336     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7337     while ( itN->more() )
7338     {
7339       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7340
7341       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7342       if ( nnIt != nodeNodeMap.end() ) { // n sticks
7343         n = (*nnIt).second;
7344         { ////////// BUG 0020185: begin
7345           bool stopRecur = false;
7346           set<const SMDS_MeshNode*> nodesRecur;
7347           nodesRecur.insert(n);
7348           while (!stopRecur) {
7349             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7350             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7351               n = (*nnIt_i).second;
7352               if (!nodesRecur.insert(n).second) {
7353                 // error: recursive dependency
7354                 stopRecur = true;
7355               }
7356             }
7357             else
7358               stopRecur = true;
7359           }
7360         } ////////// BUG 0020185: end
7361       }
7362       curNodes[ iCur ] = n;
7363       bool isUnique = nodeSet.insert( n ).second;
7364       if ( isUnique )
7365         uniqueNodes[ iUnique++ ] = n;
7366       else
7367         iRepl[ nbRepl++ ] = iCur;
7368       iCur++;
7369     }
7370
7371     // Analyse element topology after replacement
7372
7373     bool isOk = true;
7374     int nbUniqueNodes = nodeSet.size();
7375     if ( nbNodes != nbUniqueNodes ) // some nodes stick
7376     {
7377       if ( elem->IsPoly() ) // Polygons and Polyhedral volumes
7378       {
7379         if ( elem->GetType() == SMDSAbs_Face ) // Polygon
7380         {
7381           elemType.Init( elem );
7382           const bool isQuad = elemType.myIsQuad;
7383           if ( isQuad )
7384             SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7385               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7386
7387           // a polygon can divide into several elements
7388           vector<const SMDS_MeshNode *> polygons_nodes;
7389           vector<int> quantities;
7390           int nbNew = SimplifyFace( curNodes, polygons_nodes, quantities );
7391           if (nbNew > 0)
7392           {
7393             vector<const SMDS_MeshNode *> face_nodes;
7394             int inode = 0;
7395             for (int iface = 0; iface < nbNew; iface++)
7396             {
7397               int nbNewNodes = quantities[iface];
7398               face_nodes.assign( polygons_nodes.begin() + inode,
7399                                  polygons_nodes.begin() + inode + nbNewNodes );
7400               inode += nbNewNodes;
7401               if ( isQuad ) // check if a result elem is a valid quadratic polygon
7402               {
7403                 bool isValid = ( nbNewNodes % 2 == 0 );
7404                 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7405                   isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7406                 elemType.SetQuad( isValid );
7407                 if ( isValid ) // put medium nodes after corners
7408                   SMDS_MeshCell::applyInterlaceRev
7409                     ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7410                                                           nbNewNodes ), face_nodes );
7411               }
7412               elemType.SetPoly(( nbNewNodes / ( elemType.myIsQuad + 1 ) > 4 ));
7413
7414               SMDS_MeshElement* newElem = AddElement( face_nodes, elemType.SetID(-1));
7415               if ( aShapeId )
7416                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7417             }
7418           }
7419           rmElemIds.push_back(elem->GetID());
7420
7421         } // Polygon
7422
7423         else if ( elem->GetType() == SMDSAbs_Volume ) // Polyhedral volume
7424         {
7425           if ( nbUniqueNodes < 4 ) {
7426             rmElemIds.push_back(elem->GetID());
7427           }
7428           else {
7429             // each face has to be analyzed in order to check volume validity
7430             const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
7431             if ( aPolyedre )
7432             {
7433               int nbFaces = aPolyedre->NbFaces();
7434
7435               vector<const SMDS_MeshNode *> poly_nodes;
7436               vector<int>                   quantities;
7437               vector<const SMDS_MeshNode *> faceNodes;
7438
7439               for (int iface = 1; iface <= nbFaces; iface++)
7440               {
7441                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7442                 faceNodes.resize( nbFaceNodes );
7443                 for (int inode = 1; inode <= nbFaceNodes; inode++)
7444                 {
7445                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7446                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7447                   if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7448                     faceNode = (*nnIt).second;
7449                   faceNodes[inode - 1] = faceNode;
7450                 }
7451                 SimplifyFace(faceNodes, poly_nodes, quantities);
7452               }
7453
7454               if ( quantities.size() > 3 ) {
7455                 // TODO: remove coincident faces
7456               }
7457
7458               if ( quantities.size() > 3 )
7459               {
7460                 const SMDS_MeshElement* newElem =
7461                   aMesh->AddPolyhedralVolume( poly_nodes, quantities );
7462                 myLastCreatedElems.Append( newElem );
7463                 if ( aShapeId && newElem )
7464                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7465                 rmElemIds.push_back( elem->GetID() );
7466               }
7467             }
7468             else {
7469               rmElemIds.push_back( elem->GetID() );
7470             }
7471           }
7472         }
7473         else {
7474         }
7475
7476         continue;
7477       } // poly element
7478
7479       // Regular elements
7480       // TODO not all the possible cases are solved. Find something more generic?
7481       switch ( entity ) {
7482       case SMDSEntity_Edge: //////// EDGE
7483       case SMDSEntity_Triangle: //// TRIANGLE
7484       case SMDSEntity_Quad_Triangle:
7485       case SMDSEntity_Tetra:
7486       case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7487       {
7488         isOk = false;
7489         break;
7490       }
7491       case SMDSEntity_Quad_Edge:
7492       {
7493         isOk = false; // to linear EDGE ???????
7494         break;
7495       }
7496       case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7497       {
7498         if ( nbUniqueNodes < 3 )
7499           isOk = false;
7500         else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7501           isOk = false; // opposite nodes stick
7502         break;
7503       }
7504       case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7505       {
7506         //   1    5    2
7507         //    +---+---+
7508         //    |       |
7509         //   4+       +6
7510         //    |       |
7511         //    +---+---+
7512         //   0    7    3
7513         if (( nbUniqueNodes == 6 && nbRepl == 2 ) &&
7514             (( iRepl[0] == 1 && iRepl[1] == 4 && curNodes[1] == curNodes[0] ) ||
7515              ( iRepl[0] == 2 && iRepl[1] == 5 && curNodes[2] == curNodes[1] ) ||
7516              ( iRepl[0] == 3 && iRepl[1] == 6 && curNodes[3] == curNodes[2] ) ||
7517              ( iRepl[0] == 3 && iRepl[1] == 7 && curNodes[3] == curNodes[0] )))
7518         {
7519           isOk = true;
7520         }
7521         break;
7522       }
7523       case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7524       {
7525         //   1    5    2
7526         //    +---+---+
7527         //    |       |
7528         //   4+  8+   +6
7529         //    |       |
7530         //    +---+---+
7531         //   0    7    3
7532         if (( nbUniqueNodes == 7 && nbRepl == 2 && iRepl[1] != 8 ) &&
7533             (( iRepl[0] == 1 && iRepl[1] == 4 && curNodes[1] == curNodes[0] ) ||
7534              ( iRepl[0] == 2 && iRepl[1] == 5 && curNodes[2] == curNodes[1] ) ||
7535              ( iRepl[0] == 3 && iRepl[1] == 6 && curNodes[3] == curNodes[2] ) ||
7536              ( iRepl[0] == 3 && iRepl[1] == 7 && curNodes[3] == curNodes[0] )))
7537         {
7538           isOk = true;
7539         }
7540         break;
7541       }
7542       case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7543       {
7544         isOk = false;
7545         if ( nbUniqueNodes == 4 ) {
7546           // ---------------------------------> tetrahedron
7547           if ( curNodes[3] == curNodes[4] &&
7548                curNodes[3] == curNodes[5] ) {
7549             // top nodes stick
7550             isOk = true;
7551           }
7552           else if ( curNodes[0] == curNodes[1] &&
7553                     curNodes[0] == curNodes[2] ) {
7554             // bottom nodes stick: set a top before
7555             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7556             uniqueNodes[ 0 ] = curNodes [ 5 ];
7557             uniqueNodes[ 1 ] = curNodes [ 4 ];
7558             uniqueNodes[ 2 ] = curNodes [ 3 ];
7559             isOk = true;
7560           }
7561           else if (( curNodes[0] == curNodes[3] ) +
7562                    ( curNodes[1] == curNodes[4] ) +
7563                    ( curNodes[2] == curNodes[5] ) == 2 ) {
7564             // a lateral face turns into a line
7565             isOk = true;
7566           }
7567         }
7568         else if ( nbUniqueNodes == 5 ) {
7569           // PENTAHEDRON --------------------> pyramid
7570           if ( curNodes[0] == curNodes[3] )
7571           {
7572             uniqueNodes[ 0 ] = curNodes[ 1 ];
7573             uniqueNodes[ 1 ] = curNodes[ 4 ];
7574             uniqueNodes[ 2 ] = curNodes[ 5 ];
7575             uniqueNodes[ 3 ] = curNodes[ 2 ];
7576             uniqueNodes[ 4 ] = curNodes[ 0 ];
7577             isOk = true;
7578           }
7579           if ( curNodes[1] == curNodes[4] )
7580           {
7581             uniqueNodes[ 0 ] = curNodes[ 0 ];
7582             uniqueNodes[ 1 ] = curNodes[ 2 ];
7583             uniqueNodes[ 2 ] = curNodes[ 5 ];
7584             uniqueNodes[ 3 ] = curNodes[ 3 ];
7585             uniqueNodes[ 4 ] = curNodes[ 1 ];
7586             isOk = true;
7587           }
7588           if ( curNodes[2] == curNodes[5] )
7589           {
7590             uniqueNodes[ 0 ] = curNodes[ 0 ];
7591             uniqueNodes[ 1 ] = curNodes[ 3 ];
7592             uniqueNodes[ 2 ] = curNodes[ 4 ];
7593             uniqueNodes[ 3 ] = curNodes[ 1 ];
7594             uniqueNodes[ 4 ] = curNodes[ 2 ];
7595             isOk = true;
7596           }
7597         }
7598         break;
7599       }
7600       case SMDSEntity_Hexa:
7601       {
7602         //////////////////////////////////// HEXAHEDRON
7603         isOk = false;
7604         SMDS_VolumeTool hexa (elem);
7605         hexa.SetExternalNormal();
7606         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7607           //////////////////////// HEX ---> tetrahedron
7608           for ( int iFace = 0; iFace < 6; iFace++ ) {
7609             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7610             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7611                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7612                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7613               // one face turns into a point ...
7614               int  pickInd = ind[ 0 ];
7615               int iOppFace = hexa.GetOppFaceIndex( iFace );
7616               ind = hexa.GetFaceNodesIndices( iOppFace );
7617               int nbStick = 0;
7618               uniqueNodes.clear();
7619               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7620                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7621                   nbStick++;
7622                 else
7623                   uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7624               }
7625               if ( nbStick == 1 ) {
7626                 // ... and the opposite one - into a triangle.
7627                 // set a top node
7628                 uniqueNodes.push_back( curNodes[ pickInd ]);
7629                 isOk = true;
7630               }
7631               break;
7632             }
7633           }
7634         }
7635         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7636           //////////////////////// HEX ---> prism
7637           int nbTria = 0, iTria[3];
7638           const int *ind; // indices of face nodes
7639           // look for triangular faces
7640           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7641             ind = hexa.GetFaceNodesIndices( iFace );
7642             TIDSortedNodeSet faceNodes;
7643             for ( iCur = 0; iCur < 4; iCur++ )
7644               faceNodes.insert( curNodes[ind[iCur]] );
7645             if ( faceNodes.size() == 3 )
7646               iTria[ nbTria++ ] = iFace;
7647           }
7648           // check if triangles are opposite
7649           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7650           {
7651             // set nodes of the bottom triangle
7652             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7653             vector<int> indB;
7654             for ( iCur = 0; iCur < 4; iCur++ )
7655               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7656                 indB.push_back( ind[iCur] );
7657             if ( !hexa.IsForward() )
7658               std::swap( indB[0], indB[2] );
7659             for ( iCur = 0; iCur < 3; iCur++ )
7660               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7661             // set nodes of the top triangle
7662             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7663             for ( iCur = 0; iCur < 3; ++iCur )
7664               for ( int j = 0; j < 4; ++j )
7665                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7666                 {
7667                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7668                   break;
7669                 }
7670             isOk = true;
7671             break;
7672           }
7673         }
7674         else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7675           //////////////////// HEXAHEDRON ---> pyramid
7676           for ( int iFace = 0; iFace < 6; iFace++ ) {
7677             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7678             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7679                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7680                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7681               // one face turns into a point ...
7682               int iOppFace = hexa.GetOppFaceIndex( iFace );
7683               ind = hexa.GetFaceNodesIndices( iOppFace );
7684               uniqueNodes.clear();
7685               for ( iCur = 0; iCur < 4; iCur++ ) {
7686                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7687                   break;
7688                 else
7689                   uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7690               }
7691               if ( uniqueNodes.size() == 4 ) {
7692                 // ... and the opposite one is a quadrangle
7693                 // set a top node
7694                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7695                 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7696                 isOk = true;
7697               }
7698               break;
7699             }
7700           }
7701         }
7702
7703         if ( !isOk && nbUniqueNodes > 4 ) {
7704           ////////////////// HEXAHEDRON ---> polyhedron
7705           hexa.SetExternalNormal();
7706           vector<const SMDS_MeshNode *> poly_nodes; poly_nodes.reserve( 6 * 4 );
7707           vector<int>                   quantities; quantities.reserve( 6 );
7708           for ( int iFace = 0; iFace < 6; iFace++ )
7709           {
7710             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7711             if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7712                  curNodes[ind[1]] == curNodes[ind[3]] )
7713             {
7714               quantities.clear();
7715               break; // opposite nodes stick
7716             }
7717             nodeSet.clear();
7718             for ( iCur = 0; iCur < 4; iCur++ )
7719             {
7720               if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7721                 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7722             }
7723             if ( nodeSet.size() < 3 )
7724               poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7725             else
7726               quantities.push_back( nodeSet.size() );
7727           }
7728           if ( quantities.size() >= 4 )
7729           {
7730             const SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities );
7731             myLastCreatedElems.Append( newElem );
7732             if ( aShapeId && newElem )
7733               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7734             rmElemIds.push_back( elem->GetID() );
7735           }
7736         }
7737         break;
7738       } // case HEXAHEDRON
7739
7740       default:
7741         isOk = false;
7742       } // switch ( nbNodes )
7743
7744     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7745
7746     if ( isOk ) // a non-poly elem remains valid after sticking nodes
7747     {
7748       if ( nbNodes != nbUniqueNodes ||
7749            !aMesh->ChangeElementNodes( elem, & curNodes[0], nbNodes ))
7750       {
7751         elemType.Init( elem ).SetID( elem->GetID() );
7752
7753         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7754         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7755
7756         uniqueNodes.resize(nbUniqueNodes);
7757         SMDS_MeshElement* newElem = this->AddElement( uniqueNodes, elemType );
7758         if ( sm && newElem )
7759           sm->AddElement( newElem );
7760         if ( elem != newElem )
7761           ReplaceElemInGroups( elem, newElem, aMesh );
7762       }
7763     }
7764     else {
7765       // Remove invalid regular element or invalid polygon
7766       rmElemIds.push_back( elem->GetID() );
7767     }
7768
7769   } // loop on elements
7770
7771   // Remove bad elements, then equal nodes (order important)
7772
7773   Remove( rmElemIds, false );
7774   Remove( rmNodeIds, true );
7775
7776   return;
7777 }
7778
7779
7780 // ========================================================
7781 // class   : SortableElement
7782 // purpose : allow sorting elements basing on their nodes
7783 // ========================================================
7784 class SortableElement : public set <const SMDS_MeshElement*>
7785 {
7786 public:
7787
7788   SortableElement( const SMDS_MeshElement* theElem )
7789   {
7790     myElem = theElem;
7791     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7792     while ( nodeIt->more() )
7793       this->insert( nodeIt->next() );
7794   }
7795
7796   const SMDS_MeshElement* Get() const
7797   { return myElem; }
7798
7799 private:
7800   mutable const SMDS_MeshElement* myElem;
7801 };
7802
7803 //=======================================================================
7804 //function : FindEqualElements
7805 //purpose  : Return list of group of elements built on the same nodes.
7806 //           Search among theElements or in the whole mesh if theElements is empty
7807 //=======================================================================
7808
7809 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
7810                                          TListOfListOfElementsID & theGroupsOfElementsID)
7811 {
7812   myLastCreatedElems.Clear();
7813   myLastCreatedNodes.Clear();
7814
7815   typedef map< SortableElement, int > TMapOfNodeSet;
7816   typedef list<int> TGroupOfElems;
7817
7818   if ( theElements.empty() )
7819   { // get all elements in the mesh
7820     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7821     while ( eIt->more() )
7822       theElements.insert( theElements.end(), eIt->next() );
7823   }
7824
7825   vector< TGroupOfElems > arrayOfGroups;
7826   TGroupOfElems groupOfElems;
7827   TMapOfNodeSet mapOfNodeSet;
7828
7829   TIDSortedElemSet::iterator elemIt = theElements.begin();
7830   for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7831   {
7832     const SMDS_MeshElement* curElem = *elemIt;
7833     SortableElement SE(curElem);
7834     // check uniqueness
7835     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7836     if ( !pp.second ) { // one more coincident elem
7837       TMapOfNodeSet::iterator& itSE = pp.first;
7838       int ind = (*itSE).second;
7839       arrayOfGroups[ind].push_back( curElem->GetID() );
7840     }
7841     else {
7842       arrayOfGroups.push_back( groupOfElems );
7843       arrayOfGroups.back().push_back( curElem->GetID() );
7844       i++;
7845     }
7846   }
7847
7848   groupOfElems.clear();
7849   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7850   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7851   {
7852     if ( groupIt->size() > 1 ) {
7853       //groupOfElems.sort(); -- theElements is sorted already
7854       theGroupsOfElementsID.push_back( groupOfElems );
7855       theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
7856     }
7857   }
7858 }
7859
7860 //=======================================================================
7861 //function : MergeElements
7862 //purpose  : In each given group, substitute all elements by the first one.
7863 //=======================================================================
7864
7865 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7866 {
7867   myLastCreatedElems.Clear();
7868   myLastCreatedNodes.Clear();
7869
7870   typedef list<int> TListOfIDs;
7871   TListOfIDs rmElemIds; // IDs of elems to remove
7872
7873   SMESHDS_Mesh* aMesh = GetMeshDS();
7874
7875   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7876   while ( groupsIt != theGroupsOfElementsID.end() ) {
7877     TListOfIDs& aGroupOfElemID = *groupsIt;
7878     aGroupOfElemID.sort();
7879     int elemIDToKeep = aGroupOfElemID.front();
7880     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7881     aGroupOfElemID.pop_front();
7882     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7883     while ( idIt != aGroupOfElemID.end() ) {
7884       int elemIDToRemove = *idIt;
7885       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7886       // add the kept element in groups of removed one (PAL15188)
7887       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7888       rmElemIds.push_back( elemIDToRemove );
7889       ++idIt;
7890     }
7891     ++groupsIt;
7892   }
7893
7894   Remove( rmElemIds, false );
7895 }
7896
7897 //=======================================================================
7898 //function : MergeEqualElements
7899 //purpose  : Remove all but one of elements built on the same nodes.
7900 //=======================================================================
7901
7902 void SMESH_MeshEditor::MergeEqualElements()
7903 {
7904   TIDSortedElemSet aMeshElements; /* empty input ==
7905                                      to merge equal elements in the whole mesh */
7906   TListOfListOfElementsID aGroupsOfElementsID;
7907   FindEqualElements(aMeshElements, aGroupsOfElementsID);
7908   MergeElements(aGroupsOfElementsID);
7909 }
7910
7911 //=======================================================================
7912 //function : findAdjacentFace
7913 //purpose  :
7914 //=======================================================================
7915
7916 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7917                                                 const SMDS_MeshNode* n2,
7918                                                 const SMDS_MeshElement* elem)
7919 {
7920   TIDSortedElemSet elemSet, avoidSet;
7921   if ( elem )
7922     avoidSet.insert ( elem );
7923   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7924 }
7925
7926 //=======================================================================
7927 //function : findSegment
7928 //purpose  : Return a mesh segment by two nodes one of which can be medium
7929 //=======================================================================
7930
7931 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7932                                            const SMDS_MeshNode* n2)
7933 {
7934   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7935   while ( it->more() )
7936   {
7937     const SMDS_MeshElement* seg = it->next();
7938     if ( seg->GetNodeIndex( n2 ) >= 0 )
7939       return seg;
7940   }
7941   return 0;
7942 }
7943
7944 //=======================================================================
7945 //function : FindFreeBorder
7946 //purpose  :
7947 //=======================================================================
7948
7949 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7950
7951 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7952                                        const SMDS_MeshNode*             theSecondNode,
7953                                        const SMDS_MeshNode*             theLastNode,
7954                                        list< const SMDS_MeshNode* > &   theNodes,
7955                                        list< const SMDS_MeshElement* >& theFaces)
7956 {
7957   if ( !theFirstNode || !theSecondNode )
7958     return false;
7959   // find border face between theFirstNode and theSecondNode
7960   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7961   if ( !curElem )
7962     return false;
7963
7964   theFaces.push_back( curElem );
7965   theNodes.push_back( theFirstNode );
7966   theNodes.push_back( theSecondNode );
7967
7968   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7969   TIDSortedElemSet foundElems;
7970   bool needTheLast = ( theLastNode != 0 );
7971
7972   while ( nStart != theLastNode ) {
7973     if ( nStart == theFirstNode )
7974       return !needTheLast;
7975
7976     // find all free border faces sharing form nStart
7977
7978     list< const SMDS_MeshElement* > curElemList;
7979     list< const SMDS_MeshNode* >    nStartList;
7980     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7981     while ( invElemIt->more() ) {
7982       const SMDS_MeshElement* e = invElemIt->next();
7983       if ( e == curElem || foundElems.insert( e ).second ) {
7984         // get nodes
7985         int iNode = 0, nbNodes = e->NbNodes();
7986         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7987
7988         if ( e->IsQuadratic() ) {
7989           const SMDS_VtkFace* F =
7990             dynamic_cast<const SMDS_VtkFace*>(e);
7991           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7992           // use special nodes iterator
7993           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7994           while( anIter->more() ) {
7995             nodes[ iNode++ ] = cast2Node(anIter->next());
7996           }
7997         }
7998         else {
7999           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8000           while ( nIt->more() )
8001             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8002         }
8003         nodes[ iNode ] = nodes[ 0 ];
8004         // check 2 links
8005         for ( iNode = 0; iNode < nbNodes; iNode++ )
8006           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8007                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8008               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8009           {
8010             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8011             curElemList.push_back( e );
8012           }
8013       }
8014     }
8015     // analyse the found
8016
8017     int nbNewBorders = curElemList.size();
8018     if ( nbNewBorders == 0 ) {
8019       // no free border furthermore
8020       return !needTheLast;
8021     }
8022     else if ( nbNewBorders == 1 ) {
8023       // one more element found
8024       nIgnore = nStart;
8025       nStart = nStartList.front();
8026       curElem = curElemList.front();
8027       theFaces.push_back( curElem );
8028       theNodes.push_back( nStart );
8029     }
8030     else {
8031       // several continuations found
8032       list< const SMDS_MeshElement* >::iterator curElemIt;
8033       list< const SMDS_MeshNode* >::iterator nStartIt;
8034       // check if one of them reached the last node
8035       if ( needTheLast ) {
8036         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8037              curElemIt!= curElemList.end();
8038              curElemIt++, nStartIt++ )
8039           if ( *nStartIt == theLastNode ) {
8040             theFaces.push_back( *curElemIt );
8041             theNodes.push_back( *nStartIt );
8042             return true;
8043           }
8044       }
8045       // find the best free border by the continuations
8046       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8047       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8048       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8049            curElemIt!= curElemList.end();
8050            curElemIt++, nStartIt++ )
8051       {
8052         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8053         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8054         // find one more free border
8055         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8056           cNL->clear();
8057           cFL->clear();
8058         }
8059         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8060           // choice: clear a worse one
8061           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8062           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8063           contNodes[ iWorse ].clear();
8064           contFaces[ iWorse ].clear();
8065         }
8066       }
8067       if ( contNodes[0].empty() && contNodes[1].empty() )
8068         return false;
8069
8070       // append the best free border
8071       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8072       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8073       theNodes.pop_back(); // remove nIgnore
8074       theNodes.pop_back(); // remove nStart
8075       theFaces.pop_back(); // remove curElem
8076       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8077       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8078       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8079       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8080       return true;
8081
8082     } // several continuations found
8083   } // while ( nStart != theLastNode )
8084
8085   return true;
8086 }
8087
8088 //=======================================================================
8089 //function : CheckFreeBorderNodes
8090 //purpose  : Return true if the tree nodes are on a free border
8091 //=======================================================================
8092
8093 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8094                                             const SMDS_MeshNode* theNode2,
8095                                             const SMDS_MeshNode* theNode3)
8096 {
8097   list< const SMDS_MeshNode* > nodes;
8098   list< const SMDS_MeshElement* > faces;
8099   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8100 }
8101
8102 //=======================================================================
8103 //function : SewFreeBorder
8104 //purpose  :
8105 //warning  : for border-to-side sewing theSideSecondNode is considered as
8106 //           the last side node and theSideThirdNode is not used
8107 //=======================================================================
8108
8109 SMESH_MeshEditor::Sew_Error
8110 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8111                                  const SMDS_MeshNode* theBordSecondNode,
8112                                  const SMDS_MeshNode* theBordLastNode,
8113                                  const SMDS_MeshNode* theSideFirstNode,
8114                                  const SMDS_MeshNode* theSideSecondNode,
8115                                  const SMDS_MeshNode* theSideThirdNode,
8116                                  const bool           theSideIsFreeBorder,
8117                                  const bool           toCreatePolygons,
8118                                  const bool           toCreatePolyedrs)
8119 {
8120   myLastCreatedElems.Clear();
8121   myLastCreatedNodes.Clear();
8122
8123   MESSAGE("::SewFreeBorder()");
8124   Sew_Error aResult = SEW_OK;
8125
8126   // ====================================
8127   //    find side nodes and elements
8128   // ====================================
8129
8130   list< const SMDS_MeshNode* >    nSide[ 2 ];
8131   list< const SMDS_MeshElement* > eSide[ 2 ];
8132   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
8133   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8134
8135   // Free border 1
8136   // --------------
8137   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8138                       nSide[0], eSide[0])) {
8139     MESSAGE(" Free Border 1 not found " );
8140     aResult = SEW_BORDER1_NOT_FOUND;
8141   }
8142   if (theSideIsFreeBorder) {
8143     // Free border 2
8144     // --------------
8145     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8146                         nSide[1], eSide[1])) {
8147       MESSAGE(" Free Border 2 not found " );
8148       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8149     }
8150   }
8151   if ( aResult != SEW_OK )
8152     return aResult;
8153
8154   if (!theSideIsFreeBorder) {
8155     // Side 2
8156     // --------------
8157
8158     // -------------------------------------------------------------------------
8159     // Algo:
8160     // 1. If nodes to merge are not coincident, move nodes of the free border
8161     //    from the coord sys defined by the direction from the first to last
8162     //    nodes of the border to the correspondent sys of the side 2
8163     // 2. On the side 2, find the links most co-directed with the correspondent
8164     //    links of the free border
8165     // -------------------------------------------------------------------------
8166
8167     // 1. Since sewing may break if there are volumes to split on the side 2,
8168     //    we wont move nodes but just compute new coordinates for them
8169     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8170     TNodeXYZMap nBordXYZ;
8171     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8172     list< const SMDS_MeshNode* >::iterator nBordIt;
8173
8174     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8175     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8176     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8177     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8178     double tol2 = 1.e-8;
8179     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8180     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8181       // Need node movement.
8182
8183       // find X and Z axes to create trsf
8184       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8185       gp_Vec X = Zs ^ Zb;
8186       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8187         // Zb || Zs
8188         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8189
8190       // coord systems
8191       gp_Ax3 toBordAx( Pb1, Zb, X );
8192       gp_Ax3 fromSideAx( Ps1, Zs, X );
8193       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8194       // set trsf
8195       gp_Trsf toBordSys, fromSide2Sys;
8196       toBordSys.SetTransformation( toBordAx );
8197       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8198       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8199
8200       // move
8201       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8202         const SMDS_MeshNode* n = *nBordIt;
8203         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8204         toBordSys.Transforms( xyz );
8205         fromSide2Sys.Transforms( xyz );
8206         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8207       }
8208     }
8209     else {
8210       // just insert nodes XYZ in the nBordXYZ map
8211       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8212         const SMDS_MeshNode* n = *nBordIt;
8213         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8214       }
8215     }
8216
8217     // 2. On the side 2, find the links most co-directed with the correspondent
8218     //    links of the free border
8219
8220     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8221     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8222     sideNodes.push_back( theSideFirstNode );
8223
8224     bool hasVolumes = false;
8225     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8226     set<long> foundSideLinkIDs, checkedLinkIDs;
8227     SMDS_VolumeTool volume;
8228     //const SMDS_MeshNode* faceNodes[ 4 ];
8229
8230     const SMDS_MeshNode*    sideNode;
8231     const SMDS_MeshElement* sideElem;
8232     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8233     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8234     nBordIt = bordNodes.begin();
8235     nBordIt++;
8236     // border node position and border link direction to compare with
8237     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8238     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8239     // choose next side node by link direction or by closeness to
8240     // the current border node:
8241     bool searchByDir = ( *nBordIt != theBordLastNode );
8242     do {
8243       // find the next node on the Side 2
8244       sideNode = 0;
8245       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8246       long linkID;
8247       checkedLinkIDs.clear();
8248       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8249
8250       // loop on inverse elements of current node (prevSideNode) on the Side 2
8251       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8252       while ( invElemIt->more() )
8253       {
8254         const SMDS_MeshElement* elem = invElemIt->next();
8255         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8256         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8257         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8258         bool isVolume = volume.Set( elem );
8259         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8260         if ( isVolume ) // --volume
8261           hasVolumes = true;
8262         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8263           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8264           if(elem->IsQuadratic()) {
8265             const SMDS_VtkFace* F =
8266               dynamic_cast<const SMDS_VtkFace*>(elem);
8267             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8268             // use special nodes iterator
8269             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8270             while( anIter->more() ) {
8271               nodes[ iNode ] = cast2Node(anIter->next());
8272               if ( nodes[ iNode++ ] == prevSideNode )
8273                 iPrevNode = iNode - 1;
8274             }
8275           }
8276           else {
8277             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8278             while ( nIt->more() ) {
8279               nodes[ iNode ] = cast2Node( nIt->next() );
8280               if ( nodes[ iNode++ ] == prevSideNode )
8281                 iPrevNode = iNode - 1;
8282             }
8283           }
8284           // there are 2 links to check
8285           nbNodes = 2;
8286         }
8287         else // --edge
8288           continue;
8289         // loop on links, to be precise, on the second node of links
8290         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8291           const SMDS_MeshNode* n = nodes[ iNode ];
8292           if ( isVolume ) {
8293             if ( !volume.IsLinked( n, prevSideNode ))
8294               continue;
8295           }
8296           else {
8297             if ( iNode ) // a node before prevSideNode
8298               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8299             else         // a node after prevSideNode
8300               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8301           }
8302           // check if this link was already used
8303           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8304           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8305           if (!isJustChecked &&
8306               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8307           {
8308             // test a link geometrically
8309             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8310             bool linkIsBetter = false;
8311             double dot = 0.0, dist = 0.0;
8312             if ( searchByDir ) { // choose most co-directed link
8313               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8314               linkIsBetter = ( dot > maxDot );
8315             }
8316             else { // choose link with the node closest to bordPos
8317               dist = ( nextXYZ - bordPos ).SquareModulus();
8318               linkIsBetter = ( dist < minDist );
8319             }
8320             if ( linkIsBetter ) {
8321               maxDot = dot;
8322               minDist = dist;
8323               linkID = iLink;
8324               sideNode = n;
8325               sideElem = elem;
8326             }
8327           }
8328         }
8329       } // loop on inverse elements of prevSideNode
8330
8331       if ( !sideNode ) {
8332         MESSAGE(" Cant find path by links of the Side 2 ");
8333         return SEW_BAD_SIDE_NODES;
8334       }
8335       sideNodes.push_back( sideNode );
8336       sideElems.push_back( sideElem );
8337       foundSideLinkIDs.insert ( linkID );
8338       prevSideNode = sideNode;
8339
8340       if ( *nBordIt == theBordLastNode )
8341         searchByDir = false;
8342       else {
8343         // find the next border link to compare with
8344         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8345         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8346         // move to next border node if sideNode is before forward border node (bordPos)
8347         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8348           prevBordNode = *nBordIt;
8349           nBordIt++;
8350           bordPos = nBordXYZ[ *nBordIt ];
8351           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8352           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8353         }
8354       }
8355     }
8356     while ( sideNode != theSideSecondNode );
8357
8358     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8359       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8360       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8361     }
8362   } // end nodes search on the side 2
8363
8364   // ============================
8365   // sew the border to the side 2
8366   // ============================
8367
8368   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8369   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8370
8371   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8372   if ( toMergeConformal && toCreatePolygons )
8373   {
8374     // do not merge quadrangles if polygons are OK (IPAL0052824)
8375     eIt[0] = eSide[0].begin();
8376     eIt[1] = eSide[1].begin();
8377     bool allQuads[2] = { true, true };
8378     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8379       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8380         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8381     }
8382     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8383   }
8384
8385   TListOfListOfNodes nodeGroupsToMerge;
8386   if (( toMergeConformal ) ||
8387       ( theSideIsFreeBorder && !theSideThirdNode )) {
8388
8389     // all nodes are to be merged
8390
8391     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8392          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8393          nIt[0]++, nIt[1]++ )
8394     {
8395       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8396       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8397       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8398     }
8399   }
8400   else {
8401
8402     // insert new nodes into the border and the side to get equal nb of segments
8403
8404     // get normalized parameters of nodes on the borders
8405     vector< double > param[ 2 ];
8406     param[0].resize( maxNbNodes );
8407     param[1].resize( maxNbNodes );
8408     int iNode, iBord;
8409     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8410       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8411       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8412       const SMDS_MeshNode* nPrev = *nIt;
8413       double bordLength = 0;
8414       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8415         const SMDS_MeshNode* nCur = *nIt;
8416         gp_XYZ segment (nCur->X() - nPrev->X(),
8417                         nCur->Y() - nPrev->Y(),
8418                         nCur->Z() - nPrev->Z());
8419         double segmentLen = segment.Modulus();
8420         bordLength += segmentLen;
8421         param[ iBord ][ iNode ] = bordLength;
8422         nPrev = nCur;
8423       }
8424       // normalize within [0,1]
8425       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8426         param[ iBord ][ iNode ] /= bordLength;
8427       }
8428     }
8429
8430     // loop on border segments
8431     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8432     int i[ 2 ] = { 0, 0 };
8433     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8434     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8435
8436     TElemOfNodeListMap insertMap;
8437     TElemOfNodeListMap::iterator insertMapIt;
8438     // insertMap is
8439     // key:   elem to insert nodes into
8440     // value: 2 nodes to insert between + nodes to be inserted
8441     do {
8442       bool next[ 2 ] = { false, false };
8443
8444       // find min adjacent segment length after sewing
8445       double nextParam = 10., prevParam = 0;
8446       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8447         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8448           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8449         if ( i[ iBord ] > 0 )
8450           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8451       }
8452       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8453       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8454       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8455
8456       // choose to insert or to merge nodes
8457       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8458       if ( Abs( du ) <= minSegLen * 0.2 ) {
8459         // merge
8460         // ------
8461         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8462         const SMDS_MeshNode* n0 = *nIt[0];
8463         const SMDS_MeshNode* n1 = *nIt[1];
8464         nodeGroupsToMerge.back().push_back( n1 );
8465         nodeGroupsToMerge.back().push_back( n0 );
8466         // position of node of the border changes due to merge
8467         param[ 0 ][ i[0] ] += du;
8468         // move n1 for the sake of elem shape evaluation during insertion.
8469         // n1 will be removed by MergeNodes() anyway
8470         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8471         next[0] = next[1] = true;
8472       }
8473       else {
8474         // insert
8475         // ------
8476         int intoBord = ( du < 0 ) ? 0 : 1;
8477         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8478         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8479         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8480         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8481         if ( intoBord == 1 ) {
8482           // move node of the border to be on a link of elem of the side
8483           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8484           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8485           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8486           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8487           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8488         }
8489         insertMapIt = insertMap.find( elem );
8490         bool  notFound = ( insertMapIt == insertMap.end() );
8491         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8492         if ( otherLink ) {
8493           // insert into another link of the same element:
8494           // 1. perform insertion into the other link of the elem
8495           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8496           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8497           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8498           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8499           // 2. perform insertion into the link of adjacent faces
8500           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8501             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8502           }
8503           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8504             InsertNodesIntoLink( seg, n12, n22, nodeList );
8505           }
8506           if (toCreatePolyedrs) {
8507             // perform insertion into the links of adjacent volumes
8508             UpdateVolumes(n12, n22, nodeList);
8509           }
8510           // 3. find an element appeared on n1 and n2 after the insertion
8511           insertMap.erase( elem );
8512           elem = findAdjacentFace( n1, n2, 0 );
8513         }
8514         if ( notFound || otherLink ) {
8515           // add element and nodes of the side into the insertMap
8516           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8517           (*insertMapIt).second.push_back( n1 );
8518           (*insertMapIt).second.push_back( n2 );
8519         }
8520         // add node to be inserted into elem
8521         (*insertMapIt).second.push_back( nIns );
8522         next[ 1 - intoBord ] = true;
8523       }
8524
8525       // go to the next segment
8526       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8527         if ( next[ iBord ] ) {
8528           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8529             eIt[ iBord ]++;
8530           nPrev[ iBord ] = *nIt[ iBord ];
8531           nIt[ iBord ]++; i[ iBord ]++;
8532         }
8533       }
8534     }
8535     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8536
8537     // perform insertion of nodes into elements
8538
8539     for (insertMapIt = insertMap.begin();
8540          insertMapIt != insertMap.end();
8541          insertMapIt++ )
8542     {
8543       const SMDS_MeshElement* elem = (*insertMapIt).first;
8544       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8545       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8546       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8547
8548       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8549
8550       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8551         InsertNodesIntoLink( seg, n1, n2, nodeList );
8552       }
8553
8554       if ( !theSideIsFreeBorder ) {
8555         // look for and insert nodes into the faces adjacent to elem
8556         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8557           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8558         }
8559       }
8560       if (toCreatePolyedrs) {
8561         // perform insertion into the links of adjacent volumes
8562         UpdateVolumes(n1, n2, nodeList);
8563       }
8564     }
8565   } // end: insert new nodes
8566
8567   MergeNodes ( nodeGroupsToMerge );
8568
8569
8570   // Remove coincident segments
8571
8572   // get new segments
8573   TIDSortedElemSet segments;
8574   SMESH_SequenceOfElemPtr newFaces;
8575   for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8576   {
8577     if ( !myLastCreatedElems(i) ) continue;
8578     if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8579       segments.insert( segments.end(), myLastCreatedElems(i) );
8580     else
8581       newFaces.Append( myLastCreatedElems(i) );
8582   }
8583   // get segments adjacent to merged nodes
8584   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8585   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8586   {
8587     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8588     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8589     while ( segIt->more() )
8590       segments.insert( segIt->next() );
8591   }
8592
8593   // find coincident
8594   TListOfListOfElementsID equalGroups;
8595   if ( !segments.empty() )
8596     FindEqualElements( segments, equalGroups );
8597   if ( !equalGroups.empty() )
8598   {
8599     // remove from segments those that will be removed
8600     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8601     for ( ; itGroups != equalGroups.end(); ++itGroups )
8602     {
8603       list< int >& group = *itGroups;
8604       list< int >::iterator id = group.begin();
8605       for ( ++id; id != group.end(); ++id )
8606         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8607           segments.erase( seg );
8608     }
8609     // remove equal segments
8610     MergeElements( equalGroups );
8611
8612     // restore myLastCreatedElems
8613     myLastCreatedElems = newFaces;
8614     TIDSortedElemSet::iterator seg = segments.begin();
8615     for ( ; seg != segments.end(); ++seg )
8616       myLastCreatedElems.Append( *seg );
8617   }
8618
8619   return aResult;
8620 }
8621
8622 //=======================================================================
8623 //function : InsertNodesIntoLink
8624 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8625 //           and theBetweenNode2 and split theElement
8626 //=======================================================================
8627
8628 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8629                                            const SMDS_MeshNode*        theBetweenNode1,
8630                                            const SMDS_MeshNode*        theBetweenNode2,
8631                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8632                                            const bool                  toCreatePoly)
8633 {
8634   if ( !theElement ) return;
8635
8636   SMESHDS_Mesh *aMesh = GetMeshDS();
8637   vector<const SMDS_MeshElement*> newElems;
8638
8639   if ( theElement->GetType() == SMDSAbs_Edge )
8640   {
8641     theNodesToInsert.push_front( theBetweenNode1 );
8642     theNodesToInsert.push_back ( theBetweenNode2 );
8643     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8644     const SMDS_MeshNode* n1 = *n;
8645     for ( ++n; n != theNodesToInsert.end(); ++n )
8646     {
8647       const SMDS_MeshNode* n2 = *n;
8648       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8649         AddToSameGroups( seg, theElement, aMesh );
8650       else
8651         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8652       n1 = n2;
8653     }
8654     theNodesToInsert.pop_front();
8655     theNodesToInsert.pop_back();
8656
8657     if ( theElement->IsQuadratic() ) // add a not split part
8658     {
8659       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8660                                           theElement->end_nodes() );
8661       int iOther = 0, nbN = nodes.size();
8662       for ( ; iOther < nbN; ++iOther )
8663         if ( nodes[iOther] != theBetweenNode1 &&
8664              nodes[iOther] != theBetweenNode2 )
8665           break;
8666       if      ( iOther == 0 )
8667       {
8668         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8669           AddToSameGroups( seg, theElement, aMesh );
8670         else
8671           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8672       }
8673       else if ( iOther == 2 )
8674       {
8675         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8676           AddToSameGroups( seg, theElement, aMesh );
8677         else
8678           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8679       }
8680     }
8681     // treat new elements
8682     for ( size_t i = 0; i < newElems.size(); ++i )
8683       if ( newElems[i] )
8684       {
8685         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8686         myLastCreatedElems.Append( newElems[i] );
8687       }
8688     ReplaceElemInGroups( theElement, newElems, aMesh );
8689     aMesh->RemoveElement( theElement );
8690     return;
8691
8692   } // if ( theElement->GetType() == SMDSAbs_Edge )
8693
8694   const SMDS_MeshElement* theFace = theElement;
8695   if ( theFace->GetType() != SMDSAbs_Face ) return;
8696
8697   // find indices of 2 link nodes and of the rest nodes
8698   int iNode = 0, il1, il2, i3, i4;
8699   il1 = il2 = i3 = i4 = -1;
8700   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8701
8702   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8703   while ( nodeIt->more() ) {
8704     const SMDS_MeshNode* n = nodeIt->next();
8705     if ( n == theBetweenNode1 )
8706       il1 = iNode;
8707     else if ( n == theBetweenNode2 )
8708       il2 = iNode;
8709     else if ( i3 < 0 )
8710       i3 = iNode;
8711     else
8712       i4 = iNode;
8713     nodes[ iNode++ ] = n;
8714   }
8715   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8716     return ;
8717
8718   // arrange link nodes to go one after another regarding the face orientation
8719   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8720   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8721   if ( reverse ) {
8722     iNode = il1;
8723     il1 = il2;
8724     il2 = iNode;
8725     aNodesToInsert.reverse();
8726   }
8727   // check that not link nodes of a quadrangles are in good order
8728   int nbFaceNodes = theFace->NbNodes();
8729   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8730     iNode = i3;
8731     i3 = i4;
8732     i4 = iNode;
8733   }
8734
8735   if (toCreatePoly || theFace->IsPoly()) {
8736
8737     iNode = 0;
8738     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8739
8740     // add nodes of face up to first node of link
8741     bool isFLN = false;
8742
8743     if ( theFace->IsQuadratic() ) {
8744       const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8745       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8746       // use special nodes iterator
8747       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8748       while( anIter->more()  && !isFLN ) {
8749         const SMDS_MeshNode* n = cast2Node(anIter->next());
8750         poly_nodes[iNode++] = n;
8751         if (n == nodes[il1]) {
8752           isFLN = true;
8753         }
8754       }
8755       // add nodes to insert
8756       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8757       for (; nIt != aNodesToInsert.end(); nIt++) {
8758         poly_nodes[iNode++] = *nIt;
8759       }
8760       // add nodes of face starting from last node of link
8761       while ( anIter->more() ) {
8762         poly_nodes[iNode++] = cast2Node(anIter->next());
8763       }
8764     }
8765     else {
8766       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8767       while ( nodeIt->more() && !isFLN ) {
8768         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8769         poly_nodes[iNode++] = n;
8770         if (n == nodes[il1]) {
8771           isFLN = true;
8772         }
8773       }
8774       // add nodes to insert
8775       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8776       for (; nIt != aNodesToInsert.end(); nIt++) {
8777         poly_nodes[iNode++] = *nIt;
8778       }
8779       // add nodes of face starting from last node of link
8780       while ( nodeIt->more() ) {
8781         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8782         poly_nodes[iNode++] = n;
8783       }
8784     }
8785
8786     // make a new face
8787     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8788   }
8789
8790   else if ( !theFace->IsQuadratic() )
8791   {
8792     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8793     int nbLinkNodes = 2 + aNodesToInsert.size();
8794     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8795     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8796     linkNodes[ 0 ] = nodes[ il1 ];
8797     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8798     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8799     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8800       linkNodes[ iNode++ ] = *nIt;
8801     }
8802     // decide how to split a quadrangle: compare possible variants
8803     // and choose which of splits to be a quadrangle
8804     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8805     if ( nbFaceNodes == 3 ) {
8806       iBestQuad = nbSplits;
8807       i4 = i3;
8808     }
8809     else if ( nbFaceNodes == 4 ) {
8810       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8811       double aBestRate = DBL_MAX;
8812       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8813         i1 = 0; i2 = 1;
8814         double aBadRate = 0;
8815         // evaluate elements quality
8816         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8817           if ( iSplit == iQuad ) {
8818             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8819                                    linkNodes[ i2++ ],
8820                                    nodes[ i3 ],
8821                                    nodes[ i4 ]);
8822             aBadRate += getBadRate( &quad, aCrit );
8823           }
8824           else {
8825             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8826                                    linkNodes[ i2++ ],
8827                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8828             aBadRate += getBadRate( &tria, aCrit );
8829           }
8830         }
8831         // choice
8832         if ( aBadRate < aBestRate ) {
8833           iBestQuad = iQuad;
8834           aBestRate = aBadRate;
8835         }
8836       }
8837     }
8838
8839     // create new elements
8840     i1 = 0; i2 = 1;
8841     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8842     {
8843       if ( iSplit == iBestQuad )
8844         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8845                                             linkNodes[ i2++ ],
8846                                             nodes[ i3 ],
8847                                             nodes[ i4 ]));
8848       else
8849         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8850                                             linkNodes[ i2++ ],
8851                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8852     }
8853
8854     const SMDS_MeshNode* newNodes[ 4 ];
8855     newNodes[ 0 ] = linkNodes[ i1 ];
8856     newNodes[ 1 ] = linkNodes[ i2 ];
8857     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8858     newNodes[ 3 ] = nodes[ i4 ];
8859     if (iSplit == iBestQuad)
8860       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8861     else
8862       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8863
8864   } // end if(!theFace->IsQuadratic())
8865
8866   else { // theFace is quadratic
8867     // we have to split theFace on simple triangles and one simple quadrangle
8868     int tmp = il1/2;
8869     int nbshift = tmp*2;
8870     // shift nodes in nodes[] by nbshift
8871     int i,j;
8872     for(i=0; i<nbshift; i++) {
8873       const SMDS_MeshNode* n = nodes[0];
8874       for(j=0; j<nbFaceNodes-1; j++) {
8875         nodes[j] = nodes[j+1];
8876       }
8877       nodes[nbFaceNodes-1] = n;
8878     }
8879     il1 = il1 - nbshift;
8880     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8881     //   n0      n1     n2    n0      n1     n2
8882     //     +-----+-----+        +-----+-----+
8883     //      \         /         |           |
8884     //       \       /          |           |
8885     //      n5+     +n3       n7+           +n3
8886     //         \   /            |           |
8887     //          \ /             |           |
8888     //           +              +-----+-----+
8889     //           n4           n6      n5     n4
8890
8891     // create new elements
8892     int n1,n2,n3;
8893     if ( nbFaceNodes == 6 ) { // quadratic triangle
8894       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8895       if ( theFace->IsMediumNode(nodes[il1]) ) {
8896         // create quadrangle
8897         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8898         n1 = 1;
8899         n2 = 2;
8900         n3 = 3;
8901       }
8902       else {
8903         // create quadrangle
8904         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8905         n1 = 0;
8906         n2 = 1;
8907         n3 = 5;
8908       }
8909     }
8910     else { // nbFaceNodes==8 - quadratic quadrangle
8911       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8912       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8913       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8914       if ( theFace->IsMediumNode( nodes[ il1 ])) {
8915         // create quadrangle
8916         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8917         n1 = 1;
8918         n2 = 2;
8919         n3 = 3;
8920       }
8921       else {
8922         // create quadrangle
8923         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8924         n1 = 0;
8925         n2 = 1;
8926         n3 = 7;
8927       }
8928     }
8929     // create needed triangles using n1,n2,n3 and inserted nodes
8930     int nbn = 2 + aNodesToInsert.size();
8931     vector<const SMDS_MeshNode*> aNodes(nbn);
8932     aNodes[0    ] = nodes[n1];
8933     aNodes[nbn-1] = nodes[n2];
8934     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8935     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8936       aNodes[iNode++] = *nIt;
8937     }
8938     for ( i = 1; i < nbn; i++ )
8939       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8940   }
8941
8942   // remove the old face
8943   for ( size_t i = 0; i < newElems.size(); ++i )
8944     if ( newElems[i] )
8945     {
8946       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8947       myLastCreatedElems.Append( newElems[i] );
8948     }
8949   ReplaceElemInGroups( theFace, newElems, aMesh );
8950   aMesh->RemoveElement(theFace);
8951
8952 } // InsertNodesIntoLink()
8953
8954 //=======================================================================
8955 //function : UpdateVolumes
8956 //purpose  :
8957 //=======================================================================
8958
8959 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8960                                       const SMDS_MeshNode*        theBetweenNode2,
8961                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8962 {
8963   myLastCreatedElems.Clear();
8964   myLastCreatedNodes.Clear();
8965
8966   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8967   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8968     const SMDS_MeshElement* elem = invElemIt->next();
8969
8970     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8971     SMDS_VolumeTool aVolume (elem);
8972     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8973       continue;
8974
8975     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8976     int iface, nbFaces = aVolume.NbFaces();
8977     vector<const SMDS_MeshNode *> poly_nodes;
8978     vector<int> quantities (nbFaces);
8979
8980     for (iface = 0; iface < nbFaces; iface++) {
8981       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8982       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8983       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8984
8985       for (int inode = 0; inode < nbFaceNodes; inode++) {
8986         poly_nodes.push_back(faceNodes[inode]);
8987
8988         if (nbInserted == 0) {
8989           if (faceNodes[inode] == theBetweenNode1) {
8990             if (faceNodes[inode + 1] == theBetweenNode2) {
8991               nbInserted = theNodesToInsert.size();
8992
8993               // add nodes to insert
8994               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8995               for (; nIt != theNodesToInsert.end(); nIt++) {
8996                 poly_nodes.push_back(*nIt);
8997               }
8998             }
8999           }
9000           else if (faceNodes[inode] == theBetweenNode2) {
9001             if (faceNodes[inode + 1] == theBetweenNode1) {
9002               nbInserted = theNodesToInsert.size();
9003
9004               // add nodes to insert in reversed order
9005               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9006               nIt--;
9007               for (; nIt != theNodesToInsert.begin(); nIt--) {
9008                 poly_nodes.push_back(*nIt);
9009               }
9010               poly_nodes.push_back(*nIt);
9011             }
9012           }
9013           else {
9014           }
9015         }
9016       }
9017       quantities[iface] = nbFaceNodes + nbInserted;
9018     }
9019
9020     // Replace the volume
9021     SMESHDS_Mesh *aMesh = GetMeshDS();
9022
9023     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9024     {
9025       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9026       myLastCreatedElems.Append( newElem );
9027       ReplaceElemInGroups( elem, newElem, aMesh );
9028     }
9029     aMesh->RemoveElement( elem );
9030   }
9031 }
9032
9033 namespace
9034 {
9035   //================================================================================
9036   /*!
9037    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9038    */
9039   //================================================================================
9040
9041   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9042                            vector<const SMDS_MeshNode *> & nodes,
9043                            vector<int> &                   nbNodeInFaces )
9044   {
9045     nodes.clear();
9046     nbNodeInFaces.clear();
9047     SMDS_VolumeTool vTool ( elem );
9048     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9049     {
9050       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9051       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9052       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9053     }
9054   }
9055 }
9056
9057 //=======================================================================
9058 /*!
9059  * \brief Convert elements contained in a sub-mesh to quadratic
9060  * \return int - nb of checked elements
9061  */
9062 //=======================================================================
9063
9064 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9065                                              SMESH_MesherHelper& theHelper,
9066                                              const bool          theForce3d)
9067 {
9068   int nbElem = 0;
9069   if( !theSm ) return nbElem;
9070
9071   vector<int> nbNodeInFaces;
9072   vector<const SMDS_MeshNode *> nodes;
9073   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9074   while(ElemItr->more())
9075   {
9076     nbElem++;
9077     const SMDS_MeshElement* elem = ElemItr->next();
9078     if( !elem ) continue;
9079
9080     // analyse a necessity of conversion
9081     const SMDSAbs_ElementType aType = elem->GetType();
9082     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9083       continue;
9084     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9085     bool hasCentralNodes = false;
9086     if ( elem->IsQuadratic() )
9087     {
9088       bool alreadyOK;
9089       switch ( aGeomType ) {
9090       case SMDSEntity_Quad_Triangle:
9091       case SMDSEntity_Quad_Quadrangle:
9092       case SMDSEntity_Quad_Hexa:
9093         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9094
9095       case SMDSEntity_BiQuad_Triangle:
9096       case SMDSEntity_BiQuad_Quadrangle:
9097       case SMDSEntity_TriQuad_Hexa:
9098         alreadyOK = theHelper.GetIsBiQuadratic();
9099         hasCentralNodes = true;
9100         break;
9101       default:
9102         alreadyOK = true;
9103       }
9104       // take into account already present modium nodes
9105       switch ( aType ) {
9106       case SMDSAbs_Volume:
9107         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9108       case SMDSAbs_Face:
9109         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9110       case SMDSAbs_Edge:
9111         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9112       default:;
9113       }
9114       if ( alreadyOK )
9115         continue;
9116     }
9117     // get elem data needed to re-create it
9118     //
9119     const int id      = elem->GetID();
9120     const int nbNodes = elem->NbCornerNodes();
9121     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9122     if ( aGeomType == SMDSEntity_Polyhedra )
9123       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9124     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9125       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9126
9127     // remove a linear element
9128     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9129
9130     // remove central nodes of biquadratic elements (biquad->quad convertion)
9131     if ( hasCentralNodes )
9132       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9133         if ( nodes[i]->NbInverseElements() == 0 )
9134           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9135
9136     const SMDS_MeshElement* NewElem = 0;
9137
9138     switch( aType )
9139     {
9140     case SMDSAbs_Edge :
9141       {
9142         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9143         break;
9144       }
9145     case SMDSAbs_Face :
9146       {
9147         switch(nbNodes)
9148         {
9149         case 3:
9150           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9151           break;
9152         case 4:
9153           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9154           break;
9155         default:
9156           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9157         }
9158         break;
9159       }
9160     case SMDSAbs_Volume :
9161       {
9162         switch( aGeomType )
9163         {
9164         case SMDSEntity_Tetra:
9165           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9166           break;
9167         case SMDSEntity_Pyramid:
9168           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9169           break;
9170         case SMDSEntity_Penta:
9171           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9172           break;
9173         case SMDSEntity_Hexa:
9174         case SMDSEntity_Quad_Hexa:
9175         case SMDSEntity_TriQuad_Hexa:
9176           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9177                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9178           break;
9179         case SMDSEntity_Hexagonal_Prism:
9180         default:
9181           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9182         }
9183         break;
9184       }
9185     default :
9186       continue;
9187     }
9188     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9189     if( NewElem && NewElem->getshapeId() < 1 )
9190       theSm->AddElement( NewElem );
9191   }
9192   return nbElem;
9193 }
9194 //=======================================================================
9195 //function : ConvertToQuadratic
9196 //purpose  :
9197 //=======================================================================
9198
9199 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9200 {
9201   SMESHDS_Mesh* meshDS = GetMeshDS();
9202
9203   SMESH_MesherHelper aHelper(*myMesh);
9204
9205   aHelper.SetIsQuadratic( true );
9206   aHelper.SetIsBiQuadratic( theToBiQuad );
9207   aHelper.SetElementsOnShape(true);
9208   aHelper.ToFixNodeParameters( true );
9209
9210   // convert elements assigned to sub-meshes
9211   int nbCheckedElems = 0;
9212   if ( myMesh->HasShapeToMesh() )
9213   {
9214     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9215     {
9216       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9217       while ( smIt->more() ) {
9218         SMESH_subMesh* sm = smIt->next();
9219         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9220           aHelper.SetSubShape( sm->GetSubShape() );
9221           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9222         }
9223       }
9224     }
9225   }
9226
9227   // convert elements NOT assigned to sub-meshes
9228   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9229   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9230   {
9231     aHelper.SetElementsOnShape(false);
9232     SMESHDS_SubMesh *smDS = 0;
9233
9234     // convert edges
9235     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9236     while( aEdgeItr->more() )
9237     {
9238       const SMDS_MeshEdge* edge = aEdgeItr->next();
9239       if ( !edge->IsQuadratic() )
9240       {
9241         int                  id = edge->GetID();
9242         const SMDS_MeshNode* n1 = edge->GetNode(0);
9243         const SMDS_MeshNode* n2 = edge->GetNode(1);
9244
9245         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9246
9247         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9248         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9249       }
9250       else
9251       {
9252         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9253       }
9254     }
9255
9256     // convert faces
9257     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9258     while( aFaceItr->more() )
9259     {
9260       const SMDS_MeshFace* face = aFaceItr->next();
9261       if ( !face ) continue;
9262       
9263       const SMDSAbs_EntityType type = face->GetEntityType();
9264       bool alreadyOK;
9265       switch( type )
9266       {
9267       case SMDSEntity_Quad_Triangle:
9268       case SMDSEntity_Quad_Quadrangle:
9269         alreadyOK = !theToBiQuad;
9270         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9271         break;
9272       case SMDSEntity_BiQuad_Triangle:
9273       case SMDSEntity_BiQuad_Quadrangle:
9274         alreadyOK = theToBiQuad;
9275         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9276         break;
9277       default: alreadyOK = false;
9278       }
9279       if ( alreadyOK )
9280         continue;
9281
9282       const int id = face->GetID();
9283       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9284
9285       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9286
9287       SMDS_MeshFace * NewFace = 0;
9288       switch( type )
9289       {
9290       case SMDSEntity_Triangle:
9291       case SMDSEntity_Quad_Triangle:
9292       case SMDSEntity_BiQuad_Triangle:
9293         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9294         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9295           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9296         break;
9297
9298       case SMDSEntity_Quadrangle:
9299       case SMDSEntity_Quad_Quadrangle:
9300       case SMDSEntity_BiQuad_Quadrangle:
9301         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9302         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9303           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9304         break;
9305
9306       default:;
9307         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9308       }
9309       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9310     }
9311
9312     // convert volumes
9313     vector<int> nbNodeInFaces;
9314     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9315     while(aVolumeItr->more())
9316     {
9317       const SMDS_MeshVolume* volume = aVolumeItr->next();
9318       if ( !volume ) continue;
9319
9320       const SMDSAbs_EntityType type = volume->GetEntityType();
9321       if ( volume->IsQuadratic() )
9322       {
9323         bool alreadyOK;
9324         switch ( type )
9325         {
9326         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9327         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9328         default:                      alreadyOK = true;
9329         }
9330         if ( alreadyOK )
9331         {
9332           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9333           continue;
9334         }
9335       }
9336       const int id = volume->GetID();
9337       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9338       if ( type == SMDSEntity_Polyhedra )
9339         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9340       else if ( type == SMDSEntity_Hexagonal_Prism )
9341         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9342
9343       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9344
9345       SMDS_MeshVolume * NewVolume = 0;
9346       switch ( type )
9347       {
9348       case SMDSEntity_Tetra:
9349         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9350         break;
9351       case SMDSEntity_Hexa:
9352       case SMDSEntity_Quad_Hexa:
9353       case SMDSEntity_TriQuad_Hexa:
9354         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9355                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9356         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9357           if ( nodes[i]->NbInverseElements() == 0 )
9358             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9359         break;
9360       case SMDSEntity_Pyramid:
9361         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9362                                       nodes[3], nodes[4], id, theForce3d);
9363         break;
9364       case SMDSEntity_Penta:
9365         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9366                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9367         break;
9368       case SMDSEntity_Hexagonal_Prism:
9369       default:
9370         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9371       }
9372       ReplaceElemInGroups(volume, NewVolume, meshDS);
9373     }
9374   }
9375
9376   if ( !theForce3d )
9377   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9378     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9379     // aHelper.FixQuadraticElements(myError);
9380     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9381   }
9382 }
9383
9384 //================================================================================
9385 /*!
9386  * \brief Makes given elements quadratic
9387  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9388  *  \param theElements - elements to make quadratic
9389  */
9390 //================================================================================
9391
9392 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9393                                           TIDSortedElemSet& theElements,
9394                                           const bool        theToBiQuad)
9395 {
9396   if ( theElements.empty() ) return;
9397
9398   // we believe that all theElements are of the same type
9399   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9400
9401   // get all nodes shared by theElements
9402   TIDSortedNodeSet allNodes;
9403   TIDSortedElemSet::iterator eIt = theElements.begin();
9404   for ( ; eIt != theElements.end(); ++eIt )
9405     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9406
9407   // complete theElements with elements of lower dim whose all nodes are in allNodes
9408
9409   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9410   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9411   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9412   for ( ; nIt != allNodes.end(); ++nIt )
9413   {
9414     const SMDS_MeshNode* n = *nIt;
9415     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9416     while ( invIt->more() )
9417     {
9418       const SMDS_MeshElement*      e = invIt->next();
9419       const SMDSAbs_ElementType type = e->GetType();
9420       if ( e->IsQuadratic() )
9421       {
9422         quadAdjacentElems[ type ].insert( e );
9423
9424         bool alreadyOK;
9425         switch ( e->GetEntityType() ) {
9426         case SMDSEntity_Quad_Triangle:
9427         case SMDSEntity_Quad_Quadrangle:
9428         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9429         case SMDSEntity_BiQuad_Triangle:
9430         case SMDSEntity_BiQuad_Quadrangle:
9431         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9432         default:                           alreadyOK = true;
9433         }
9434         if ( alreadyOK )
9435           continue;
9436       }
9437       if ( type >= elemType )
9438         continue; // same type or more complex linear element
9439
9440       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9441         continue; // e is already checked
9442
9443       // check nodes
9444       bool allIn = true;
9445       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9446       while ( nodeIt->more() && allIn )
9447         allIn = allNodes.count( nodeIt->next() );
9448       if ( allIn )
9449         theElements.insert(e );
9450     }
9451   }
9452
9453   SMESH_MesherHelper helper(*myMesh);
9454   helper.SetIsQuadratic( true );
9455   helper.SetIsBiQuadratic( theToBiQuad );
9456
9457   // add links of quadratic adjacent elements to the helper
9458
9459   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9460     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9461           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9462     {
9463       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9464     }
9465   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9466     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9467           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9468     {
9469       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9470     }
9471   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9472     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9473           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9474     {
9475       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9476     }
9477
9478   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9479
9480   SMESHDS_Mesh*  meshDS = GetMeshDS();
9481   SMESHDS_SubMesh* smDS = 0;
9482   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9483   {
9484     const SMDS_MeshElement* elem = *eIt;
9485
9486     bool alreadyOK;
9487     int nbCentralNodes = 0;
9488     switch ( elem->GetEntityType() ) {
9489       // linear convertible
9490     case SMDSEntity_Edge:
9491     case SMDSEntity_Triangle:
9492     case SMDSEntity_Quadrangle:
9493     case SMDSEntity_Tetra:
9494     case SMDSEntity_Pyramid:
9495     case SMDSEntity_Hexa:
9496     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9497       // quadratic that can become bi-quadratic
9498     case SMDSEntity_Quad_Triangle:
9499     case SMDSEntity_Quad_Quadrangle:
9500     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9501       // bi-quadratic
9502     case SMDSEntity_BiQuad_Triangle:
9503     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9504     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9505       // the rest
9506     default:                           alreadyOK = true;
9507     }
9508     if ( alreadyOK ) continue;
9509
9510     const SMDSAbs_ElementType type = elem->GetType();
9511     const int                   id = elem->GetID();
9512     const int              nbNodes = elem->NbCornerNodes();
9513     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9514
9515     helper.SetSubShape( elem->getshapeId() );
9516
9517     if ( !smDS || !smDS->Contains( elem ))
9518       smDS = meshDS->MeshElements( elem->getshapeId() );
9519     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9520
9521     SMDS_MeshElement * newElem = 0;
9522     switch( nbNodes )
9523     {
9524     case 4: // cases for most frequently used element types go first (for optimization)
9525       if ( type == SMDSAbs_Volume )
9526         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9527       else
9528         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9529       break;
9530     case 8:
9531       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9532                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9533       break;
9534     case 3:
9535       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9536       break;
9537     case 2:
9538       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9539       break;
9540     case 5:
9541       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9542                                  nodes[4], id, theForce3d);
9543       break;
9544     case 6:
9545       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9546                                  nodes[4], nodes[5], id, theForce3d);
9547       break;
9548     default:;
9549     }
9550     ReplaceElemInGroups( elem, newElem, meshDS);
9551     if( newElem && smDS )
9552       smDS->AddElement( newElem );
9553
9554      // remove central nodes
9555     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9556       if ( nodes[i]->NbInverseElements() == 0 )
9557         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9558
9559   } // loop on theElements
9560
9561   if ( !theForce3d )
9562   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9563     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9564     // helper.FixQuadraticElements( myError );
9565     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9566   }
9567 }
9568
9569 //=======================================================================
9570 /*!
9571  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9572  * \return int - nb of checked elements
9573  */
9574 //=======================================================================
9575
9576 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9577                                      SMDS_ElemIteratorPtr theItr,
9578                                      const int            theShapeID)
9579 {
9580   int nbElem = 0;
9581   SMESHDS_Mesh* meshDS = GetMeshDS();
9582   ElemFeatures elemType;
9583   vector<const SMDS_MeshNode *> nodes;
9584
9585   while( theItr->more() )
9586   {
9587     const SMDS_MeshElement* elem = theItr->next();
9588     nbElem++;
9589     if( elem && elem->IsQuadratic())
9590     {
9591       // get elem data
9592       int nbCornerNodes = elem->NbCornerNodes();
9593       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9594
9595       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9596
9597       //remove a quadratic element
9598       if ( !theSm || !theSm->Contains( elem ))
9599         theSm = meshDS->MeshElements( elem->getshapeId() );
9600       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9601
9602       // remove medium nodes
9603       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9604         if ( nodes[i]->NbInverseElements() == 0 )
9605           meshDS->RemoveFreeNode( nodes[i], theSm );
9606
9607       // add a linear element
9608       nodes.resize( nbCornerNodes );
9609       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9610       ReplaceElemInGroups(elem, newElem, meshDS);
9611       if( theSm && newElem )
9612         theSm->AddElement( newElem );
9613     }
9614   }
9615   return nbElem;
9616 }
9617
9618 //=======================================================================
9619 //function : ConvertFromQuadratic
9620 //purpose  :
9621 //=======================================================================
9622
9623 bool SMESH_MeshEditor::ConvertFromQuadratic()
9624 {
9625   int nbCheckedElems = 0;
9626   if ( myMesh->HasShapeToMesh() )
9627   {
9628     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9629     {
9630       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9631       while ( smIt->more() ) {
9632         SMESH_subMesh* sm = smIt->next();
9633         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9634           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9635       }
9636     }
9637   }
9638
9639   int totalNbElems =
9640     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9641   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9642   {
9643     SMESHDS_SubMesh *aSM = 0;
9644     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9645   }
9646
9647   return true;
9648 }
9649
9650 namespace
9651 {
9652   //================================================================================
9653   /*!
9654    * \brief Return true if all medium nodes of the element are in the node set
9655    */
9656   //================================================================================
9657
9658   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9659   {
9660     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9661       if ( !nodeSet.count( elem->GetNode(i) ))
9662         return false;
9663     return true;
9664   }
9665 }
9666
9667 //================================================================================
9668 /*!
9669  * \brief Makes given elements linear
9670  */
9671 //================================================================================
9672
9673 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9674 {
9675   if ( theElements.empty() ) return;
9676
9677   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9678   set<int> mediumNodeIDs;
9679   TIDSortedElemSet::iterator eIt = theElements.begin();
9680   for ( ; eIt != theElements.end(); ++eIt )
9681   {
9682     const SMDS_MeshElement* e = *eIt;
9683     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9684       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9685   }
9686
9687   // replace given elements by linear ones
9688   SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9689   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9690
9691   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9692   // except those elements sharing medium nodes of quadratic element whose medium nodes
9693   // are not all in mediumNodeIDs
9694
9695   // get remaining medium nodes
9696   TIDSortedNodeSet mediumNodes;
9697   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9698   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9699     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9700       mediumNodes.insert( mediumNodes.end(), n );
9701
9702   // find more quadratic elements to convert
9703   TIDSortedElemSet moreElemsToConvert;
9704   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9705   for ( ; nIt != mediumNodes.end(); ++nIt )
9706   {
9707     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9708     while ( invIt->more() )
9709     {
9710       const SMDS_MeshElement* e = invIt->next();
9711       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9712       {
9713         // find a more complex element including e and
9714         // whose medium nodes are not in mediumNodes
9715         bool complexFound = false;
9716         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9717         {
9718           SMDS_ElemIteratorPtr invIt2 =
9719             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9720           while ( invIt2->more() )
9721           {
9722             const SMDS_MeshElement* eComplex = invIt2->next();
9723             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9724             {
9725               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9726               if ( nbCommonNodes == e->NbNodes())
9727               {
9728                 complexFound = true;
9729                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9730                 break;
9731               }
9732             }
9733           }
9734         }
9735         if ( !complexFound )
9736           moreElemsToConvert.insert( e );
9737       }
9738     }
9739   }
9740   elemIt = elemSetIterator( moreElemsToConvert );
9741   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9742 }
9743
9744 //=======================================================================
9745 //function : SewSideElements
9746 //purpose  :
9747 //=======================================================================
9748
9749 SMESH_MeshEditor::Sew_Error
9750 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9751                                    TIDSortedElemSet&    theSide2,
9752                                    const SMDS_MeshNode* theFirstNode1,
9753                                    const SMDS_MeshNode* theFirstNode2,
9754                                    const SMDS_MeshNode* theSecondNode1,
9755                                    const SMDS_MeshNode* theSecondNode2)
9756 {
9757   myLastCreatedElems.Clear();
9758   myLastCreatedNodes.Clear();
9759
9760   MESSAGE ("::::SewSideElements()");
9761   if ( theSide1.size() != theSide2.size() )
9762     return SEW_DIFF_NB_OF_ELEMENTS;
9763
9764   Sew_Error aResult = SEW_OK;
9765   // Algo:
9766   // 1. Build set of faces representing each side
9767   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9768   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9769
9770   // =======================================================================
9771   // 1. Build set of faces representing each side:
9772   // =======================================================================
9773   // a. build set of nodes belonging to faces
9774   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9775   // c. create temporary faces representing side of volumes if correspondent
9776   //    face does not exist
9777
9778   SMESHDS_Mesh* aMesh = GetMeshDS();
9779   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9780   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9781   TIDSortedElemSet             faceSet1, faceSet2;
9782   set<const SMDS_MeshElement*> volSet1,  volSet2;
9783   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9784   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9785   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9786   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9787   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9788   int iSide, iFace, iNode;
9789
9790   list<const SMDS_MeshElement* > tempFaceList;
9791   for ( iSide = 0; iSide < 2; iSide++ ) {
9792     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9793     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9794     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9795     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9796     set<const SMDS_MeshElement*>::iterator vIt;
9797     TIDSortedElemSet::iterator eIt;
9798     set<const SMDS_MeshNode*>::iterator    nIt;
9799
9800     // check that given nodes belong to given elements
9801     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9802     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9803     int firstIndex = -1, secondIndex = -1;
9804     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9805       const SMDS_MeshElement* elem = *eIt;
9806       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9807       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9808       if ( firstIndex > -1 && secondIndex > -1 ) break;
9809     }
9810     if ( firstIndex < 0 || secondIndex < 0 ) {
9811       // we can simply return until temporary faces created
9812       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9813     }
9814
9815     // -----------------------------------------------------------
9816     // 1a. Collect nodes of existing faces
9817     //     and build set of face nodes in order to detect missing
9818     //     faces corresponding to sides of volumes
9819     // -----------------------------------------------------------
9820
9821     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9822
9823     // loop on the given element of a side
9824     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9825       //const SMDS_MeshElement* elem = *eIt;
9826       const SMDS_MeshElement* elem = *eIt;
9827       if ( elem->GetType() == SMDSAbs_Face ) {
9828         faceSet->insert( elem );
9829         set <const SMDS_MeshNode*> faceNodeSet;
9830         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9831         while ( nodeIt->more() ) {
9832           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9833           nodeSet->insert( n );
9834           faceNodeSet.insert( n );
9835         }
9836         setOfFaceNodeSet.insert( faceNodeSet );
9837       }
9838       else if ( elem->GetType() == SMDSAbs_Volume )
9839         volSet->insert( elem );
9840     }
9841     // ------------------------------------------------------------------------------
9842     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9843     // ------------------------------------------------------------------------------
9844
9845     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9846       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9847       while ( fIt->more() ) { // loop on faces sharing a node
9848         const SMDS_MeshElement* f = fIt->next();
9849         if ( faceSet->find( f ) == faceSet->end() ) {
9850           // check if all nodes are in nodeSet and
9851           // complete setOfFaceNodeSet if they are
9852           set <const SMDS_MeshNode*> faceNodeSet;
9853           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9854           bool allInSet = true;
9855           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9856             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9857             if ( nodeSet->find( n ) == nodeSet->end() )
9858               allInSet = false;
9859             else
9860               faceNodeSet.insert( n );
9861           }
9862           if ( allInSet ) {
9863             faceSet->insert( f );
9864             setOfFaceNodeSet.insert( faceNodeSet );
9865           }
9866         }
9867       }
9868     }
9869
9870     // -------------------------------------------------------------------------
9871     // 1c. Create temporary faces representing sides of volumes if correspondent
9872     //     face does not exist
9873     // -------------------------------------------------------------------------
9874
9875     if ( !volSet->empty() ) {
9876       //int nodeSetSize = nodeSet->size();
9877
9878       // loop on given volumes
9879       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9880         SMDS_VolumeTool vol (*vIt);
9881         // loop on volume faces: find free faces
9882         // --------------------------------------
9883         list<const SMDS_MeshElement* > freeFaceList;
9884         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9885           if ( !vol.IsFreeFace( iFace ))
9886             continue;
9887           // check if there is already a face with same nodes in a face set
9888           const SMDS_MeshElement* aFreeFace = 0;
9889           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9890           int nbNodes = vol.NbFaceNodes( iFace );
9891           set <const SMDS_MeshNode*> faceNodeSet;
9892           vol.GetFaceNodes( iFace, faceNodeSet );
9893           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9894           if ( isNewFace ) {
9895             // no such a face is given but it still can exist, check it
9896             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9897             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9898           }
9899           if ( !aFreeFace ) {
9900             // create a temporary face
9901             if ( nbNodes == 3 ) {
9902               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9903               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9904             }
9905             else if ( nbNodes == 4 ) {
9906               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9907               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9908             }
9909             else {
9910               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9911               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9912               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9913             }
9914             if ( aFreeFace )
9915               tempFaceList.push_back( aFreeFace );
9916           }
9917
9918           if ( aFreeFace )
9919             freeFaceList.push_back( aFreeFace );
9920
9921         } // loop on faces of a volume
9922
9923         // choose one of several free faces of a volume
9924         // --------------------------------------------
9925         if ( freeFaceList.size() > 1 ) {
9926           // choose a face having max nb of nodes shared by other elems of a side
9927           int maxNbNodes = -1;
9928           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9929           while ( fIt != freeFaceList.end() ) { // loop on free faces
9930             int nbSharedNodes = 0;
9931             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9932             while ( nodeIt->more() ) { // loop on free face nodes
9933               const SMDS_MeshNode* n =
9934                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9935               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9936               while ( invElemIt->more() ) {
9937                 const SMDS_MeshElement* e = invElemIt->next();
9938                 nbSharedNodes += faceSet->count( e );
9939                 nbSharedNodes += elemSet->count( e );
9940               }
9941             }
9942             if ( nbSharedNodes > maxNbNodes ) {
9943               maxNbNodes = nbSharedNodes;
9944               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9945             }
9946             else if ( nbSharedNodes == maxNbNodes ) {
9947               fIt++;
9948             }
9949             else {
9950               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9951             }
9952           }
9953           if ( freeFaceList.size() > 1 )
9954           {
9955             // could not choose one face, use another way
9956             // choose a face most close to the bary center of the opposite side
9957             gp_XYZ aBC( 0., 0., 0. );
9958             set <const SMDS_MeshNode*> addedNodes;
9959             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9960             eIt = elemSet2->begin();
9961             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9962               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9963               while ( nodeIt->more() ) { // loop on free face nodes
9964                 const SMDS_MeshNode* n =
9965                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9966                 if ( addedNodes.insert( n ).second )
9967                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9968               }
9969             }
9970             aBC /= addedNodes.size();
9971             double minDist = DBL_MAX;
9972             fIt = freeFaceList.begin();
9973             while ( fIt != freeFaceList.end() ) { // loop on free faces
9974               double dist = 0;
9975               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9976               while ( nodeIt->more() ) { // loop on free face nodes
9977                 const SMDS_MeshNode* n =
9978                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9979                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9980                 dist += ( aBC - p ).SquareModulus();
9981               }
9982               if ( dist < minDist ) {
9983                 minDist = dist;
9984                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9985               }
9986               else
9987                 fIt = freeFaceList.erase( fIt++ );
9988             }
9989           }
9990         } // choose one of several free faces of a volume
9991
9992         if ( freeFaceList.size() == 1 ) {
9993           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9994           faceSet->insert( aFreeFace );
9995           // complete a node set with nodes of a found free face
9996           //           for ( iNode = 0; iNode < ; iNode++ )
9997           //             nodeSet->insert( fNodes[ iNode ] );
9998         }
9999
10000       } // loop on volumes of a side
10001
10002       //       // complete a set of faces if new nodes in a nodeSet appeared
10003       //       // ----------------------------------------------------------
10004       //       if ( nodeSetSize != nodeSet->size() ) {
10005       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10006       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10007       //           while ( fIt->more() ) { // loop on faces sharing a node
10008       //             const SMDS_MeshElement* f = fIt->next();
10009       //             if ( faceSet->find( f ) == faceSet->end() ) {
10010       //               // check if all nodes are in nodeSet and
10011       //               // complete setOfFaceNodeSet if they are
10012       //               set <const SMDS_MeshNode*> faceNodeSet;
10013       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10014       //               bool allInSet = true;
10015       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10016       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10017       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10018       //                   allInSet = false;
10019       //                 else
10020       //                   faceNodeSet.insert( n );
10021       //               }
10022       //               if ( allInSet ) {
10023       //                 faceSet->insert( f );
10024       //                 setOfFaceNodeSet.insert( faceNodeSet );
10025       //               }
10026       //             }
10027       //           }
10028       //         }
10029       //       }
10030     } // Create temporary faces, if there are volumes given
10031   } // loop on sides
10032
10033   if ( faceSet1.size() != faceSet2.size() ) {
10034     // delete temporary faces: they are in reverseElements of actual nodes
10035 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10036 //    while ( tmpFaceIt->more() )
10037 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10038 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10039 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10040 //      aMesh->RemoveElement(*tmpFaceIt);
10041     MESSAGE("Diff nb of faces");
10042     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10043   }
10044
10045   // ============================================================
10046   // 2. Find nodes to merge:
10047   //              bind a node to remove to a node to put instead
10048   // ============================================================
10049
10050   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10051   if ( theFirstNode1 != theFirstNode2 )
10052     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10053   if ( theSecondNode1 != theSecondNode2 )
10054     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10055
10056   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10057   set< long > linkIdSet; // links to process
10058   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10059
10060   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10061   list< NLink > linkList[2];
10062   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10063   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10064   // loop on links in linkList; find faces by links and append links
10065   // of the found faces to linkList
10066   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10067   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10068   {
10069     NLink link[] = { *linkIt[0], *linkIt[1] };
10070     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10071     if ( !linkIdSet.count( linkID ) )
10072       continue;
10073
10074     // by links, find faces in the face sets,
10075     // and find indices of link nodes in the found faces;
10076     // in a face set, there is only one or no face sharing a link
10077     // ---------------------------------------------------------------
10078
10079     const SMDS_MeshElement* face[] = { 0, 0 };
10080     vector<const SMDS_MeshNode*> fnodes[2];
10081     int iLinkNode[2][2];
10082     TIDSortedElemSet avoidSet;
10083     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10084       const SMDS_MeshNode* n1 = link[iSide].first;
10085       const SMDS_MeshNode* n2 = link[iSide].second;
10086       //cout << "Side " << iSide << " ";
10087       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10088       // find a face by two link nodes
10089       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10090                                                       *faceSetPtr[ iSide ], avoidSet,
10091                                                       &iLinkNode[iSide][0],
10092                                                       &iLinkNode[iSide][1] );
10093       if ( face[ iSide ])
10094       {
10095         //cout << " F " << face[ iSide]->GetID() <<endl;
10096         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10097         // put face nodes to fnodes
10098         if ( face[ iSide ]->IsQuadratic() )
10099         {
10100           // use interlaced nodes iterator
10101           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10102           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10103           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10104           while ( nIter->more() )
10105             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10106         }
10107         else
10108         {
10109           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10110                                   face[ iSide ]->end_nodes() );
10111         }
10112         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10113       }
10114     }
10115
10116     // check similarity of elements of the sides
10117     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10118       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10119       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10120         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10121       }
10122       else {
10123         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10124       }
10125       break; // do not return because it's necessary to remove tmp faces
10126     }
10127
10128     // set nodes to merge
10129     // -------------------
10130
10131     if ( face[0] && face[1] )  {
10132       const int nbNodes = face[0]->NbNodes();
10133       if ( nbNodes != face[1]->NbNodes() ) {
10134         MESSAGE("Diff nb of face nodes");
10135         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10136         break; // do not return because it s necessary to remove tmp faces
10137       }
10138       bool reverse[] = { false, false }; // order of nodes in the link
10139       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10140         // analyse link orientation in faces
10141         int i1 = iLinkNode[ iSide ][ 0 ];
10142         int i2 = iLinkNode[ iSide ][ 1 ];
10143         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10144       }
10145       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10146       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10147       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10148       {
10149         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10150                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10151       }
10152
10153       // add other links of the faces to linkList
10154       // -----------------------------------------
10155
10156       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10157         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10158         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10159         if ( !iter_isnew.second ) { // already in a set: no need to process
10160           linkIdSet.erase( iter_isnew.first );
10161         }
10162         else // new in set == encountered for the first time: add
10163         {
10164           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10165           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10166           linkList[0].push_back ( NLink( n1, n2 ));
10167           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10168         }
10169       }
10170     } // 2 faces found
10171
10172     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10173       break;
10174
10175   } // loop on link lists
10176
10177   if ( aResult == SEW_OK &&
10178        ( //linkIt[0] != linkList[0].end() ||
10179          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10180     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10181              " " << (faceSetPtr[1]->empty()));
10182     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10183   }
10184
10185   // ====================================================================
10186   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10187   // ====================================================================
10188
10189   // delete temporary faces
10190 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10191 //  while ( tmpFaceIt->more() )
10192 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10193   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10194   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10195     aMesh->RemoveElement(*tmpFaceIt);
10196
10197   if ( aResult != SEW_OK)
10198     return aResult;
10199
10200   list< int > nodeIDsToRemove;
10201   vector< const SMDS_MeshNode*> nodes;
10202   ElemFeatures elemType;
10203
10204   // loop on nodes replacement map
10205   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10206   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10207     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10208     {
10209       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10210       nodeIDsToRemove.push_back( nToRemove->GetID() );
10211       // loop on elements sharing nToRemove
10212       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10213       while ( invElemIt->more() ) {
10214         const SMDS_MeshElement* e = invElemIt->next();
10215         // get a new suite of nodes: make replacement
10216         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10217         nodes.resize( nbNodes );
10218         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10219         while ( nIt->more() ) {
10220           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10221           nnIt = nReplaceMap.find( n );
10222           if ( nnIt != nReplaceMap.end() ) {
10223             nbReplaced++;
10224             n = (*nnIt).second;
10225           }
10226           nodes[ i++ ] = n;
10227         }
10228         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10229         //         elemIDsToRemove.push_back( e->GetID() );
10230         //       else
10231         if ( nbReplaced )
10232         {
10233           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10234           aMesh->RemoveElement( e );
10235
10236           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10237           {
10238             AddToSameGroups( newElem, e, aMesh );
10239             if ( int aShapeId = e->getshapeId() )
10240               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10241           }
10242         }
10243       }
10244     }
10245
10246   Remove( nodeIDsToRemove, true );
10247
10248   return aResult;
10249 }
10250
10251 //================================================================================
10252 /*!
10253  * \brief Find corresponding nodes in two sets of faces
10254  * \param theSide1 - first face set
10255  * \param theSide2 - second first face
10256  * \param theFirstNode1 - a boundary node of set 1
10257  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10258  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10259  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10260  * \param nReplaceMap - output map of corresponding nodes
10261  * \return bool  - is a success or not
10262  */
10263 //================================================================================
10264
10265 #ifdef _DEBUG_
10266 //#define DEBUG_MATCHING_NODES
10267 #endif
10268
10269 SMESH_MeshEditor::Sew_Error
10270 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10271                                     set<const SMDS_MeshElement*>& theSide2,
10272                                     const SMDS_MeshNode*          theFirstNode1,
10273                                     const SMDS_MeshNode*          theFirstNode2,
10274                                     const SMDS_MeshNode*          theSecondNode1,
10275                                     const SMDS_MeshNode*          theSecondNode2,
10276                                     TNodeNodeMap &                nReplaceMap)
10277 {
10278   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10279
10280   nReplaceMap.clear();
10281   if ( theFirstNode1 != theFirstNode2 )
10282     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10283   if ( theSecondNode1 != theSecondNode2 )
10284     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10285
10286   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10287   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10288
10289   list< NLink > linkList[2];
10290   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10291   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10292
10293   // loop on links in linkList; find faces by links and append links
10294   // of the found faces to linkList
10295   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10296   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10297     NLink link[] = { *linkIt[0], *linkIt[1] };
10298     if ( linkSet.find( link[0] ) == linkSet.end() )
10299       continue;
10300
10301     // by links, find faces in the face sets,
10302     // and find indices of link nodes in the found faces;
10303     // in a face set, there is only one or no face sharing a link
10304     // ---------------------------------------------------------------
10305
10306     const SMDS_MeshElement* face[] = { 0, 0 };
10307     list<const SMDS_MeshNode*> notLinkNodes[2];
10308     //bool reverse[] = { false, false }; // order of notLinkNodes
10309     int nbNodes[2];
10310     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10311     {
10312       const SMDS_MeshNode* n1 = link[iSide].first;
10313       const SMDS_MeshNode* n2 = link[iSide].second;
10314       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10315       set< const SMDS_MeshElement* > facesOfNode1;
10316       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10317       {
10318         // during a loop of the first node, we find all faces around n1,
10319         // during a loop of the second node, we find one face sharing both n1 and n2
10320         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10321         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10322         while ( fIt->more() ) { // loop on faces sharing a node
10323           const SMDS_MeshElement* f = fIt->next();
10324           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10325               ! facesOfNode1.insert( f ).second ) // f encounters twice
10326           {
10327             if ( face[ iSide ] ) {
10328               MESSAGE( "2 faces per link " );
10329               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10330             }
10331             face[ iSide ] = f;
10332             faceSet->erase( f );
10333
10334             // get not link nodes
10335             int nbN = f->NbNodes();
10336             if ( f->IsQuadratic() )
10337               nbN /= 2;
10338             nbNodes[ iSide ] = nbN;
10339             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10340             int i1 = f->GetNodeIndex( n1 );
10341             int i2 = f->GetNodeIndex( n2 );
10342             int iEnd = nbN, iBeg = -1, iDelta = 1;
10343             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10344             if ( reverse ) {
10345               std::swap( iEnd, iBeg ); iDelta = -1;
10346             }
10347             int i = i2;
10348             while ( true ) {
10349               i += iDelta;
10350               if ( i == iEnd ) i = iBeg + iDelta;
10351               if ( i == i1 ) break;
10352               nodes.push_back ( f->GetNode( i ) );
10353             }
10354           }
10355         }
10356       }
10357     }
10358     // check similarity of elements of the sides
10359     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10360       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10361       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10362         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10363       }
10364       else {
10365         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10366       }
10367     }
10368
10369     // set nodes to merge
10370     // -------------------
10371
10372     if ( face[0] && face[1] )  {
10373       if ( nbNodes[0] != nbNodes[1] ) {
10374         MESSAGE("Diff nb of face nodes");
10375         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10376       }
10377 #ifdef DEBUG_MATCHING_NODES
10378       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10379                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10380                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10381 #endif
10382       int nbN = nbNodes[0];
10383       {
10384         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10385         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10386         for ( int i = 0 ; i < nbN - 2; ++i ) {
10387 #ifdef DEBUG_MATCHING_NODES
10388           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10389 #endif
10390           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10391         }
10392       }
10393
10394       // add other links of the face 1 to linkList
10395       // -----------------------------------------
10396
10397       const SMDS_MeshElement* f0 = face[0];
10398       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10399       for ( int i = 0; i < nbN; i++ )
10400       {
10401         const SMDS_MeshNode* n2 = f0->GetNode( i );
10402         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10403           linkSet.insert( SMESH_TLink( n1, n2 ));
10404         if ( !iter_isnew.second ) { // already in a set: no need to process
10405           linkSet.erase( iter_isnew.first );
10406         }
10407         else // new in set == encountered for the first time: add
10408         {
10409 #ifdef DEBUG_MATCHING_NODES
10410           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10411                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10412 #endif
10413           linkList[0].push_back ( NLink( n1, n2 ));
10414           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10415         }
10416         n1 = n2;
10417       }
10418     } // 2 faces found
10419   } // loop on link lists
10420
10421   return SEW_OK;
10422 }
10423
10424 //================================================================================
10425 /*!
10426  * \brief Create elements equal (on same nodes) to given ones
10427  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10428  *              elements of the uppest dimension are duplicated.
10429  */
10430 //================================================================================
10431
10432 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10433 {
10434   ClearLastCreated();
10435   SMESHDS_Mesh* mesh = GetMeshDS();
10436
10437   // get an element type and an iterator over elements
10438
10439   SMDSAbs_ElementType type;
10440   SMDS_ElemIteratorPtr elemIt;
10441   vector< const SMDS_MeshElement* > allElems;
10442   if ( theElements.empty() )
10443   {
10444     if ( mesh->NbNodes() == 0 )
10445       return;
10446     // get most complex type
10447     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10448       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10449       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10450     };
10451     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10452       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10453       {
10454         type = types[i];
10455         break;
10456       }
10457     // put all elements in the vector <allElems>
10458     allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10459     elemIt = mesh->elementsIterator( type );
10460     while ( elemIt->more() )
10461       allElems.push_back( elemIt->next());
10462     elemIt = elemSetIterator( allElems );
10463   }
10464   else
10465   {
10466     type = (*theElements.begin())->GetType();
10467     elemIt = elemSetIterator( theElements );
10468   }
10469
10470   // duplicate elements
10471
10472   ElemFeatures elemType;
10473
10474   vector< const SMDS_MeshNode* > nodes;
10475   while ( elemIt->more() )
10476   {
10477     const SMDS_MeshElement* elem = elemIt->next();
10478     if ( elem->GetType() != type )
10479       continue;
10480
10481     elemType.Init( elem, /*basicOnly=*/false );
10482     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10483
10484     AddElement( nodes, elemType );
10485   }
10486 }
10487
10488 //================================================================================
10489 /*!
10490   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10491   \param theElems - the list of elements (edges or faces) to be replicated
10492   The nodes for duplication could be found from these elements
10493   \param theNodesNot - list of nodes to NOT replicate
10494   \param theAffectedElems - the list of elements (cells and edges) to which the
10495   replicated nodes should be associated to.
10496   \return TRUE if operation has been completed successfully, FALSE otherwise
10497 */
10498 //================================================================================
10499
10500 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10501                                     const TIDSortedElemSet& theNodesNot,
10502                                     const TIDSortedElemSet& theAffectedElems )
10503 {
10504   myLastCreatedElems.Clear();
10505   myLastCreatedNodes.Clear();
10506
10507   if ( theElems.size() == 0 )
10508     return false;
10509
10510   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10511   if ( !aMeshDS )
10512     return false;
10513
10514   bool res = false;
10515   TNodeNodeMap anOldNodeToNewNode;
10516   // duplicate elements and nodes
10517   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10518   // replce nodes by duplications
10519   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10520   return res;
10521 }
10522
10523 //================================================================================
10524 /*!
10525   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10526   \param theMeshDS - mesh instance
10527   \param theElems - the elements replicated or modified (nodes should be changed)
10528   \param theNodesNot - nodes to NOT replicate
10529   \param theNodeNodeMap - relation of old node to new created node
10530   \param theIsDoubleElem - flag os to replicate element or modify
10531   \return TRUE if operation has been completed successfully, FALSE otherwise
10532 */
10533 //================================================================================
10534
10535 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10536                                    const TIDSortedElemSet& theElems,
10537                                    const TIDSortedElemSet& theNodesNot,
10538                                    TNodeNodeMap&           theNodeNodeMap,
10539                                    const bool              theIsDoubleElem )
10540 {
10541   MESSAGE("doubleNodes");
10542   // iterate through element and duplicate them (by nodes duplication)
10543   bool res = false;
10544   std::vector<const SMDS_MeshNode*> newNodes;
10545   ElemFeatures elemType;
10546
10547   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10548   for ( ;  elemItr != theElems.end(); ++elemItr )
10549   {
10550     const SMDS_MeshElement* anElem = *elemItr;
10551     if (!anElem)
10552       continue;
10553
10554     // duplicate nodes to duplicate element
10555     bool isDuplicate = false;
10556     newNodes.resize( anElem->NbNodes() );
10557     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10558     int ind = 0;
10559     while ( anIter->more() )
10560     {
10561       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10562       const SMDS_MeshNode*  aNewNode = aCurrNode;
10563       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10564       if ( n2n != theNodeNodeMap.end() )
10565       {
10566         aNewNode = n2n->second;
10567       }
10568       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10569       {
10570         // duplicate node
10571         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10572         copyPosition( aCurrNode, aNewNode );
10573         theNodeNodeMap[ aCurrNode ] = aNewNode;
10574         myLastCreatedNodes.Append( aNewNode );
10575       }
10576       isDuplicate |= (aCurrNode != aNewNode);
10577       newNodes[ ind++ ] = aNewNode;
10578     }
10579     if ( !isDuplicate )
10580       continue;
10581
10582     if ( theIsDoubleElem )
10583       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10584     else
10585       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10586
10587     res = true;
10588   }
10589   return res;
10590 }
10591
10592 //================================================================================
10593 /*!
10594   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10595   \param theNodes - identifiers of nodes to be doubled
10596   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10597   nodes. If list of element identifiers is empty then nodes are doubled but
10598   they not assigned to elements
10599   \return TRUE if operation has been completed successfully, FALSE otherwise
10600 */
10601 //================================================================================
10602
10603 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10604                                     const std::list< int >& theListOfModifiedElems )
10605 {
10606   MESSAGE("DoubleNodes");
10607   myLastCreatedElems.Clear();
10608   myLastCreatedNodes.Clear();
10609
10610   if ( theListOfNodes.size() == 0 )
10611     return false;
10612
10613   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10614   if ( !aMeshDS )
10615     return false;
10616
10617   // iterate through nodes and duplicate them
10618
10619   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10620
10621   std::list< int >::const_iterator aNodeIter;
10622   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10623   {
10624     int aCurr = *aNodeIter;
10625     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10626     if ( !aNode )
10627       continue;
10628
10629     // duplicate node
10630
10631     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10632     if ( aNewNode )
10633     {
10634       copyPosition( aNode, aNewNode );
10635       anOldNodeToNewNode[ aNode ] = aNewNode;
10636       myLastCreatedNodes.Append( aNewNode );
10637     }
10638   }
10639
10640   // Create map of new nodes for modified elements
10641
10642   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10643
10644   std::list< int >::const_iterator anElemIter;
10645   for ( anElemIter = theListOfModifiedElems.begin();
10646         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10647   {
10648     int aCurr = *anElemIter;
10649     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10650     if ( !anElem )
10651       continue;
10652
10653     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10654
10655     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10656     int ind = 0;
10657     while ( anIter->more() )
10658     {
10659       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10660       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10661       {
10662         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10663         aNodeArr[ ind++ ] = aNewNode;
10664       }
10665       else
10666         aNodeArr[ ind++ ] = aCurrNode;
10667     }
10668     anElemToNodes[ anElem ] = aNodeArr;
10669   }
10670
10671   // Change nodes of elements
10672
10673   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10674     anElemToNodesIter = anElemToNodes.begin();
10675   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10676   {
10677     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10678     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10679     if ( anElem )
10680       {
10681       MESSAGE("ChangeElementNodes");
10682       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10683       }
10684   }
10685
10686   return true;
10687 }
10688
10689 namespace {
10690
10691   //================================================================================
10692   /*!
10693   \brief Check if element located inside shape
10694   \return TRUE if IN or ON shape, FALSE otherwise
10695   */
10696   //================================================================================
10697
10698   template<class Classifier>
10699   bool isInside(const SMDS_MeshElement* theElem,
10700                 Classifier&             theClassifier,
10701                 const double            theTol)
10702   {
10703     gp_XYZ centerXYZ (0, 0, 0);
10704     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10705     while (aNodeItr->more())
10706       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10707
10708     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10709     theClassifier.Perform(aPnt, theTol);
10710     TopAbs_State aState = theClassifier.State();
10711     return (aState == TopAbs_IN || aState == TopAbs_ON );
10712   }
10713
10714   //================================================================================
10715   /*!
10716    * \brief Classifier of the 3D point on the TopoDS_Face
10717    *        with interaface suitable for isInside()
10718    */
10719   //================================================================================
10720
10721   struct _FaceClassifier
10722   {
10723     Extrema_ExtPS       _extremum;
10724     BRepAdaptor_Surface _surface;
10725     TopAbs_State        _state;
10726
10727     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10728     {
10729       _extremum.Initialize( _surface,
10730                             _surface.FirstUParameter(), _surface.LastUParameter(),
10731                             _surface.FirstVParameter(), _surface.LastVParameter(),
10732                             _surface.Tolerance(), _surface.Tolerance() );
10733     }
10734     void Perform(const gp_Pnt& aPnt, double theTol)
10735     {
10736       theTol *= theTol;
10737       _state = TopAbs_OUT;
10738       _extremum.Perform(aPnt);
10739       if ( _extremum.IsDone() )
10740         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10741           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10742     }
10743     TopAbs_State State() const
10744     {
10745       return _state;
10746     }
10747   };
10748 }
10749
10750 //================================================================================
10751 /*!
10752   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10753   This method is the first step of DoubleNodeElemGroupsInRegion.
10754   \param theElems - list of groups of elements (edges or faces) to be replicated
10755   \param theNodesNot - list of groups of nodes not to replicated
10756   \param theShape - shape to detect affected elements (element which geometric center
10757          located on or inside shape). If the shape is null, detection is done on faces orientations
10758          (select elements with a gravity center on the side given by faces normals).
10759          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10760          The replicated nodes should be associated to affected elements.
10761   \return groups of affected elements
10762   \sa DoubleNodeElemGroupsInRegion()
10763  */
10764 //================================================================================
10765
10766 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10767                                                    const TIDSortedElemSet& theNodesNot,
10768                                                    const TopoDS_Shape&     theShape,
10769                                                    TIDSortedElemSet&       theAffectedElems)
10770 {
10771   if ( theShape.IsNull() )
10772   {
10773     std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10774     std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10775     std::set<const SMDS_MeshElement*> edgesToCheck;
10776     alreadyCheckedNodes.clear();
10777     alreadyCheckedElems.clear();
10778     edgesToCheck.clear();
10779
10780     // --- iterates on elements to be replicated and get elements by back references from their nodes
10781
10782     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10783     int ielem;
10784     for ( ielem=1;  elemItr != theElems.end(); ++elemItr )
10785     {
10786       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10787       if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10788         continue;
10789       gp_XYZ normal;
10790       SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10791       MESSAGE("element " << ielem++ <<  " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10792       std::set<const SMDS_MeshNode*> nodesElem;
10793       nodesElem.clear();
10794       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10795       while ( nodeItr->more() )
10796       {
10797         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10798         nodesElem.insert(aNode);
10799       }
10800       std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10801       for (; nodit != nodesElem.end(); nodit++)
10802       {
10803         MESSAGE("  noeud ");
10804         const SMDS_MeshNode* aNode = *nodit;
10805         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10806           continue;
10807         if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10808           continue;
10809         alreadyCheckedNodes.insert(aNode);
10810         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10811         while ( backElemItr->more() )
10812         {
10813           MESSAGE("    backelem ");
10814           const SMDS_MeshElement* curElem = backElemItr->next();
10815           if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10816             continue;
10817           if (theElems.find(curElem) != theElems.end())
10818             continue;
10819           alreadyCheckedElems.insert(curElem);
10820           double x=0, y=0, z=0;
10821           int nb = 0;
10822           SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10823           while ( nodeItr2->more() )
10824           {
10825             const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10826             x += anotherNode->X();
10827             y += anotherNode->Y();
10828             z += anotherNode->Z();
10829             nb++;
10830           }
10831           gp_XYZ p;
10832           p.SetCoord( x/nb -aNode->X(),
10833                       y/nb -aNode->Y(),
10834                       z/nb -aNode->Z() );
10835           MESSAGE("      check " << p.X() << " " << p.Y() << " " << p.Z());
10836           if (normal*p > 0)
10837           {
10838             MESSAGE("    --- inserted")
10839             theAffectedElems.insert( curElem );
10840           }
10841           else if (curElem->GetType() == SMDSAbs_Edge)
10842             edgesToCheck.insert(curElem);
10843         }
10844       }
10845     }
10846     // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10847     std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10848     for( ; eit != edgesToCheck.end(); eit++)
10849     {
10850       bool onside = true;
10851       const SMDS_MeshElement* anEdge = *eit;
10852       SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10853       while ( nodeItr->more() )
10854       {
10855         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10856         if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10857         {
10858           onside = false;
10859           break;
10860         }
10861       }
10862       if (onside)
10863       {
10864         MESSAGE("    --- edge onside inserted")
10865         theAffectedElems.insert(anEdge);
10866       }
10867     }
10868   }
10869   else
10870   {
10871     const double aTol = Precision::Confusion();
10872     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10873     auto_ptr<_FaceClassifier>              aFaceClassifier;
10874     if ( theShape.ShapeType() == TopAbs_SOLID )
10875     {
10876       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10877       bsc3d->PerformInfinitePoint(aTol);
10878     }
10879     else if (theShape.ShapeType() == TopAbs_FACE )
10880     {
10881       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10882     }
10883
10884     // iterates on indicated elements and get elements by back references from their nodes
10885     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10886     int ielem;
10887     for ( ielem = 1;  elemItr != theElems.end(); ++elemItr )
10888     {
10889       MESSAGE("element " << ielem++);
10890       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10891       if (!anElem)
10892         continue;
10893       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10894       while ( nodeItr->more() )
10895       {
10896         MESSAGE("  noeud ");
10897         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10898         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10899           continue;
10900         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10901         while ( backElemItr->more() )
10902         {
10903           MESSAGE("    backelem ");
10904           const SMDS_MeshElement* curElem = backElemItr->next();
10905           if ( curElem && theElems.find(curElem) == theElems.end() &&
10906               ( bsc3d.get() ?
10907                 isInside( curElem, *bsc3d, aTol ) :
10908                 isInside( curElem, *aFaceClassifier, aTol )))
10909             theAffectedElems.insert( curElem );
10910         }
10911       }
10912     }
10913   }
10914   return true;
10915 }
10916
10917 //================================================================================
10918 /*!
10919   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10920   \param theElems - group of of elements (edges or faces) to be replicated
10921   \param theNodesNot - group of nodes not to replicate
10922   \param theShape - shape to detect affected elements (element which geometric center
10923   located on or inside shape).
10924   The replicated nodes should be associated to affected elements.
10925   \return TRUE if operation has been completed successfully, FALSE otherwise
10926 */
10927 //================================================================================
10928
10929 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10930                                             const TIDSortedElemSet& theNodesNot,
10931                                             const TopoDS_Shape&     theShape )
10932 {
10933   if ( theShape.IsNull() )
10934     return false;
10935
10936   const double aTol = Precision::Confusion();
10937   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10938   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
10939   if ( theShape.ShapeType() == TopAbs_SOLID )
10940   {
10941     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
10942     bsc3d->PerformInfinitePoint(aTol);
10943   }
10944   else if (theShape.ShapeType() == TopAbs_FACE )
10945   {
10946     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
10947   }
10948
10949   // iterates on indicated elements and get elements by back references from their nodes
10950   TIDSortedElemSet anAffected;
10951   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10952   for ( ;  elemItr != theElems.end(); ++elemItr )
10953   {
10954     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10955     if (!anElem)
10956       continue;
10957
10958     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10959     while ( nodeItr->more() )
10960     {
10961       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10962       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10963         continue;
10964       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10965       while ( backElemItr->more() )
10966       {
10967         const SMDS_MeshElement* curElem = backElemItr->next();
10968         if ( curElem && theElems.find(curElem) == theElems.end() &&
10969              ( bsc3d ?
10970                isInside( curElem, *bsc3d, aTol ) :
10971                isInside( curElem, *aFaceClassifier, aTol )))
10972           anAffected.insert( curElem );
10973       }
10974     }
10975   }
10976   return DoubleNodes( theElems, theNodesNot, anAffected );
10977 }
10978
10979 /*!
10980  *  \brief compute an oriented angle between two planes defined by four points.
10981  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10982  *  @param p0 base of the rotation axe
10983  *  @param p1 extremity of the rotation axe
10984  *  @param g1 belongs to the first plane
10985  *  @param g2 belongs to the second plane
10986  */
10987 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10988 {
10989 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10990 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10991 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10992 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10993   gp_Vec vref(p0, p1);
10994   gp_Vec v1(p0, g1);
10995   gp_Vec v2(p0, g2);
10996   gp_Vec n1 = vref.Crossed(v1);
10997   gp_Vec n2 = vref.Crossed(v2);
10998   try {
10999     return n2.AngleWithRef(n1, vref);
11000   }
11001   catch ( Standard_Failure ) {
11002   }
11003   return Max( v1.Magnitude(), v2.Magnitude() );
11004 }
11005
11006 /*!
11007  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11008  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11009  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11010  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11011  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11012  * 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.
11013  * 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.
11014  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11015  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11016  * \param theElems - list of groups of volumes, where a group of volume is a set of
11017  *        SMDS_MeshElements sorted by Id.
11018  * \param createJointElems - if TRUE, create the elements
11019  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11020  *        the boundary between \a theDomains and the rest mesh
11021  * \return TRUE if operation has been completed successfully, FALSE otherwise
11022  */
11023 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11024                                                      bool                                 createJointElems,
11025                                                      bool                                 onAllBoundaries)
11026 {
11027   MESSAGE("----------------------------------------------");
11028   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11029   MESSAGE("----------------------------------------------");
11030
11031   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11032   meshDS->BuildDownWardConnectivity(true);
11033   CHRONO(50);
11034   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11035
11036   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11037   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11038   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11039
11040   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11041   std::map<int,int>celldom; // cell vtkId --> domain
11042   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11043   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11044   faceDomains.clear();
11045   celldom.clear();
11046   cellDomains.clear();
11047   nodeDomains.clear();
11048   std::map<int,int> emptyMap;
11049   std::set<int> emptySet;
11050   emptyMap.clear();
11051
11052   MESSAGE(".. Number of domains :"<<theElems.size());
11053
11054   TIDSortedElemSet theRestDomElems;
11055   const int iRestDom  = -1;
11056   const int idom0     = onAllBoundaries ? iRestDom : 0;
11057   const int nbDomains = theElems.size();
11058
11059   // Check if the domains do not share an element
11060   for (int idom = 0; idom < nbDomains-1; idom++)
11061   {
11062     //       MESSAGE("... Check of domain #" << idom);
11063     const TIDSortedElemSet& domain = theElems[idom];
11064     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11065     for (; elemItr != domain.end(); ++elemItr)
11066     {
11067       const SMDS_MeshElement* anElem = *elemItr;
11068       int idombisdeb = idom + 1 ;
11069       // check if the element belongs to a domain further in the list
11070       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11071       {
11072         const TIDSortedElemSet& domainbis = theElems[idombis];
11073         if ( domainbis.count( anElem ))
11074         {
11075           MESSAGE(".... Domain #" << idom);
11076           MESSAGE(".... Domain #" << idombis);
11077           throw SALOME_Exception("The domains are not disjoint.");
11078           return false ;
11079         }
11080       }
11081     }
11082   }
11083
11084   for (int idom = 0; idom < nbDomains; idom++)
11085   {
11086
11087     // --- build a map (face to duplicate --> volume to modify)
11088     //     with all the faces shared by 2 domains (group of elements)
11089     //     and corresponding volume of this domain, for each shared face.
11090     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11091
11092     MESSAGE("... Neighbors of domain #" << idom);
11093     const TIDSortedElemSet& domain = theElems[idom];
11094     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11095     for (; elemItr != domain.end(); ++elemItr)
11096     {
11097       const SMDS_MeshElement* anElem = *elemItr;
11098       if (!anElem)
11099         continue;
11100       int vtkId = anElem->getVtkId();
11101       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11102       int neighborsVtkIds[NBMAXNEIGHBORS];
11103       int downIds[NBMAXNEIGHBORS];
11104       unsigned char downTypes[NBMAXNEIGHBORS];
11105       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11106       for (int n = 0; n < nbNeighbors; n++)
11107       {
11108         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11109         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11110         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11111         {
11112           bool ok = false;
11113           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11114           {
11115             // MESSAGE("Domain " << idombis);
11116             const TIDSortedElemSet& domainbis = theElems[idombis];
11117             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11118           }
11119           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11120           {
11121             DownIdType face(downIds[n], downTypes[n]);
11122             if (!faceDomains[face].count(idom))
11123             {
11124               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11125               celldom[vtkId] = idom;
11126               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11127             }
11128             if ( !ok )
11129             {
11130               theRestDomElems.insert( elem );
11131               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11132               celldom[neighborsVtkIds[n]] = iRestDom;
11133             }
11134           }
11135         }
11136       }
11137     }
11138   }
11139
11140   //MESSAGE("Number of shared faces " << faceDomains.size());
11141   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11142
11143   // --- explore the shared faces domain by domain,
11144   //     explore the nodes of the face and see if they belong to a cell in the domain,
11145   //     which has only a node or an edge on the border (not a shared face)
11146
11147   for (int idomain = idom0; idomain < nbDomains; idomain++)
11148   {
11149     //MESSAGE("Domain " << idomain);
11150     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11151     itface = faceDomains.begin();
11152     for (; itface != faceDomains.end(); ++itface)
11153     {
11154       const std::map<int, int>& domvol = itface->second;
11155       if (!domvol.count(idomain))
11156         continue;
11157       DownIdType face = itface->first;
11158       //MESSAGE(" --- face " << face.cellId);
11159       std::set<int> oldNodes;
11160       oldNodes.clear();
11161       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11162       std::set<int>::iterator itn = oldNodes.begin();
11163       for (; itn != oldNodes.end(); ++itn)
11164       {
11165         int oldId = *itn;
11166         //MESSAGE("     node " << oldId);
11167         vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11168         for (int i=0; i<l.ncells; i++)
11169         {
11170           int vtkId = l.cells[i];
11171           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11172           if (!domain.count(anElem))
11173             continue;
11174           int vtkType = grid->GetCellType(vtkId);
11175           int downId = grid->CellIdToDownId(vtkId);
11176           if (downId < 0)
11177           {
11178             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11179             continue; // not OK at this stage of the algorithm:
11180             //no cells created after BuildDownWardConnectivity
11181           }
11182           DownIdType aCell(downId, vtkType);
11183           cellDomains[aCell][idomain] = vtkId;
11184           celldom[vtkId] = idomain;
11185           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11186         }
11187       }
11188     }
11189   }
11190
11191   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11192   //     for each shared face, get the nodes
11193   //     for each node, for each domain of the face, create a clone of the node
11194
11195   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11196   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11197   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11198
11199   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11200   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11201   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11202
11203   MESSAGE(".. Duplication of the nodes");
11204   for (int idomain = idom0; idomain < nbDomains; idomain++)
11205   {
11206     itface = faceDomains.begin();
11207     for (; itface != faceDomains.end(); ++itface)
11208     {
11209       const std::map<int, int>& domvol = itface->second;
11210       if (!domvol.count(idomain))
11211         continue;
11212       DownIdType face = itface->first;
11213       //MESSAGE(" --- face " << face.cellId);
11214       std::set<int> oldNodes;
11215       oldNodes.clear();
11216       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11217       std::set<int>::iterator itn = oldNodes.begin();
11218       for (; itn != oldNodes.end(); ++itn)
11219       {
11220         int oldId = *itn;
11221         if (nodeDomains[oldId].empty())
11222         {
11223           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11224           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11225         }
11226         std::map<int, int>::const_iterator itdom = domvol.begin();
11227         for (; itdom != domvol.end(); ++itdom)
11228         {
11229           int idom = itdom->first;
11230           //MESSAGE("         domain " << idom);
11231           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11232           {
11233             if (nodeDomains[oldId].size() >= 2) // a multiple node
11234             {
11235               vector<int> orderedDoms;
11236               //MESSAGE("multiple node " << oldId);
11237               if (mutipleNodes.count(oldId))
11238                 orderedDoms = mutipleNodes[oldId];
11239               else
11240               {
11241                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11242                 for (; it != nodeDomains[oldId].end(); ++it)
11243                   orderedDoms.push_back(it->first);
11244               }
11245               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11246               //stringstream txt;
11247               //for (int i=0; i<orderedDoms.size(); i++)
11248               //  txt << orderedDoms[i] << " ";
11249               //MESSAGE("orderedDoms " << txt.str());
11250               mutipleNodes[oldId] = orderedDoms;
11251             }
11252             double *coords = grid->GetPoint(oldId);
11253             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11254             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11255             int newId = newNode->getVtkId();
11256             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11257             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11258           }
11259         }
11260       }
11261     }
11262   }
11263
11264   MESSAGE(".. Creation of elements");
11265   for (int idomain = idom0; idomain < nbDomains; idomain++)
11266   {
11267     itface = faceDomains.begin();
11268     for (; itface != faceDomains.end(); ++itface)
11269     {
11270       std::map<int, int> domvol = itface->second;
11271       if (!domvol.count(idomain))
11272         continue;
11273       DownIdType face = itface->first;
11274       //MESSAGE(" --- face " << face.cellId);
11275       std::set<int> oldNodes;
11276       oldNodes.clear();
11277       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11278       int nbMultipleNodes = 0;
11279       std::set<int>::iterator itn = oldNodes.begin();
11280       for (; itn != oldNodes.end(); ++itn)
11281       {
11282         int oldId = *itn;
11283         if (mutipleNodes.count(oldId))
11284           nbMultipleNodes++;
11285       }
11286       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11287       {
11288         //MESSAGE("multiple Nodes detected on a shared face");
11289         int downId = itface->first.cellId;
11290         unsigned char cellType = itface->first.cellType;
11291         // --- shared edge or shared face ?
11292         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11293         {
11294           int nodes[3];
11295           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11296           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11297             if (mutipleNodes.count(nodes[i]))
11298               if (!mutipleNodesToFace.count(nodes[i]))
11299                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11300         }
11301         else // shared face (between two volumes)
11302         {
11303           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11304           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11305           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11306           for (int ie =0; ie < nbEdges; ie++)
11307           {
11308             int nodes[3];
11309             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11310             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11311             {
11312               vector<int> vn0 = mutipleNodes[nodes[0]];
11313               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11314               vector<int> doms;
11315               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11316                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11317                   if ( vn0[i0] == vn1[i1] )
11318                     doms.push_back( vn0[ i0 ]);
11319               if ( doms.size() > 2 )
11320               {
11321                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11322                 double *coords = grid->GetPoint(nodes[0]);
11323                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11324                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11325                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11326                 gp_Pnt gref;
11327                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11328                 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11329                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11330                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11331                 for ( size_t id = 0; id < doms.size(); id++ )
11332                 {
11333                   int idom = doms[id];
11334                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11335                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11336                   {
11337                     int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11338                     SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11339                     if (domain.count(elem))
11340                     {
11341                       SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11342                       domvol[idom] = svol;
11343                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11344                       double values[3];
11345                       vtkIdType npts = 0;
11346                       vtkIdType* pts = 0;
11347                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11348                       SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11349                       if (id ==0)
11350                       {
11351                         gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11352                         angleDom[idom] = 0;
11353                       }
11354                       else
11355                       {
11356                         gp_Pnt g(values[0], values[1], values[2]);
11357                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11358                         //MESSAGE("  angle=" << angleDom[idom]);
11359                       }
11360                       break;
11361                     }
11362                   }
11363                 }
11364                 map<double, int> sortedDom; // sort domains by angle
11365                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11366                   sortedDom[ia->second] = ia->first;
11367                 vector<int> vnodes;
11368                 vector<int> vdom;
11369                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11370                 {
11371                   vdom.push_back(ib->second);
11372                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11373                 }
11374                 for (int ino = 0; ino < nbNodes; ino++)
11375                   vnodes.push_back(nodes[ino]);
11376                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11377               }
11378             }
11379           }
11380         }
11381       }
11382     }
11383   }
11384
11385   // --- iterate on shared faces (volumes to modify, face to extrude)
11386   //     get node id's of the face (id SMDS = id VTK)
11387   //     create flat element with old and new nodes if requested
11388
11389   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11390   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11391
11392   std::map<int, std::map<long,int> > nodeQuadDomains;
11393   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11394
11395   MESSAGE(".. Creation of elements: simple junction");
11396   if (createJointElems)
11397   {
11398     int idg;
11399     string joints2DName = "joints2D";
11400     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11401     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11402     string joints3DName = "joints3D";
11403     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11404     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11405
11406     itface = faceDomains.begin();
11407     for (; itface != faceDomains.end(); ++itface)
11408     {
11409       DownIdType face = itface->first;
11410       std::set<int> oldNodes;
11411       std::set<int>::iterator itn;
11412       oldNodes.clear();
11413       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11414
11415       std::map<int, int> domvol = itface->second;
11416       std::map<int, int>::iterator itdom = domvol.begin();
11417       int dom1 = itdom->first;
11418       int vtkVolId = itdom->second;
11419       itdom++;
11420       int dom2 = itdom->first;
11421       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11422                                                        nodeQuadDomains);
11423       stringstream grpname;
11424       grpname << "j_";
11425       if (dom1 < dom2)
11426         grpname << dom1 << "_" << dom2;
11427       else
11428         grpname << dom2 << "_" << dom1;
11429       string namegrp = grpname.str();
11430       if (!mapOfJunctionGroups.count(namegrp))
11431         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11432       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11433       if (sgrp)
11434         sgrp->Add(vol->GetID());
11435       if (vol->GetType() == SMDSAbs_Volume)
11436         joints3DGrp->Add(vol->GetID());
11437       else if (vol->GetType() == SMDSAbs_Face)
11438         joints2DGrp->Add(vol->GetID());
11439     }
11440   }
11441
11442   // --- create volumes on multiple domain intersection if requested
11443   //     iterate on mutipleNodesToFace
11444   //     iterate on edgesMultiDomains
11445
11446   MESSAGE(".. Creation of elements: multiple junction");
11447   if (createJointElems)
11448   {
11449     // --- iterate on mutipleNodesToFace
11450
11451     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11452     for (; itn != mutipleNodesToFace.end(); ++itn)
11453     {
11454       int node = itn->first;
11455       vector<int> orderDom = itn->second;
11456       vector<vtkIdType> orderedNodes;
11457       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11458         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11459       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11460
11461       stringstream grpname;
11462       grpname << "m2j_";
11463       grpname << 0 << "_" << 0;
11464       int idg;
11465       string namegrp = grpname.str();
11466       if (!mapOfJunctionGroups.count(namegrp))
11467         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11468       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11469       if (sgrp)
11470         sgrp->Add(face->GetID());
11471     }
11472
11473     // --- iterate on edgesMultiDomains
11474
11475     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11476     for (; ite != edgesMultiDomains.end(); ++ite)
11477     {
11478       vector<int> nodes = ite->first;
11479       vector<int> orderDom = ite->second;
11480       vector<vtkIdType> orderedNodes;
11481       if (nodes.size() == 2)
11482       {
11483         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11484         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11485           if ( orderDom.size() == 3 )
11486             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11487               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11488           else
11489             for (int idom = orderDom.size()-1; idom >=0; idom--)
11490               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11491         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11492
11493         int idg;
11494         string namegrp = "jointsMultiples";
11495         if (!mapOfJunctionGroups.count(namegrp))
11496           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11497         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11498         if (sgrp)
11499           sgrp->Add(vol->GetID());
11500       }
11501       else
11502       {
11503         //INFOS("Quadratic multiple joints not implemented");
11504         // TODO quadratic nodes
11505       }
11506     }
11507   }
11508
11509   // --- list the explicit faces and edges of the mesh that need to be modified,
11510   //     i.e. faces and edges built with one or more duplicated nodes.
11511   //     associate these faces or edges to their corresponding domain.
11512   //     only the first domain found is kept when a face or edge is shared
11513
11514   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11515   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11516   faceOrEdgeDom.clear();
11517   feDom.clear();
11518
11519   MESSAGE(".. Modification of elements");
11520   for (int idomain = idom0; idomain < nbDomains; idomain++)
11521   {
11522     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11523     for (; itnod != nodeDomains.end(); ++itnod)
11524     {
11525       int oldId = itnod->first;
11526       //MESSAGE("     node " << oldId);
11527       vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11528       for (int i = 0; i < l.ncells; i++)
11529       {
11530         int vtkId = l.cells[i];
11531         int vtkType = grid->GetCellType(vtkId);
11532         int downId = grid->CellIdToDownId(vtkId);
11533         if (downId < 0)
11534           continue; // new cells: not to be modified
11535         DownIdType aCell(downId, vtkType);
11536         int volParents[1000];
11537         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11538         for (int j = 0; j < nbvol; j++)
11539           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11540             if (!feDom.count(vtkId))
11541             {
11542               feDom[vtkId] = idomain;
11543               faceOrEdgeDom[aCell] = emptyMap;
11544               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11545               //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11546               //        << " type " << vtkType << " downId " << downId);
11547             }
11548       }
11549     }
11550   }
11551
11552   // --- iterate on shared faces (volumes to modify, face to extrude)
11553   //     get node id's of the face
11554   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11555
11556   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11557   for (int m=0; m<3; m++)
11558   {
11559     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11560     itface = (*amap).begin();
11561     for (; itface != (*amap).end(); ++itface)
11562     {
11563       DownIdType face = itface->first;
11564       std::set<int> oldNodes;
11565       std::set<int>::iterator itn;
11566       oldNodes.clear();
11567       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11568       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11569       std::map<int, int> localClonedNodeIds;
11570
11571       std::map<int, int> domvol = itface->second;
11572       std::map<int, int>::iterator itdom = domvol.begin();
11573       for (; itdom != domvol.end(); ++itdom)
11574       {
11575         int idom = itdom->first;
11576         int vtkVolId = itdom->second;
11577         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11578         localClonedNodeIds.clear();
11579         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11580         {
11581           int oldId = *itn;
11582           if (nodeDomains[oldId].count(idom))
11583           {
11584             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11585             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11586           }
11587         }
11588         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11589       }
11590     }
11591   }
11592
11593   // Remove empty groups (issue 0022812)
11594   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11595   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11596   {
11597     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11598       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11599   }
11600
11601   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11602   grid->BuildLinks();
11603
11604   CHRONOSTOP(50);
11605   counters::stats();
11606   return true;
11607 }
11608
11609 /*!
11610  * \brief Double nodes on some external faces and create flat elements.
11611  * Flat elements are mainly used by some types of mechanic calculations.
11612  *
11613  * Each group of the list must be constituted of faces.
11614  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11615  * @param theElems - list of groups of faces, where a group of faces is a set of
11616  * SMDS_MeshElements sorted by Id.
11617  * @return TRUE if operation has been completed successfully, FALSE otherwise
11618  */
11619 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11620 {
11621   MESSAGE("-------------------------------------------------");
11622   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11623   MESSAGE("-------------------------------------------------");
11624
11625   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11626
11627   // --- For each group of faces
11628   //     duplicate the nodes, create a flat element based on the face
11629   //     replace the nodes of the faces by their clones
11630
11631   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11632   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11633   clonedNodes.clear();
11634   intermediateNodes.clear();
11635   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11636   mapOfJunctionGroups.clear();
11637
11638   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11639   {
11640     const TIDSortedElemSet&           domain = theElems[idom];
11641     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11642     for ( ; elemItr != domain.end(); ++elemItr )
11643     {
11644       SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11645       SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11646       if (!aFace)
11647         continue;
11648       // MESSAGE("aFace=" << aFace->GetID());
11649       bool isQuad = aFace->IsQuadratic();
11650       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11651
11652       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11653
11654       SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11655       while (nodeIt->more())
11656       {
11657         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11658         bool isMedium = isQuad && (aFace->IsMediumNode(node));
11659         if (isMedium)
11660           ln2.push_back(node);
11661         else
11662           ln0.push_back(node);
11663
11664         const SMDS_MeshNode* clone = 0;
11665         if (!clonedNodes.count(node))
11666         {
11667           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11668           copyPosition( node, clone );
11669           clonedNodes[node] = clone;
11670         }
11671         else
11672           clone = clonedNodes[node];
11673
11674         if (isMedium)
11675           ln3.push_back(clone);
11676         else
11677           ln1.push_back(clone);
11678
11679         const SMDS_MeshNode* inter = 0;
11680         if (isQuad && (!isMedium))
11681         {
11682           if (!intermediateNodes.count(node))
11683           {
11684             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11685             copyPosition( node, inter );
11686             intermediateNodes[node] = inter;
11687           }
11688           else
11689             inter = intermediateNodes[node];
11690           ln4.push_back(inter);
11691         }
11692       }
11693
11694       // --- extrude the face
11695
11696       vector<const SMDS_MeshNode*> ln;
11697       SMDS_MeshVolume* vol = 0;
11698       vtkIdType aType = aFace->GetVtkType();
11699       switch (aType)
11700       {
11701       case VTK_TRIANGLE:
11702         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11703         // MESSAGE("vol prism " << vol->GetID());
11704         ln.push_back(ln1[0]);
11705         ln.push_back(ln1[1]);
11706         ln.push_back(ln1[2]);
11707         break;
11708       case VTK_QUAD:
11709         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11710         // MESSAGE("vol hexa " << vol->GetID());
11711         ln.push_back(ln1[0]);
11712         ln.push_back(ln1[1]);
11713         ln.push_back(ln1[2]);
11714         ln.push_back(ln1[3]);
11715         break;
11716       case VTK_QUADRATIC_TRIANGLE:
11717         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11718                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11719         // MESSAGE("vol quad prism " << vol->GetID());
11720         ln.push_back(ln1[0]);
11721         ln.push_back(ln1[1]);
11722         ln.push_back(ln1[2]);
11723         ln.push_back(ln3[0]);
11724         ln.push_back(ln3[1]);
11725         ln.push_back(ln3[2]);
11726         break;
11727       case VTK_QUADRATIC_QUAD:
11728         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11729         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11730         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11731         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11732                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11733                                 ln4[0], ln4[1], ln4[2], ln4[3]);
11734         // MESSAGE("vol quad hexa " << vol->GetID());
11735         ln.push_back(ln1[0]);
11736         ln.push_back(ln1[1]);
11737         ln.push_back(ln1[2]);
11738         ln.push_back(ln1[3]);
11739         ln.push_back(ln3[0]);
11740         ln.push_back(ln3[1]);
11741         ln.push_back(ln3[2]);
11742         ln.push_back(ln3[3]);
11743         break;
11744       case VTK_POLYGON:
11745         break;
11746       default:
11747         break;
11748       }
11749
11750       if (vol)
11751       {
11752         stringstream grpname;
11753         grpname << "jf_";
11754         grpname << idom;
11755         int idg;
11756         string namegrp = grpname.str();
11757         if (!mapOfJunctionGroups.count(namegrp))
11758           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11759         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11760         if (sgrp)
11761           sgrp->Add(vol->GetID());
11762       }
11763
11764       // --- modify the face
11765
11766       aFace->ChangeNodes(&ln[0], ln.size());
11767     }
11768   }
11769   return true;
11770 }
11771
11772 /*!
11773  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11774  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11775  *  groups of faces to remove inside the object, (idem edges).
11776  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11777  */
11778 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
11779                                       const TopoDS_Shape&             theShape,
11780                                       SMESH_NodeSearcher*             theNodeSearcher,
11781                                       const char*                     groupName,
11782                                       std::vector<double>&            nodesCoords,
11783                                       std::vector<std::vector<int> >& listOfListOfNodes)
11784 {
11785   MESSAGE("--------------------------------");
11786   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11787   MESSAGE("--------------------------------");
11788
11789   // --- zone of volumes to remove is given :
11790   //     1 either by a geom shape (one or more vertices) and a radius,
11791   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11792   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11793   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11794   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11795   //     defined by it's name.
11796
11797   SMESHDS_GroupBase* groupDS = 0;
11798   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11799   while ( groupIt->more() )
11800   {
11801     groupDS = 0;
11802     SMESH_Group * group = groupIt->next();
11803     if ( !group ) continue;
11804     groupDS = group->GetGroupDS();
11805     if ( !groupDS || groupDS->IsEmpty() ) continue;
11806     std::string grpName = group->GetName();
11807     //MESSAGE("grpName=" << grpName);
11808     if (grpName == groupName)
11809       break;
11810     else
11811       groupDS = 0;
11812   }
11813
11814   bool isNodeGroup = false;
11815   bool isNodeCoords = false;
11816   if (groupDS)
11817   {
11818     if (groupDS->GetType() != SMDSAbs_Node)
11819       return;
11820     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11821   }
11822
11823   if (nodesCoords.size() > 0)
11824     isNodeCoords = true; // a list o nodes given by their coordinates
11825   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11826
11827   // --- define groups to build
11828
11829   int idg; // --- group of SMDS volumes
11830   string grpvName = groupName;
11831   grpvName += "_vol";
11832   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11833   if (!grp)
11834   {
11835     MESSAGE("group not created " << grpvName);
11836     return;
11837   }
11838   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11839
11840   int idgs; // --- group of SMDS faces on the skin
11841   string grpsName = groupName;
11842   grpsName += "_skin";
11843   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11844   if (!grps)
11845   {
11846     MESSAGE("group not created " << grpsName);
11847     return;
11848   }
11849   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11850
11851   int idgi; // --- group of SMDS faces internal (several shapes)
11852   string grpiName = groupName;
11853   grpiName += "_internalFaces";
11854   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11855   if (!grpi)
11856   {
11857     MESSAGE("group not created " << grpiName);
11858     return;
11859   }
11860   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11861
11862   int idgei; // --- group of SMDS faces internal (several shapes)
11863   string grpeiName = groupName;
11864   grpeiName += "_internalEdges";
11865   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11866   if (!grpei)
11867   {
11868     MESSAGE("group not created " << grpeiName);
11869     return;
11870   }
11871   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11872
11873   // --- build downward connectivity
11874
11875   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11876   meshDS->BuildDownWardConnectivity(true);
11877   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11878
11879   // --- set of volumes detected inside
11880
11881   std::set<int> setOfInsideVol;
11882   std::set<int> setOfVolToCheck;
11883
11884   std::vector<gp_Pnt> gpnts;
11885   gpnts.clear();
11886
11887   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11888   {
11889     MESSAGE("group of nodes provided");
11890     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11891     while ( elemIt->more() )
11892     {
11893       const SMDS_MeshElement* elem = elemIt->next();
11894       if (!elem)
11895         continue;
11896       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11897       if (!node)
11898         continue;
11899       SMDS_MeshElement* vol = 0;
11900       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11901       while (volItr->more())
11902       {
11903         vol = (SMDS_MeshElement*)volItr->next();
11904         setOfInsideVol.insert(vol->getVtkId());
11905         sgrp->Add(vol->GetID());
11906       }
11907     }
11908   }
11909   else if (isNodeCoords)
11910   {
11911     MESSAGE("list of nodes coordinates provided");
11912     size_t i = 0;
11913     int k = 0;
11914     while ( i < nodesCoords.size()-2 )
11915     {
11916       double x = nodesCoords[i++];
11917       double y = nodesCoords[i++];
11918       double z = nodesCoords[i++];
11919       gp_Pnt p = gp_Pnt(x, y ,z);
11920       gpnts.push_back(p);
11921       MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11922       k++;
11923     }
11924   }
11925   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11926   {
11927     MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11928     TopTools_IndexedMapOfShape vertexMap;
11929     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11930     gp_Pnt p = gp_Pnt(0,0,0);
11931     if (vertexMap.Extent() < 1)
11932       return;
11933
11934     for ( int i = 1; i <= vertexMap.Extent(); ++i )
11935     {
11936       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11937       p = BRep_Tool::Pnt(vertex);
11938       gpnts.push_back(p);
11939       MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11940     }
11941   }
11942
11943   if (gpnts.size() > 0)
11944   {
11945     int nodeId = 0;
11946     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11947     if (startNode)
11948       nodeId = startNode->GetID();
11949     MESSAGE("nodeId " << nodeId);
11950
11951     double radius2 = radius*radius;
11952     MESSAGE("radius2 " << radius2);
11953
11954     // --- volumes on start node
11955
11956     setOfVolToCheck.clear();
11957     SMDS_MeshElement* startVol = 0;
11958     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11959     while (volItr->more())
11960     {
11961       startVol = (SMDS_MeshElement*)volItr->next();
11962       setOfVolToCheck.insert(startVol->getVtkId());
11963     }
11964     if (setOfVolToCheck.empty())
11965     {
11966       MESSAGE("No volumes found");
11967       return;
11968     }
11969
11970     // --- starting with central volumes then their neighbors, check if they are inside
11971     //     or outside the domain, until no more new neighbor volume is inside.
11972     //     Fill the group of inside volumes
11973
11974     std::map<int, double> mapOfNodeDistance2;
11975     mapOfNodeDistance2.clear();
11976     std::set<int> setOfOutsideVol;
11977     while (!setOfVolToCheck.empty())
11978     {
11979       std::set<int>::iterator it = setOfVolToCheck.begin();
11980       int vtkId = *it;
11981       MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11982       bool volInside = false;
11983       vtkIdType npts = 0;
11984       vtkIdType* pts = 0;
11985       grid->GetCellPoints(vtkId, npts, pts);
11986       for (int i=0; i<npts; i++)
11987       {
11988         double distance2 = 0;
11989         if (mapOfNodeDistance2.count(pts[i]))
11990         {
11991           distance2 = mapOfNodeDistance2[pts[i]];
11992           MESSAGE("point " << pts[i] << " distance2 " << distance2);
11993         }
11994         else
11995         {
11996           double *coords = grid->GetPoint(pts[i]);
11997           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11998           distance2 = 1.E40;
11999           for ( size_t j = 0; j < gpnts.size(); j++ )
12000           {
12001             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12002             if (d2 < distance2)
12003             {
12004               distance2 = d2;
12005               if (distance2 < radius2)
12006                 break;
12007             }
12008           }
12009           mapOfNodeDistance2[pts[i]] = distance2;
12010           MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12011         }
12012         if (distance2 < radius2)
12013         {
12014           volInside = true; // one or more nodes inside the domain
12015           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12016           break;
12017         }
12018       }
12019       if (volInside)
12020       {
12021         setOfInsideVol.insert(vtkId);
12022         MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12023         int neighborsVtkIds[NBMAXNEIGHBORS];
12024         int downIds[NBMAXNEIGHBORS];
12025         unsigned char downTypes[NBMAXNEIGHBORS];
12026         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12027         for (int n = 0; n < nbNeighbors; n++)
12028           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12029             setOfVolToCheck.insert(neighborsVtkIds[n]);
12030       }
12031       else
12032       {
12033         setOfOutsideVol.insert(vtkId);
12034         MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12035       }
12036       setOfVolToCheck.erase(vtkId);
12037     }
12038   }
12039
12040   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12041   //     If yes, add the volume to the inside set
12042
12043   bool addedInside = true;
12044   std::set<int> setOfVolToReCheck;
12045   while (addedInside)
12046   {
12047     MESSAGE(" --------------------------- re check");
12048     addedInside = false;
12049     std::set<int>::iterator itv = setOfInsideVol.begin();
12050     for (; itv != setOfInsideVol.end(); ++itv)
12051     {
12052       int vtkId = *itv;
12053       int neighborsVtkIds[NBMAXNEIGHBORS];
12054       int downIds[NBMAXNEIGHBORS];
12055       unsigned char downTypes[NBMAXNEIGHBORS];
12056       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12057       for (int n = 0; n < nbNeighbors; n++)
12058         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12059           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12060     }
12061     setOfVolToCheck = setOfVolToReCheck;
12062     setOfVolToReCheck.clear();
12063     while  (!setOfVolToCheck.empty())
12064     {
12065       std::set<int>::iterator it = setOfVolToCheck.begin();
12066       int vtkId = *it;
12067       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12068       {
12069         MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12070         int countInside = 0;
12071         int neighborsVtkIds[NBMAXNEIGHBORS];
12072         int downIds[NBMAXNEIGHBORS];
12073         unsigned char downTypes[NBMAXNEIGHBORS];
12074         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12075         for (int n = 0; n < nbNeighbors; n++)
12076           if (setOfInsideVol.count(neighborsVtkIds[n]))
12077             countInside++;
12078         MESSAGE("countInside " << countInside);
12079         if (countInside > 1)
12080         {
12081           MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12082           setOfInsideVol.insert(vtkId);
12083           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12084           addedInside = true;
12085         }
12086         else
12087           setOfVolToReCheck.insert(vtkId);
12088       }
12089       setOfVolToCheck.erase(vtkId);
12090     }
12091   }
12092
12093   // --- map of Downward faces at the boundary, inside the global volume
12094   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12095   //     fill group of SMDS faces inside the volume (when several volume shapes)
12096   //     fill group of SMDS faces on the skin of the global volume (if skin)
12097
12098   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12099   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12100   std::set<int>::iterator it = setOfInsideVol.begin();
12101   for (; it != setOfInsideVol.end(); ++it)
12102   {
12103     int vtkId = *it;
12104     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12105     int neighborsVtkIds[NBMAXNEIGHBORS];
12106     int downIds[NBMAXNEIGHBORS];
12107     unsigned char downTypes[NBMAXNEIGHBORS];
12108     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12109     for (int n = 0; n < nbNeighbors; n++)
12110     {
12111       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12112       if (neighborDim == 3)
12113       {
12114         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12115         {
12116           DownIdType face(downIds[n], downTypes[n]);
12117           boundaryFaces[face] = vtkId;
12118         }
12119         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12120         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12121         if (vtkFaceId >= 0)
12122         {
12123           sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12124           // find also the smds edges on this face
12125           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12126           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12127           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12128           for (int i = 0; i < nbEdges; i++)
12129           {
12130             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12131             if (vtkEdgeId >= 0)
12132               sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12133           }
12134         }
12135       }
12136       else if (neighborDim == 2) // skin of the volume
12137       {
12138         DownIdType face(downIds[n], downTypes[n]);
12139         skinFaces[face] = vtkId;
12140         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12141         if (vtkFaceId >= 0)
12142           sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12143       }
12144     }
12145   }
12146
12147   // --- identify the edges constituting the wire of each subshape on the skin
12148   //     define polylines with the nodes of edges, equivalent to wires
12149   //     project polylines on subshapes, and partition, to get geom faces
12150
12151   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12152   std::set<int> emptySet;
12153   emptySet.clear();
12154   std::set<int> shapeIds;
12155
12156   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12157   while (itelem->more())
12158   {
12159     const SMDS_MeshElement *elem = itelem->next();
12160     int shapeId = elem->getshapeId();
12161     int vtkId = elem->getVtkId();
12162     if (!shapeIdToVtkIdSet.count(shapeId))
12163     {
12164       shapeIdToVtkIdSet[shapeId] = emptySet;
12165       shapeIds.insert(shapeId);
12166     }
12167     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12168   }
12169
12170   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12171   std::set<DownIdType, DownIdCompare> emptyEdges;
12172   emptyEdges.clear();
12173
12174   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12175   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12176   {
12177     int shapeId = itShape->first;
12178     MESSAGE(" --- Shape ID --- "<< shapeId);
12179     shapeIdToEdges[shapeId] = emptyEdges;
12180
12181     std::vector<int> nodesEdges;
12182
12183     std::set<int>::iterator its = itShape->second.begin();
12184     for (; its != itShape->second.end(); ++its)
12185     {
12186       int vtkId = *its;
12187       MESSAGE("     " << vtkId);
12188       int neighborsVtkIds[NBMAXNEIGHBORS];
12189       int downIds[NBMAXNEIGHBORS];
12190       unsigned char downTypes[NBMAXNEIGHBORS];
12191       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12192       for (int n = 0; n < nbNeighbors; n++)
12193       {
12194         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12195           continue;
12196         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12197         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12198         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12199         {
12200           DownIdType edge(downIds[n], downTypes[n]);
12201           if (!shapeIdToEdges[shapeId].count(edge))
12202           {
12203             shapeIdToEdges[shapeId].insert(edge);
12204             int vtkNodeId[3];
12205             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12206             nodesEdges.push_back(vtkNodeId[0]);
12207             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12208             MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12209           }
12210         }
12211       }
12212     }
12213
12214     std::list<int> order;
12215     order.clear();
12216     if (nodesEdges.size() > 0)
12217     {
12218       order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12219       nodesEdges[0] = -1;
12220       order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
12221       nodesEdges[1] = -1; // do not reuse this edge
12222       bool found = true;
12223       while (found)
12224       {
12225         int nodeTofind = order.back(); // try first to push back
12226         int i = 0;
12227         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12228           if (nodesEdges[i] == nodeTofind)
12229             break;
12230         if ( i == (int) nodesEdges.size() )
12231           found = false; // no follower found on back
12232         else
12233         {
12234           if (i%2) // odd ==> use the previous one
12235             if (nodesEdges[i-1] < 0)
12236               found = false;
12237             else
12238             {
12239               order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
12240               nodesEdges[i-1] = -1;
12241             }
12242           else // even ==> use the next one
12243             if (nodesEdges[i+1] < 0)
12244               found = false;
12245             else
12246             {
12247               order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
12248               nodesEdges[i+1] = -1;
12249             }
12250         }
12251         if (found)
12252           continue;
12253         // try to push front
12254         found = true;
12255         nodeTofind = order.front(); // try to push front
12256         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12257           if ( nodesEdges[i] == nodeTofind )
12258             break;
12259         if ( i == (int)nodesEdges.size() )
12260         {
12261           found = false; // no predecessor found on front
12262           continue;
12263         }
12264         if (i%2) // odd ==> use the previous one
12265           if (nodesEdges[i-1] < 0)
12266             found = false;
12267           else
12268           {
12269             order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
12270             nodesEdges[i-1] = -1;
12271           }
12272         else // even ==> use the next one
12273           if (nodesEdges[i+1] < 0)
12274             found = false;
12275           else
12276           {
12277             order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
12278             nodesEdges[i+1] = -1;
12279           }
12280       }
12281     }
12282
12283
12284     std::vector<int> nodes;
12285     nodes.push_back(shapeId);
12286     std::list<int>::iterator itl = order.begin();
12287     for (; itl != order.end(); itl++)
12288     {
12289       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12290       MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12291     }
12292     listOfListOfNodes.push_back(nodes);
12293   }
12294
12295   //     partition geom faces with blocFissure
12296   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12297   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12298
12299   return;
12300 }
12301
12302
12303 //================================================================================
12304 /*!
12305  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12306  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12307  * \return TRUE if operation has been completed successfully, FALSE otherwise
12308  */
12309 //================================================================================
12310
12311 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12312 {
12313   // iterates on volume elements and detect all free faces on them
12314   SMESHDS_Mesh* aMesh = GetMeshDS();
12315   if (!aMesh)
12316     return false;
12317
12318   ElemFeatures faceType( SMDSAbs_Face );
12319   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12320   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12321   while(vIt->more())
12322   {
12323     const SMDS_MeshVolume* volume = vIt->next();
12324     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12325     vTool.SetExternalNormal();
12326     const int iQuad = volume->IsQuadratic();
12327     faceType.SetQuad( iQuad );
12328     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12329     {
12330       if (!vTool.IsFreeFace(iface))
12331         continue;
12332       nbFree++;
12333       vector<const SMDS_MeshNode *> nodes;
12334       int nbFaceNodes = vTool.NbFaceNodes(iface);
12335       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12336       int inode = 0;
12337       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12338         nodes.push_back(faceNodes[inode]);
12339
12340       if (iQuad) // add medium nodes
12341       {
12342         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12343           nodes.push_back(faceNodes[inode]);
12344         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12345           nodes.push_back(faceNodes[8]);
12346       }
12347       // add new face based on volume nodes
12348       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12349       {
12350         nbExisted++; // face already exsist
12351       }
12352       else
12353       {
12354         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12355         nbCreated++;
12356       }
12357     }
12358   }
12359   return ( nbFree == ( nbExisted + nbCreated ));
12360 }
12361
12362 namespace
12363 {
12364   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12365   {
12366     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12367       return n;
12368     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12369   }
12370 }
12371 //================================================================================
12372 /*!
12373  * \brief Creates missing boundary elements
12374  *  \param elements - elements whose boundary is to be checked
12375  *  \param dimension - defines type of boundary elements to create
12376  *  \param group - a group to store created boundary elements in
12377  *  \param targetMesh - a mesh to store created boundary elements in
12378  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12379  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12380  *                                boundary elements will be copied into the targetMesh
12381  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12382  *                                boundary elements will be added into the new group
12383  *  \param aroundElements - if true, elements will be created on boundary of given
12384  *                          elements else, on boundary of the whole mesh.
12385  * \return nb of added boundary elements
12386  */
12387 //================================================================================
12388
12389 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12390                                        Bnd_Dimension           dimension,
12391                                        SMESH_Group*            group/*=0*/,
12392                                        SMESH_Mesh*             targetMesh/*=0*/,
12393                                        bool                    toCopyElements/*=false*/,
12394                                        bool                    toCopyExistingBoundary/*=false*/,
12395                                        bool                    toAddExistingBondary/*= false*/,
12396                                        bool                    aroundElements/*= false*/)
12397 {
12398   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12399   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12400   // hope that all elements are of the same type, do not check them all
12401   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12402     throw SALOME_Exception(LOCALIZED("wrong element type"));
12403
12404   if ( !targetMesh )
12405     toCopyElements = toCopyExistingBoundary = false;
12406
12407   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12408   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12409   int nbAddedBnd = 0;
12410
12411   // editor adding present bnd elements and optionally holding elements to add to the group
12412   SMESH_MeshEditor* presentEditor;
12413   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12414   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12415
12416   SMESH_MesherHelper helper( *myMesh );
12417   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12418   SMDS_VolumeTool vTool;
12419   TIDSortedElemSet avoidSet;
12420   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12421   size_t inode;
12422
12423   typedef vector<const SMDS_MeshNode*> TConnectivity;
12424   TConnectivity tgtNodes;
12425   ElemFeatures elemKind( missType ), elemToCopy;
12426
12427   vector<const SMDS_MeshElement*> presentBndElems;
12428   vector<TConnectivity>           missingBndElems;
12429   vector<int>                     freeFacets;
12430   TConnectivity nodes, elemNodes;
12431
12432   SMDS_ElemIteratorPtr eIt;
12433   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12434   else                  eIt = elemSetIterator( elements );
12435
12436   while (eIt->more())
12437   {
12438     const SMDS_MeshElement* elem = eIt->next();
12439     const int              iQuad = elem->IsQuadratic();
12440     elemKind.SetQuad( iQuad );
12441
12442     // ------------------------------------------------------------------------------------
12443     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12444     // ------------------------------------------------------------------------------------
12445     presentBndElems.clear();
12446     missingBndElems.clear();
12447     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12448     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12449     {
12450       const SMDS_MeshElement* otherVol = 0;
12451       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12452       {
12453         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12454              ( !aroundElements || elements.count( otherVol )))
12455           continue;
12456         freeFacets.push_back( iface );
12457       }
12458       if ( missType == SMDSAbs_Face )
12459         vTool.SetExternalNormal();
12460       for ( size_t i = 0; i < freeFacets.size(); ++i )
12461       {
12462         int                iface = freeFacets[i];
12463         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12464         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12465         if ( missType == SMDSAbs_Edge ) // boundary edges
12466         {
12467           nodes.resize( 2+iQuad );
12468           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12469           {
12470             for ( size_t j = 0; j < nodes.size(); ++j )
12471               nodes[ j ] = nn[ i+j ];
12472             if ( const SMDS_MeshElement* edge =
12473                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12474               presentBndElems.push_back( edge );
12475             else
12476               missingBndElems.push_back( nodes );
12477           }
12478         }
12479         else // boundary face
12480         {
12481           nodes.clear();
12482           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12483             nodes.push_back( nn[inode] ); // add corner nodes
12484           if (iQuad)
12485             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12486               nodes.push_back( nn[inode] ); // add medium nodes
12487           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12488           if ( iCenter > 0 )
12489             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12490
12491           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12492                                                                SMDSAbs_Face, /*noMedium=*/false ))
12493             presentBndElems.push_back( f );
12494           else
12495             missingBndElems.push_back( nodes );
12496
12497           if ( targetMesh != myMesh )
12498           {
12499             // add 1D elements on face boundary to be added to a new mesh
12500             const SMDS_MeshElement* edge;
12501             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12502             {
12503               if ( iQuad )
12504                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12505               else
12506                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12507               if ( edge && avoidSet.insert( edge ).second )
12508                 presentBndElems.push_back( edge );
12509             }
12510           }
12511         }
12512       }
12513     }
12514     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12515     {
12516       avoidSet.clear(), avoidSet.insert( elem );
12517       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12518                         SMDS_MeshElement::iterator() );
12519       elemNodes.push_back( elemNodes[0] );
12520       nodes.resize( 2 + iQuad );
12521       const int nbLinks = elem->NbCornerNodes();
12522       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12523       {
12524         nodes[0] = elemNodes[iN];
12525         nodes[1] = elemNodes[iN+1+iQuad];
12526         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12527           continue; // not free link
12528
12529         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12530         if ( const SMDS_MeshElement* edge =
12531              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12532           presentBndElems.push_back( edge );
12533         else
12534           missingBndElems.push_back( nodes );
12535       }
12536     }
12537
12538     // ---------------------------------
12539     // 2. Add missing boundary elements
12540     // ---------------------------------
12541     if ( targetMesh != myMesh )
12542       // instead of making a map of nodes in this mesh and targetMesh,
12543       // we create nodes with same IDs.
12544       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12545       {
12546         TConnectivity& srcNodes = missingBndElems[i];
12547         tgtNodes.resize( srcNodes.size() );
12548         for ( inode = 0; inode < srcNodes.size(); ++inode )
12549           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12550         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12551                                                                    missType,
12552                                                                    /*noMedium=*/false))
12553           continue;
12554         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12555         ++nbAddedBnd;
12556       }
12557     else
12558       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12559       {
12560         TConnectivity& nodes = missingBndElems[ i ];
12561         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12562                                                                    missType,
12563                                                                    /*noMedium=*/false))
12564           continue;
12565         SMDS_MeshElement* newElem =
12566           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12567         nbAddedBnd += bool( newElem );
12568
12569         // try to set a new element to a shape
12570         if ( myMesh->HasShapeToMesh() )
12571         {
12572           bool ok = true;
12573           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12574           const size_t nbN = nodes.size() / (iQuad+1 );
12575           for ( inode = 0; inode < nbN && ok; ++inode )
12576           {
12577             pair<int, TopAbs_ShapeEnum> i_stype =
12578               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12579             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12580               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12581           }
12582           if ( ok && mediumShapes.size() > 1 )
12583           {
12584             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12585             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12586             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12587             {
12588               if (( ok = ( stype_i->first != stype_i_0.first )))
12589                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12590                                         aMesh->IndexToShape( stype_i_0.second ));
12591             }
12592           }
12593           if ( ok && mediumShapes.begin()->first == missShapeType )
12594             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12595         }
12596       }
12597
12598     // ----------------------------------
12599     // 3. Copy present boundary elements
12600     // ----------------------------------
12601     if ( toCopyExistingBoundary )
12602       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12603       {
12604         const SMDS_MeshElement* e = presentBndElems[i];
12605         tgtNodes.resize( e->NbNodes() );
12606         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12607           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12608         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12609       }
12610     else // store present elements to add them to a group
12611       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12612       {
12613         presentEditor->myLastCreatedElems.Append( presentBndElems[ i ]);
12614       }
12615
12616   } // loop on given elements
12617
12618   // ---------------------------------------------
12619   // 4. Fill group with boundary elements
12620   // ---------------------------------------------
12621   if ( group )
12622   {
12623     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12624       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12625         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12626   }
12627   tgtEditor.myLastCreatedElems.Clear();
12628   tgtEditor2.myLastCreatedElems.Clear();
12629
12630   // -----------------------
12631   // 5. Copy given elements
12632   // -----------------------
12633   if ( toCopyElements && targetMesh != myMesh )
12634   {
12635     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12636     else                  eIt = elemSetIterator( elements );
12637     while (eIt->more())
12638     {
12639       const SMDS_MeshElement* elem = eIt->next();
12640       tgtNodes.resize( elem->NbNodes() );
12641       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12642         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12643       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12644
12645       tgtEditor.myLastCreatedElems.Clear();
12646     }
12647   }
12648   return nbAddedBnd;
12649 }
12650
12651 //================================================================================
12652 /*!
12653  * \brief Copy node position and set \a to node on the same geometry
12654  */
12655 //================================================================================
12656
12657 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12658                                      const SMDS_MeshNode* to )
12659 {
12660   if ( !from || !to ) return;
12661
12662   SMDS_PositionPtr pos = from->GetPosition();
12663   if ( !pos || from->getshapeId() < 1 ) return;
12664
12665   switch ( pos->GetTypeOfPosition() )
12666   {
12667   case SMDS_TOP_3DSPACE: break;
12668
12669   case SMDS_TOP_FACE:
12670   {
12671     const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12672     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12673                                 fPos->GetUParameter(), fPos->GetVParameter() );
12674     break;
12675   }
12676   case SMDS_TOP_EDGE:
12677   {
12678     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12679     const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12680     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12681     break;
12682   }
12683   case SMDS_TOP_VERTEX:
12684   {
12685     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12686     break;
12687   }
12688   case SMDS_TOP_UNSPEC:
12689   default:;
12690   }
12691 }