Salome HOME
Fix pb of make test
[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
7219   if (nbNodes < 3)
7220     return 0;
7221
7222   set<const SMDS_MeshNode*> nodeSet;
7223
7224   // get simple seq of nodes
7225   vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7226   int iSimple = 0;
7227
7228   simpleNodes[iSimple++] = faceNodes[0];
7229   for (int iCur = 1; iCur < nbNodes; iCur++) {
7230     if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7231       simpleNodes[iSimple++] = faceNodes[iCur];
7232       nodeSet.insert( faceNodes[iCur] );
7233     }
7234   }
7235   int nbUnique = nodeSet.size();
7236   int nbSimple = iSimple;
7237   if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7238     nbSimple--;
7239     iSimple--;
7240   }
7241
7242   if (nbUnique < 3)
7243     return 0;
7244
7245   // separate loops
7246   int nbNew = 0;
7247   bool foundLoop = (nbSimple > nbUnique);
7248   while (foundLoop) {
7249     foundLoop = false;
7250     set<const SMDS_MeshNode*> loopSet;
7251     for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7252       const SMDS_MeshNode* n = simpleNodes[iSimple];
7253       if (!loopSet.insert( n ).second) {
7254         foundLoop = true;
7255
7256         // separate loop
7257         int iC = 0, curLast = iSimple;
7258         for (; iC < curLast; iC++) {
7259           if (simpleNodes[iC] == n) break;
7260         }
7261         int loopLen = curLast - iC;
7262         if (loopLen > 2) {
7263           // create sub-element
7264           nbNew++;
7265           quantities.push_back(loopLen);
7266           for (; iC < curLast; iC++) {
7267             poly_nodes.push_back(simpleNodes[iC]);
7268           }
7269         }
7270         // shift the rest nodes (place from the first loop position)
7271         for (iC = curLast + 1; iC < nbSimple; iC++) {
7272           simpleNodes[iC - loopLen] = simpleNodes[iC];
7273         }
7274         nbSimple -= loopLen;
7275         iSimple -= loopLen;
7276       }
7277     } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7278   } // while (foundLoop)
7279
7280   if (iSimple > 2) {
7281     nbNew++;
7282     quantities.push_back(iSimple);
7283     for (int i = 0; i < iSimple; i++)
7284       poly_nodes.push_back(simpleNodes[i]);
7285   }
7286
7287   return nbNew;
7288 }
7289
7290 //=======================================================================
7291 //function : MergeNodes
7292 //purpose  : In each group, the cdr of nodes are substituted by the first one
7293 //           in all elements.
7294 //=======================================================================
7295
7296 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7297 {
7298   MESSAGE("MergeNodes");
7299   myLastCreatedElems.Clear();
7300   myLastCreatedNodes.Clear();
7301
7302   SMESHDS_Mesh* aMesh = GetMeshDS();
7303
7304   TNodeNodeMap nodeNodeMap; // node to replace - new node
7305   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7306   list< int > rmElemIds, rmNodeIds;
7307
7308   // Fill nodeNodeMap and elems
7309
7310   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7311   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7312   {
7313     list<const SMDS_MeshNode*>& nodes = *grIt;
7314     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7315     const SMDS_MeshNode* nToKeep = *nIt;
7316     for ( ++nIt; nIt != nodes.end(); nIt++ )
7317     {
7318       const SMDS_MeshNode* nToRemove = *nIt;
7319       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7320       if ( nToRemove != nToKeep )
7321       {
7322         rmNodeIds.push_back( nToRemove->GetID() );
7323         AddToSameGroups( nToKeep, nToRemove, aMesh );
7324         // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7325         // after MergeNodes() w/o creating node in place of merged ones.
7326         const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7327         if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7328           if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7329             sm->SetIsAlwaysComputed( true );
7330       }
7331       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7332       while ( invElemIt->more() ) {
7333         const SMDS_MeshElement* elem = invElemIt->next();
7334         elems.insert(elem);
7335       }
7336     }
7337   }
7338   // Change element nodes or remove an element
7339
7340   set<const SMDS_MeshNode*> nodeSet;
7341   vector< const SMDS_MeshNode*> curNodes, uniqueNodes;
7342   vector<int> iRepl;
7343   ElemFeatures elemType;
7344
7345   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7346   for ( ; eIt != elems.end(); eIt++ )
7347   {
7348     const SMDS_MeshElement* elem = *eIt;
7349     const           int  nbNodes = elem->NbNodes();
7350     const           int aShapeId = FindShape( elem );
7351
7352     nodeSet.clear();
7353     curNodes.resize( nbNodes );
7354     uniqueNodes.resize( nbNodes );
7355     iRepl.resize( nbNodes );
7356     int iUnique = 0, iCur = 0, nbRepl = 0;
7357
7358     // get new seq of nodes
7359     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7360     while ( itN->more() )
7361     {
7362       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7363
7364       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7365       if ( nnIt != nodeNodeMap.end() ) { // n sticks
7366         n = (*nnIt).second;
7367         { ////////// BUG 0020185: begin
7368           bool stopRecur = false;
7369           set<const SMDS_MeshNode*> nodesRecur;
7370           nodesRecur.insert(n);
7371           while (!stopRecur) {
7372             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7373             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7374               n = (*nnIt_i).second;
7375               if (!nodesRecur.insert(n).second) {
7376                 // error: recursive dependancy
7377                 stopRecur = true;
7378               }
7379             }
7380             else
7381               stopRecur = true;
7382           }
7383         } ////////// BUG 0020185: end
7384       }
7385       curNodes[ iCur ] = n;
7386       bool isUnique = nodeSet.insert( n ).second;
7387       if ( isUnique )
7388         uniqueNodes[ iUnique++ ] = n;
7389       else
7390         iRepl[ nbRepl++ ] = iCur;
7391       iCur++;
7392     }
7393
7394     // Analyse element topology after replacement
7395
7396     bool isOk = true;
7397     int nbUniqueNodes = nodeSet.size();
7398     if ( nbNodes != nbUniqueNodes ) // some nodes stick
7399     {
7400       if (elem->IsPoly()) // Polygons and Polyhedral volumes
7401       {
7402         if (elem->GetType() == SMDSAbs_Face) // Polygon
7403         {
7404           elemType.Init( elem );
7405           const bool isQuad = elemType.myIsQuad;
7406           if ( isQuad )
7407             SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7408               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7409
7410           // a polygon can divide into several elements
7411           vector<const SMDS_MeshNode *> polygons_nodes;
7412           vector<int> quantities;
7413           int nbNew = SimplifyFace( curNodes, polygons_nodes, quantities );
7414           if (nbNew > 0)
7415           {
7416             vector<const SMDS_MeshNode *> face_nodes;
7417             int inode = 0;
7418             for (int iface = 0; iface < nbNew; iface++)
7419             {
7420               int nbNewNodes = quantities[iface];
7421               face_nodes.assign( polygons_nodes.begin() + inode,
7422                                  polygons_nodes.begin() + inode + nbNewNodes );
7423               inode += nbNewNodes;
7424               if ( isQuad ) // check if a result elem is a valid quadratic polygon
7425               {
7426                 bool isValid = ( nbNewNodes % 2 == 0 );
7427                 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7428                   isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7429                 elemType.SetQuad( isValid );
7430                 if ( isValid ) // put medium nodes after corners
7431                   SMDS_MeshCell::applyInterlaceRev
7432                     ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7433                                                           nbNewNodes ), face_nodes );
7434               }
7435               elemType.SetPoly(( nbNewNodes / ( elemType.myIsQuad + 1 ) > 4 ));
7436
7437               SMDS_MeshElement* newElem = AddElement( face_nodes, elemType );
7438               if ( aShapeId )
7439                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7440             }
7441           }
7442           rmElemIds.push_back(elem->GetID());
7443
7444         } // Polygon
7445
7446         else if (elem->GetType() == SMDSAbs_Volume) // Polyhedral volume
7447         {
7448           if (nbUniqueNodes < 4) {
7449             rmElemIds.push_back(elem->GetID());
7450           }
7451           else {
7452             // each face has to be analyzed in order to check volume validity
7453             const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
7454             if (aPolyedre)
7455             {
7456               int nbFaces = aPolyedre->NbFaces();
7457
7458               vector<const SMDS_MeshNode *> poly_nodes;
7459               vector<int> quantities;
7460
7461               for (int iface = 1; iface <= nbFaces; iface++) {
7462                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7463                 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7464
7465                 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7466                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7467                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7468                   if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7469                     faceNode = (*nnIt).second;
7470                   }
7471                   faceNodes[inode - 1] = faceNode;
7472                 }
7473
7474                 SimplifyFace(faceNodes, poly_nodes, quantities);
7475               }
7476
7477               if (quantities.size() > 3) {
7478                 // to be done: remove coincident faces
7479               }
7480
7481               if (quantities.size() > 3)
7482               {
7483                 const SMDS_MeshElement* newElem =
7484                   aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7485                 myLastCreatedElems.Append(newElem);
7486                 if ( aShapeId && newElem )
7487                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7488                 rmElemIds.push_back(elem->GetID());
7489               }
7490             }
7491             else {
7492               rmElemIds.push_back(elem->GetID());
7493             }
7494           }
7495         }
7496         else {
7497         }
7498
7499         continue;
7500       } // poly element
7501
7502       // Regular elements
7503       // TODO not all the possible cases are solved. Find something more generic?
7504       switch ( nbNodes ) {
7505       case 2: ///////////////////////////////////// EDGE
7506         isOk = false; break;
7507       case 3: ///////////////////////////////////// TRIANGLE
7508         isOk = false; break;
7509       case 4:
7510         if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7511           isOk = false;
7512         else { //////////////////////////////////// QUADRANGLE
7513           if ( nbUniqueNodes < 3 )
7514             isOk = false;
7515           else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7516             isOk = false; // opposite nodes stick
7517           //MESSAGE("isOk " << isOk);
7518         }
7519         break;
7520       case 6: ///////////////////////////////////// PENTAHEDRON
7521         if ( nbUniqueNodes == 4 ) {
7522           // ---------------------------------> tetrahedron
7523           if (nbRepl == 3 &&
7524               iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7525             // all top nodes stick: reverse a bottom
7526             uniqueNodes[ 0 ] = curNodes [ 1 ];
7527             uniqueNodes[ 1 ] = curNodes [ 0 ];
7528           }
7529           else if (nbRepl == 3 &&
7530                    iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7531             // all bottom nodes stick: set a top before
7532             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7533             uniqueNodes[ 0 ] = curNodes [ 3 ];
7534             uniqueNodes[ 1 ] = curNodes [ 4 ];
7535             uniqueNodes[ 2 ] = curNodes [ 5 ];
7536           }
7537           else if (nbRepl == 4 &&
7538                    iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7539             // a lateral face turns into a line: reverse a bottom
7540             uniqueNodes[ 0 ] = curNodes [ 1 ];
7541             uniqueNodes[ 1 ] = curNodes [ 0 ];
7542           }
7543           else
7544             isOk = false;
7545         }
7546         else if ( nbUniqueNodes == 5 ) {
7547           // PENTAHEDRON --------------------> 2 tetrahedrons
7548           if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7549             // a bottom node sticks with a linked top one
7550             // 1.
7551             SMDS_MeshElement* newElem =
7552               aMesh->AddVolume(curNodes[ 3 ],
7553                                curNodes[ 4 ],
7554                                curNodes[ 5 ],
7555                                curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7556             myLastCreatedElems.Append(newElem);
7557             if ( aShapeId )
7558               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7559             // 2. : reverse a bottom
7560             uniqueNodes[ 0 ] = curNodes [ 1 ];
7561             uniqueNodes[ 1 ] = curNodes [ 0 ];
7562             nbUniqueNodes = 4;
7563           }
7564           else
7565             isOk = false;
7566         }
7567         else
7568           isOk = false;
7569         break;
7570       case 8: {
7571         if(elem->IsQuadratic()) { // Quadratic quadrangle
7572           //   1    5    2
7573           //    +---+---+
7574           //    |       |
7575           //    |       |
7576           //   4+       +6
7577           //    |       |
7578           //    |       |
7579           //    +---+---+
7580           //   0    7    3
7581           isOk = false;
7582           if(nbRepl==2) {
7583             MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7584           }
7585           if(nbRepl==3) {
7586             MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2]);
7587             nbUniqueNodes = 6;
7588             if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7589               uniqueNodes[0] = curNodes[0];
7590               uniqueNodes[1] = curNodes[2];
7591               uniqueNodes[2] = curNodes[3];
7592               uniqueNodes[3] = curNodes[5];
7593               uniqueNodes[4] = curNodes[6];
7594               uniqueNodes[5] = curNodes[7];
7595               isOk = true;
7596             }
7597             if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7598               uniqueNodes[0] = curNodes[0];
7599               uniqueNodes[1] = curNodes[1];
7600               uniqueNodes[2] = curNodes[2];
7601               uniqueNodes[3] = curNodes[4];
7602               uniqueNodes[4] = curNodes[5];
7603               uniqueNodes[5] = curNodes[6];
7604               isOk = true;
7605             }
7606             if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7607               uniqueNodes[0] = curNodes[1];
7608               uniqueNodes[1] = curNodes[2];
7609               uniqueNodes[2] = curNodes[3];
7610               uniqueNodes[3] = curNodes[5];
7611               uniqueNodes[4] = curNodes[6];
7612               uniqueNodes[5] = curNodes[0];
7613               isOk = true;
7614             }
7615             if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7616               uniqueNodes[0] = curNodes[0];
7617               uniqueNodes[1] = curNodes[1];
7618               uniqueNodes[2] = curNodes[3];
7619               uniqueNodes[3] = curNodes[4];
7620               uniqueNodes[4] = curNodes[6];
7621               uniqueNodes[5] = curNodes[7];
7622               isOk = true;
7623             }
7624             if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7625               uniqueNodes[0] = curNodes[0];
7626               uniqueNodes[1] = curNodes[2];
7627               uniqueNodes[2] = curNodes[3];
7628               uniqueNodes[3] = curNodes[1];
7629               uniqueNodes[4] = curNodes[6];
7630               uniqueNodes[5] = curNodes[7];
7631               isOk = true;
7632             }
7633             if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7634               uniqueNodes[0] = curNodes[0];
7635               uniqueNodes[1] = curNodes[1];
7636               uniqueNodes[2] = curNodes[2];
7637               uniqueNodes[3] = curNodes[4];
7638               uniqueNodes[4] = curNodes[5];
7639               uniqueNodes[5] = curNodes[7];
7640               isOk = true;
7641             }
7642             if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7643               uniqueNodes[0] = curNodes[0];
7644               uniqueNodes[1] = curNodes[1];
7645               uniqueNodes[2] = curNodes[3];
7646               uniqueNodes[3] = curNodes[4];
7647               uniqueNodes[4] = curNodes[2];
7648               uniqueNodes[5] = curNodes[7];
7649               isOk = true;
7650             }
7651             if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7652               uniqueNodes[0] = curNodes[0];
7653               uniqueNodes[1] = curNodes[1];
7654               uniqueNodes[2] = curNodes[2];
7655               uniqueNodes[3] = curNodes[4];
7656               uniqueNodes[4] = curNodes[5];
7657               uniqueNodes[5] = curNodes[3];
7658               isOk = true;
7659             }
7660           }
7661           if(nbRepl==4) {
7662             MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3]);
7663           }
7664           if(nbRepl==5) {
7665             MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7666           }
7667           break;
7668         }
7669         //////////////////////////////////// HEXAHEDRON
7670         isOk = false;
7671         SMDS_VolumeTool hexa (elem);
7672         hexa.SetExternalNormal();
7673         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7674           //////////////////////// HEX ---> 1 tetrahedron
7675           for ( int iFace = 0; iFace < 6; iFace++ ) {
7676             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7677             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7678                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7679                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7680               // one face turns into a point ...
7681               int iOppFace = hexa.GetOppFaceIndex( iFace );
7682               ind = hexa.GetFaceNodesIndices( iOppFace );
7683               int nbStick = 0;
7684               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7685                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7686                   nbStick++;
7687               }
7688               if ( nbStick == 1 ) {
7689                 // ... and the opposite one - into a triangle.
7690                 // set a top node
7691                 ind = hexa.GetFaceNodesIndices( iFace );
7692                 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7693                 isOk = true;
7694               }
7695               break;
7696             }
7697           }
7698         }
7699         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7700           //////////////////////// HEX ---> 1 prism
7701           int nbTria = 0, iTria[3];
7702           const int *ind; // indices of face nodes
7703           // look for triangular faces
7704           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7705             ind = hexa.GetFaceNodesIndices( iFace );
7706             TIDSortedNodeSet faceNodes;
7707             for ( iCur = 0; iCur < 4; iCur++ )
7708               faceNodes.insert( curNodes[ind[iCur]] );
7709             if ( faceNodes.size() == 3 )
7710               iTria[ nbTria++ ] = iFace;
7711           }
7712           // check if triangles are opposite
7713           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7714           {
7715             isOk = true;
7716             // set nodes of the bottom triangle
7717             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7718             vector<int> indB;
7719             for ( iCur = 0; iCur < 4; iCur++ )
7720               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7721                 indB.push_back( ind[iCur] );
7722             if ( !hexa.IsForward() )
7723               std::swap( indB[0], indB[2] );
7724             for ( iCur = 0; iCur < 3; iCur++ )
7725               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7726             // set nodes of the top triangle
7727             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7728             for ( iCur = 0; iCur < 3; ++iCur )
7729               for ( int j = 0; j < 4; ++j )
7730                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7731                 {
7732                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7733                   break;
7734                 }
7735           }
7736           break;
7737         }
7738         else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7739           //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7740           for ( int iFace = 0; iFace < 6; iFace++ ) {
7741             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7742             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7743                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7744                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7745               // one face turns into a point ...
7746               int iOppFace = hexa.GetOppFaceIndex( iFace );
7747               ind = hexa.GetFaceNodesIndices( iOppFace );
7748               int nbStick = 0;
7749               iUnique = 2;  // reverse a tetrahedron 1 bottom
7750               for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7751                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7752                   nbStick++;
7753                 else if ( iUnique >= 0 )
7754                   uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7755               }
7756               if ( nbStick == 0 ) {
7757                 // ... and the opposite one is a quadrangle
7758                 // set a top node
7759                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7760                 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7761                 nbUniqueNodes = 4;
7762                 // tetrahedron 2
7763                 SMDS_MeshElement* newElem =
7764                   aMesh->AddVolume(curNodes[ind[ 0 ]],
7765                                    curNodes[ind[ 3 ]],
7766                                    curNodes[ind[ 2 ]],
7767                                    curNodes[indTop[ 0 ]]);
7768                 myLastCreatedElems.Append(newElem);
7769                 if ( aShapeId )
7770                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7771                 isOk = true;
7772               }
7773               break;
7774             }
7775           }
7776         }
7777         else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7778           ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7779           // find indices of quad and tri faces
7780           int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7781           for ( iFace = 0; iFace < 6; iFace++ ) {
7782             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7783             nodeSet.clear();
7784             for ( iCur = 0; iCur < 4; iCur++ )
7785               nodeSet.insert( curNodes[ind[ iCur ]] );
7786             nbUniqueNodes = nodeSet.size();
7787             if ( nbUniqueNodes == 3 )
7788               iTriFace[ nbTri++ ] = iFace;
7789             else if ( nbUniqueNodes == 4 )
7790               iQuadFace[ nbQuad++ ] = iFace;
7791           }
7792           if (nbQuad == 2 && nbTri == 4 &&
7793               hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7794             // 2 opposite quadrangles stuck with a diagonal;
7795             // sample groups of merged indices: (0-4)(2-6)
7796             // --------------------------------------------> 2 tetrahedrons
7797             const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7798             const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7799             int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7800             if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7801                 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7802               // stuck with 0-2 diagonal
7803               i0  = ind1[ 3 ];
7804               i1d = ind1[ 0 ];
7805               i2  = ind1[ 1 ];
7806               i3d = ind1[ 2 ];
7807               i0t = ind2[ 1 ];
7808               i2t = ind2[ 3 ];
7809             }
7810             else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7811                      curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7812               // stuck with 1-3 diagonal
7813               i0  = ind1[ 0 ];
7814               i1d = ind1[ 1 ];
7815               i2  = ind1[ 2 ];
7816               i3d = ind1[ 3 ];
7817               i0t = ind2[ 0 ];
7818               i2t = ind2[ 1 ];
7819             }
7820             else {
7821               ASSERT(0);
7822             }
7823             // tetrahedron 1
7824             uniqueNodes[ 0 ] = curNodes [ i0 ];
7825             uniqueNodes[ 1 ] = curNodes [ i1d ];
7826             uniqueNodes[ 2 ] = curNodes [ i3d ];
7827             uniqueNodes[ 3 ] = curNodes [ i0t ];
7828             nbUniqueNodes = 4;
7829             // tetrahedron 2
7830             SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7831                                                          curNodes[ i2 ],
7832                                                          curNodes[ i3d ],
7833                                                          curNodes[ i2t ]);
7834             myLastCreatedElems.Append(newElem);
7835             if ( aShapeId )
7836               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7837             isOk = true;
7838           }
7839           else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7840                    ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7841             // --------------------------------------------> prism
7842             // find 2 opposite triangles
7843             nbUniqueNodes = 6;
7844             for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7845               if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7846                 // find indices of kept and replaced nodes
7847                 // and fill unique nodes of 2 opposite triangles
7848                 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7849                 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7850                 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7851                 // fill unique nodes
7852                 iUnique = 0;
7853                 isOk = true;
7854                 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7855                   const SMDS_MeshNode* n     = curNodes[ind1[ iCur ]];
7856                   const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7857                   if ( n == nInit ) {
7858                     // iCur of a linked node of the opposite face (make normals co-directed):
7859                     int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7860                     // check that correspondent corners of triangles are linked
7861                     if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7862                       isOk = false;
7863                     else {
7864                       uniqueNodes[ iUnique ] = n;
7865                       uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7866                       iUnique++;
7867                     }
7868                   }
7869                 }
7870                 break;
7871               }
7872             }
7873           }
7874         } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7875         else
7876         {
7877           MESSAGE("MergeNodes() removes hexahedron "<< elem);
7878         }
7879         break;
7880       } // HEXAHEDRON
7881
7882       default:
7883         isOk = false;
7884       } // switch ( nbNodes )
7885
7886     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7887
7888     if ( isOk ) // the non-poly elem remains valid after sticking nodes
7889     {
7890       if ( nbNodes != nbUniqueNodes ||
7891            !aMesh->ChangeElementNodes( elem, & curNodes[0], nbNodes ))
7892       {
7893         elemType.Init( elem ).SetID( elem->GetID() );
7894
7895         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7896         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7897
7898         uniqueNodes.resize(nbUniqueNodes);
7899         SMDS_MeshElement* newElem = this->AddElement( uniqueNodes, elemType );
7900         if ( sm && newElem )
7901           sm->AddElement( newElem );
7902         if ( elem != newElem )
7903           ReplaceElemInGroups( elem, newElem, aMesh );
7904       }
7905     }
7906     else {
7907       // Remove invalid regular element or invalid polygon
7908       rmElemIds.push_back( elem->GetID() );
7909     }
7910
7911   } // loop on elements
7912
7913   // Remove bad elements, then equal nodes (order important)
7914
7915   Remove( rmElemIds, false );
7916   Remove( rmNodeIds, true );
7917
7918   return;
7919 }
7920
7921
7922 // ========================================================
7923 // class   : SortableElement
7924 // purpose : allow sorting elements basing on their nodes
7925 // ========================================================
7926 class SortableElement : public set <const SMDS_MeshElement*>
7927 {
7928 public:
7929
7930   SortableElement( const SMDS_MeshElement* theElem )
7931   {
7932     myElem = theElem;
7933     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7934     while ( nodeIt->more() )
7935       this->insert( nodeIt->next() );
7936   }
7937
7938   const SMDS_MeshElement* Get() const
7939   { return myElem; }
7940
7941 private:
7942   mutable const SMDS_MeshElement* myElem;
7943 };
7944
7945 //=======================================================================
7946 //function : FindEqualElements
7947 //purpose  : Return list of group of elements built on the same nodes.
7948 //           Search among theElements or in the whole mesh if theElements is empty
7949 //=======================================================================
7950
7951 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
7952                                          TListOfListOfElementsID & theGroupsOfElementsID)
7953 {
7954   myLastCreatedElems.Clear();
7955   myLastCreatedNodes.Clear();
7956
7957   typedef map< SortableElement, int > TMapOfNodeSet;
7958   typedef list<int> TGroupOfElems;
7959
7960   if ( theElements.empty() )
7961   { // get all elements in the mesh
7962     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7963     while ( eIt->more() )
7964       theElements.insert( theElements.end(), eIt->next() );
7965   }
7966
7967   vector< TGroupOfElems > arrayOfGroups;
7968   TGroupOfElems groupOfElems;
7969   TMapOfNodeSet mapOfNodeSet;
7970
7971   TIDSortedElemSet::iterator elemIt = theElements.begin();
7972   for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7973   {
7974     const SMDS_MeshElement* curElem = *elemIt;
7975     SortableElement SE(curElem);
7976     // check uniqueness
7977     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7978     if ( !pp.second ) { // one more coincident elem
7979       TMapOfNodeSet::iterator& itSE = pp.first;
7980       int ind = (*itSE).second;
7981       arrayOfGroups[ind].push_back( curElem->GetID() );
7982     }
7983     else {
7984       arrayOfGroups.push_back( groupOfElems );
7985       arrayOfGroups.back().push_back( curElem->GetID() );
7986       i++;
7987     }
7988   }
7989
7990   groupOfElems.clear();
7991   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7992   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7993   {
7994     if ( groupIt->size() > 1 ) {
7995       //groupOfElems.sort(); -- theElements is sorted already
7996       theGroupsOfElementsID.push_back( groupOfElems );
7997       theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
7998     }
7999   }
8000 }
8001
8002 //=======================================================================
8003 //function : MergeElements
8004 //purpose  : In each given group, substitute all elements by the first one.
8005 //=======================================================================
8006
8007 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8008 {
8009   myLastCreatedElems.Clear();
8010   myLastCreatedNodes.Clear();
8011
8012   typedef list<int> TListOfIDs;
8013   TListOfIDs rmElemIds; // IDs of elems to remove
8014
8015   SMESHDS_Mesh* aMesh = GetMeshDS();
8016
8017   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8018   while ( groupsIt != theGroupsOfElementsID.end() ) {
8019     TListOfIDs& aGroupOfElemID = *groupsIt;
8020     aGroupOfElemID.sort();
8021     int elemIDToKeep = aGroupOfElemID.front();
8022     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8023     aGroupOfElemID.pop_front();
8024     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8025     while ( idIt != aGroupOfElemID.end() ) {
8026       int elemIDToRemove = *idIt;
8027       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8028       // add the kept element in groups of removed one (PAL15188)
8029       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8030       rmElemIds.push_back( elemIDToRemove );
8031       ++idIt;
8032     }
8033     ++groupsIt;
8034   }
8035
8036   Remove( rmElemIds, false );
8037 }
8038
8039 //=======================================================================
8040 //function : MergeEqualElements
8041 //purpose  : Remove all but one of elements built on the same nodes.
8042 //=======================================================================
8043
8044 void SMESH_MeshEditor::MergeEqualElements()
8045 {
8046   TIDSortedElemSet aMeshElements; /* empty input ==
8047                                      to merge equal elements in the whole mesh */
8048   TListOfListOfElementsID aGroupsOfElementsID;
8049   FindEqualElements(aMeshElements, aGroupsOfElementsID);
8050   MergeElements(aGroupsOfElementsID);
8051 }
8052
8053 //=======================================================================
8054 //function : findAdjacentFace
8055 //purpose  :
8056 //=======================================================================
8057
8058 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8059                                                 const SMDS_MeshNode* n2,
8060                                                 const SMDS_MeshElement* elem)
8061 {
8062   TIDSortedElemSet elemSet, avoidSet;
8063   if ( elem )
8064     avoidSet.insert ( elem );
8065   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8066 }
8067
8068 //=======================================================================
8069 //function : findSegment
8070 //purpose  : Return a mesh segment by two nodes one of which can be medium
8071 //=======================================================================
8072
8073 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8074                                            const SMDS_MeshNode* n2)
8075 {
8076   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8077   while ( it->more() )
8078   {
8079     const SMDS_MeshElement* seg = it->next();
8080     if ( seg->GetNodeIndex( n2 ) >= 0 )
8081       return seg;
8082   }
8083   return 0;
8084 }
8085
8086 //=======================================================================
8087 //function : FindFreeBorder
8088 //purpose  :
8089 //=======================================================================
8090
8091 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8092
8093 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
8094                                        const SMDS_MeshNode*             theSecondNode,
8095                                        const SMDS_MeshNode*             theLastNode,
8096                                        list< const SMDS_MeshNode* > &   theNodes,
8097                                        list< const SMDS_MeshElement* >& theFaces)
8098 {
8099   if ( !theFirstNode || !theSecondNode )
8100     return false;
8101   // find border face between theFirstNode and theSecondNode
8102   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8103   if ( !curElem )
8104     return false;
8105
8106   theFaces.push_back( curElem );
8107   theNodes.push_back( theFirstNode );
8108   theNodes.push_back( theSecondNode );
8109
8110   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8111   TIDSortedElemSet foundElems;
8112   bool needTheLast = ( theLastNode != 0 );
8113
8114   while ( nStart != theLastNode ) {
8115     if ( nStart == theFirstNode )
8116       return !needTheLast;
8117
8118     // find all free border faces sharing form nStart
8119
8120     list< const SMDS_MeshElement* > curElemList;
8121     list< const SMDS_MeshNode* >    nStartList;
8122     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8123     while ( invElemIt->more() ) {
8124       const SMDS_MeshElement* e = invElemIt->next();
8125       if ( e == curElem || foundElems.insert( e ).second ) {
8126         // get nodes
8127         int iNode = 0, nbNodes = e->NbNodes();
8128         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8129
8130         if ( e->IsQuadratic() ) {
8131           const SMDS_VtkFace* F =
8132             dynamic_cast<const SMDS_VtkFace*>(e);
8133           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8134           // use special nodes iterator
8135           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8136           while( anIter->more() ) {
8137             nodes[ iNode++ ] = cast2Node(anIter->next());
8138           }
8139         }
8140         else {
8141           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8142           while ( nIt->more() )
8143             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8144         }
8145         nodes[ iNode ] = nodes[ 0 ];
8146         // check 2 links
8147         for ( iNode = 0; iNode < nbNodes; iNode++ )
8148           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8149                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8150               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8151           {
8152             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8153             curElemList.push_back( e );
8154           }
8155       }
8156     }
8157     // analyse the found
8158
8159     int nbNewBorders = curElemList.size();
8160     if ( nbNewBorders == 0 ) {
8161       // no free border furthermore
8162       return !needTheLast;
8163     }
8164     else if ( nbNewBorders == 1 ) {
8165       // one more element found
8166       nIgnore = nStart;
8167       nStart = nStartList.front();
8168       curElem = curElemList.front();
8169       theFaces.push_back( curElem );
8170       theNodes.push_back( nStart );
8171     }
8172     else {
8173       // several continuations found
8174       list< const SMDS_MeshElement* >::iterator curElemIt;
8175       list< const SMDS_MeshNode* >::iterator nStartIt;
8176       // check if one of them reached the last node
8177       if ( needTheLast ) {
8178         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8179              curElemIt!= curElemList.end();
8180              curElemIt++, nStartIt++ )
8181           if ( *nStartIt == theLastNode ) {
8182             theFaces.push_back( *curElemIt );
8183             theNodes.push_back( *nStartIt );
8184             return true;
8185           }
8186       }
8187       // find the best free border by the continuations
8188       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8189       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8190       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8191            curElemIt!= curElemList.end();
8192            curElemIt++, nStartIt++ )
8193       {
8194         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8195         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8196         // find one more free border
8197         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8198           cNL->clear();
8199           cFL->clear();
8200         }
8201         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8202           // choice: clear a worse one
8203           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8204           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8205           contNodes[ iWorse ].clear();
8206           contFaces[ iWorse ].clear();
8207         }
8208       }
8209       if ( contNodes[0].empty() && contNodes[1].empty() )
8210         return false;
8211
8212       // append the best free border
8213       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8214       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8215       theNodes.pop_back(); // remove nIgnore
8216       theNodes.pop_back(); // remove nStart
8217       theFaces.pop_back(); // remove curElem
8218       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8219       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8220       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8221       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8222       return true;
8223
8224     } // several continuations found
8225   } // while ( nStart != theLastNode )
8226
8227   return true;
8228 }
8229
8230 //=======================================================================
8231 //function : CheckFreeBorderNodes
8232 //purpose  : Return true if the tree nodes are on a free border
8233 //=======================================================================
8234
8235 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8236                                             const SMDS_MeshNode* theNode2,
8237                                             const SMDS_MeshNode* theNode3)
8238 {
8239   list< const SMDS_MeshNode* > nodes;
8240   list< const SMDS_MeshElement* > faces;
8241   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8242 }
8243
8244 //=======================================================================
8245 //function : SewFreeBorder
8246 //purpose  :
8247 //warning  : for border-to-side sewing theSideSecondNode is considered as
8248 //           the last side node and theSideThirdNode is not used
8249 //=======================================================================
8250
8251 SMESH_MeshEditor::Sew_Error
8252 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8253                                  const SMDS_MeshNode* theBordSecondNode,
8254                                  const SMDS_MeshNode* theBordLastNode,
8255                                  const SMDS_MeshNode* theSideFirstNode,
8256                                  const SMDS_MeshNode* theSideSecondNode,
8257                                  const SMDS_MeshNode* theSideThirdNode,
8258                                  const bool           theSideIsFreeBorder,
8259                                  const bool           toCreatePolygons,
8260                                  const bool           toCreatePolyedrs)
8261 {
8262   myLastCreatedElems.Clear();
8263   myLastCreatedNodes.Clear();
8264
8265   MESSAGE("::SewFreeBorder()");
8266   Sew_Error aResult = SEW_OK;
8267
8268   // ====================================
8269   //    find side nodes and elements
8270   // ====================================
8271
8272   list< const SMDS_MeshNode* >    nSide[ 2 ];
8273   list< const SMDS_MeshElement* > eSide[ 2 ];
8274   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
8275   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8276
8277   // Free border 1
8278   // --------------
8279   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8280                       nSide[0], eSide[0])) {
8281     MESSAGE(" Free Border 1 not found " );
8282     aResult = SEW_BORDER1_NOT_FOUND;
8283   }
8284   if (theSideIsFreeBorder) {
8285     // Free border 2
8286     // --------------
8287     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8288                         nSide[1], eSide[1])) {
8289       MESSAGE(" Free Border 2 not found " );
8290       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8291     }
8292   }
8293   if ( aResult != SEW_OK )
8294     return aResult;
8295
8296   if (!theSideIsFreeBorder) {
8297     // Side 2
8298     // --------------
8299
8300     // -------------------------------------------------------------------------
8301     // Algo:
8302     // 1. If nodes to merge are not coincident, move nodes of the free border
8303     //    from the coord sys defined by the direction from the first to last
8304     //    nodes of the border to the correspondent sys of the side 2
8305     // 2. On the side 2, find the links most co-directed with the correspondent
8306     //    links of the free border
8307     // -------------------------------------------------------------------------
8308
8309     // 1. Since sewing may break if there are volumes to split on the side 2,
8310     //    we wont move nodes but just compute new coordinates for them
8311     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8312     TNodeXYZMap nBordXYZ;
8313     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8314     list< const SMDS_MeshNode* >::iterator nBordIt;
8315
8316     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8317     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8318     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8319     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8320     double tol2 = 1.e-8;
8321     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8322     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8323       // Need node movement.
8324
8325       // find X and Z axes to create trsf
8326       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8327       gp_Vec X = Zs ^ Zb;
8328       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8329         // Zb || Zs
8330         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8331
8332       // coord systems
8333       gp_Ax3 toBordAx( Pb1, Zb, X );
8334       gp_Ax3 fromSideAx( Ps1, Zs, X );
8335       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8336       // set trsf
8337       gp_Trsf toBordSys, fromSide2Sys;
8338       toBordSys.SetTransformation( toBordAx );
8339       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8340       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8341
8342       // move
8343       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8344         const SMDS_MeshNode* n = *nBordIt;
8345         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8346         toBordSys.Transforms( xyz );
8347         fromSide2Sys.Transforms( xyz );
8348         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8349       }
8350     }
8351     else {
8352       // just insert nodes XYZ in the nBordXYZ map
8353       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8354         const SMDS_MeshNode* n = *nBordIt;
8355         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8356       }
8357     }
8358
8359     // 2. On the side 2, find the links most co-directed with the correspondent
8360     //    links of the free border
8361
8362     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8363     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8364     sideNodes.push_back( theSideFirstNode );
8365
8366     bool hasVolumes = false;
8367     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8368     set<long> foundSideLinkIDs, checkedLinkIDs;
8369     SMDS_VolumeTool volume;
8370     //const SMDS_MeshNode* faceNodes[ 4 ];
8371
8372     const SMDS_MeshNode*    sideNode;
8373     const SMDS_MeshElement* sideElem;
8374     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8375     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8376     nBordIt = bordNodes.begin();
8377     nBordIt++;
8378     // border node position and border link direction to compare with
8379     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8380     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8381     // choose next side node by link direction or by closeness to
8382     // the current border node:
8383     bool searchByDir = ( *nBordIt != theBordLastNode );
8384     do {
8385       // find the next node on the Side 2
8386       sideNode = 0;
8387       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8388       long linkID;
8389       checkedLinkIDs.clear();
8390       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8391
8392       // loop on inverse elements of current node (prevSideNode) on the Side 2
8393       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8394       while ( invElemIt->more() )
8395       {
8396         const SMDS_MeshElement* elem = invElemIt->next();
8397         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8398         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8399         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8400         bool isVolume = volume.Set( elem );
8401         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8402         if ( isVolume ) // --volume
8403           hasVolumes = true;
8404         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8405           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8406           if(elem->IsQuadratic()) {
8407             const SMDS_VtkFace* F =
8408               dynamic_cast<const SMDS_VtkFace*>(elem);
8409             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8410             // use special nodes iterator
8411             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8412             while( anIter->more() ) {
8413               nodes[ iNode ] = cast2Node(anIter->next());
8414               if ( nodes[ iNode++ ] == prevSideNode )
8415                 iPrevNode = iNode - 1;
8416             }
8417           }
8418           else {
8419             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8420             while ( nIt->more() ) {
8421               nodes[ iNode ] = cast2Node( nIt->next() );
8422               if ( nodes[ iNode++ ] == prevSideNode )
8423                 iPrevNode = iNode - 1;
8424             }
8425           }
8426           // there are 2 links to check
8427           nbNodes = 2;
8428         }
8429         else // --edge
8430           continue;
8431         // loop on links, to be precise, on the second node of links
8432         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8433           const SMDS_MeshNode* n = nodes[ iNode ];
8434           if ( isVolume ) {
8435             if ( !volume.IsLinked( n, prevSideNode ))
8436               continue;
8437           }
8438           else {
8439             if ( iNode ) // a node before prevSideNode
8440               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8441             else         // a node after prevSideNode
8442               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8443           }
8444           // check if this link was already used
8445           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8446           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8447           if (!isJustChecked &&
8448               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8449           {
8450             // test a link geometrically
8451             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8452             bool linkIsBetter = false;
8453             double dot = 0.0, dist = 0.0;
8454             if ( searchByDir ) { // choose most co-directed link
8455               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8456               linkIsBetter = ( dot > maxDot );
8457             }
8458             else { // choose link with the node closest to bordPos
8459               dist = ( nextXYZ - bordPos ).SquareModulus();
8460               linkIsBetter = ( dist < minDist );
8461             }
8462             if ( linkIsBetter ) {
8463               maxDot = dot;
8464               minDist = dist;
8465               linkID = iLink;
8466               sideNode = n;
8467               sideElem = elem;
8468             }
8469           }
8470         }
8471       } // loop on inverse elements of prevSideNode
8472
8473       if ( !sideNode ) {
8474         MESSAGE(" Cant find path by links of the Side 2 ");
8475         return SEW_BAD_SIDE_NODES;
8476       }
8477       sideNodes.push_back( sideNode );
8478       sideElems.push_back( sideElem );
8479       foundSideLinkIDs.insert ( linkID );
8480       prevSideNode = sideNode;
8481
8482       if ( *nBordIt == theBordLastNode )
8483         searchByDir = false;
8484       else {
8485         // find the next border link to compare with
8486         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8487         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8488         // move to next border node if sideNode is before forward border node (bordPos)
8489         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8490           prevBordNode = *nBordIt;
8491           nBordIt++;
8492           bordPos = nBordXYZ[ *nBordIt ];
8493           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8494           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8495         }
8496       }
8497     }
8498     while ( sideNode != theSideSecondNode );
8499
8500     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8501       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8502       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8503     }
8504   } // end nodes search on the side 2
8505
8506   // ============================
8507   // sew the border to the side 2
8508   // ============================
8509
8510   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8511   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8512
8513   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8514   if ( toMergeConformal && toCreatePolygons )
8515   {
8516     // do not merge quadrangles if polygons are OK (IPAL0052824)
8517     eIt[0] = eSide[0].begin();
8518     eIt[1] = eSide[1].begin();
8519     bool allQuads[2] = { true, true };
8520     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8521       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8522         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8523     }
8524     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8525   }
8526
8527   TListOfListOfNodes nodeGroupsToMerge;
8528   if (( toMergeConformal ) ||
8529       ( theSideIsFreeBorder && !theSideThirdNode )) {
8530
8531     // all nodes are to be merged
8532
8533     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8534          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8535          nIt[0]++, nIt[1]++ )
8536     {
8537       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8538       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8539       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8540     }
8541   }
8542   else {
8543
8544     // insert new nodes into the border and the side to get equal nb of segments
8545
8546     // get normalized parameters of nodes on the borders
8547     vector< double > param[ 2 ];
8548     param[0].resize( maxNbNodes );
8549     param[1].resize( maxNbNodes );
8550     int iNode, iBord;
8551     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8552       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8553       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8554       const SMDS_MeshNode* nPrev = *nIt;
8555       double bordLength = 0;
8556       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8557         const SMDS_MeshNode* nCur = *nIt;
8558         gp_XYZ segment (nCur->X() - nPrev->X(),
8559                         nCur->Y() - nPrev->Y(),
8560                         nCur->Z() - nPrev->Z());
8561         double segmentLen = segment.Modulus();
8562         bordLength += segmentLen;
8563         param[ iBord ][ iNode ] = bordLength;
8564         nPrev = nCur;
8565       }
8566       // normalize within [0,1]
8567       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8568         param[ iBord ][ iNode ] /= bordLength;
8569       }
8570     }
8571
8572     // loop on border segments
8573     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8574     int i[ 2 ] = { 0, 0 };
8575     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8576     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8577
8578     TElemOfNodeListMap insertMap;
8579     TElemOfNodeListMap::iterator insertMapIt;
8580     // insertMap is
8581     // key:   elem to insert nodes into
8582     // value: 2 nodes to insert between + nodes to be inserted
8583     do {
8584       bool next[ 2 ] = { false, false };
8585
8586       // find min adjacent segment length after sewing
8587       double nextParam = 10., prevParam = 0;
8588       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8589         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8590           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8591         if ( i[ iBord ] > 0 )
8592           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8593       }
8594       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8595       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8596       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8597
8598       // choose to insert or to merge nodes
8599       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8600       if ( Abs( du ) <= minSegLen * 0.2 ) {
8601         // merge
8602         // ------
8603         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8604         const SMDS_MeshNode* n0 = *nIt[0];
8605         const SMDS_MeshNode* n1 = *nIt[1];
8606         nodeGroupsToMerge.back().push_back( n1 );
8607         nodeGroupsToMerge.back().push_back( n0 );
8608         // position of node of the border changes due to merge
8609         param[ 0 ][ i[0] ] += du;
8610         // move n1 for the sake of elem shape evaluation during insertion.
8611         // n1 will be removed by MergeNodes() anyway
8612         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8613         next[0] = next[1] = true;
8614       }
8615       else {
8616         // insert
8617         // ------
8618         int intoBord = ( du < 0 ) ? 0 : 1;
8619         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8620         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8621         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8622         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8623         if ( intoBord == 1 ) {
8624           // move node of the border to be on a link of elem of the side
8625           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8626           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8627           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8628           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8629           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8630         }
8631         insertMapIt = insertMap.find( elem );
8632         bool  notFound = ( insertMapIt == insertMap.end() );
8633         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8634         if ( otherLink ) {
8635           // insert into another link of the same element:
8636           // 1. perform insertion into the other link of the elem
8637           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8638           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8639           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8640           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8641           // 2. perform insertion into the link of adjacent faces
8642           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8643             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8644           }
8645           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8646             InsertNodesIntoLink( seg, n12, n22, nodeList );
8647           }
8648           if (toCreatePolyedrs) {
8649             // perform insertion into the links of adjacent volumes
8650             UpdateVolumes(n12, n22, nodeList);
8651           }
8652           // 3. find an element appeared on n1 and n2 after the insertion
8653           insertMap.erase( elem );
8654           elem = findAdjacentFace( n1, n2, 0 );
8655         }
8656         if ( notFound || otherLink ) {
8657           // add element and nodes of the side into the insertMap
8658           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8659           (*insertMapIt).second.push_back( n1 );
8660           (*insertMapIt).second.push_back( n2 );
8661         }
8662         // add node to be inserted into elem
8663         (*insertMapIt).second.push_back( nIns );
8664         next[ 1 - intoBord ] = true;
8665       }
8666
8667       // go to the next segment
8668       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8669         if ( next[ iBord ] ) {
8670           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8671             eIt[ iBord ]++;
8672           nPrev[ iBord ] = *nIt[ iBord ];
8673           nIt[ iBord ]++; i[ iBord ]++;
8674         }
8675       }
8676     }
8677     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8678
8679     // perform insertion of nodes into elements
8680
8681     for (insertMapIt = insertMap.begin();
8682          insertMapIt != insertMap.end();
8683          insertMapIt++ )
8684     {
8685       const SMDS_MeshElement* elem = (*insertMapIt).first;
8686       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8687       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8688       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8689
8690       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8691
8692       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8693         InsertNodesIntoLink( seg, n1, n2, nodeList );
8694       }
8695
8696       if ( !theSideIsFreeBorder ) {
8697         // look for and insert nodes into the faces adjacent to elem
8698         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8699           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8700         }
8701       }
8702       if (toCreatePolyedrs) {
8703         // perform insertion into the links of adjacent volumes
8704         UpdateVolumes(n1, n2, nodeList);
8705       }
8706     }
8707   } // end: insert new nodes
8708
8709   MergeNodes ( nodeGroupsToMerge );
8710
8711
8712   // Remove coincident segments
8713
8714   // get new segments
8715   TIDSortedElemSet segments;
8716   SMESH_SequenceOfElemPtr newFaces;
8717   for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8718   {
8719     if ( !myLastCreatedElems(i) ) continue;
8720     if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8721       segments.insert( segments.end(), myLastCreatedElems(i) );
8722     else
8723       newFaces.Append( myLastCreatedElems(i) );
8724   }
8725   // get segments adjacent to merged nodes
8726   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8727   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8728   {
8729     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8730     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8731     while ( segIt->more() )
8732       segments.insert( segIt->next() );
8733   }
8734
8735   // find coincident
8736   TListOfListOfElementsID equalGroups;
8737   if ( !segments.empty() )
8738     FindEqualElements( segments, equalGroups );
8739   if ( !equalGroups.empty() )
8740   {
8741     // remove from segments those that will be removed
8742     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8743     for ( ; itGroups != equalGroups.end(); ++itGroups )
8744     {
8745       list< int >& group = *itGroups;
8746       list< int >::iterator id = group.begin();
8747       for ( ++id; id != group.end(); ++id )
8748         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8749           segments.erase( seg );
8750     }
8751     // remove equal segments
8752     MergeElements( equalGroups );
8753
8754     // restore myLastCreatedElems
8755     myLastCreatedElems = newFaces;
8756     TIDSortedElemSet::iterator seg = segments.begin();
8757     for ( ; seg != segments.end(); ++seg )
8758       myLastCreatedElems.Append( *seg );
8759   }
8760
8761   return aResult;
8762 }
8763
8764 //=======================================================================
8765 //function : InsertNodesIntoLink
8766 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8767 //           and theBetweenNode2 and split theElement
8768 //=======================================================================
8769
8770 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8771                                            const SMDS_MeshNode*        theBetweenNode1,
8772                                            const SMDS_MeshNode*        theBetweenNode2,
8773                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8774                                            const bool                  toCreatePoly)
8775 {
8776   if ( !theElement ) return;
8777
8778   SMESHDS_Mesh *aMesh = GetMeshDS();
8779   vector<const SMDS_MeshElement*> newElems;
8780
8781   if ( theElement->GetType() == SMDSAbs_Edge )
8782   {
8783     theNodesToInsert.push_front( theBetweenNode1 );
8784     theNodesToInsert.push_back ( theBetweenNode2 );
8785     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8786     const SMDS_MeshNode* n1 = *n;
8787     for ( ++n; n != theNodesToInsert.end(); ++n )
8788     {
8789       const SMDS_MeshNode* n2 = *n;
8790       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8791         AddToSameGroups( seg, theElement, aMesh );
8792       else
8793         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8794       n1 = n2;
8795     }
8796     theNodesToInsert.pop_front();
8797     theNodesToInsert.pop_back();
8798
8799     if ( theElement->IsQuadratic() ) // add a not split part
8800     {
8801       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8802                                           theElement->end_nodes() );
8803       int iOther = 0, nbN = nodes.size();
8804       for ( ; iOther < nbN; ++iOther )
8805         if ( nodes[iOther] != theBetweenNode1 &&
8806              nodes[iOther] != theBetweenNode2 )
8807           break;
8808       if      ( iOther == 0 )
8809       {
8810         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8811           AddToSameGroups( seg, theElement, aMesh );
8812         else
8813           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8814       }
8815       else if ( iOther == 2 )
8816       {
8817         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8818           AddToSameGroups( seg, theElement, aMesh );
8819         else
8820           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8821       }
8822     }
8823     // treat new elements
8824     for ( size_t i = 0; i < newElems.size(); ++i )
8825       if ( newElems[i] )
8826       {
8827         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8828         myLastCreatedElems.Append( newElems[i] );
8829       }
8830     ReplaceElemInGroups( theElement, newElems, aMesh );
8831     aMesh->RemoveElement( theElement );
8832     return;
8833
8834   } // if ( theElement->GetType() == SMDSAbs_Edge )
8835
8836   const SMDS_MeshElement* theFace = theElement;
8837   if ( theFace->GetType() != SMDSAbs_Face ) return;
8838
8839   // find indices of 2 link nodes and of the rest nodes
8840   int iNode = 0, il1, il2, i3, i4;
8841   il1 = il2 = i3 = i4 = -1;
8842   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8843
8844   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8845   while ( nodeIt->more() ) {
8846     const SMDS_MeshNode* n = nodeIt->next();
8847     if ( n == theBetweenNode1 )
8848       il1 = iNode;
8849     else if ( n == theBetweenNode2 )
8850       il2 = iNode;
8851     else if ( i3 < 0 )
8852       i3 = iNode;
8853     else
8854       i4 = iNode;
8855     nodes[ iNode++ ] = n;
8856   }
8857   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8858     return ;
8859
8860   // arrange link nodes to go one after another regarding the face orientation
8861   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8862   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8863   if ( reverse ) {
8864     iNode = il1;
8865     il1 = il2;
8866     il2 = iNode;
8867     aNodesToInsert.reverse();
8868   }
8869   // check that not link nodes of a quadrangles are in good order
8870   int nbFaceNodes = theFace->NbNodes();
8871   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8872     iNode = i3;
8873     i3 = i4;
8874     i4 = iNode;
8875   }
8876
8877   if (toCreatePoly || theFace->IsPoly()) {
8878
8879     iNode = 0;
8880     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8881
8882     // add nodes of face up to first node of link
8883     bool isFLN = false;
8884
8885     if ( theFace->IsQuadratic() ) {
8886       const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8887       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8888       // use special nodes iterator
8889       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8890       while( anIter->more()  && !isFLN ) {
8891         const SMDS_MeshNode* n = cast2Node(anIter->next());
8892         poly_nodes[iNode++] = n;
8893         if (n == nodes[il1]) {
8894           isFLN = true;
8895         }
8896       }
8897       // add nodes to insert
8898       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8899       for (; nIt != aNodesToInsert.end(); nIt++) {
8900         poly_nodes[iNode++] = *nIt;
8901       }
8902       // add nodes of face starting from last node of link
8903       while ( anIter->more() ) {
8904         poly_nodes[iNode++] = cast2Node(anIter->next());
8905       }
8906     }
8907     else {
8908       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8909       while ( nodeIt->more() && !isFLN ) {
8910         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8911         poly_nodes[iNode++] = n;
8912         if (n == nodes[il1]) {
8913           isFLN = true;
8914         }
8915       }
8916       // add nodes to insert
8917       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8918       for (; nIt != aNodesToInsert.end(); nIt++) {
8919         poly_nodes[iNode++] = *nIt;
8920       }
8921       // add nodes of face starting from last node of link
8922       while ( nodeIt->more() ) {
8923         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8924         poly_nodes[iNode++] = n;
8925       }
8926     }
8927
8928     // make a new face
8929     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8930   }
8931
8932   else if ( !theFace->IsQuadratic() )
8933   {
8934     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8935     int nbLinkNodes = 2 + aNodesToInsert.size();
8936     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8937     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8938     linkNodes[ 0 ] = nodes[ il1 ];
8939     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8940     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8941     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8942       linkNodes[ iNode++ ] = *nIt;
8943     }
8944     // decide how to split a quadrangle: compare possible variants
8945     // and choose which of splits to be a quadrangle
8946     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8947     if ( nbFaceNodes == 3 ) {
8948       iBestQuad = nbSplits;
8949       i4 = i3;
8950     }
8951     else if ( nbFaceNodes == 4 ) {
8952       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8953       double aBestRate = DBL_MAX;
8954       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8955         i1 = 0; i2 = 1;
8956         double aBadRate = 0;
8957         // evaluate elements quality
8958         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8959           if ( iSplit == iQuad ) {
8960             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8961                                    linkNodes[ i2++ ],
8962                                    nodes[ i3 ],
8963                                    nodes[ i4 ]);
8964             aBadRate += getBadRate( &quad, aCrit );
8965           }
8966           else {
8967             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8968                                    linkNodes[ i2++ ],
8969                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8970             aBadRate += getBadRate( &tria, aCrit );
8971           }
8972         }
8973         // choice
8974         if ( aBadRate < aBestRate ) {
8975           iBestQuad = iQuad;
8976           aBestRate = aBadRate;
8977         }
8978       }
8979     }
8980
8981     // create new elements
8982     i1 = 0; i2 = 1;
8983     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8984     {
8985       if ( iSplit == iBestQuad )
8986         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8987                                             linkNodes[ i2++ ],
8988                                             nodes[ i3 ],
8989                                             nodes[ i4 ]));
8990       else
8991         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8992                                             linkNodes[ i2++ ],
8993                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8994     }
8995
8996     const SMDS_MeshNode* newNodes[ 4 ];
8997     newNodes[ 0 ] = linkNodes[ i1 ];
8998     newNodes[ 1 ] = linkNodes[ i2 ];
8999     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9000     newNodes[ 3 ] = nodes[ i4 ];
9001     if (iSplit == iBestQuad)
9002       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
9003     else
9004       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
9005
9006   } // end if(!theFace->IsQuadratic())
9007
9008   else { // theFace is quadratic
9009     // we have to split theFace on simple triangles and one simple quadrangle
9010     int tmp = il1/2;
9011     int nbshift = tmp*2;
9012     // shift nodes in nodes[] by nbshift
9013     int i,j;
9014     for(i=0; i<nbshift; i++) {
9015       const SMDS_MeshNode* n = nodes[0];
9016       for(j=0; j<nbFaceNodes-1; j++) {
9017         nodes[j] = nodes[j+1];
9018       }
9019       nodes[nbFaceNodes-1] = n;
9020     }
9021     il1 = il1 - nbshift;
9022     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9023     //   n0      n1     n2    n0      n1     n2
9024     //     +-----+-----+        +-----+-----+
9025     //      \         /         |           |
9026     //       \       /          |           |
9027     //      n5+     +n3       n7+           +n3
9028     //         \   /            |           |
9029     //          \ /             |           |
9030     //           +              +-----+-----+
9031     //           n4           n6      n5     n4
9032
9033     // create new elements
9034     int n1,n2,n3;
9035     if ( nbFaceNodes == 6 ) { // quadratic triangle
9036       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9037       if ( theFace->IsMediumNode(nodes[il1]) ) {
9038         // create quadrangle
9039         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
9040         n1 = 1;
9041         n2 = 2;
9042         n3 = 3;
9043       }
9044       else {
9045         // create quadrangle
9046         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
9047         n1 = 0;
9048         n2 = 1;
9049         n3 = 5;
9050       }
9051     }
9052     else { // nbFaceNodes==8 - quadratic quadrangle
9053       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9054       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9055       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9056       if ( theFace->IsMediumNode( nodes[ il1 ])) {
9057         // create quadrangle
9058         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9059         n1 = 1;
9060         n2 = 2;
9061         n3 = 3;
9062       }
9063       else {
9064         // create quadrangle
9065         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9066         n1 = 0;
9067         n2 = 1;
9068         n3 = 7;
9069       }
9070     }
9071     // create needed triangles using n1,n2,n3 and inserted nodes
9072     int nbn = 2 + aNodesToInsert.size();
9073     vector<const SMDS_MeshNode*> aNodes(nbn);
9074     aNodes[0    ] = nodes[n1];
9075     aNodes[nbn-1] = nodes[n2];
9076     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9077     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9078       aNodes[iNode++] = *nIt;
9079     }
9080     for ( i = 1; i < nbn; i++ )
9081       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9082   }
9083
9084   // remove the old face
9085   for ( size_t i = 0; i < newElems.size(); ++i )
9086     if ( newElems[i] )
9087     {
9088       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9089       myLastCreatedElems.Append( newElems[i] );
9090     }
9091   ReplaceElemInGroups( theFace, newElems, aMesh );
9092   aMesh->RemoveElement(theFace);
9093
9094 } // InsertNodesIntoLink()
9095
9096 //=======================================================================
9097 //function : UpdateVolumes
9098 //purpose  :
9099 //=======================================================================
9100
9101 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
9102                                       const SMDS_MeshNode*        theBetweenNode2,
9103                                       list<const SMDS_MeshNode*>& theNodesToInsert)
9104 {
9105   myLastCreatedElems.Clear();
9106   myLastCreatedNodes.Clear();
9107
9108   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9109   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9110     const SMDS_MeshElement* elem = invElemIt->next();
9111
9112     // check, if current volume has link theBetweenNode1 - theBetweenNode2
9113     SMDS_VolumeTool aVolume (elem);
9114     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9115       continue;
9116
9117     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9118     int iface, nbFaces = aVolume.NbFaces();
9119     vector<const SMDS_MeshNode *> poly_nodes;
9120     vector<int> quantities (nbFaces);
9121
9122     for (iface = 0; iface < nbFaces; iface++) {
9123       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9124       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9125       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9126
9127       for (int inode = 0; inode < nbFaceNodes; inode++) {
9128         poly_nodes.push_back(faceNodes[inode]);
9129
9130         if (nbInserted == 0) {
9131           if (faceNodes[inode] == theBetweenNode1) {
9132             if (faceNodes[inode + 1] == theBetweenNode2) {
9133               nbInserted = theNodesToInsert.size();
9134
9135               // add nodes to insert
9136               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9137               for (; nIt != theNodesToInsert.end(); nIt++) {
9138                 poly_nodes.push_back(*nIt);
9139               }
9140             }
9141           }
9142           else if (faceNodes[inode] == theBetweenNode2) {
9143             if (faceNodes[inode + 1] == theBetweenNode1) {
9144               nbInserted = theNodesToInsert.size();
9145
9146               // add nodes to insert in reversed order
9147               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9148               nIt--;
9149               for (; nIt != theNodesToInsert.begin(); nIt--) {
9150                 poly_nodes.push_back(*nIt);
9151               }
9152               poly_nodes.push_back(*nIt);
9153             }
9154           }
9155           else {
9156           }
9157         }
9158       }
9159       quantities[iface] = nbFaceNodes + nbInserted;
9160     }
9161
9162     // Replace the volume
9163     SMESHDS_Mesh *aMesh = GetMeshDS();
9164
9165     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9166     {
9167       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9168       myLastCreatedElems.Append( newElem );
9169       ReplaceElemInGroups( elem, newElem, aMesh );
9170     }
9171     aMesh->RemoveElement( elem );
9172   }
9173 }
9174
9175 namespace
9176 {
9177   //================================================================================
9178   /*!
9179    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9180    */
9181   //================================================================================
9182
9183   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9184                            vector<const SMDS_MeshNode *> & nodes,
9185                            vector<int> &                   nbNodeInFaces )
9186   {
9187     nodes.clear();
9188     nbNodeInFaces.clear();
9189     SMDS_VolumeTool vTool ( elem );
9190     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9191     {
9192       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9193       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9194       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9195     }
9196   }
9197 }
9198
9199 //=======================================================================
9200 /*!
9201  * \brief Convert elements contained in a sub-mesh to quadratic
9202  * \return int - nb of checked elements
9203  */
9204 //=======================================================================
9205
9206 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9207                                              SMESH_MesherHelper& theHelper,
9208                                              const bool          theForce3d)
9209 {
9210   int nbElem = 0;
9211   if( !theSm ) return nbElem;
9212
9213   vector<int> nbNodeInFaces;
9214   vector<const SMDS_MeshNode *> nodes;
9215   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9216   while(ElemItr->more())
9217   {
9218     nbElem++;
9219     const SMDS_MeshElement* elem = ElemItr->next();
9220     if( !elem ) continue;
9221
9222     // analyse a necessity of conversion
9223     const SMDSAbs_ElementType aType = elem->GetType();
9224     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9225       continue;
9226     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9227     bool hasCentralNodes = false;
9228     if ( elem->IsQuadratic() )
9229     {
9230       bool alreadyOK;
9231       switch ( aGeomType ) {
9232       case SMDSEntity_Quad_Triangle:
9233       case SMDSEntity_Quad_Quadrangle:
9234       case SMDSEntity_Quad_Hexa:
9235         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9236
9237       case SMDSEntity_BiQuad_Triangle:
9238       case SMDSEntity_BiQuad_Quadrangle:
9239       case SMDSEntity_TriQuad_Hexa:
9240         alreadyOK = theHelper.GetIsBiQuadratic();
9241         hasCentralNodes = true;
9242         break;
9243       default:
9244         alreadyOK = true;
9245       }
9246       // take into account already present modium nodes
9247       switch ( aType ) {
9248       case SMDSAbs_Volume:
9249         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9250       case SMDSAbs_Face:
9251         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9252       case SMDSAbs_Edge:
9253         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9254       default:;
9255       }
9256       if ( alreadyOK )
9257         continue;
9258     }
9259     // get elem data needed to re-create it
9260     //
9261     const int id      = elem->GetID();
9262     const int nbNodes = elem->NbCornerNodes();
9263     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9264     if ( aGeomType == SMDSEntity_Polyhedra )
9265       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9266     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9267       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9268
9269     // remove a linear element
9270     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9271
9272     // remove central nodes of biquadratic elements (biquad->quad convertion)
9273     if ( hasCentralNodes )
9274       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9275         if ( nodes[i]->NbInverseElements() == 0 )
9276           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9277
9278     const SMDS_MeshElement* NewElem = 0;
9279
9280     switch( aType )
9281     {
9282     case SMDSAbs_Edge :
9283       {
9284         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9285         break;
9286       }
9287     case SMDSAbs_Face :
9288       {
9289         switch(nbNodes)
9290         {
9291         case 3:
9292           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9293           break;
9294         case 4:
9295           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9296           break;
9297         default:
9298           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9299         }
9300         break;
9301       }
9302     case SMDSAbs_Volume :
9303       {
9304         switch( aGeomType )
9305         {
9306         case SMDSEntity_Tetra:
9307           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9308           break;
9309         case SMDSEntity_Pyramid:
9310           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9311           break;
9312         case SMDSEntity_Penta:
9313           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9314           break;
9315         case SMDSEntity_Hexa:
9316         case SMDSEntity_Quad_Hexa:
9317         case SMDSEntity_TriQuad_Hexa:
9318           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9319                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9320           break;
9321         case SMDSEntity_Hexagonal_Prism:
9322         default:
9323           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9324         }
9325         break;
9326       }
9327     default :
9328       continue;
9329     }
9330     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9331     if( NewElem && NewElem->getshapeId() < 1 )
9332       theSm->AddElement( NewElem );
9333   }
9334   return nbElem;
9335 }
9336 //=======================================================================
9337 //function : ConvertToQuadratic
9338 //purpose  :
9339 //=======================================================================
9340
9341 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9342 {
9343   SMESHDS_Mesh* meshDS = GetMeshDS();
9344
9345   SMESH_MesherHelper aHelper(*myMesh);
9346
9347   aHelper.SetIsQuadratic( true );
9348   aHelper.SetIsBiQuadratic( theToBiQuad );
9349   aHelper.SetElementsOnShape(true);
9350   aHelper.ToFixNodeParameters( true );
9351
9352   // convert elements assigned to sub-meshes
9353   int nbCheckedElems = 0;
9354   if ( myMesh->HasShapeToMesh() )
9355   {
9356     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9357     {
9358       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9359       while ( smIt->more() ) {
9360         SMESH_subMesh* sm = smIt->next();
9361         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9362           aHelper.SetSubShape( sm->GetSubShape() );
9363           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9364         }
9365       }
9366     }
9367   }
9368
9369   // convert elements NOT assigned to sub-meshes
9370   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9371   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9372   {
9373     aHelper.SetElementsOnShape(false);
9374     SMESHDS_SubMesh *smDS = 0;
9375
9376     // convert edges
9377     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9378     while( aEdgeItr->more() )
9379     {
9380       const SMDS_MeshEdge* edge = aEdgeItr->next();
9381       if ( !edge->IsQuadratic() )
9382       {
9383         int                  id = edge->GetID();
9384         const SMDS_MeshNode* n1 = edge->GetNode(0);
9385         const SMDS_MeshNode* n2 = edge->GetNode(1);
9386
9387         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9388
9389         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9390         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9391       }
9392       else
9393       {
9394         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9395       }
9396     }
9397
9398     // convert faces
9399     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9400     while( aFaceItr->more() )
9401     {
9402       const SMDS_MeshFace* face = aFaceItr->next();
9403       if ( !face ) continue;
9404       
9405       const SMDSAbs_EntityType type = face->GetEntityType();
9406       bool alreadyOK;
9407       switch( type )
9408       {
9409       case SMDSEntity_Quad_Triangle:
9410       case SMDSEntity_Quad_Quadrangle:
9411         alreadyOK = !theToBiQuad;
9412         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9413         break;
9414       case SMDSEntity_BiQuad_Triangle:
9415       case SMDSEntity_BiQuad_Quadrangle:
9416         alreadyOK = theToBiQuad;
9417         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9418         break;
9419       default: alreadyOK = false;
9420       }
9421       if ( alreadyOK )
9422         continue;
9423
9424       const int id = face->GetID();
9425       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9426
9427       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9428
9429       SMDS_MeshFace * NewFace = 0;
9430       switch( type )
9431       {
9432       case SMDSEntity_Triangle:
9433       case SMDSEntity_Quad_Triangle:
9434       case SMDSEntity_BiQuad_Triangle:
9435         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9436         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9437           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9438         break;
9439
9440       case SMDSEntity_Quadrangle:
9441       case SMDSEntity_Quad_Quadrangle:
9442       case SMDSEntity_BiQuad_Quadrangle:
9443         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9444         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9445           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9446         break;
9447
9448       default:;
9449         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9450       }
9451       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9452     }
9453
9454     // convert volumes
9455     vector<int> nbNodeInFaces;
9456     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9457     while(aVolumeItr->more())
9458     {
9459       const SMDS_MeshVolume* volume = aVolumeItr->next();
9460       if ( !volume ) continue;
9461
9462       const SMDSAbs_EntityType type = volume->GetEntityType();
9463       if ( volume->IsQuadratic() )
9464       {
9465         bool alreadyOK;
9466         switch ( type )
9467         {
9468         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9469         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9470         default:                      alreadyOK = true;
9471         }
9472         if ( alreadyOK )
9473         {
9474           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9475           continue;
9476         }
9477       }
9478       const int id = volume->GetID();
9479       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9480       if ( type == SMDSEntity_Polyhedra )
9481         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9482       else if ( type == SMDSEntity_Hexagonal_Prism )
9483         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9484
9485       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9486
9487       SMDS_MeshVolume * NewVolume = 0;
9488       switch ( type )
9489       {
9490       case SMDSEntity_Tetra:
9491         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9492         break;
9493       case SMDSEntity_Hexa:
9494       case SMDSEntity_Quad_Hexa:
9495       case SMDSEntity_TriQuad_Hexa:
9496         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9497                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9498         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9499           if ( nodes[i]->NbInverseElements() == 0 )
9500             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9501         break;
9502       case SMDSEntity_Pyramid:
9503         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9504                                       nodes[3], nodes[4], id, theForce3d);
9505         break;
9506       case SMDSEntity_Penta:
9507         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9508                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9509         break;
9510       case SMDSEntity_Hexagonal_Prism:
9511       default:
9512         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9513       }
9514       ReplaceElemInGroups(volume, NewVolume, meshDS);
9515     }
9516   }
9517
9518   if ( !theForce3d )
9519   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9520     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9521     // aHelper.FixQuadraticElements(myError);
9522     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9523   }
9524 }
9525
9526 //================================================================================
9527 /*!
9528  * \brief Makes given elements quadratic
9529  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9530  *  \param theElements - elements to make quadratic
9531  */
9532 //================================================================================
9533
9534 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9535                                           TIDSortedElemSet& theElements,
9536                                           const bool        theToBiQuad)
9537 {
9538   if ( theElements.empty() ) return;
9539
9540   // we believe that all theElements are of the same type
9541   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9542
9543   // get all nodes shared by theElements
9544   TIDSortedNodeSet allNodes;
9545   TIDSortedElemSet::iterator eIt = theElements.begin();
9546   for ( ; eIt != theElements.end(); ++eIt )
9547     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9548
9549   // complete theElements with elements of lower dim whose all nodes are in allNodes
9550
9551   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9552   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9553   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9554   for ( ; nIt != allNodes.end(); ++nIt )
9555   {
9556     const SMDS_MeshNode* n = *nIt;
9557     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9558     while ( invIt->more() )
9559     {
9560       const SMDS_MeshElement*      e = invIt->next();
9561       const SMDSAbs_ElementType type = e->GetType();
9562       if ( e->IsQuadratic() )
9563       {
9564         quadAdjacentElems[ type ].insert( e );
9565
9566         bool alreadyOK;
9567         switch ( e->GetEntityType() ) {
9568         case SMDSEntity_Quad_Triangle:
9569         case SMDSEntity_Quad_Quadrangle:
9570         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9571         case SMDSEntity_BiQuad_Triangle:
9572         case SMDSEntity_BiQuad_Quadrangle:
9573         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9574         default:                           alreadyOK = true;
9575         }
9576         if ( alreadyOK )
9577           continue;
9578       }
9579       if ( type >= elemType )
9580         continue; // same type or more complex linear element
9581
9582       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9583         continue; // e is already checked
9584
9585       // check nodes
9586       bool allIn = true;
9587       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9588       while ( nodeIt->more() && allIn )
9589         allIn = allNodes.count( nodeIt->next() );
9590       if ( allIn )
9591         theElements.insert(e );
9592     }
9593   }
9594
9595   SMESH_MesherHelper helper(*myMesh);
9596   helper.SetIsQuadratic( true );
9597   helper.SetIsBiQuadratic( theToBiQuad );
9598
9599   // add links of quadratic adjacent elements to the helper
9600
9601   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9602     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9603           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9604     {
9605       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9606     }
9607   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9608     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9609           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9610     {
9611       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9612     }
9613   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9614     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9615           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9616     {
9617       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9618     }
9619
9620   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9621
9622   SMESHDS_Mesh*  meshDS = GetMeshDS();
9623   SMESHDS_SubMesh* smDS = 0;
9624   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9625   {
9626     const SMDS_MeshElement* elem = *eIt;
9627
9628     bool alreadyOK;
9629     int nbCentralNodes = 0;
9630     switch ( elem->GetEntityType() ) {
9631       // linear convertible
9632     case SMDSEntity_Edge:
9633     case SMDSEntity_Triangle:
9634     case SMDSEntity_Quadrangle:
9635     case SMDSEntity_Tetra:
9636     case SMDSEntity_Pyramid:
9637     case SMDSEntity_Hexa:
9638     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9639       // quadratic that can become bi-quadratic
9640     case SMDSEntity_Quad_Triangle:
9641     case SMDSEntity_Quad_Quadrangle:
9642     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9643       // bi-quadratic
9644     case SMDSEntity_BiQuad_Triangle:
9645     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9646     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9647       // the rest
9648     default:                           alreadyOK = true;
9649     }
9650     if ( alreadyOK ) continue;
9651
9652     const SMDSAbs_ElementType type = elem->GetType();
9653     const int                   id = elem->GetID();
9654     const int              nbNodes = elem->NbCornerNodes();
9655     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9656
9657     helper.SetSubShape( elem->getshapeId() );
9658
9659     if ( !smDS || !smDS->Contains( elem ))
9660       smDS = meshDS->MeshElements( elem->getshapeId() );
9661     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9662
9663     SMDS_MeshElement * newElem = 0;
9664     switch( nbNodes )
9665     {
9666     case 4: // cases for most frequently used element types go first (for optimization)
9667       if ( type == SMDSAbs_Volume )
9668         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9669       else
9670         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9671       break;
9672     case 8:
9673       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9674                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9675       break;
9676     case 3:
9677       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9678       break;
9679     case 2:
9680       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9681       break;
9682     case 5:
9683       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9684                                  nodes[4], id, theForce3d);
9685       break;
9686     case 6:
9687       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9688                                  nodes[4], nodes[5], id, theForce3d);
9689       break;
9690     default:;
9691     }
9692     ReplaceElemInGroups( elem, newElem, meshDS);
9693     if( newElem && smDS )
9694       smDS->AddElement( newElem );
9695
9696      // remove central nodes
9697     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9698       if ( nodes[i]->NbInverseElements() == 0 )
9699         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9700
9701   } // loop on theElements
9702
9703   if ( !theForce3d )
9704   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9705     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9706     // helper.FixQuadraticElements( myError );
9707     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9708   }
9709 }
9710
9711 //=======================================================================
9712 /*!
9713  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9714  * \return int - nb of checked elements
9715  */
9716 //=======================================================================
9717
9718 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9719                                      SMDS_ElemIteratorPtr theItr,
9720                                      const int            theShapeID)
9721 {
9722   int nbElem = 0;
9723   SMESHDS_Mesh* meshDS = GetMeshDS();
9724   ElemFeatures elemType;
9725   vector<const SMDS_MeshNode *> nodes;
9726
9727   while( theItr->more() )
9728   {
9729     const SMDS_MeshElement* elem = theItr->next();
9730     nbElem++;
9731     if( elem && elem->IsQuadratic())
9732     {
9733       // get elem data
9734       int nbCornerNodes = elem->NbCornerNodes();
9735       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9736
9737       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9738
9739       //remove a quadratic element
9740       if ( !theSm || !theSm->Contains( elem ))
9741         theSm = meshDS->MeshElements( elem->getshapeId() );
9742       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9743
9744       // remove medium nodes
9745       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9746         if ( nodes[i]->NbInverseElements() == 0 )
9747           meshDS->RemoveFreeNode( nodes[i], theSm );
9748
9749       // add a linear element
9750       nodes.resize( nbCornerNodes );
9751       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9752       ReplaceElemInGroups(elem, newElem, meshDS);
9753       if( theSm && newElem )
9754         theSm->AddElement( newElem );
9755     }
9756   }
9757   return nbElem;
9758 }
9759
9760 //=======================================================================
9761 //function : ConvertFromQuadratic
9762 //purpose  :
9763 //=======================================================================
9764
9765 bool SMESH_MeshEditor::ConvertFromQuadratic()
9766 {
9767   int nbCheckedElems = 0;
9768   if ( myMesh->HasShapeToMesh() )
9769   {
9770     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9771     {
9772       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9773       while ( smIt->more() ) {
9774         SMESH_subMesh* sm = smIt->next();
9775         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9776           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9777       }
9778     }
9779   }
9780
9781   int totalNbElems =
9782     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9783   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9784   {
9785     SMESHDS_SubMesh *aSM = 0;
9786     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9787   }
9788
9789   return true;
9790 }
9791
9792 namespace
9793 {
9794   //================================================================================
9795   /*!
9796    * \brief Return true if all medium nodes of the element are in the node set
9797    */
9798   //================================================================================
9799
9800   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9801   {
9802     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9803       if ( !nodeSet.count( elem->GetNode(i) ))
9804         return false;
9805     return true;
9806   }
9807 }
9808
9809 //================================================================================
9810 /*!
9811  * \brief Makes given elements linear
9812  */
9813 //================================================================================
9814
9815 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9816 {
9817   if ( theElements.empty() ) return;
9818
9819   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9820   set<int> mediumNodeIDs;
9821   TIDSortedElemSet::iterator eIt = theElements.begin();
9822   for ( ; eIt != theElements.end(); ++eIt )
9823   {
9824     const SMDS_MeshElement* e = *eIt;
9825     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9826       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9827   }
9828
9829   // replace given elements by linear ones
9830   SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9831   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9832
9833   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9834   // except those elements sharing medium nodes of quadratic element whose medium nodes
9835   // are not all in mediumNodeIDs
9836
9837   // get remaining medium nodes
9838   TIDSortedNodeSet mediumNodes;
9839   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9840   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9841     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9842       mediumNodes.insert( mediumNodes.end(), n );
9843
9844   // find more quadratic elements to convert
9845   TIDSortedElemSet moreElemsToConvert;
9846   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9847   for ( ; nIt != mediumNodes.end(); ++nIt )
9848   {
9849     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9850     while ( invIt->more() )
9851     {
9852       const SMDS_MeshElement* e = invIt->next();
9853       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9854       {
9855         // find a more complex element including e and
9856         // whose medium nodes are not in mediumNodes
9857         bool complexFound = false;
9858         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9859         {
9860           SMDS_ElemIteratorPtr invIt2 =
9861             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9862           while ( invIt2->more() )
9863           {
9864             const SMDS_MeshElement* eComplex = invIt2->next();
9865             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9866             {
9867               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9868               if ( nbCommonNodes == e->NbNodes())
9869               {
9870                 complexFound = true;
9871                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9872                 break;
9873               }
9874             }
9875           }
9876         }
9877         if ( !complexFound )
9878           moreElemsToConvert.insert( e );
9879       }
9880     }
9881   }
9882   elemIt = elemSetIterator( moreElemsToConvert );
9883   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9884 }
9885
9886 //=======================================================================
9887 //function : SewSideElements
9888 //purpose  :
9889 //=======================================================================
9890
9891 SMESH_MeshEditor::Sew_Error
9892 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9893                                    TIDSortedElemSet&    theSide2,
9894                                    const SMDS_MeshNode* theFirstNode1,
9895                                    const SMDS_MeshNode* theFirstNode2,
9896                                    const SMDS_MeshNode* theSecondNode1,
9897                                    const SMDS_MeshNode* theSecondNode2)
9898 {
9899   myLastCreatedElems.Clear();
9900   myLastCreatedNodes.Clear();
9901
9902   MESSAGE ("::::SewSideElements()");
9903   if ( theSide1.size() != theSide2.size() )
9904     return SEW_DIFF_NB_OF_ELEMENTS;
9905
9906   Sew_Error aResult = SEW_OK;
9907   // Algo:
9908   // 1. Build set of faces representing each side
9909   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9910   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9911
9912   // =======================================================================
9913   // 1. Build set of faces representing each side:
9914   // =======================================================================
9915   // a. build set of nodes belonging to faces
9916   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9917   // c. create temporary faces representing side of volumes if correspondent
9918   //    face does not exist
9919
9920   SMESHDS_Mesh* aMesh = GetMeshDS();
9921   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9922   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9923   TIDSortedElemSet             faceSet1, faceSet2;
9924   set<const SMDS_MeshElement*> volSet1,  volSet2;
9925   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9926   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9927   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9928   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9929   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9930   int iSide, iFace, iNode;
9931
9932   list<const SMDS_MeshElement* > tempFaceList;
9933   for ( iSide = 0; iSide < 2; iSide++ ) {
9934     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9935     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9936     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9937     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9938     set<const SMDS_MeshElement*>::iterator vIt;
9939     TIDSortedElemSet::iterator eIt;
9940     set<const SMDS_MeshNode*>::iterator    nIt;
9941
9942     // check that given nodes belong to given elements
9943     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9944     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9945     int firstIndex = -1, secondIndex = -1;
9946     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9947       const SMDS_MeshElement* elem = *eIt;
9948       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9949       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9950       if ( firstIndex > -1 && secondIndex > -1 ) break;
9951     }
9952     if ( firstIndex < 0 || secondIndex < 0 ) {
9953       // we can simply return until temporary faces created
9954       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9955     }
9956
9957     // -----------------------------------------------------------
9958     // 1a. Collect nodes of existing faces
9959     //     and build set of face nodes in order to detect missing
9960     //     faces corresponding to sides of volumes
9961     // -----------------------------------------------------------
9962
9963     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9964
9965     // loop on the given element of a side
9966     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9967       //const SMDS_MeshElement* elem = *eIt;
9968       const SMDS_MeshElement* elem = *eIt;
9969       if ( elem->GetType() == SMDSAbs_Face ) {
9970         faceSet->insert( elem );
9971         set <const SMDS_MeshNode*> faceNodeSet;
9972         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9973         while ( nodeIt->more() ) {
9974           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9975           nodeSet->insert( n );
9976           faceNodeSet.insert( n );
9977         }
9978         setOfFaceNodeSet.insert( faceNodeSet );
9979       }
9980       else if ( elem->GetType() == SMDSAbs_Volume )
9981         volSet->insert( elem );
9982     }
9983     // ------------------------------------------------------------------------------
9984     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9985     // ------------------------------------------------------------------------------
9986
9987     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9988       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9989       while ( fIt->more() ) { // loop on faces sharing a node
9990         const SMDS_MeshElement* f = fIt->next();
9991         if ( faceSet->find( f ) == faceSet->end() ) {
9992           // check if all nodes are in nodeSet and
9993           // complete setOfFaceNodeSet if they are
9994           set <const SMDS_MeshNode*> faceNodeSet;
9995           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9996           bool allInSet = true;
9997           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9998             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9999             if ( nodeSet->find( n ) == nodeSet->end() )
10000               allInSet = false;
10001             else
10002               faceNodeSet.insert( n );
10003           }
10004           if ( allInSet ) {
10005             faceSet->insert( f );
10006             setOfFaceNodeSet.insert( faceNodeSet );
10007           }
10008         }
10009       }
10010     }
10011
10012     // -------------------------------------------------------------------------
10013     // 1c. Create temporary faces representing sides of volumes if correspondent
10014     //     face does not exist
10015     // -------------------------------------------------------------------------
10016
10017     if ( !volSet->empty() ) {
10018       //int nodeSetSize = nodeSet->size();
10019
10020       // loop on given volumes
10021       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10022         SMDS_VolumeTool vol (*vIt);
10023         // loop on volume faces: find free faces
10024         // --------------------------------------
10025         list<const SMDS_MeshElement* > freeFaceList;
10026         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10027           if ( !vol.IsFreeFace( iFace ))
10028             continue;
10029           // check if there is already a face with same nodes in a face set
10030           const SMDS_MeshElement* aFreeFace = 0;
10031           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10032           int nbNodes = vol.NbFaceNodes( iFace );
10033           set <const SMDS_MeshNode*> faceNodeSet;
10034           vol.GetFaceNodes( iFace, faceNodeSet );
10035           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10036           if ( isNewFace ) {
10037             // no such a face is given but it still can exist, check it
10038             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10039             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10040           }
10041           if ( !aFreeFace ) {
10042             // create a temporary face
10043             if ( nbNodes == 3 ) {
10044               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10045               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10046             }
10047             else if ( nbNodes == 4 ) {
10048               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10049               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10050             }
10051             else {
10052               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10053               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10054               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10055             }
10056             if ( aFreeFace )
10057               tempFaceList.push_back( aFreeFace );
10058           }
10059
10060           if ( aFreeFace )
10061             freeFaceList.push_back( aFreeFace );
10062
10063         } // loop on faces of a volume
10064
10065         // choose one of several free faces of a volume
10066         // --------------------------------------------
10067         if ( freeFaceList.size() > 1 ) {
10068           // choose a face having max nb of nodes shared by other elems of a side
10069           int maxNbNodes = -1;
10070           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10071           while ( fIt != freeFaceList.end() ) { // loop on free faces
10072             int nbSharedNodes = 0;
10073             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10074             while ( nodeIt->more() ) { // loop on free face nodes
10075               const SMDS_MeshNode* n =
10076                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10077               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10078               while ( invElemIt->more() ) {
10079                 const SMDS_MeshElement* e = invElemIt->next();
10080                 nbSharedNodes += faceSet->count( e );
10081                 nbSharedNodes += elemSet->count( e );
10082               }
10083             }
10084             if ( nbSharedNodes > maxNbNodes ) {
10085               maxNbNodes = nbSharedNodes;
10086               freeFaceList.erase( freeFaceList.begin(), fIt++ );
10087             }
10088             else if ( nbSharedNodes == maxNbNodes ) {
10089               fIt++;
10090             }
10091             else {
10092               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10093             }
10094           }
10095           if ( freeFaceList.size() > 1 )
10096           {
10097             // could not choose one face, use another way
10098             // choose a face most close to the bary center of the opposite side
10099             gp_XYZ aBC( 0., 0., 0. );
10100             set <const SMDS_MeshNode*> addedNodes;
10101             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10102             eIt = elemSet2->begin();
10103             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10104               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10105               while ( nodeIt->more() ) { // loop on free face nodes
10106                 const SMDS_MeshNode* n =
10107                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10108                 if ( addedNodes.insert( n ).second )
10109                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10110               }
10111             }
10112             aBC /= addedNodes.size();
10113             double minDist = DBL_MAX;
10114             fIt = freeFaceList.begin();
10115             while ( fIt != freeFaceList.end() ) { // loop on free faces
10116               double dist = 0;
10117               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10118               while ( nodeIt->more() ) { // loop on free face nodes
10119                 const SMDS_MeshNode* n =
10120                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10121                 gp_XYZ p( n->X(),n->Y(),n->Z() );
10122                 dist += ( aBC - p ).SquareModulus();
10123               }
10124               if ( dist < minDist ) {
10125                 minDist = dist;
10126                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10127               }
10128               else
10129                 fIt = freeFaceList.erase( fIt++ );
10130             }
10131           }
10132         } // choose one of several free faces of a volume
10133
10134         if ( freeFaceList.size() == 1 ) {
10135           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10136           faceSet->insert( aFreeFace );
10137           // complete a node set with nodes of a found free face
10138           //           for ( iNode = 0; iNode < ; iNode++ )
10139           //             nodeSet->insert( fNodes[ iNode ] );
10140         }
10141
10142       } // loop on volumes of a side
10143
10144       //       // complete a set of faces if new nodes in a nodeSet appeared
10145       //       // ----------------------------------------------------------
10146       //       if ( nodeSetSize != nodeSet->size() ) {
10147       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10148       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10149       //           while ( fIt->more() ) { // loop on faces sharing a node
10150       //             const SMDS_MeshElement* f = fIt->next();
10151       //             if ( faceSet->find( f ) == faceSet->end() ) {
10152       //               // check if all nodes are in nodeSet and
10153       //               // complete setOfFaceNodeSet if they are
10154       //               set <const SMDS_MeshNode*> faceNodeSet;
10155       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10156       //               bool allInSet = true;
10157       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10158       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10159       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10160       //                   allInSet = false;
10161       //                 else
10162       //                   faceNodeSet.insert( n );
10163       //               }
10164       //               if ( allInSet ) {
10165       //                 faceSet->insert( f );
10166       //                 setOfFaceNodeSet.insert( faceNodeSet );
10167       //               }
10168       //             }
10169       //           }
10170       //         }
10171       //       }
10172     } // Create temporary faces, if there are volumes given
10173   } // loop on sides
10174
10175   if ( faceSet1.size() != faceSet2.size() ) {
10176     // delete temporary faces: they are in reverseElements of actual nodes
10177 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10178 //    while ( tmpFaceIt->more() )
10179 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10180 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10181 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10182 //      aMesh->RemoveElement(*tmpFaceIt);
10183     MESSAGE("Diff nb of faces");
10184     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10185   }
10186
10187   // ============================================================
10188   // 2. Find nodes to merge:
10189   //              bind a node to remove to a node to put instead
10190   // ============================================================
10191
10192   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10193   if ( theFirstNode1 != theFirstNode2 )
10194     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10195   if ( theSecondNode1 != theSecondNode2 )
10196     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10197
10198   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10199   set< long > linkIdSet; // links to process
10200   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10201
10202   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10203   list< NLink > linkList[2];
10204   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10205   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10206   // loop on links in linkList; find faces by links and append links
10207   // of the found faces to linkList
10208   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10209   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10210   {
10211     NLink link[] = { *linkIt[0], *linkIt[1] };
10212     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10213     if ( !linkIdSet.count( linkID ) )
10214       continue;
10215
10216     // by links, find faces in the face sets,
10217     // and find indices of link nodes in the found faces;
10218     // in a face set, there is only one or no face sharing a link
10219     // ---------------------------------------------------------------
10220
10221     const SMDS_MeshElement* face[] = { 0, 0 };
10222     vector<const SMDS_MeshNode*> fnodes[2];
10223     int iLinkNode[2][2];
10224     TIDSortedElemSet avoidSet;
10225     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10226       const SMDS_MeshNode* n1 = link[iSide].first;
10227       const SMDS_MeshNode* n2 = link[iSide].second;
10228       //cout << "Side " << iSide << " ";
10229       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10230       // find a face by two link nodes
10231       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10232                                                       *faceSetPtr[ iSide ], avoidSet,
10233                                                       &iLinkNode[iSide][0],
10234                                                       &iLinkNode[iSide][1] );
10235       if ( face[ iSide ])
10236       {
10237         //cout << " F " << face[ iSide]->GetID() <<endl;
10238         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10239         // put face nodes to fnodes
10240         if ( face[ iSide ]->IsQuadratic() )
10241         {
10242           // use interlaced nodes iterator
10243           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10244           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10245           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10246           while ( nIter->more() )
10247             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10248         }
10249         else
10250         {
10251           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10252                                   face[ iSide ]->end_nodes() );
10253         }
10254         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10255       }
10256     }
10257
10258     // check similarity of elements of the sides
10259     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10260       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10261       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10262         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10263       }
10264       else {
10265         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10266       }
10267       break; // do not return because it's necessary to remove tmp faces
10268     }
10269
10270     // set nodes to merge
10271     // -------------------
10272
10273     if ( face[0] && face[1] )  {
10274       const int nbNodes = face[0]->NbNodes();
10275       if ( nbNodes != face[1]->NbNodes() ) {
10276         MESSAGE("Diff nb of face nodes");
10277         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10278         break; // do not return because it s necessary to remove tmp faces
10279       }
10280       bool reverse[] = { false, false }; // order of nodes in the link
10281       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10282         // analyse link orientation in faces
10283         int i1 = iLinkNode[ iSide ][ 0 ];
10284         int i2 = iLinkNode[ iSide ][ 1 ];
10285         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10286       }
10287       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10288       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10289       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10290       {
10291         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10292                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10293       }
10294
10295       // add other links of the faces to linkList
10296       // -----------------------------------------
10297
10298       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10299         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10300         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10301         if ( !iter_isnew.second ) { // already in a set: no need to process
10302           linkIdSet.erase( iter_isnew.first );
10303         }
10304         else // new in set == encountered for the first time: add
10305         {
10306           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10307           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10308           linkList[0].push_back ( NLink( n1, n2 ));
10309           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10310         }
10311       }
10312     } // 2 faces found
10313
10314     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10315       break;
10316
10317   } // loop on link lists
10318
10319   if ( aResult == SEW_OK &&
10320        ( //linkIt[0] != linkList[0].end() ||
10321          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10322     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10323              " " << (faceSetPtr[1]->empty()));
10324     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10325   }
10326
10327   // ====================================================================
10328   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10329   // ====================================================================
10330
10331   // delete temporary faces
10332 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10333 //  while ( tmpFaceIt->more() )
10334 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10335   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10336   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10337     aMesh->RemoveElement(*tmpFaceIt);
10338
10339   if ( aResult != SEW_OK)
10340     return aResult;
10341
10342   list< int > nodeIDsToRemove;
10343   vector< const SMDS_MeshNode*> nodes;
10344   ElemFeatures elemType;
10345
10346   // loop on nodes replacement map
10347   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10348   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10349     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10350     {
10351       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10352       nodeIDsToRemove.push_back( nToRemove->GetID() );
10353       // loop on elements sharing nToRemove
10354       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10355       while ( invElemIt->more() ) {
10356         const SMDS_MeshElement* e = invElemIt->next();
10357         // get a new suite of nodes: make replacement
10358         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10359         nodes.resize( nbNodes );
10360         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10361         while ( nIt->more() ) {
10362           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10363           nnIt = nReplaceMap.find( n );
10364           if ( nnIt != nReplaceMap.end() ) {
10365             nbReplaced++;
10366             n = (*nnIt).second;
10367           }
10368           nodes[ i++ ] = n;
10369         }
10370         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10371         //         elemIDsToRemove.push_back( e->GetID() );
10372         //       else
10373         if ( nbReplaced )
10374         {
10375           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10376           aMesh->RemoveElement( e );
10377
10378           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10379           {
10380             AddToSameGroups( newElem, e, aMesh );
10381             if ( int aShapeId = e->getshapeId() )
10382               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10383           }
10384         }
10385       }
10386     }
10387
10388   Remove( nodeIDsToRemove, true );
10389
10390   return aResult;
10391 }
10392
10393 //================================================================================
10394 /*!
10395  * \brief Find corresponding nodes in two sets of faces
10396  * \param theSide1 - first face set
10397  * \param theSide2 - second first face
10398  * \param theFirstNode1 - a boundary node of set 1
10399  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10400  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10401  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10402  * \param nReplaceMap - output map of corresponding nodes
10403  * \return bool  - is a success or not
10404  */
10405 //================================================================================
10406
10407 #ifdef _DEBUG_
10408 //#define DEBUG_MATCHING_NODES
10409 #endif
10410
10411 SMESH_MeshEditor::Sew_Error
10412 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10413                                     set<const SMDS_MeshElement*>& theSide2,
10414                                     const SMDS_MeshNode*          theFirstNode1,
10415                                     const SMDS_MeshNode*          theFirstNode2,
10416                                     const SMDS_MeshNode*          theSecondNode1,
10417                                     const SMDS_MeshNode*          theSecondNode2,
10418                                     TNodeNodeMap &                nReplaceMap)
10419 {
10420   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10421
10422   nReplaceMap.clear();
10423   if ( theFirstNode1 != theFirstNode2 )
10424     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10425   if ( theSecondNode1 != theSecondNode2 )
10426     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10427
10428   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10429   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10430
10431   list< NLink > linkList[2];
10432   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10433   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10434
10435   // loop on links in linkList; find faces by links and append links
10436   // of the found faces to linkList
10437   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10438   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10439     NLink link[] = { *linkIt[0], *linkIt[1] };
10440     if ( linkSet.find( link[0] ) == linkSet.end() )
10441       continue;
10442
10443     // by links, find faces in the face sets,
10444     // and find indices of link nodes in the found faces;
10445     // in a face set, there is only one or no face sharing a link
10446     // ---------------------------------------------------------------
10447
10448     const SMDS_MeshElement* face[] = { 0, 0 };
10449     list<const SMDS_MeshNode*> notLinkNodes[2];
10450     //bool reverse[] = { false, false }; // order of notLinkNodes
10451     int nbNodes[2];
10452     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10453     {
10454       const SMDS_MeshNode* n1 = link[iSide].first;
10455       const SMDS_MeshNode* n2 = link[iSide].second;
10456       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10457       set< const SMDS_MeshElement* > facesOfNode1;
10458       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10459       {
10460         // during a loop of the first node, we find all faces around n1,
10461         // during a loop of the second node, we find one face sharing both n1 and n2
10462         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10463         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10464         while ( fIt->more() ) { // loop on faces sharing a node
10465           const SMDS_MeshElement* f = fIt->next();
10466           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10467               ! facesOfNode1.insert( f ).second ) // f encounters twice
10468           {
10469             if ( face[ iSide ] ) {
10470               MESSAGE( "2 faces per link " );
10471               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10472             }
10473             face[ iSide ] = f;
10474             faceSet->erase( f );
10475
10476             // get not link nodes
10477             int nbN = f->NbNodes();
10478             if ( f->IsQuadratic() )
10479               nbN /= 2;
10480             nbNodes[ iSide ] = nbN;
10481             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10482             int i1 = f->GetNodeIndex( n1 );
10483             int i2 = f->GetNodeIndex( n2 );
10484             int iEnd = nbN, iBeg = -1, iDelta = 1;
10485             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10486             if ( reverse ) {
10487               std::swap( iEnd, iBeg ); iDelta = -1;
10488             }
10489             int i = i2;
10490             while ( true ) {
10491               i += iDelta;
10492               if ( i == iEnd ) i = iBeg + iDelta;
10493               if ( i == i1 ) break;
10494               nodes.push_back ( f->GetNode( i ) );
10495             }
10496           }
10497         }
10498       }
10499     }
10500     // check similarity of elements of the sides
10501     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10502       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10503       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10504         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10505       }
10506       else {
10507         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10508       }
10509     }
10510
10511     // set nodes to merge
10512     // -------------------
10513
10514     if ( face[0] && face[1] )  {
10515       if ( nbNodes[0] != nbNodes[1] ) {
10516         MESSAGE("Diff nb of face nodes");
10517         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10518       }
10519 #ifdef DEBUG_MATCHING_NODES
10520       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10521                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10522                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10523 #endif
10524       int nbN = nbNodes[0];
10525       {
10526         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10527         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10528         for ( int i = 0 ; i < nbN - 2; ++i ) {
10529 #ifdef DEBUG_MATCHING_NODES
10530           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10531 #endif
10532           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10533         }
10534       }
10535
10536       // add other links of the face 1 to linkList
10537       // -----------------------------------------
10538
10539       const SMDS_MeshElement* f0 = face[0];
10540       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10541       for ( int i = 0; i < nbN; i++ )
10542       {
10543         const SMDS_MeshNode* n2 = f0->GetNode( i );
10544         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10545           linkSet.insert( SMESH_TLink( n1, n2 ));
10546         if ( !iter_isnew.second ) { // already in a set: no need to process
10547           linkSet.erase( iter_isnew.first );
10548         }
10549         else // new in set == encountered for the first time: add
10550         {
10551 #ifdef DEBUG_MATCHING_NODES
10552           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10553                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10554 #endif
10555           linkList[0].push_back ( NLink( n1, n2 ));
10556           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10557         }
10558         n1 = n2;
10559       }
10560     } // 2 faces found
10561   } // loop on link lists
10562
10563   return SEW_OK;
10564 }
10565
10566 //================================================================================
10567 /*!
10568  * \brief Create elements equal (on same nodes) to given ones
10569  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10570  *              elements of the uppest dimension are duplicated.
10571  */
10572 //================================================================================
10573
10574 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10575 {
10576   ClearLastCreated();
10577   SMESHDS_Mesh* mesh = GetMeshDS();
10578
10579   // get an element type and an iterator over elements
10580
10581   SMDSAbs_ElementType type;
10582   SMDS_ElemIteratorPtr elemIt;
10583   vector< const SMDS_MeshElement* > allElems;
10584   if ( theElements.empty() )
10585   {
10586     if ( mesh->NbNodes() == 0 )
10587       return;
10588     // get most complex type
10589     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10590       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10591       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10592     };
10593     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10594       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10595       {
10596         type = types[i];
10597         break;
10598       }
10599     // put all elements in the vector <allElems>
10600     allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10601     elemIt = mesh->elementsIterator( type );
10602     while ( elemIt->more() )
10603       allElems.push_back( elemIt->next());
10604     elemIt = elemSetIterator( allElems );
10605   }
10606   else
10607   {
10608     type = (*theElements.begin())->GetType();
10609     elemIt = elemSetIterator( theElements );
10610   }
10611
10612   // duplicate elements
10613
10614   ElemFeatures elemType;
10615
10616   vector< const SMDS_MeshNode* > nodes;
10617   while ( elemIt->more() )
10618   {
10619     const SMDS_MeshElement* elem = elemIt->next();
10620     if ( elem->GetType() != type )
10621       continue;
10622
10623     elemType.Init( elem, /*basicOnly=*/false );
10624     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10625
10626     AddElement( nodes, elemType );
10627   }
10628 }
10629
10630 //================================================================================
10631 /*!
10632   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10633   \param theElems - the list of elements (edges or faces) to be replicated
10634   The nodes for duplication could be found from these elements
10635   \param theNodesNot - list of nodes to NOT replicate
10636   \param theAffectedElems - the list of elements (cells and edges) to which the
10637   replicated nodes should be associated to.
10638   \return TRUE if operation has been completed successfully, FALSE otherwise
10639 */
10640 //================================================================================
10641
10642 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10643                                     const TIDSortedElemSet& theNodesNot,
10644                                     const TIDSortedElemSet& theAffectedElems )
10645 {
10646   myLastCreatedElems.Clear();
10647   myLastCreatedNodes.Clear();
10648
10649   if ( theElems.size() == 0 )
10650     return false;
10651
10652   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10653   if ( !aMeshDS )
10654     return false;
10655
10656   bool res = false;
10657   TNodeNodeMap anOldNodeToNewNode;
10658   // duplicate elements and nodes
10659   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10660   // replce nodes by duplications
10661   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10662   return res;
10663 }
10664
10665 //================================================================================
10666 /*!
10667   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10668   \param theMeshDS - mesh instance
10669   \param theElems - the elements replicated or modified (nodes should be changed)
10670   \param theNodesNot - nodes to NOT replicate
10671   \param theNodeNodeMap - relation of old node to new created node
10672   \param theIsDoubleElem - flag os to replicate element or modify
10673   \return TRUE if operation has been completed successfully, FALSE otherwise
10674 */
10675 //================================================================================
10676
10677 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10678                                    const TIDSortedElemSet& theElems,
10679                                    const TIDSortedElemSet& theNodesNot,
10680                                    TNodeNodeMap&           theNodeNodeMap,
10681                                    const bool              theIsDoubleElem )
10682 {
10683   MESSAGE("doubleNodes");
10684   // iterate through element and duplicate them (by nodes duplication)
10685   bool res = false;
10686   std::vector<const SMDS_MeshNode*> newNodes;
10687   ElemFeatures elemType;
10688
10689   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10690   for ( ;  elemItr != theElems.end(); ++elemItr )
10691   {
10692     const SMDS_MeshElement* anElem = *elemItr;
10693     if (!anElem)
10694       continue;
10695
10696     // duplicate nodes to duplicate element
10697     bool isDuplicate = false;
10698     newNodes.resize( anElem->NbNodes() );
10699     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10700     int ind = 0;
10701     while ( anIter->more() )
10702     {
10703       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10704       const SMDS_MeshNode*  aNewNode = aCurrNode;
10705       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10706       if ( n2n != theNodeNodeMap.end() )
10707       {
10708         aNewNode = n2n->second;
10709       }
10710       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10711       {
10712         // duplicate node
10713         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10714         copyPosition( aCurrNode, aNewNode );
10715         theNodeNodeMap[ aCurrNode ] = aNewNode;
10716         myLastCreatedNodes.Append( aNewNode );
10717       }
10718       isDuplicate |= (aCurrNode != aNewNode);
10719       newNodes[ ind++ ] = aNewNode;
10720     }
10721     if ( !isDuplicate )
10722       continue;
10723
10724     if ( theIsDoubleElem )
10725       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10726     else
10727       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10728
10729     res = true;
10730   }
10731   return res;
10732 }
10733
10734 //================================================================================
10735 /*!
10736   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10737   \param theNodes - identifiers of nodes to be doubled
10738   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10739   nodes. If list of element identifiers is empty then nodes are doubled but
10740   they not assigned to elements
10741   \return TRUE if operation has been completed successfully, FALSE otherwise
10742 */
10743 //================================================================================
10744
10745 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10746                                     const std::list< int >& theListOfModifiedElems )
10747 {
10748   MESSAGE("DoubleNodes");
10749   myLastCreatedElems.Clear();
10750   myLastCreatedNodes.Clear();
10751
10752   if ( theListOfNodes.size() == 0 )
10753     return false;
10754
10755   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10756   if ( !aMeshDS )
10757     return false;
10758
10759   // iterate through nodes and duplicate them
10760
10761   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10762
10763   std::list< int >::const_iterator aNodeIter;
10764   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10765   {
10766     int aCurr = *aNodeIter;
10767     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10768     if ( !aNode )
10769       continue;
10770
10771     // duplicate node
10772
10773     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10774     if ( aNewNode )
10775     {
10776       copyPosition( aNode, aNewNode );
10777       anOldNodeToNewNode[ aNode ] = aNewNode;
10778       myLastCreatedNodes.Append( aNewNode );
10779     }
10780   }
10781
10782   // Create map of new nodes for modified elements
10783
10784   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10785
10786   std::list< int >::const_iterator anElemIter;
10787   for ( anElemIter = theListOfModifiedElems.begin();
10788         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10789   {
10790     int aCurr = *anElemIter;
10791     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10792     if ( !anElem )
10793       continue;
10794
10795     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10796
10797     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10798     int ind = 0;
10799     while ( anIter->more() )
10800     {
10801       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10802       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10803       {
10804         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10805         aNodeArr[ ind++ ] = aNewNode;
10806       }
10807       else
10808         aNodeArr[ ind++ ] = aCurrNode;
10809     }
10810     anElemToNodes[ anElem ] = aNodeArr;
10811   }
10812
10813   // Change nodes of elements
10814
10815   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10816     anElemToNodesIter = anElemToNodes.begin();
10817   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10818   {
10819     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10820     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10821     if ( anElem )
10822       {
10823       MESSAGE("ChangeElementNodes");
10824       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10825       }
10826   }
10827
10828   return true;
10829 }
10830
10831 namespace {
10832
10833   //================================================================================
10834   /*!
10835   \brief Check if element located inside shape
10836   \return TRUE if IN or ON shape, FALSE otherwise
10837   */
10838   //================================================================================
10839
10840   template<class Classifier>
10841   bool isInside(const SMDS_MeshElement* theElem,
10842                 Classifier&             theClassifier,
10843                 const double            theTol)
10844   {
10845     gp_XYZ centerXYZ (0, 0, 0);
10846     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10847     while (aNodeItr->more())
10848       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10849
10850     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10851     theClassifier.Perform(aPnt, theTol);
10852     TopAbs_State aState = theClassifier.State();
10853     return (aState == TopAbs_IN || aState == TopAbs_ON );
10854   }
10855
10856   //================================================================================
10857   /*!
10858    * \brief Classifier of the 3D point on the TopoDS_Face
10859    *        with interaface suitable for isInside()
10860    */
10861   //================================================================================
10862
10863   struct _FaceClassifier
10864   {
10865     Extrema_ExtPS       _extremum;
10866     BRepAdaptor_Surface _surface;
10867     TopAbs_State        _state;
10868
10869     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10870     {
10871       _extremum.Initialize( _surface,
10872                             _surface.FirstUParameter(), _surface.LastUParameter(),
10873                             _surface.FirstVParameter(), _surface.LastVParameter(),
10874                             _surface.Tolerance(), _surface.Tolerance() );
10875     }
10876     void Perform(const gp_Pnt& aPnt, double theTol)
10877     {
10878       theTol *= theTol;
10879       _state = TopAbs_OUT;
10880       _extremum.Perform(aPnt);
10881       if ( _extremum.IsDone() )
10882         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10883           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10884     }
10885     TopAbs_State State() const
10886     {
10887       return _state;
10888     }
10889   };
10890 }
10891
10892 //================================================================================
10893 /*!
10894   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10895   This method is the first step of DoubleNodeElemGroupsInRegion.
10896   \param theElems - list of groups of elements (edges or faces) to be replicated
10897   \param theNodesNot - list of groups of nodes not to replicated
10898   \param theShape - shape to detect affected elements (element which geometric center
10899          located on or inside shape). If the shape is null, detection is done on faces orientations
10900          (select elements with a gravity center on the side given by faces normals).
10901          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10902          The replicated nodes should be associated to affected elements.
10903   \return groups of affected elements
10904   \sa DoubleNodeElemGroupsInRegion()
10905  */
10906 //================================================================================
10907
10908 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10909                                                    const TIDSortedElemSet& theNodesNot,
10910                                                    const TopoDS_Shape&     theShape,
10911                                                    TIDSortedElemSet&       theAffectedElems)
10912 {
10913   if ( theShape.IsNull() )
10914   {
10915     std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10916     std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10917     std::set<const SMDS_MeshElement*> edgesToCheck;
10918     alreadyCheckedNodes.clear();
10919     alreadyCheckedElems.clear();
10920     edgesToCheck.clear();
10921
10922     // --- iterates on elements to be replicated and get elements by back references from their nodes
10923
10924     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10925     int ielem;
10926     for ( ielem=1;  elemItr != theElems.end(); ++elemItr )
10927     {
10928       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10929       if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10930         continue;
10931       gp_XYZ normal;
10932       SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10933       MESSAGE("element " << ielem++ <<  " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10934       std::set<const SMDS_MeshNode*> nodesElem;
10935       nodesElem.clear();
10936       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10937       while ( nodeItr->more() )
10938       {
10939         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10940         nodesElem.insert(aNode);
10941       }
10942       std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10943       for (; nodit != nodesElem.end(); nodit++)
10944       {
10945         MESSAGE("  noeud ");
10946         const SMDS_MeshNode* aNode = *nodit;
10947         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10948           continue;
10949         if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10950           continue;
10951         alreadyCheckedNodes.insert(aNode);
10952         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10953         while ( backElemItr->more() )
10954         {
10955           MESSAGE("    backelem ");
10956           const SMDS_MeshElement* curElem = backElemItr->next();
10957           if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10958             continue;
10959           if (theElems.find(curElem) != theElems.end())
10960             continue;
10961           alreadyCheckedElems.insert(curElem);
10962           double x=0, y=0, z=0;
10963           int nb = 0;
10964           SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10965           while ( nodeItr2->more() )
10966           {
10967             const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10968             x += anotherNode->X();
10969             y += anotherNode->Y();
10970             z += anotherNode->Z();
10971             nb++;
10972           }
10973           gp_XYZ p;
10974           p.SetCoord( x/nb -aNode->X(),
10975                       y/nb -aNode->Y(),
10976                       z/nb -aNode->Z() );
10977           MESSAGE("      check " << p.X() << " " << p.Y() << " " << p.Z());
10978           if (normal*p > 0)
10979           {
10980             MESSAGE("    --- inserted")
10981             theAffectedElems.insert( curElem );
10982           }
10983           else if (curElem->GetType() == SMDSAbs_Edge)
10984             edgesToCheck.insert(curElem);
10985         }
10986       }
10987     }
10988     // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10989     std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10990     for( ; eit != edgesToCheck.end(); eit++)
10991     {
10992       bool onside = true;
10993       const SMDS_MeshElement* anEdge = *eit;
10994       SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10995       while ( nodeItr->more() )
10996       {
10997         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10998         if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10999         {
11000           onside = false;
11001           break;
11002         }
11003       }
11004       if (onside)
11005       {
11006         MESSAGE("    --- edge onside inserted")
11007         theAffectedElems.insert(anEdge);
11008       }
11009     }
11010   }
11011   else
11012   {
11013     const double aTol = Precision::Confusion();
11014     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11015     auto_ptr<_FaceClassifier>              aFaceClassifier;
11016     if ( theShape.ShapeType() == TopAbs_SOLID )
11017     {
11018       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11019       bsc3d->PerformInfinitePoint(aTol);
11020     }
11021     else if (theShape.ShapeType() == TopAbs_FACE )
11022     {
11023       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11024     }
11025
11026     // iterates on indicated elements and get elements by back references from their nodes
11027     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11028     int ielem;
11029     for ( ielem = 1;  elemItr != theElems.end(); ++elemItr )
11030     {
11031       MESSAGE("element " << ielem++);
11032       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11033       if (!anElem)
11034         continue;
11035       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11036       while ( nodeItr->more() )
11037       {
11038         MESSAGE("  noeud ");
11039         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11040         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11041           continue;
11042         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11043         while ( backElemItr->more() )
11044         {
11045           MESSAGE("    backelem ");
11046           const SMDS_MeshElement* curElem = backElemItr->next();
11047           if ( curElem && theElems.find(curElem) == theElems.end() &&
11048               ( bsc3d.get() ?
11049                 isInside( curElem, *bsc3d, aTol ) :
11050                 isInside( curElem, *aFaceClassifier, aTol )))
11051             theAffectedElems.insert( curElem );
11052         }
11053       }
11054     }
11055   }
11056   return true;
11057 }
11058
11059 //================================================================================
11060 /*!
11061   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11062   \param theElems - group of of elements (edges or faces) to be replicated
11063   \param theNodesNot - group of nodes not to replicate
11064   \param theShape - shape to detect affected elements (element which geometric center
11065   located on or inside shape).
11066   The replicated nodes should be associated to affected elements.
11067   \return TRUE if operation has been completed successfully, FALSE otherwise
11068 */
11069 //================================================================================
11070
11071 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11072                                             const TIDSortedElemSet& theNodesNot,
11073                                             const TopoDS_Shape&     theShape )
11074 {
11075   if ( theShape.IsNull() )
11076     return false;
11077
11078   const double aTol = Precision::Confusion();
11079   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11080   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
11081   if ( theShape.ShapeType() == TopAbs_SOLID )
11082   {
11083     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11084     bsc3d->PerformInfinitePoint(aTol);
11085   }
11086   else if (theShape.ShapeType() == TopAbs_FACE )
11087   {
11088     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11089   }
11090
11091   // iterates on indicated elements and get elements by back references from their nodes
11092   TIDSortedElemSet anAffected;
11093   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11094   for ( ;  elemItr != theElems.end(); ++elemItr )
11095   {
11096     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11097     if (!anElem)
11098       continue;
11099
11100     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11101     while ( nodeItr->more() )
11102     {
11103       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11104       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11105         continue;
11106       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11107       while ( backElemItr->more() )
11108       {
11109         const SMDS_MeshElement* curElem = backElemItr->next();
11110         if ( curElem && theElems.find(curElem) == theElems.end() &&
11111              ( bsc3d ?
11112                isInside( curElem, *bsc3d, aTol ) :
11113                isInside( curElem, *aFaceClassifier, aTol )))
11114           anAffected.insert( curElem );
11115       }
11116     }
11117   }
11118   return DoubleNodes( theElems, theNodesNot, anAffected );
11119 }
11120
11121 /*!
11122  *  \brief compute an oriented angle between two planes defined by four points.
11123  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11124  *  @param p0 base of the rotation axe
11125  *  @param p1 extremity of the rotation axe
11126  *  @param g1 belongs to the first plane
11127  *  @param g2 belongs to the second plane
11128  */
11129 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11130 {
11131 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11132 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11133 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11134 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11135   gp_Vec vref(p0, p1);
11136   gp_Vec v1(p0, g1);
11137   gp_Vec v2(p0, g2);
11138   gp_Vec n1 = vref.Crossed(v1);
11139   gp_Vec n2 = vref.Crossed(v2);
11140   try {
11141     return n2.AngleWithRef(n1, vref);
11142   }
11143   catch ( Standard_Failure ) {
11144   }
11145   return Max( v1.Magnitude(), v2.Magnitude() );
11146 }
11147
11148 /*!
11149  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11150  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11151  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11152  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11153  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11154  * 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.
11155  * 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.
11156  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11157  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11158  * \param theElems - list of groups of volumes, where a group of volume is a set of
11159  *        SMDS_MeshElements sorted by Id.
11160  * \param createJointElems - if TRUE, create the elements
11161  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11162  *        the boundary between \a theDomains and the rest mesh
11163  * \return TRUE if operation has been completed successfully, FALSE otherwise
11164  */
11165 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11166                                                      bool                                 createJointElems,
11167                                                      bool                                 onAllBoundaries)
11168 {
11169   MESSAGE("----------------------------------------------");
11170   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11171   MESSAGE("----------------------------------------------");
11172
11173   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11174   meshDS->BuildDownWardConnectivity(true);
11175   CHRONO(50);
11176   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11177
11178   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11179   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11180   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11181
11182   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11183   std::map<int,int>celldom; // cell vtkId --> domain
11184   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11185   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11186   faceDomains.clear();
11187   celldom.clear();
11188   cellDomains.clear();
11189   nodeDomains.clear();
11190   std::map<int,int> emptyMap;
11191   std::set<int> emptySet;
11192   emptyMap.clear();
11193
11194   MESSAGE(".. Number of domains :"<<theElems.size());
11195
11196   TIDSortedElemSet theRestDomElems;
11197   const int iRestDom  = -1;
11198   const int idom0     = onAllBoundaries ? iRestDom : 0;
11199   const int nbDomains = theElems.size();
11200
11201   // Check if the domains do not share an element
11202   for (int idom = 0; idom < nbDomains-1; idom++)
11203   {
11204     //       MESSAGE("... Check of domain #" << idom);
11205     const TIDSortedElemSet& domain = theElems[idom];
11206     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11207     for (; elemItr != domain.end(); ++elemItr)
11208     {
11209       const SMDS_MeshElement* anElem = *elemItr;
11210       int idombisdeb = idom + 1 ;
11211       // check if the element belongs to a domain further in the list
11212       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11213       {
11214         const TIDSortedElemSet& domainbis = theElems[idombis];
11215         if ( domainbis.count( anElem ))
11216         {
11217           MESSAGE(".... Domain #" << idom);
11218           MESSAGE(".... Domain #" << idombis);
11219           throw SALOME_Exception("The domains are not disjoint.");
11220           return false ;
11221         }
11222       }
11223     }
11224   }
11225
11226   for (int idom = 0; idom < nbDomains; idom++)
11227   {
11228
11229     // --- build a map (face to duplicate --> volume to modify)
11230     //     with all the faces shared by 2 domains (group of elements)
11231     //     and corresponding volume of this domain, for each shared face.
11232     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11233
11234     MESSAGE("... Neighbors of domain #" << idom);
11235     const TIDSortedElemSet& domain = theElems[idom];
11236     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11237     for (; elemItr != domain.end(); ++elemItr)
11238     {
11239       const SMDS_MeshElement* anElem = *elemItr;
11240       if (!anElem)
11241         continue;
11242       int vtkId = anElem->getVtkId();
11243       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11244       int neighborsVtkIds[NBMAXNEIGHBORS];
11245       int downIds[NBMAXNEIGHBORS];
11246       unsigned char downTypes[NBMAXNEIGHBORS];
11247       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11248       for (int n = 0; n < nbNeighbors; n++)
11249       {
11250         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11251         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11252         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11253         {
11254           bool ok = false;
11255           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11256           {
11257             // MESSAGE("Domain " << idombis);
11258             const TIDSortedElemSet& domainbis = theElems[idombis];
11259             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11260           }
11261           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11262           {
11263             DownIdType face(downIds[n], downTypes[n]);
11264             if (!faceDomains[face].count(idom))
11265             {
11266               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11267               celldom[vtkId] = idom;
11268               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11269             }
11270             if ( !ok )
11271             {
11272               theRestDomElems.insert( elem );
11273               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11274               celldom[neighborsVtkIds[n]] = iRestDom;
11275             }
11276           }
11277         }
11278       }
11279     }
11280   }
11281
11282   //MESSAGE("Number of shared faces " << faceDomains.size());
11283   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11284
11285   // --- explore the shared faces domain by domain,
11286   //     explore the nodes of the face and see if they belong to a cell in the domain,
11287   //     which has only a node or an edge on the border (not a shared face)
11288
11289   for (int idomain = idom0; idomain < nbDomains; idomain++)
11290   {
11291     //MESSAGE("Domain " << idomain);
11292     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11293     itface = faceDomains.begin();
11294     for (; itface != faceDomains.end(); ++itface)
11295     {
11296       const std::map<int, int>& domvol = itface->second;
11297       if (!domvol.count(idomain))
11298         continue;
11299       DownIdType face = itface->first;
11300       //MESSAGE(" --- face " << face.cellId);
11301       std::set<int> oldNodes;
11302       oldNodes.clear();
11303       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11304       std::set<int>::iterator itn = oldNodes.begin();
11305       for (; itn != oldNodes.end(); ++itn)
11306       {
11307         int oldId = *itn;
11308         //MESSAGE("     node " << oldId);
11309         vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11310         for (int i=0; i<l.ncells; i++)
11311         {
11312           int vtkId = l.cells[i];
11313           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11314           if (!domain.count(anElem))
11315             continue;
11316           int vtkType = grid->GetCellType(vtkId);
11317           int downId = grid->CellIdToDownId(vtkId);
11318           if (downId < 0)
11319           {
11320             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11321             continue; // not OK at this stage of the algorithm:
11322             //no cells created after BuildDownWardConnectivity
11323           }
11324           DownIdType aCell(downId, vtkType);
11325           cellDomains[aCell][idomain] = vtkId;
11326           celldom[vtkId] = idomain;
11327           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11328         }
11329       }
11330     }
11331   }
11332
11333   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11334   //     for each shared face, get the nodes
11335   //     for each node, for each domain of the face, create a clone of the node
11336
11337   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11338   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11339   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11340
11341   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11342   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11343   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11344
11345   MESSAGE(".. Duplication of the nodes");
11346   for (int idomain = idom0; idomain < nbDomains; idomain++)
11347   {
11348     itface = faceDomains.begin();
11349     for (; itface != faceDomains.end(); ++itface)
11350     {
11351       const std::map<int, int>& domvol = itface->second;
11352       if (!domvol.count(idomain))
11353         continue;
11354       DownIdType face = itface->first;
11355       //MESSAGE(" --- face " << face.cellId);
11356       std::set<int> oldNodes;
11357       oldNodes.clear();
11358       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11359       std::set<int>::iterator itn = oldNodes.begin();
11360       for (; itn != oldNodes.end(); ++itn)
11361       {
11362         int oldId = *itn;
11363         if (nodeDomains[oldId].empty())
11364         {
11365           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11366           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11367         }
11368         std::map<int, int>::const_iterator itdom = domvol.begin();
11369         for (; itdom != domvol.end(); ++itdom)
11370         {
11371           int idom = itdom->first;
11372           //MESSAGE("         domain " << idom);
11373           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11374           {
11375             if (nodeDomains[oldId].size() >= 2) // a multiple node
11376             {
11377               vector<int> orderedDoms;
11378               //MESSAGE("multiple node " << oldId);
11379               if (mutipleNodes.count(oldId))
11380                 orderedDoms = mutipleNodes[oldId];
11381               else
11382               {
11383                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11384                 for (; it != nodeDomains[oldId].end(); ++it)
11385                   orderedDoms.push_back(it->first);
11386               }
11387               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11388               //stringstream txt;
11389               //for (int i=0; i<orderedDoms.size(); i++)
11390               //  txt << orderedDoms[i] << " ";
11391               //MESSAGE("orderedDoms " << txt.str());
11392               mutipleNodes[oldId] = orderedDoms;
11393             }
11394             double *coords = grid->GetPoint(oldId);
11395             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11396             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11397             int newId = newNode->getVtkId();
11398             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11399             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11400           }
11401         }
11402       }
11403     }
11404   }
11405
11406   MESSAGE(".. Creation of elements");
11407   for (int idomain = idom0; idomain < nbDomains; idomain++)
11408   {
11409     itface = faceDomains.begin();
11410     for (; itface != faceDomains.end(); ++itface)
11411     {
11412       std::map<int, int> domvol = itface->second;
11413       if (!domvol.count(idomain))
11414         continue;
11415       DownIdType face = itface->first;
11416       //MESSAGE(" --- face " << face.cellId);
11417       std::set<int> oldNodes;
11418       oldNodes.clear();
11419       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11420       int nbMultipleNodes = 0;
11421       std::set<int>::iterator itn = oldNodes.begin();
11422       for (; itn != oldNodes.end(); ++itn)
11423       {
11424         int oldId = *itn;
11425         if (mutipleNodes.count(oldId))
11426           nbMultipleNodes++;
11427       }
11428       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11429       {
11430         //MESSAGE("multiple Nodes detected on a shared face");
11431         int downId = itface->first.cellId;
11432         unsigned char cellType = itface->first.cellType;
11433         // --- shared edge or shared face ?
11434         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11435         {
11436           int nodes[3];
11437           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11438           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11439             if (mutipleNodes.count(nodes[i]))
11440               if (!mutipleNodesToFace.count(nodes[i]))
11441                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11442         }
11443         else // shared face (between two volumes)
11444         {
11445           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11446           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11447           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11448           for (int ie =0; ie < nbEdges; ie++)
11449           {
11450             int nodes[3];
11451             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11452             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11453             {
11454               vector<int> vn0 = mutipleNodes[nodes[0]];
11455               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11456               vector<int> doms;
11457               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11458                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11459                   if ( vn0[i0] == vn1[i1] )
11460                     doms.push_back( vn0[ i0 ]);
11461               if ( doms.size() > 2 )
11462               {
11463                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11464                 double *coords = grid->GetPoint(nodes[0]);
11465                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11466                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11467                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11468                 gp_Pnt gref;
11469                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11470                 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11471                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11472                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11473                 for ( size_t id = 0; id < doms.size(); id++ )
11474                 {
11475                   int idom = doms[id];
11476                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11477                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11478                   {
11479                     int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11480                     SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11481                     if (domain.count(elem))
11482                     {
11483                       SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11484                       domvol[idom] = svol;
11485                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11486                       double values[3];
11487                       vtkIdType npts = 0;
11488                       vtkIdType* pts = 0;
11489                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11490                       SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11491                       if (id ==0)
11492                       {
11493                         gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11494                         angleDom[idom] = 0;
11495                       }
11496                       else
11497                       {
11498                         gp_Pnt g(values[0], values[1], values[2]);
11499                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11500                         //MESSAGE("  angle=" << angleDom[idom]);
11501                       }
11502                       break;
11503                     }
11504                   }
11505                 }
11506                 map<double, int> sortedDom; // sort domains by angle
11507                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11508                   sortedDom[ia->second] = ia->first;
11509                 vector<int> vnodes;
11510                 vector<int> vdom;
11511                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11512                 {
11513                   vdom.push_back(ib->second);
11514                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11515                 }
11516                 for (int ino = 0; ino < nbNodes; ino++)
11517                   vnodes.push_back(nodes[ino]);
11518                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11519               }
11520             }
11521           }
11522         }
11523       }
11524     }
11525   }
11526
11527   // --- iterate on shared faces (volumes to modify, face to extrude)
11528   //     get node id's of the face (id SMDS = id VTK)
11529   //     create flat element with old and new nodes if requested
11530
11531   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11532   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11533
11534   std::map<int, std::map<long,int> > nodeQuadDomains;
11535   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11536
11537   MESSAGE(".. Creation of elements: simple junction");
11538   if (createJointElems)
11539   {
11540     int idg;
11541     string joints2DName = "joints2D";
11542     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11543     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11544     string joints3DName = "joints3D";
11545     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11546     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11547
11548     itface = faceDomains.begin();
11549     for (; itface != faceDomains.end(); ++itface)
11550     {
11551       DownIdType face = itface->first;
11552       std::set<int> oldNodes;
11553       std::set<int>::iterator itn;
11554       oldNodes.clear();
11555       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11556
11557       std::map<int, int> domvol = itface->second;
11558       std::map<int, int>::iterator itdom = domvol.begin();
11559       int dom1 = itdom->first;
11560       int vtkVolId = itdom->second;
11561       itdom++;
11562       int dom2 = itdom->first;
11563       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11564                                                        nodeQuadDomains);
11565       stringstream grpname;
11566       grpname << "j_";
11567       if (dom1 < dom2)
11568         grpname << dom1 << "_" << dom2;
11569       else
11570         grpname << dom2 << "_" << dom1;
11571       string namegrp = grpname.str();
11572       if (!mapOfJunctionGroups.count(namegrp))
11573         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11574       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11575       if (sgrp)
11576         sgrp->Add(vol->GetID());
11577       if (vol->GetType() == SMDSAbs_Volume)
11578         joints3DGrp->Add(vol->GetID());
11579       else if (vol->GetType() == SMDSAbs_Face)
11580         joints2DGrp->Add(vol->GetID());
11581     }
11582   }
11583
11584   // --- create volumes on multiple domain intersection if requested
11585   //     iterate on mutipleNodesToFace
11586   //     iterate on edgesMultiDomains
11587
11588   MESSAGE(".. Creation of elements: multiple junction");
11589   if (createJointElems)
11590   {
11591     // --- iterate on mutipleNodesToFace
11592
11593     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11594     for (; itn != mutipleNodesToFace.end(); ++itn)
11595     {
11596       int node = itn->first;
11597       vector<int> orderDom = itn->second;
11598       vector<vtkIdType> orderedNodes;
11599       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11600         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11601       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11602
11603       stringstream grpname;
11604       grpname << "m2j_";
11605       grpname << 0 << "_" << 0;
11606       int idg;
11607       string namegrp = grpname.str();
11608       if (!mapOfJunctionGroups.count(namegrp))
11609         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11610       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11611       if (sgrp)
11612         sgrp->Add(face->GetID());
11613     }
11614
11615     // --- iterate on edgesMultiDomains
11616
11617     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11618     for (; ite != edgesMultiDomains.end(); ++ite)
11619     {
11620       vector<int> nodes = ite->first;
11621       vector<int> orderDom = ite->second;
11622       vector<vtkIdType> orderedNodes;
11623       if (nodes.size() == 2)
11624       {
11625         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11626         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11627           if ( orderDom.size() == 3 )
11628             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11629               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11630           else
11631             for (int idom = orderDom.size()-1; idom >=0; idom--)
11632               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11633         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11634
11635         int idg;
11636         string namegrp = "jointsMultiples";
11637         if (!mapOfJunctionGroups.count(namegrp))
11638           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11639         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11640         if (sgrp)
11641           sgrp->Add(vol->GetID());
11642       }
11643       else
11644       {
11645         //INFOS("Quadratic multiple joints not implemented");
11646         // TODO quadratic nodes
11647       }
11648     }
11649   }
11650
11651   // --- list the explicit faces and edges of the mesh that need to be modified,
11652   //     i.e. faces and edges built with one or more duplicated nodes.
11653   //     associate these faces or edges to their corresponding domain.
11654   //     only the first domain found is kept when a face or edge is shared
11655
11656   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11657   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11658   faceOrEdgeDom.clear();
11659   feDom.clear();
11660
11661   MESSAGE(".. Modification of elements");
11662   for (int idomain = idom0; idomain < nbDomains; idomain++)
11663   {
11664     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11665     for (; itnod != nodeDomains.end(); ++itnod)
11666     {
11667       int oldId = itnod->first;
11668       //MESSAGE("     node " << oldId);
11669       vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11670       for (int i = 0; i < l.ncells; i++)
11671       {
11672         int vtkId = l.cells[i];
11673         int vtkType = grid->GetCellType(vtkId);
11674         int downId = grid->CellIdToDownId(vtkId);
11675         if (downId < 0)
11676           continue; // new cells: not to be modified
11677         DownIdType aCell(downId, vtkType);
11678         int volParents[1000];
11679         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11680         for (int j = 0; j < nbvol; j++)
11681           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11682             if (!feDom.count(vtkId))
11683             {
11684               feDom[vtkId] = idomain;
11685               faceOrEdgeDom[aCell] = emptyMap;
11686               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11687               //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11688               //        << " type " << vtkType << " downId " << downId);
11689             }
11690       }
11691     }
11692   }
11693
11694   // --- iterate on shared faces (volumes to modify, face to extrude)
11695   //     get node id's of the face
11696   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11697
11698   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11699   for (int m=0; m<3; m++)
11700   {
11701     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11702     itface = (*amap).begin();
11703     for (; itface != (*amap).end(); ++itface)
11704     {
11705       DownIdType face = itface->first;
11706       std::set<int> oldNodes;
11707       std::set<int>::iterator itn;
11708       oldNodes.clear();
11709       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11710       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11711       std::map<int, int> localClonedNodeIds;
11712
11713       std::map<int, int> domvol = itface->second;
11714       std::map<int, int>::iterator itdom = domvol.begin();
11715       for (; itdom != domvol.end(); ++itdom)
11716       {
11717         int idom = itdom->first;
11718         int vtkVolId = itdom->second;
11719         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11720         localClonedNodeIds.clear();
11721         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11722         {
11723           int oldId = *itn;
11724           if (nodeDomains[oldId].count(idom))
11725           {
11726             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11727             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11728           }
11729         }
11730         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11731       }
11732     }
11733   }
11734
11735   // Remove empty groups (issue 0022812)
11736   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11737   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11738   {
11739     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11740       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11741   }
11742
11743   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11744   grid->BuildLinks();
11745
11746   CHRONOSTOP(50);
11747   counters::stats();
11748   return true;
11749 }
11750
11751 /*!
11752  * \brief Double nodes on some external faces and create flat elements.
11753  * Flat elements are mainly used by some types of mechanic calculations.
11754  *
11755  * Each group of the list must be constituted of faces.
11756  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11757  * @param theElems - list of groups of faces, where a group of faces is a set of
11758  * SMDS_MeshElements sorted by Id.
11759  * @return TRUE if operation has been completed successfully, FALSE otherwise
11760  */
11761 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11762 {
11763   MESSAGE("-------------------------------------------------");
11764   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11765   MESSAGE("-------------------------------------------------");
11766
11767   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11768
11769   // --- For each group of faces
11770   //     duplicate the nodes, create a flat element based on the face
11771   //     replace the nodes of the faces by their clones
11772
11773   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11774   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11775   clonedNodes.clear();
11776   intermediateNodes.clear();
11777   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11778   mapOfJunctionGroups.clear();
11779
11780   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11781   {
11782     const TIDSortedElemSet&           domain = theElems[idom];
11783     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11784     for ( ; elemItr != domain.end(); ++elemItr )
11785     {
11786       SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11787       SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11788       if (!aFace)
11789         continue;
11790       // MESSAGE("aFace=" << aFace->GetID());
11791       bool isQuad = aFace->IsQuadratic();
11792       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11793
11794       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11795
11796       SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11797       while (nodeIt->more())
11798       {
11799         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11800         bool isMedium = isQuad && (aFace->IsMediumNode(node));
11801         if (isMedium)
11802           ln2.push_back(node);
11803         else
11804           ln0.push_back(node);
11805
11806         const SMDS_MeshNode* clone = 0;
11807         if (!clonedNodes.count(node))
11808         {
11809           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11810           copyPosition( node, clone );
11811           clonedNodes[node] = clone;
11812         }
11813         else
11814           clone = clonedNodes[node];
11815
11816         if (isMedium)
11817           ln3.push_back(clone);
11818         else
11819           ln1.push_back(clone);
11820
11821         const SMDS_MeshNode* inter = 0;
11822         if (isQuad && (!isMedium))
11823         {
11824           if (!intermediateNodes.count(node))
11825           {
11826             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11827             copyPosition( node, inter );
11828             intermediateNodes[node] = inter;
11829           }
11830           else
11831             inter = intermediateNodes[node];
11832           ln4.push_back(inter);
11833         }
11834       }
11835
11836       // --- extrude the face
11837
11838       vector<const SMDS_MeshNode*> ln;
11839       SMDS_MeshVolume* vol = 0;
11840       vtkIdType aType = aFace->GetVtkType();
11841       switch (aType)
11842       {
11843       case VTK_TRIANGLE:
11844         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11845         // MESSAGE("vol prism " << vol->GetID());
11846         ln.push_back(ln1[0]);
11847         ln.push_back(ln1[1]);
11848         ln.push_back(ln1[2]);
11849         break;
11850       case VTK_QUAD:
11851         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11852         // MESSAGE("vol hexa " << vol->GetID());
11853         ln.push_back(ln1[0]);
11854         ln.push_back(ln1[1]);
11855         ln.push_back(ln1[2]);
11856         ln.push_back(ln1[3]);
11857         break;
11858       case VTK_QUADRATIC_TRIANGLE:
11859         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11860                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11861         // MESSAGE("vol quad prism " << vol->GetID());
11862         ln.push_back(ln1[0]);
11863         ln.push_back(ln1[1]);
11864         ln.push_back(ln1[2]);
11865         ln.push_back(ln3[0]);
11866         ln.push_back(ln3[1]);
11867         ln.push_back(ln3[2]);
11868         break;
11869       case VTK_QUADRATIC_QUAD:
11870         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11871         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11872         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11873         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11874                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11875                                 ln4[0], ln4[1], ln4[2], ln4[3]);
11876         // MESSAGE("vol quad hexa " << vol->GetID());
11877         ln.push_back(ln1[0]);
11878         ln.push_back(ln1[1]);
11879         ln.push_back(ln1[2]);
11880         ln.push_back(ln1[3]);
11881         ln.push_back(ln3[0]);
11882         ln.push_back(ln3[1]);
11883         ln.push_back(ln3[2]);
11884         ln.push_back(ln3[3]);
11885         break;
11886       case VTK_POLYGON:
11887         break;
11888       default:
11889         break;
11890       }
11891
11892       if (vol)
11893       {
11894         stringstream grpname;
11895         grpname << "jf_";
11896         grpname << idom;
11897         int idg;
11898         string namegrp = grpname.str();
11899         if (!mapOfJunctionGroups.count(namegrp))
11900           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11901         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11902         if (sgrp)
11903           sgrp->Add(vol->GetID());
11904       }
11905
11906       // --- modify the face
11907
11908       aFace->ChangeNodes(&ln[0], ln.size());
11909     }
11910   }
11911   return true;
11912 }
11913
11914 /*!
11915  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11916  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11917  *  groups of faces to remove inside the object, (idem edges).
11918  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11919  */
11920 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
11921                                       const TopoDS_Shape&             theShape,
11922                                       SMESH_NodeSearcher*             theNodeSearcher,
11923                                       const char*                     groupName,
11924                                       std::vector<double>&            nodesCoords,
11925                                       std::vector<std::vector<int> >& listOfListOfNodes)
11926 {
11927   MESSAGE("--------------------------------");
11928   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11929   MESSAGE("--------------------------------");
11930
11931   // --- zone of volumes to remove is given :
11932   //     1 either by a geom shape (one or more vertices) and a radius,
11933   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11934   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11935   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11936   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11937   //     defined by it's name.
11938
11939   SMESHDS_GroupBase* groupDS = 0;
11940   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11941   while ( groupIt->more() )
11942   {
11943     groupDS = 0;
11944     SMESH_Group * group = groupIt->next();
11945     if ( !group ) continue;
11946     groupDS = group->GetGroupDS();
11947     if ( !groupDS || groupDS->IsEmpty() ) continue;
11948     std::string grpName = group->GetName();
11949     //MESSAGE("grpName=" << grpName);
11950     if (grpName == groupName)
11951       break;
11952     else
11953       groupDS = 0;
11954   }
11955
11956   bool isNodeGroup = false;
11957   bool isNodeCoords = false;
11958   if (groupDS)
11959   {
11960     if (groupDS->GetType() != SMDSAbs_Node)
11961       return;
11962     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11963   }
11964
11965   if (nodesCoords.size() > 0)
11966     isNodeCoords = true; // a list o nodes given by their coordinates
11967   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11968
11969   // --- define groups to build
11970
11971   int idg; // --- group of SMDS volumes
11972   string grpvName = groupName;
11973   grpvName += "_vol";
11974   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11975   if (!grp)
11976   {
11977     MESSAGE("group not created " << grpvName);
11978     return;
11979   }
11980   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11981
11982   int idgs; // --- group of SMDS faces on the skin
11983   string grpsName = groupName;
11984   grpsName += "_skin";
11985   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11986   if (!grps)
11987   {
11988     MESSAGE("group not created " << grpsName);
11989     return;
11990   }
11991   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11992
11993   int idgi; // --- group of SMDS faces internal (several shapes)
11994   string grpiName = groupName;
11995   grpiName += "_internalFaces";
11996   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11997   if (!grpi)
11998   {
11999     MESSAGE("group not created " << grpiName);
12000     return;
12001   }
12002   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12003
12004   int idgei; // --- group of SMDS faces internal (several shapes)
12005   string grpeiName = groupName;
12006   grpeiName += "_internalEdges";
12007   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12008   if (!grpei)
12009   {
12010     MESSAGE("group not created " << grpeiName);
12011     return;
12012   }
12013   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12014
12015   // --- build downward connectivity
12016
12017   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12018   meshDS->BuildDownWardConnectivity(true);
12019   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12020
12021   // --- set of volumes detected inside
12022
12023   std::set<int> setOfInsideVol;
12024   std::set<int> setOfVolToCheck;
12025
12026   std::vector<gp_Pnt> gpnts;
12027   gpnts.clear();
12028
12029   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12030   {
12031     MESSAGE("group of nodes provided");
12032     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12033     while ( elemIt->more() )
12034     {
12035       const SMDS_MeshElement* elem = elemIt->next();
12036       if (!elem)
12037         continue;
12038       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12039       if (!node)
12040         continue;
12041       SMDS_MeshElement* vol = 0;
12042       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12043       while (volItr->more())
12044       {
12045         vol = (SMDS_MeshElement*)volItr->next();
12046         setOfInsideVol.insert(vol->getVtkId());
12047         sgrp->Add(vol->GetID());
12048       }
12049     }
12050   }
12051   else if (isNodeCoords)
12052   {
12053     MESSAGE("list of nodes coordinates provided");
12054     size_t i = 0;
12055     int k = 0;
12056     while ( i < nodesCoords.size()-2 )
12057     {
12058       double x = nodesCoords[i++];
12059       double y = nodesCoords[i++];
12060       double z = nodesCoords[i++];
12061       gp_Pnt p = gp_Pnt(x, y ,z);
12062       gpnts.push_back(p);
12063       MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12064       k++;
12065     }
12066   }
12067   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12068   {
12069     MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12070     TopTools_IndexedMapOfShape vertexMap;
12071     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12072     gp_Pnt p = gp_Pnt(0,0,0);
12073     if (vertexMap.Extent() < 1)
12074       return;
12075
12076     for ( int i = 1; i <= vertexMap.Extent(); ++i )
12077     {
12078       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12079       p = BRep_Tool::Pnt(vertex);
12080       gpnts.push_back(p);
12081       MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12082     }
12083   }
12084
12085   if (gpnts.size() > 0)
12086   {
12087     int nodeId = 0;
12088     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12089     if (startNode)
12090       nodeId = startNode->GetID();
12091     MESSAGE("nodeId " << nodeId);
12092
12093     double radius2 = radius*radius;
12094     MESSAGE("radius2 " << radius2);
12095
12096     // --- volumes on start node
12097
12098     setOfVolToCheck.clear();
12099     SMDS_MeshElement* startVol = 0;
12100     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12101     while (volItr->more())
12102     {
12103       startVol = (SMDS_MeshElement*)volItr->next();
12104       setOfVolToCheck.insert(startVol->getVtkId());
12105     }
12106     if (setOfVolToCheck.empty())
12107     {
12108       MESSAGE("No volumes found");
12109       return;
12110     }
12111
12112     // --- starting with central volumes then their neighbors, check if they are inside
12113     //     or outside the domain, until no more new neighbor volume is inside.
12114     //     Fill the group of inside volumes
12115
12116     std::map<int, double> mapOfNodeDistance2;
12117     mapOfNodeDistance2.clear();
12118     std::set<int> setOfOutsideVol;
12119     while (!setOfVolToCheck.empty())
12120     {
12121       std::set<int>::iterator it = setOfVolToCheck.begin();
12122       int vtkId = *it;
12123       MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12124       bool volInside = false;
12125       vtkIdType npts = 0;
12126       vtkIdType* pts = 0;
12127       grid->GetCellPoints(vtkId, npts, pts);
12128       for (int i=0; i<npts; i++)
12129       {
12130         double distance2 = 0;
12131         if (mapOfNodeDistance2.count(pts[i]))
12132         {
12133           distance2 = mapOfNodeDistance2[pts[i]];
12134           MESSAGE("point " << pts[i] << " distance2 " << distance2);
12135         }
12136         else
12137         {
12138           double *coords = grid->GetPoint(pts[i]);
12139           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12140           distance2 = 1.E40;
12141           for ( size_t j = 0; j < gpnts.size(); j++ )
12142           {
12143             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12144             if (d2 < distance2)
12145             {
12146               distance2 = d2;
12147               if (distance2 < radius2)
12148                 break;
12149             }
12150           }
12151           mapOfNodeDistance2[pts[i]] = distance2;
12152           MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12153         }
12154         if (distance2 < radius2)
12155         {
12156           volInside = true; // one or more nodes inside the domain
12157           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12158           break;
12159         }
12160       }
12161       if (volInside)
12162       {
12163         setOfInsideVol.insert(vtkId);
12164         MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12165         int neighborsVtkIds[NBMAXNEIGHBORS];
12166         int downIds[NBMAXNEIGHBORS];
12167         unsigned char downTypes[NBMAXNEIGHBORS];
12168         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12169         for (int n = 0; n < nbNeighbors; n++)
12170           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12171             setOfVolToCheck.insert(neighborsVtkIds[n]);
12172       }
12173       else
12174       {
12175         setOfOutsideVol.insert(vtkId);
12176         MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12177       }
12178       setOfVolToCheck.erase(vtkId);
12179     }
12180   }
12181
12182   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12183   //     If yes, add the volume to the inside set
12184
12185   bool addedInside = true;
12186   std::set<int> setOfVolToReCheck;
12187   while (addedInside)
12188   {
12189     MESSAGE(" --------------------------- re check");
12190     addedInside = false;
12191     std::set<int>::iterator itv = setOfInsideVol.begin();
12192     for (; itv != setOfInsideVol.end(); ++itv)
12193     {
12194       int vtkId = *itv;
12195       int neighborsVtkIds[NBMAXNEIGHBORS];
12196       int downIds[NBMAXNEIGHBORS];
12197       unsigned char downTypes[NBMAXNEIGHBORS];
12198       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12199       for (int n = 0; n < nbNeighbors; n++)
12200         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12201           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12202     }
12203     setOfVolToCheck = setOfVolToReCheck;
12204     setOfVolToReCheck.clear();
12205     while  (!setOfVolToCheck.empty())
12206     {
12207       std::set<int>::iterator it = setOfVolToCheck.begin();
12208       int vtkId = *it;
12209       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12210       {
12211         MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12212         int countInside = 0;
12213         int neighborsVtkIds[NBMAXNEIGHBORS];
12214         int downIds[NBMAXNEIGHBORS];
12215         unsigned char downTypes[NBMAXNEIGHBORS];
12216         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12217         for (int n = 0; n < nbNeighbors; n++)
12218           if (setOfInsideVol.count(neighborsVtkIds[n]))
12219             countInside++;
12220         MESSAGE("countInside " << countInside);
12221         if (countInside > 1)
12222         {
12223           MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12224           setOfInsideVol.insert(vtkId);
12225           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12226           addedInside = true;
12227         }
12228         else
12229           setOfVolToReCheck.insert(vtkId);
12230       }
12231       setOfVolToCheck.erase(vtkId);
12232     }
12233   }
12234
12235   // --- map of Downward faces at the boundary, inside the global volume
12236   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12237   //     fill group of SMDS faces inside the volume (when several volume shapes)
12238   //     fill group of SMDS faces on the skin of the global volume (if skin)
12239
12240   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12241   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12242   std::set<int>::iterator it = setOfInsideVol.begin();
12243   for (; it != setOfInsideVol.end(); ++it)
12244   {
12245     int vtkId = *it;
12246     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12247     int neighborsVtkIds[NBMAXNEIGHBORS];
12248     int downIds[NBMAXNEIGHBORS];
12249     unsigned char downTypes[NBMAXNEIGHBORS];
12250     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12251     for (int n = 0; n < nbNeighbors; n++)
12252     {
12253       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12254       if (neighborDim == 3)
12255       {
12256         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12257         {
12258           DownIdType face(downIds[n], downTypes[n]);
12259           boundaryFaces[face] = vtkId;
12260         }
12261         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12262         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12263         if (vtkFaceId >= 0)
12264         {
12265           sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12266           // find also the smds edges on this face
12267           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12268           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12269           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12270           for (int i = 0; i < nbEdges; i++)
12271           {
12272             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12273             if (vtkEdgeId >= 0)
12274               sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12275           }
12276         }
12277       }
12278       else if (neighborDim == 2) // skin of the volume
12279       {
12280         DownIdType face(downIds[n], downTypes[n]);
12281         skinFaces[face] = vtkId;
12282         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12283         if (vtkFaceId >= 0)
12284           sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12285       }
12286     }
12287   }
12288
12289   // --- identify the edges constituting the wire of each subshape on the skin
12290   //     define polylines with the nodes of edges, equivalent to wires
12291   //     project polylines on subshapes, and partition, to get geom faces
12292
12293   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12294   std::set<int> emptySet;
12295   emptySet.clear();
12296   std::set<int> shapeIds;
12297
12298   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12299   while (itelem->more())
12300   {
12301     const SMDS_MeshElement *elem = itelem->next();
12302     int shapeId = elem->getshapeId();
12303     int vtkId = elem->getVtkId();
12304     if (!shapeIdToVtkIdSet.count(shapeId))
12305     {
12306       shapeIdToVtkIdSet[shapeId] = emptySet;
12307       shapeIds.insert(shapeId);
12308     }
12309     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12310   }
12311
12312   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12313   std::set<DownIdType, DownIdCompare> emptyEdges;
12314   emptyEdges.clear();
12315
12316   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12317   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12318   {
12319     int shapeId = itShape->first;
12320     MESSAGE(" --- Shape ID --- "<< shapeId);
12321     shapeIdToEdges[shapeId] = emptyEdges;
12322
12323     std::vector<int> nodesEdges;
12324
12325     std::set<int>::iterator its = itShape->second.begin();
12326     for (; its != itShape->second.end(); ++its)
12327     {
12328       int vtkId = *its;
12329       MESSAGE("     " << vtkId);
12330       int neighborsVtkIds[NBMAXNEIGHBORS];
12331       int downIds[NBMAXNEIGHBORS];
12332       unsigned char downTypes[NBMAXNEIGHBORS];
12333       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12334       for (int n = 0; n < nbNeighbors; n++)
12335       {
12336         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12337           continue;
12338         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12339         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12340         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12341         {
12342           DownIdType edge(downIds[n], downTypes[n]);
12343           if (!shapeIdToEdges[shapeId].count(edge))
12344           {
12345             shapeIdToEdges[shapeId].insert(edge);
12346             int vtkNodeId[3];
12347             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12348             nodesEdges.push_back(vtkNodeId[0]);
12349             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12350             MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12351           }
12352         }
12353       }
12354     }
12355
12356     std::list<int> order;
12357     order.clear();
12358     if (nodesEdges.size() > 0)
12359     {
12360       order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12361       nodesEdges[0] = -1;
12362       order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
12363       nodesEdges[1] = -1; // do not reuse this edge
12364       bool found = true;
12365       while (found)
12366       {
12367         int nodeTofind = order.back(); // try first to push back
12368         int i = 0;
12369         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12370           if (nodesEdges[i] == nodeTofind)
12371             break;
12372         if ( i == (int) nodesEdges.size() )
12373           found = false; // no follower found on back
12374         else
12375         {
12376           if (i%2) // odd ==> use the previous one
12377             if (nodesEdges[i-1] < 0)
12378               found = false;
12379             else
12380             {
12381               order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
12382               nodesEdges[i-1] = -1;
12383             }
12384           else // even ==> use the next one
12385             if (nodesEdges[i+1] < 0)
12386               found = false;
12387             else
12388             {
12389               order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
12390               nodesEdges[i+1] = -1;
12391             }
12392         }
12393         if (found)
12394           continue;
12395         // try to push front
12396         found = true;
12397         nodeTofind = order.front(); // try to push front
12398         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12399           if ( nodesEdges[i] == nodeTofind )
12400             break;
12401         if ( i == (int)nodesEdges.size() )
12402         {
12403           found = false; // no predecessor found on front
12404           continue;
12405         }
12406         if (i%2) // odd ==> use the previous one
12407           if (nodesEdges[i-1] < 0)
12408             found = false;
12409           else
12410           {
12411             order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
12412             nodesEdges[i-1] = -1;
12413           }
12414         else // even ==> use the next one
12415           if (nodesEdges[i+1] < 0)
12416             found = false;
12417           else
12418           {
12419             order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
12420             nodesEdges[i+1] = -1;
12421           }
12422       }
12423     }
12424
12425
12426     std::vector<int> nodes;
12427     nodes.push_back(shapeId);
12428     std::list<int>::iterator itl = order.begin();
12429     for (; itl != order.end(); itl++)
12430     {
12431       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12432       MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12433     }
12434     listOfListOfNodes.push_back(nodes);
12435   }
12436
12437   //     partition geom faces with blocFissure
12438   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12439   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12440
12441   return;
12442 }
12443
12444
12445 //================================================================================
12446 /*!
12447  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12448  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12449  * \return TRUE if operation has been completed successfully, FALSE otherwise
12450  */
12451 //================================================================================
12452
12453 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12454 {
12455   // iterates on volume elements and detect all free faces on them
12456   SMESHDS_Mesh* aMesh = GetMeshDS();
12457   if (!aMesh)
12458     return false;
12459
12460   ElemFeatures faceType( SMDSAbs_Face );
12461   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12462   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12463   while(vIt->more())
12464   {
12465     const SMDS_MeshVolume* volume = vIt->next();
12466     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12467     vTool.SetExternalNormal();
12468     const int iQuad = volume->IsQuadratic();
12469     faceType.SetQuad( iQuad );
12470     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12471     {
12472       if (!vTool.IsFreeFace(iface))
12473         continue;
12474       nbFree++;
12475       vector<const SMDS_MeshNode *> nodes;
12476       int nbFaceNodes = vTool.NbFaceNodes(iface);
12477       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12478       int inode = 0;
12479       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12480         nodes.push_back(faceNodes[inode]);
12481
12482       if (iQuad) // add medium nodes
12483       {
12484         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12485           nodes.push_back(faceNodes[inode]);
12486         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12487           nodes.push_back(faceNodes[8]);
12488       }
12489       // add new face based on volume nodes
12490       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12491       {
12492         nbExisted++; // face already exsist
12493       }
12494       else
12495       {
12496         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12497         nbCreated++;
12498       }
12499     }
12500   }
12501   return ( nbFree == ( nbExisted + nbCreated ));
12502 }
12503
12504 namespace
12505 {
12506   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12507   {
12508     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12509       return n;
12510     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12511   }
12512 }
12513 //================================================================================
12514 /*!
12515  * \brief Creates missing boundary elements
12516  *  \param elements - elements whose boundary is to be checked
12517  *  \param dimension - defines type of boundary elements to create
12518  *  \param group - a group to store created boundary elements in
12519  *  \param targetMesh - a mesh to store created boundary elements in
12520  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12521  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12522  *                                boundary elements will be copied into the targetMesh
12523  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12524  *                                boundary elements will be added into the new group
12525  *  \param aroundElements - if true, elements will be created on boundary of given
12526  *                          elements else, on boundary of the whole mesh.
12527  * \return nb of added boundary elements
12528  */
12529 //================================================================================
12530
12531 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12532                                        Bnd_Dimension           dimension,
12533                                        SMESH_Group*            group/*=0*/,
12534                                        SMESH_Mesh*             targetMesh/*=0*/,
12535                                        bool                    toCopyElements/*=false*/,
12536                                        bool                    toCopyExistingBoundary/*=false*/,
12537                                        bool                    toAddExistingBondary/*= false*/,
12538                                        bool                    aroundElements/*= false*/)
12539 {
12540   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12541   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12542   // hope that all elements are of the same type, do not check them all
12543   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12544     throw SALOME_Exception(LOCALIZED("wrong element type"));
12545
12546   if ( !targetMesh )
12547     toCopyElements = toCopyExistingBoundary = false;
12548
12549   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12550   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12551   int nbAddedBnd = 0;
12552
12553   // editor adding present bnd elements and optionally holding elements to add to the group
12554   SMESH_MeshEditor* presentEditor;
12555   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12556   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12557
12558   SMESH_MesherHelper helper( *myMesh );
12559   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12560   SMDS_VolumeTool vTool;
12561   TIDSortedElemSet avoidSet;
12562   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12563   size_t inode;
12564
12565   typedef vector<const SMDS_MeshNode*> TConnectivity;
12566   TConnectivity tgtNodes;
12567   ElemFeatures elemKind( missType ), elemToCopy;
12568
12569   vector<const SMDS_MeshElement*> presentBndElems;
12570   vector<TConnectivity>           missingBndElems;
12571   vector<int>                     freeFacets;
12572   TConnectivity nodes, elemNodes;
12573
12574   SMDS_ElemIteratorPtr eIt;
12575   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12576   else                  eIt = elemSetIterator( elements );
12577
12578   while (eIt->more())
12579   {
12580     const SMDS_MeshElement* elem = eIt->next();
12581     const int              iQuad = elem->IsQuadratic();
12582     elemKind.SetQuad( iQuad );
12583
12584     // ------------------------------------------------------------------------------------
12585     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12586     // ------------------------------------------------------------------------------------
12587     presentBndElems.clear();
12588     missingBndElems.clear();
12589     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12590     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12591     {
12592       const SMDS_MeshElement* otherVol = 0;
12593       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12594       {
12595         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12596              ( !aroundElements || elements.count( otherVol )))
12597           continue;
12598         freeFacets.push_back( iface );
12599       }
12600       if ( missType == SMDSAbs_Face )
12601         vTool.SetExternalNormal();
12602       for ( size_t i = 0; i < freeFacets.size(); ++i )
12603       {
12604         int                iface = freeFacets[i];
12605         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12606         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12607         if ( missType == SMDSAbs_Edge ) // boundary edges
12608         {
12609           nodes.resize( 2+iQuad );
12610           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12611           {
12612             for ( size_t j = 0; j < nodes.size(); ++j )
12613               nodes[ j ] = nn[ i+j ];
12614             if ( const SMDS_MeshElement* edge =
12615                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12616               presentBndElems.push_back( edge );
12617             else
12618               missingBndElems.push_back( nodes );
12619           }
12620         }
12621         else // boundary face
12622         {
12623           nodes.clear();
12624           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12625             nodes.push_back( nn[inode] ); // add corner nodes
12626           if (iQuad)
12627             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12628               nodes.push_back( nn[inode] ); // add medium nodes
12629           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12630           if ( iCenter > 0 )
12631             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12632
12633           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12634                                                                SMDSAbs_Face, /*noMedium=*/false ))
12635             presentBndElems.push_back( f );
12636           else
12637             missingBndElems.push_back( nodes );
12638
12639           if ( targetMesh != myMesh )
12640           {
12641             // add 1D elements on face boundary to be added to a new mesh
12642             const SMDS_MeshElement* edge;
12643             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12644             {
12645               if ( iQuad )
12646                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12647               else
12648                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12649               if ( edge && avoidSet.insert( edge ).second )
12650                 presentBndElems.push_back( edge );
12651             }
12652           }
12653         }
12654       }
12655     }
12656     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12657     {
12658       avoidSet.clear(), avoidSet.insert( elem );
12659       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12660                         SMDS_MeshElement::iterator() );
12661       elemNodes.push_back( elemNodes[0] );
12662       nodes.resize( 2 + iQuad );
12663       const int nbLinks = elem->NbCornerNodes();
12664       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12665       {
12666         nodes[0] = elemNodes[iN];
12667         nodes[1] = elemNodes[iN+1+iQuad];
12668         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12669           continue; // not free link
12670
12671         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12672         if ( const SMDS_MeshElement* edge =
12673              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12674           presentBndElems.push_back( edge );
12675         else
12676           missingBndElems.push_back( nodes );
12677       }
12678     }
12679
12680     // ---------------------------------
12681     // 2. Add missing boundary elements
12682     // ---------------------------------
12683     if ( targetMesh != myMesh )
12684       // instead of making a map of nodes in this mesh and targetMesh,
12685       // we create nodes with same IDs.
12686       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12687       {
12688         TConnectivity& srcNodes = missingBndElems[i];
12689         tgtNodes.resize( srcNodes.size() );
12690         for ( inode = 0; inode < srcNodes.size(); ++inode )
12691           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12692         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12693                                                                    missType,
12694                                                                    /*noMedium=*/false))
12695           continue;
12696         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12697         ++nbAddedBnd;
12698       }
12699     else
12700       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12701       {
12702         TConnectivity& nodes = missingBndElems[ i ];
12703         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12704                                                                    missType,
12705                                                                    /*noMedium=*/false))
12706           continue;
12707         SMDS_MeshElement* newElem =
12708           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12709         nbAddedBnd += bool( newElem );
12710
12711         // try to set a new element to a shape
12712         if ( myMesh->HasShapeToMesh() )
12713         {
12714           bool ok = true;
12715           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12716           const size_t nbN = nodes.size() / (iQuad+1 );
12717           for ( inode = 0; inode < nbN && ok; ++inode )
12718           {
12719             pair<int, TopAbs_ShapeEnum> i_stype =
12720               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12721             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12722               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12723           }
12724           if ( ok && mediumShapes.size() > 1 )
12725           {
12726             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12727             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12728             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12729             {
12730               if (( ok = ( stype_i->first != stype_i_0.first )))
12731                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12732                                         aMesh->IndexToShape( stype_i_0.second ));
12733             }
12734           }
12735           if ( ok && mediumShapes.begin()->first == missShapeType )
12736             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12737         }
12738       }
12739
12740     // ----------------------------------
12741     // 3. Copy present boundary elements
12742     // ----------------------------------
12743     if ( toCopyExistingBoundary )
12744       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12745       {
12746         const SMDS_MeshElement* e = presentBndElems[i];
12747         tgtNodes.resize( e->NbNodes() );
12748         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12749           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12750         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12751       }
12752     else // store present elements to add them to a group
12753       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12754       {
12755         presentEditor->myLastCreatedElems.Append( presentBndElems[ i ]);
12756       }
12757
12758   } // loop on given elements
12759
12760   // ---------------------------------------------
12761   // 4. Fill group with boundary elements
12762   // ---------------------------------------------
12763   if ( group )
12764   {
12765     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12766       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12767         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12768   }
12769   tgtEditor.myLastCreatedElems.Clear();
12770   tgtEditor2.myLastCreatedElems.Clear();
12771
12772   // -----------------------
12773   // 5. Copy given elements
12774   // -----------------------
12775   if ( toCopyElements && targetMesh != myMesh )
12776   {
12777     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12778     else                  eIt = elemSetIterator( elements );
12779     while (eIt->more())
12780     {
12781       const SMDS_MeshElement* elem = eIt->next();
12782       tgtNodes.resize( elem->NbNodes() );
12783       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12784         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12785       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12786
12787       tgtEditor.myLastCreatedElems.Clear();
12788     }
12789   }
12790   return nbAddedBnd;
12791 }
12792
12793 //================================================================================
12794 /*!
12795  * \brief Copy node position and set \a to node on the same geometry
12796  */
12797 //================================================================================
12798
12799 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12800                                      const SMDS_MeshNode* to )
12801 {
12802   if ( !from || !to ) return;
12803
12804   SMDS_PositionPtr pos = from->GetPosition();
12805   if ( !pos || from->getshapeId() < 1 ) return;
12806
12807   switch ( pos->GetTypeOfPosition() )
12808   {
12809   case SMDS_TOP_3DSPACE: break;
12810
12811   case SMDS_TOP_FACE:
12812   {
12813     const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12814     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12815                                 fPos->GetUParameter(), fPos->GetVParameter() );
12816     break;
12817   }
12818   case SMDS_TOP_EDGE:
12819   {
12820     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12821     const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12822     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12823     break;
12824   }
12825   case SMDS_TOP_VERTEX:
12826   {
12827     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12828     break;
12829   }
12830   case SMDS_TOP_UNSPEC:
12831   default:;
12832   }
12833 }