Salome HOME
OCCT Exception in "Cutting of quadrangles" dialog
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2015  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     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1605     
1606     helper.SetIsQuadratic  ( nodes.size() > 4 );
1607     helper.SetIsBiQuadratic( nodes.size() == 9 );
1608     if ( helper.GetIsQuadratic() )
1609       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1610
1611     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 [8];
3000       const SMDS_MeshNode* inFaceNode = 0;
3001       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3002       int i = 0;
3003       if ( helper.GetNodeUVneedInFaceNode() )
3004         while ( itN->more() && !inFaceNode ) {
3005           aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3006           if ( aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
3007           {
3008             inFaceNode = aNodes[ i-1 ];
3009           }
3010         }
3011
3012       // find middle point for (0,1,2,3)
3013       // and create a node in this point;
3014       gp_XYZ p( 0,0,0 );
3015       if ( surface.IsNull() ) {
3016         for(i=0; i<4; i++)
3017           p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
3018         p /= 4;
3019       }
3020       else {
3021         TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
3022         gp_XY uv( 0,0 );
3023         for(i=0; i<4; i++)
3024           uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
3025         uv /= 4.;
3026         p = surface->Value( uv.X(), uv.Y() ).XYZ();
3027       }
3028       const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
3029       myLastCreatedNodes.Append(newN);
3030
3031       // create a new element
3032       const SMDS_MeshElement* newElem1 = 0;
3033       const SMDS_MeshElement* newElem2 = 0;
3034       if ( the13Diag ) {
3035         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3036                                   aNodes[6], aNodes[7], newN );
3037         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3038                                   newN,      aNodes[4], aNodes[5] );
3039       }
3040       else {
3041         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3042                                   aNodes[7], aNodes[4], newN );
3043         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3044                                   newN,      aNodes[5], aNodes[6] );
3045       }
3046       myLastCreatedElems.Append(newElem1);
3047       myLastCreatedElems.Append(newElem2);
3048       // put a new triangle on the same shape and add to the same groups
3049       if ( aShapeId )
3050       {
3051         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3052         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3053       }
3054       AddToSameGroups( newElem1, elem, aMesh );
3055       AddToSameGroups( newElem2, elem, aMesh );
3056       aMesh->RemoveElement( elem );
3057     }
3058   }
3059
3060   return true;
3061 }
3062
3063 //=======================================================================
3064 //function : getAngle
3065 //purpose  :
3066 //=======================================================================
3067
3068 double getAngle(const SMDS_MeshElement * tr1,
3069                 const SMDS_MeshElement * tr2,
3070                 const SMDS_MeshNode *    n1,
3071                 const SMDS_MeshNode *    n2)
3072 {
3073   double angle = 2. * M_PI; // bad angle
3074
3075   // get normals
3076   SMESH::Controls::TSequenceOfXYZ P1, P2;
3077   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3078        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3079     return angle;
3080   gp_Vec N1,N2;
3081   if(!tr1->IsQuadratic())
3082     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3083   else
3084     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3085   if ( N1.SquareMagnitude() <= gp::Resolution() )
3086     return angle;
3087   if(!tr2->IsQuadratic())
3088     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3089   else
3090     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3091   if ( N2.SquareMagnitude() <= gp::Resolution() )
3092     return angle;
3093
3094   // find the first diagonal node n1 in the triangles:
3095   // take in account a diagonal link orientation
3096   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3097   for ( int t = 0; t < 2; t++ ) {
3098     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3099     int i = 0, iDiag = -1;
3100     while ( it->more()) {
3101       const SMDS_MeshElement *n = it->next();
3102       if ( n == n1 || n == n2 ) {
3103         if ( iDiag < 0)
3104           iDiag = i;
3105         else {
3106           if ( i - iDiag == 1 )
3107             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3108           else
3109             nFirst[ t ] = n;
3110           break;
3111         }
3112       }
3113       i++;
3114     }
3115   }
3116   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3117     N2.Reverse();
3118
3119   angle = N1.Angle( N2 );
3120   //SCRUTE( angle );
3121   return angle;
3122 }
3123
3124 // =================================================
3125 // class generating a unique ID for a pair of nodes
3126 // and able to return nodes by that ID
3127 // =================================================
3128 class LinkID_Gen {
3129 public:
3130
3131   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3132     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3133   {}
3134
3135   long GetLinkID (const SMDS_MeshNode * n1,
3136                   const SMDS_MeshNode * n2) const
3137   {
3138     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3139   }
3140
3141   bool GetNodes (const long             theLinkID,
3142                  const SMDS_MeshNode* & theNode1,
3143                  const SMDS_MeshNode* & theNode2) const
3144   {
3145     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3146     if ( !theNode1 ) return false;
3147     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3148     if ( !theNode2 ) return false;
3149     return true;
3150   }
3151
3152 private:
3153   LinkID_Gen();
3154   const SMESHDS_Mesh* myMesh;
3155   long                myMaxID;
3156 };
3157
3158
3159 //=======================================================================
3160 //function : TriToQuad
3161 //purpose  : Fuse neighbour triangles into quadrangles.
3162 //           theCrit is used to select a neighbour to fuse with.
3163 //           theMaxAngle is a max angle between element normals at which
3164 //           fusion is still performed.
3165 //=======================================================================
3166
3167 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3168                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3169                                   const double                         theMaxAngle)
3170 {
3171   myLastCreatedElems.Clear();
3172   myLastCreatedNodes.Clear();
3173
3174   MESSAGE( "::TriToQuad()" );
3175
3176   if ( !theCrit.get() )
3177     return false;
3178
3179   SMESHDS_Mesh * aMesh = GetMeshDS();
3180
3181   // Prepare data for algo: build
3182   // 1. map of elements with their linkIDs
3183   // 2. map of linkIDs with their elements
3184
3185   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3186   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3187   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3188   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3189
3190   TIDSortedElemSet::iterator itElem;
3191   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3192   {
3193     const SMDS_MeshElement* elem = *itElem;
3194     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3195     bool IsTria = ( elem->NbCornerNodes()==3 );
3196     if (!IsTria) continue;
3197
3198     // retrieve element nodes
3199     const SMDS_MeshNode* aNodes [4];
3200     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3201     int i = 0;
3202     while ( i < 3 )
3203       aNodes[ i++ ] = itN->next();
3204     aNodes[ 3 ] = aNodes[ 0 ];
3205
3206     // fill maps
3207     for ( i = 0; i < 3; i++ ) {
3208       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3209       // check if elements sharing a link can be fused
3210       itLE = mapLi_listEl.find( link );
3211       if ( itLE != mapLi_listEl.end() ) {
3212         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3213           continue;
3214         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3215         //if ( FindShape( elem ) != FindShape( elem2 ))
3216         //  continue; // do not fuse triangles laying on different shapes
3217         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3218           continue; // avoid making badly shaped quads
3219         (*itLE).second.push_back( elem );
3220       }
3221       else {
3222         mapLi_listEl[ link ].push_back( elem );
3223       }
3224       mapEl_setLi [ elem ].insert( link );
3225     }
3226   }
3227   // Clean the maps from the links shared by a sole element, ie
3228   // links to which only one element is bound in mapLi_listEl
3229
3230   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3231     int nbElems = (*itLE).second.size();
3232     if ( nbElems < 2  ) {
3233       const SMDS_MeshElement* elem = (*itLE).second.front();
3234       SMESH_TLink link = (*itLE).first;
3235       mapEl_setLi[ elem ].erase( link );
3236       if ( mapEl_setLi[ elem ].empty() )
3237         mapEl_setLi.erase( elem );
3238     }
3239   }
3240
3241   // Algo: fuse triangles into quadrangles
3242
3243   while ( ! mapEl_setLi.empty() ) {
3244     // Look for the start element:
3245     // the element having the least nb of shared links
3246     const SMDS_MeshElement* startElem = 0;
3247     int minNbLinks = 4;
3248     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3249       int nbLinks = (*itEL).second.size();
3250       if ( nbLinks < minNbLinks ) {
3251         startElem = (*itEL).first;
3252         minNbLinks = nbLinks;
3253         if ( minNbLinks == 1 )
3254           break;
3255       }
3256     }
3257
3258     // search elements to fuse starting from startElem or links of elements
3259     // fused earlyer - startLinks
3260     list< SMESH_TLink > startLinks;
3261     while ( startElem || !startLinks.empty() ) {
3262       while ( !startElem && !startLinks.empty() ) {
3263         // Get an element to start, by a link
3264         SMESH_TLink linkId = startLinks.front();
3265         startLinks.pop_front();
3266         itLE = mapLi_listEl.find( linkId );
3267         if ( itLE != mapLi_listEl.end() ) {
3268           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3269           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3270           for ( ; itE != listElem.end() ; itE++ )
3271             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3272               startElem = (*itE);
3273           mapLi_listEl.erase( itLE );
3274         }
3275       }
3276
3277       if ( startElem ) {
3278         // Get candidates to be fused
3279         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3280         const SMESH_TLink *link12, *link13;
3281         startElem = 0;
3282         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3283         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3284         ASSERT( !setLi.empty() );
3285         set< SMESH_TLink >::iterator itLi;
3286         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3287         {
3288           const SMESH_TLink & link = (*itLi);
3289           itLE = mapLi_listEl.find( link );
3290           if ( itLE == mapLi_listEl.end() )
3291             continue;
3292
3293           const SMDS_MeshElement* elem = (*itLE).second.front();
3294           if ( elem == tr1 )
3295             elem = (*itLE).second.back();
3296           mapLi_listEl.erase( itLE );
3297           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3298             continue;
3299           if ( tr2 ) {
3300             tr3 = elem;
3301             link13 = &link;
3302           }
3303           else {
3304             tr2 = elem;
3305             link12 = &link;
3306           }
3307
3308           // add other links of elem to list of links to re-start from
3309           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3310           set< SMESH_TLink >::iterator it;
3311           for ( it = links.begin(); it != links.end(); it++ ) {
3312             const SMESH_TLink& link2 = (*it);
3313             if ( link2 != link )
3314               startLinks.push_back( link2 );
3315           }
3316         }
3317
3318         // Get nodes of possible quadrangles
3319         const SMDS_MeshNode *n12 [4], *n13 [4];
3320         bool Ok12 = false, Ok13 = false;
3321         const SMDS_MeshNode *linkNode1, *linkNode2;
3322         if(tr2) {
3323           linkNode1 = link12->first;
3324           linkNode2 = link12->second;
3325           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3326             Ok12 = true;
3327         }
3328         if(tr3) {
3329           linkNode1 = link13->first;
3330           linkNode2 = link13->second;
3331           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3332             Ok13 = true;
3333         }
3334
3335         // Choose a pair to fuse
3336         if ( Ok12 && Ok13 ) {
3337           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3338           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3339           double aBadRate12 = getBadRate( &quad12, theCrit );
3340           double aBadRate13 = getBadRate( &quad13, theCrit );
3341           if (  aBadRate13 < aBadRate12 )
3342             Ok12 = false;
3343           else
3344             Ok13 = false;
3345         }
3346
3347         // Make quadrangles
3348         // and remove fused elems and remove links from the maps
3349         mapEl_setLi.erase( tr1 );
3350         if ( Ok12 )
3351         {
3352           mapEl_setLi.erase( tr2 );
3353           mapLi_listEl.erase( *link12 );
3354           if ( tr1->NbNodes() == 3 )
3355           {
3356             const SMDS_MeshElement* newElem = 0;
3357             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3358             myLastCreatedElems.Append(newElem);
3359             AddToSameGroups( newElem, tr1, aMesh );
3360             int aShapeId = tr1->getshapeId();
3361             if ( aShapeId )
3362               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3363             aMesh->RemoveElement( tr1 );
3364             aMesh->RemoveElement( tr2 );
3365           }
3366           else {
3367             vector< const SMDS_MeshNode* > N1;
3368             vector< const SMDS_MeshNode* > N2;
3369             getNodesFromTwoTria(tr1,tr2,N1,N2);
3370             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3371             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3372             // i.e. first nodes from both arrays form a new diagonal
3373             const SMDS_MeshNode* aNodes[8];
3374             aNodes[0] = N1[0];
3375             aNodes[1] = N1[1];
3376             aNodes[2] = N2[0];
3377             aNodes[3] = N2[1];
3378             aNodes[4] = N1[3];
3379             aNodes[5] = N2[5];
3380             aNodes[6] = N2[3];
3381             aNodes[7] = N1[5];
3382             const SMDS_MeshElement* newElem = 0;
3383             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3384               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3385                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3386             else
3387               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3388                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3389             myLastCreatedElems.Append(newElem);
3390             AddToSameGroups( newElem, tr1, aMesh );
3391             int aShapeId = tr1->getshapeId();
3392             if ( aShapeId )
3393               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3394             aMesh->RemoveElement( tr1 );
3395             aMesh->RemoveElement( tr2 );
3396             // remove middle node (9)
3397             if ( N1[4]->NbInverseElements() == 0 )
3398               aMesh->RemoveNode( N1[4] );
3399             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3400               aMesh->RemoveNode( N1[6] );
3401             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3402               aMesh->RemoveNode( N2[6] );
3403           }
3404         }
3405         else if ( Ok13 )
3406         {
3407           mapEl_setLi.erase( tr3 );
3408           mapLi_listEl.erase( *link13 );
3409           if ( tr1->NbNodes() == 3 ) {
3410             const SMDS_MeshElement* newElem = 0;
3411             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3412             myLastCreatedElems.Append(newElem);
3413             AddToSameGroups( newElem, tr1, aMesh );
3414             int aShapeId = tr1->getshapeId();
3415             if ( aShapeId )
3416               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3417             aMesh->RemoveElement( tr1 );
3418             aMesh->RemoveElement( tr3 );
3419           }
3420           else {
3421             vector< const SMDS_MeshNode* > N1;
3422             vector< const SMDS_MeshNode* > N2;
3423             getNodesFromTwoTria(tr1,tr3,N1,N2);
3424             // now we receive following N1 and N2 (using numeration as above image)
3425             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3426             // i.e. first nodes from both arrays form a new diagonal
3427             const SMDS_MeshNode* aNodes[8];
3428             aNodes[0] = N1[0];
3429             aNodes[1] = N1[1];
3430             aNodes[2] = N2[0];
3431             aNodes[3] = N2[1];
3432             aNodes[4] = N1[3];
3433             aNodes[5] = N2[5];
3434             aNodes[6] = N2[3];
3435             aNodes[7] = N1[5];
3436             const SMDS_MeshElement* newElem = 0;
3437             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3438               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3439                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3440             else
3441               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3442                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3443             myLastCreatedElems.Append(newElem);
3444             AddToSameGroups( newElem, tr1, aMesh );
3445             int aShapeId = tr1->getshapeId();
3446             if ( aShapeId )
3447               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3448             aMesh->RemoveElement( tr1 );
3449             aMesh->RemoveElement( tr3 );
3450             // remove middle node (9)
3451             if ( N1[4]->NbInverseElements() == 0 )
3452               aMesh->RemoveNode( N1[4] );
3453             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3454               aMesh->RemoveNode( N1[6] );
3455             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3456               aMesh->RemoveNode( N2[6] );
3457           }
3458         }
3459
3460         // Next element to fuse: the rejected one
3461         if ( tr3 )
3462           startElem = Ok12 ? tr3 : tr2;
3463
3464       } // if ( startElem )
3465     } // while ( startElem || !startLinks.empty() )
3466   } // while ( ! mapEl_setLi.empty() )
3467
3468   return true;
3469 }
3470
3471
3472 /*#define DUMPSO(txt) \
3473 //  cout << txt << endl;
3474 //=============================================================================
3475 //
3476 //
3477 //
3478 //=============================================================================
3479 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3480 {
3481 if ( i1 == i2 )
3482 return;
3483 int tmp = idNodes[ i1 ];
3484 idNodes[ i1 ] = idNodes[ i2 ];
3485 idNodes[ i2 ] = tmp;
3486 gp_Pnt Ptmp = P[ i1 ];
3487 P[ i1 ] = P[ i2 ];
3488 P[ i2 ] = Ptmp;
3489 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3490 }
3491
3492 //=======================================================================
3493 //function : SortQuadNodes
3494 //purpose  : Set 4 nodes of a quadrangle face in a good order.
3495 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
3496 //           1 or 2 else 0.
3497 //=======================================================================
3498
3499 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3500 int               idNodes[] )
3501 {
3502   gp_Pnt P[4];
3503   int i;
3504   for ( i = 0; i < 4; i++ ) {
3505     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3506     if ( !n ) return 0;
3507     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3508   }
3509
3510   gp_Vec V1(P[0], P[1]);
3511   gp_Vec V2(P[0], P[2]);
3512   gp_Vec V3(P[0], P[3]);
3513
3514   gp_Vec Cross1 = V1 ^ V2;
3515   gp_Vec Cross2 = V2 ^ V3;
3516
3517   i = 0;
3518   if (Cross1.Dot(Cross2) < 0)
3519   {
3520     Cross1 = V2 ^ V1;
3521     Cross2 = V1 ^ V3;
3522
3523     if (Cross1.Dot(Cross2) < 0)
3524       i = 2;
3525     else
3526       i = 1;
3527     swap ( i, i + 1, idNodes, P );
3528
3529     //     for ( int ii = 0; ii < 4; ii++ ) {
3530     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3531     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3532     //     }
3533   }
3534   return i;
3535 }
3536
3537 //=======================================================================
3538 //function : SortHexaNodes
3539 //purpose  : Set 8 nodes of a hexahedron in a good order.
3540 //           Return success status
3541 //=======================================================================
3542
3543 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3544                                       int               idNodes[] )
3545 {
3546   gp_Pnt P[8];
3547   int i;
3548   DUMPSO( "INPUT: ========================================");
3549   for ( i = 0; i < 8; i++ ) {
3550     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3551     if ( !n ) return false;
3552     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3553     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3554   }
3555   DUMPSO( "========================================");
3556
3557
3558   set<int> faceNodes;  // ids of bottom face nodes, to be found
3559   set<int> checkedId1; // ids of tried 2-nd nodes
3560   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3561   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
3562   int iMin, iLoop1 = 0;
3563
3564   // Loop to try the 2-nd nodes
3565
3566   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3567   {
3568     // Find not checked 2-nd node
3569     for ( i = 1; i < 8; i++ )
3570       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3571         int id1 = idNodes[i];
3572         swap ( 1, i, idNodes, P );
3573         checkedId1.insert ( id1 );
3574         break;
3575       }
3576
3577     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3578     // ie that all but meybe one (id3 which is on the same face) nodes
3579     // lay on the same side from the triangle plane.
3580
3581     bool manyInPlane = false; // more than 4 nodes lay in plane
3582     int iLoop2 = 0;
3583     while ( ++iLoop2 < 6 ) {
3584
3585       // get 1-2-3 plane coeffs
3586       Standard_Real A, B, C, D;
3587       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3588       if ( N.SquareMagnitude() > gp::Resolution() )
3589       {
3590         gp_Pln pln ( P[0], N );
3591         pln.Coefficients( A, B, C, D );
3592
3593         // find the node (iMin) closest to pln
3594         Standard_Real dist[ 8 ], minDist = DBL_MAX;
3595         set<int> idInPln;
3596         for ( i = 3; i < 8; i++ ) {
3597           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3598           if ( fabs( dist[i] ) < minDist ) {
3599             minDist = fabs( dist[i] );
3600             iMin = i;
3601           }
3602           if ( fabs( dist[i] ) <= tol )
3603             idInPln.insert( idNodes[i] );
3604         }
3605
3606         // there should not be more than 4 nodes in bottom plane
3607         if ( idInPln.size() > 1 )
3608         {
3609           DUMPSO( "### idInPln.size() = " << idInPln.size());
3610           // idInPlane does not contain the first 3 nodes
3611           if ( manyInPlane || idInPln.size() == 5)
3612             return false; // all nodes in one plane
3613           manyInPlane = true;
3614
3615           // set the 1-st node to be not in plane
3616           for ( i = 3; i < 8; i++ ) {
3617             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3618               DUMPSO( "### Reset 0-th node");
3619               swap( 0, i, idNodes, P );
3620               break;
3621             }
3622           }
3623
3624           // reset to re-check second nodes
3625           leastDist = DBL_MAX;
3626           faceNodes.clear();
3627           checkedId1.clear();
3628           iLoop1 = 0;
3629           break; // from iLoop2;
3630         }
3631
3632         // check that the other 4 nodes are on the same side
3633         bool sameSide = true;
3634         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3635         for ( i = 3; sameSide && i < 8; i++ ) {
3636           if ( i != iMin )
3637             sameSide = ( isNeg == dist[i] <= 0.);
3638         }
3639
3640         // keep best solution
3641         if ( sameSide && minDist < leastDist ) {
3642           leastDist = minDist;
3643           faceNodes.clear();
3644           faceNodes.insert( idNodes[ 1 ] );
3645           faceNodes.insert( idNodes[ 2 ] );
3646           faceNodes.insert( idNodes[ iMin ] );
3647           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3648                   << " leastDist = " << leastDist);
3649           if ( leastDist <= DBL_MIN )
3650             break;
3651         }
3652       }
3653
3654       // set next 3-d node to check
3655       int iNext = 2 + iLoop2;
3656       if ( iNext < 8 ) {
3657         DUMPSO( "Try 2-nd");
3658         swap ( 2, iNext, idNodes, P );
3659       }
3660     } // while ( iLoop2 < 6 )
3661   } // iLoop1
3662
3663   if ( faceNodes.empty() ) return false;
3664
3665   // Put the faceNodes in proper places
3666   for ( i = 4; i < 8; i++ ) {
3667     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3668       // find a place to put
3669       int iTo = 1;
3670       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3671         iTo++;
3672       DUMPSO( "Set faceNodes");
3673       swap ( iTo, i, idNodes, P );
3674     }
3675   }
3676
3677
3678   // Set nodes of the found bottom face in good order
3679   DUMPSO( " Found bottom face: ");
3680   i = SortQuadNodes( theMesh, idNodes );
3681   if ( i ) {
3682     gp_Pnt Ptmp = P[ i ];
3683     P[ i ] = P[ i+1 ];
3684     P[ i+1 ] = Ptmp;
3685   }
3686   //   else
3687   //     for ( int ii = 0; ii < 4; ii++ ) {
3688   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3689   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3690   //    }
3691
3692   // Gravity center of the top and bottom faces
3693   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3694   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3695
3696   // Get direction from the bottom to the top face
3697   gp_Vec upDir ( aGCb, aGCt );
3698   Standard_Real upDirSize = upDir.Magnitude();
3699   if ( upDirSize <= gp::Resolution() ) return false;
3700   upDir / upDirSize;
3701
3702   // Assure that the bottom face normal points up
3703   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3704   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3705   if ( Nb.Dot( upDir ) < 0 ) {
3706     DUMPSO( "Reverse bottom face");
3707     swap( 1, 3, idNodes, P );
3708   }
3709
3710   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3711   Standard_Real minDist = DBL_MAX;
3712   for ( i = 4; i < 8; i++ ) {
3713     // projection of P[i] to the plane defined by P[0] and upDir
3714     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3715     Standard_Real sqDist = P[0].SquareDistance( Pp );
3716     if ( sqDist < minDist ) {
3717       minDist = sqDist;
3718       iMin = i;
3719     }
3720   }
3721   DUMPSO( "Set 4-th");
3722   swap ( 4, iMin, idNodes, P );
3723
3724   // Set nodes of the top face in good order
3725   DUMPSO( "Sort top face");
3726   i = SortQuadNodes( theMesh, &idNodes[4] );
3727   if ( i ) {
3728     i += 4;
3729     gp_Pnt Ptmp = P[ i ];
3730     P[ i ] = P[ i+1 ];
3731     P[ i+1 ] = Ptmp;
3732   }
3733
3734   // Assure that direction of the top face normal is from the bottom face
3735   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3736   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3737   if ( Nt.Dot( upDir ) < 0 ) {
3738     DUMPSO( "Reverse top face");
3739     swap( 5, 7, idNodes, P );
3740   }
3741
3742   //   DUMPSO( "OUTPUT: ========================================");
3743   //   for ( i = 0; i < 8; i++ ) {
3744   //     float *p = ugrid->GetPoint(idNodes[i]);
3745   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3746   //   }
3747
3748   return true;
3749 }*/
3750
3751 //================================================================================
3752 /*!
3753  * \brief Return nodes linked to the given one
3754  * \param theNode - the node
3755  * \param linkedNodes - the found nodes
3756  * \param type - the type of elements to check
3757  *
3758  * Medium nodes are ignored
3759  */
3760 //================================================================================
3761
3762 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3763                                        TIDSortedElemSet &   linkedNodes,
3764                                        SMDSAbs_ElementType  type )
3765 {
3766   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3767   while ( elemIt->more() )
3768   {
3769     const SMDS_MeshElement* elem = elemIt->next();
3770     if(elem->GetType() == SMDSAbs_0DElement)
3771       continue;
3772
3773     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3774     if ( elem->GetType() == SMDSAbs_Volume )
3775     {
3776       SMDS_VolumeTool vol( elem );
3777       while ( nodeIt->more() ) {
3778         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3779         if ( theNode != n && vol.IsLinked( theNode, n ))
3780           linkedNodes.insert( n );
3781       }
3782     }
3783     else
3784     {
3785       for ( int i = 0; nodeIt->more(); ++i ) {
3786         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3787         if ( n == theNode ) {
3788           int iBefore = i - 1;
3789           int iAfter  = i + 1;
3790           if ( elem->IsQuadratic() ) {
3791             int nb = elem->NbNodes() / 2;
3792             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3793             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3794           }
3795           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3796           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3797         }
3798       }
3799     }
3800   }
3801 }
3802
3803 //=======================================================================
3804 //function : laplacianSmooth
3805 //purpose  : pulls theNode toward the center of surrounding nodes directly
3806 //           connected to that node along an element edge
3807 //=======================================================================
3808
3809 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3810                      const Handle(Geom_Surface)&          theSurface,
3811                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3812 {
3813   // find surrounding nodes
3814
3815   TIDSortedElemSet nodeSet;
3816   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3817
3818   // compute new coodrs
3819
3820   double coord[] = { 0., 0., 0. };
3821   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3822   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3823     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3824     if ( theSurface.IsNull() ) { // smooth in 3D
3825       coord[0] += node->X();
3826       coord[1] += node->Y();
3827       coord[2] += node->Z();
3828     }
3829     else { // smooth in 2D
3830       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3831       gp_XY* uv = theUVMap[ node ];
3832       coord[0] += uv->X();
3833       coord[1] += uv->Y();
3834     }
3835   }
3836   int nbNodes = nodeSet.size();
3837   if ( !nbNodes )
3838     return;
3839   coord[0] /= nbNodes;
3840   coord[1] /= nbNodes;
3841
3842   if ( !theSurface.IsNull() ) {
3843     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3844     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3845     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3846     coord[0] = p3d.X();
3847     coord[1] = p3d.Y();
3848     coord[2] = p3d.Z();
3849   }
3850   else
3851     coord[2] /= nbNodes;
3852
3853   // move node
3854
3855   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3856 }
3857
3858 //=======================================================================
3859 //function : centroidalSmooth
3860 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3861 //           surrounding elements
3862 //=======================================================================
3863
3864 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3865                       const Handle(Geom_Surface)&          theSurface,
3866                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3867 {
3868   gp_XYZ aNewXYZ(0.,0.,0.);
3869   SMESH::Controls::Area anAreaFunc;
3870   double totalArea = 0.;
3871   int nbElems = 0;
3872
3873   // compute new XYZ
3874
3875   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3876   while ( elemIt->more() )
3877   {
3878     const SMDS_MeshElement* elem = elemIt->next();
3879     nbElems++;
3880
3881     gp_XYZ elemCenter(0.,0.,0.);
3882     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3883     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3884     int nn = elem->NbNodes();
3885     if(elem->IsQuadratic()) nn = nn/2;
3886     int i=0;
3887     //while ( itN->more() ) {
3888     while ( i<nn ) {
3889       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3890       i++;
3891       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3892       aNodePoints.push_back( aP );
3893       if ( !theSurface.IsNull() ) { // smooth in 2D
3894         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3895         gp_XY* uv = theUVMap[ aNode ];
3896         aP.SetCoord( uv->X(), uv->Y(), 0. );
3897       }
3898       elemCenter += aP;
3899     }
3900     double elemArea = anAreaFunc.GetValue( aNodePoints );
3901     totalArea += elemArea;
3902     elemCenter /= nn;
3903     aNewXYZ += elemCenter * elemArea;
3904   }
3905   aNewXYZ /= totalArea;
3906   if ( !theSurface.IsNull() ) {
3907     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3908     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3909   }
3910
3911   // move node
3912
3913   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3914 }
3915
3916 //=======================================================================
3917 //function : getClosestUV
3918 //purpose  : return UV of closest projection
3919 //=======================================================================
3920
3921 static bool getClosestUV (Extrema_GenExtPS& projector,
3922                           const gp_Pnt&     point,
3923                           gp_XY &           result)
3924 {
3925   projector.Perform( point );
3926   if ( projector.IsDone() ) {
3927     double u, v, minVal = DBL_MAX;
3928     for ( int i = projector.NbExt(); i > 0; i-- )
3929       if ( projector.SquareDistance( i ) < minVal ) {
3930         minVal = projector.SquareDistance( i );
3931         projector.Point( i ).Parameter( u, v );
3932       }
3933     result.SetCoord( u, v );
3934     return true;
3935   }
3936   return false;
3937 }
3938
3939 //=======================================================================
3940 //function : Smooth
3941 //purpose  : Smooth theElements during theNbIterations or until a worst
3942 //           element has aspect ratio <= theTgtAspectRatio.
3943 //           Aspect Ratio varies in range [1.0, inf].
3944 //           If theElements is empty, the whole mesh is smoothed.
3945 //           theFixedNodes contains additionally fixed nodes. Nodes built
3946 //           on edges and boundary nodes are always fixed.
3947 //=======================================================================
3948
3949 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3950                                set<const SMDS_MeshNode*> & theFixedNodes,
3951                                const SmoothMethod          theSmoothMethod,
3952                                const int                   theNbIterations,
3953                                double                      theTgtAspectRatio,
3954                                const bool                  the2D)
3955 {
3956   myLastCreatedElems.Clear();
3957   myLastCreatedNodes.Clear();
3958
3959   MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3960
3961   if ( theTgtAspectRatio < 1.0 )
3962     theTgtAspectRatio = 1.0;
3963
3964   const double disttol = 1.e-16;
3965
3966   SMESH::Controls::AspectRatio aQualityFunc;
3967
3968   SMESHDS_Mesh* aMesh = GetMeshDS();
3969
3970   if ( theElems.empty() ) {
3971     // add all faces to theElems
3972     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3973     while ( fIt->more() ) {
3974       const SMDS_MeshElement* face = fIt->next();
3975       theElems.insert( theElems.end(), face );
3976     }
3977   }
3978   // get all face ids theElems are on
3979   set< int > faceIdSet;
3980   TIDSortedElemSet::iterator itElem;
3981   if ( the2D )
3982     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3983       int fId = FindShape( *itElem );
3984       // check that corresponding submesh exists and a shape is face
3985       if (fId &&
3986           faceIdSet.find( fId ) == faceIdSet.end() &&
3987           aMesh->MeshElements( fId )) {
3988         TopoDS_Shape F = aMesh->IndexToShape( fId );
3989         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3990           faceIdSet.insert( fId );
3991       }
3992     }
3993   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3994
3995   // ===============================================
3996   // smooth elements on each TopoDS_Face separately
3997   // ===============================================
3998
3999   SMESH_MesherHelper helper( *GetMesh() );
4000
4001   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
4002   for ( ; fId != faceIdSet.rend(); ++fId )
4003   {
4004     // get face surface and submesh
4005     Handle(Geom_Surface) surface;
4006     SMESHDS_SubMesh* faceSubMesh = 0;
4007     TopoDS_Face face;
4008     double fToler2 = 0;
4009     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
4010     bool isUPeriodic = false, isVPeriodic = false;
4011     if ( *fId )
4012     {
4013       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
4014       surface = BRep_Tool::Surface( face );
4015       faceSubMesh = aMesh->MeshElements( *fId );
4016       fToler2 = BRep_Tool::Tolerance( face );
4017       fToler2 *= fToler2 * 10.;
4018       isUPeriodic = surface->IsUPeriodic();
4019       if ( isUPeriodic )
4020         surface->UPeriod();
4021       isVPeriodic = surface->IsVPeriodic();
4022       if ( isVPeriodic )
4023         surface->VPeriod();
4024       surface->Bounds( u1, u2, v1, v2 );
4025       helper.SetSubShape( face );
4026     }
4027     // ---------------------------------------------------------
4028     // for elements on a face, find movable and fixed nodes and
4029     // compute UV for them
4030     // ---------------------------------------------------------
4031     bool checkBoundaryNodes = false;
4032     bool isQuadratic = false;
4033     set<const SMDS_MeshNode*> setMovableNodes;
4034     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4035     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4036     list< const SMDS_MeshElement* > elemsOnFace;
4037
4038     Extrema_GenExtPS projector;
4039     GeomAdaptor_Surface surfAdaptor;
4040     if ( !surface.IsNull() ) {
4041       surfAdaptor.Load( surface );
4042       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4043     }
4044     int nbElemOnFace = 0;
4045     itElem = theElems.begin();
4046     // loop on not yet smoothed elements: look for elems on a face
4047     while ( itElem != theElems.end() )
4048     {
4049       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4050         break; // all elements found
4051
4052       const SMDS_MeshElement* elem = *itElem;
4053       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4054            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4055         ++itElem;
4056         continue;
4057       }
4058       elemsOnFace.push_back( elem );
4059       theElems.erase( itElem++ );
4060       nbElemOnFace++;
4061
4062       if ( !isQuadratic )
4063         isQuadratic = elem->IsQuadratic();
4064
4065       // get movable nodes of elem
4066       const SMDS_MeshNode* node;
4067       SMDS_TypeOfPosition posType;
4068       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4069       int nn = 0, nbn =  elem->NbNodes();
4070       if(elem->IsQuadratic())
4071         nbn = nbn/2;
4072       while ( nn++ < nbn ) {
4073         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4074         const SMDS_PositionPtr& pos = node->GetPosition();
4075         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4076         if (posType != SMDS_TOP_EDGE &&
4077             posType != SMDS_TOP_VERTEX &&
4078             theFixedNodes.find( node ) == theFixedNodes.end())
4079         {
4080           // check if all faces around the node are on faceSubMesh
4081           // because a node on edge may be bound to face
4082           SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4083           bool all = true;
4084           if ( faceSubMesh ) {
4085             while ( eIt->more() && all ) {
4086               const SMDS_MeshElement* e = eIt->next();
4087               all = faceSubMesh->Contains( e );
4088             }
4089           }
4090           if ( all )
4091             setMovableNodes.insert( node );
4092           else
4093             checkBoundaryNodes = true;
4094         }
4095         if ( posType == SMDS_TOP_3DSPACE )
4096           checkBoundaryNodes = true;
4097       }
4098
4099       if ( surface.IsNull() )
4100         continue;
4101
4102       // get nodes to check UV
4103       list< const SMDS_MeshNode* > uvCheckNodes;
4104       const SMDS_MeshNode* nodeInFace = 0;
4105       itN = elem->nodesIterator();
4106       nn = 0; nbn =  elem->NbNodes();
4107       if(elem->IsQuadratic())
4108         nbn = nbn/2;
4109       while ( nn++ < nbn ) {
4110         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4111         if ( node->GetPosition()->GetDim() == 2 )
4112           nodeInFace = node;
4113         if ( uvMap.find( node ) == uvMap.end() )
4114           uvCheckNodes.push_back( node );
4115         // add nodes of elems sharing node
4116         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4117         //         while ( eIt->more() ) {
4118         //           const SMDS_MeshElement* e = eIt->next();
4119         //           if ( e != elem ) {
4120         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4121         //             while ( nIt->more() ) {
4122         //               const SMDS_MeshNode* n =
4123         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4124         //               if ( uvMap.find( n ) == uvMap.end() )
4125         //                 uvCheckNodes.push_back( n );
4126         //             }
4127         //           }
4128         //         }
4129       }
4130       // check UV on face
4131       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4132       for ( ; n != uvCheckNodes.end(); ++n ) {
4133         node = *n;
4134         gp_XY uv( 0, 0 );
4135         const SMDS_PositionPtr& pos = node->GetPosition();
4136         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4137         // get existing UV
4138         if ( pos )
4139         {
4140           bool toCheck = true;
4141           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4142         }
4143         // compute not existing UV
4144         bool project = ( posType == SMDS_TOP_3DSPACE );
4145         // double dist1 = DBL_MAX, dist2 = 0;
4146         // if ( posType != SMDS_TOP_3DSPACE ) {
4147         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4148         //   project = dist1 > fToler2;
4149         // }
4150         if ( project ) { // compute new UV
4151           gp_XY newUV;
4152           gp_Pnt pNode = SMESH_TNodeXYZ( node );
4153           if ( !getClosestUV( projector, pNode, newUV )) {
4154             MESSAGE("Node Projection Failed " << node);
4155           }
4156           else {
4157             if ( isUPeriodic )
4158               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4159             if ( isVPeriodic )
4160               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4161             // check new UV
4162             // if ( posType != SMDS_TOP_3DSPACE )
4163             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4164             // if ( dist2 < dist1 )
4165               uv = newUV;
4166           }
4167         }
4168         // store UV in the map
4169         listUV.push_back( uv );
4170         uvMap.insert( make_pair( node, &listUV.back() ));
4171       }
4172     } // loop on not yet smoothed elements
4173
4174     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4175       checkBoundaryNodes = true;
4176
4177     // fix nodes on mesh boundary
4178
4179     if ( checkBoundaryNodes ) {
4180       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4181       map< SMESH_TLink, int >::iterator link_nb;
4182       // put all elements links to linkNbMap
4183       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4184       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4185         const SMDS_MeshElement* elem = (*elemIt);
4186         int nbn =  elem->NbCornerNodes();
4187         // loop on elem links: insert them in linkNbMap
4188         for ( int iN = 0; iN < nbn; ++iN ) {
4189           const SMDS_MeshNode* n1 = elem->GetNode( iN );
4190           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4191           SMESH_TLink link( n1, n2 );
4192           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4193           link_nb->second++;
4194         }
4195       }
4196       // remove nodes that are in links encountered only once from setMovableNodes
4197       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4198         if ( link_nb->second == 1 ) {
4199           setMovableNodes.erase( link_nb->first.node1() );
4200           setMovableNodes.erase( link_nb->first.node2() );
4201         }
4202       }
4203     }
4204
4205     // -----------------------------------------------------
4206     // for nodes on seam edge, compute one more UV ( uvMap2 );
4207     // find movable nodes linked to nodes on seam and which
4208     // are to be smoothed using the second UV ( uvMap2 )
4209     // -----------------------------------------------------
4210
4211     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4212     if ( !surface.IsNull() ) {
4213       TopExp_Explorer eExp( face, TopAbs_EDGE );
4214       for ( ; eExp.More(); eExp.Next() ) {
4215         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4216         if ( !BRep_Tool::IsClosed( edge, face ))
4217           continue;
4218         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4219         if ( !sm ) continue;
4220         // find out which parameter varies for a node on seam
4221         double f,l;
4222         gp_Pnt2d uv1, uv2;
4223         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4224         if ( pcurve.IsNull() ) continue;
4225         uv1 = pcurve->Value( f );
4226         edge.Reverse();
4227         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4228         if ( pcurve.IsNull() ) continue;
4229         uv2 = pcurve->Value( f );
4230         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4231         // assure uv1 < uv2
4232         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4233           std::swap( uv1, uv2 );
4234         // get nodes on seam and its vertices
4235         list< const SMDS_MeshNode* > seamNodes;
4236         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4237         while ( nSeamIt->more() ) {
4238           const SMDS_MeshNode* node = nSeamIt->next();
4239           if ( !isQuadratic || !IsMedium( node ))
4240             seamNodes.push_back( node );
4241         }
4242         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4243         for ( ; vExp.More(); vExp.Next() ) {
4244           sm = aMesh->MeshElements( vExp.Current() );
4245           if ( sm ) {
4246             nSeamIt = sm->GetNodes();
4247             while ( nSeamIt->more() )
4248               seamNodes.push_back( nSeamIt->next() );
4249           }
4250         }
4251         // loop on nodes on seam
4252         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4253         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4254           const SMDS_MeshNode* nSeam = *noSeIt;
4255           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4256           if ( n_uv == uvMap.end() )
4257             continue;
4258           // set the first UV
4259           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4260           // set the second UV
4261           listUV.push_back( *n_uv->second );
4262           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4263           if ( uvMap2.empty() )
4264             uvMap2 = uvMap; // copy the uvMap contents
4265           uvMap2[ nSeam ] = &listUV.back();
4266
4267           // collect movable nodes linked to ones on seam in nodesNearSeam
4268           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4269           while ( eIt->more() ) {
4270             const SMDS_MeshElement* e = eIt->next();
4271             int nbUseMap1 = 0, nbUseMap2 = 0;
4272             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4273             int nn = 0, nbn =  e->NbNodes();
4274             if(e->IsQuadratic()) nbn = nbn/2;
4275             while ( nn++ < nbn )
4276             {
4277               const SMDS_MeshNode* n =
4278                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4279               if (n == nSeam ||
4280                   setMovableNodes.find( n ) == setMovableNodes.end() )
4281                 continue;
4282               // add only nodes being closer to uv2 than to uv1
4283               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4284               //              0.5 * ( n->Y() + nSeam->Y() ),
4285               //              0.5 * ( n->Z() + nSeam->Z() ));
4286               // gp_XY uv;
4287               // getClosestUV( projector, pMid, uv );
4288               double x = uvMap[ n ]->Coord( iPar );
4289               if ( Abs( uv1.Coord( iPar ) - x ) >
4290                    Abs( uv2.Coord( iPar ) - x )) {
4291                 nodesNearSeam.insert( n );
4292                 nbUseMap2++;
4293               }
4294               else
4295                 nbUseMap1++;
4296             }
4297             // for centroidalSmooth all element nodes must
4298             // be on one side of a seam
4299             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4300               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4301               nn = 0;
4302               while ( nn++ < nbn ) {
4303                 const SMDS_MeshNode* n =
4304                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4305                 setMovableNodes.erase( n );
4306               }
4307             }
4308           }
4309         } // loop on nodes on seam
4310       } // loop on edge of a face
4311     } // if ( !face.IsNull() )
4312
4313     if ( setMovableNodes.empty() ) {
4314       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4315       continue; // goto next face
4316     }
4317
4318     // -------------
4319     // SMOOTHING //
4320     // -------------
4321
4322     int it = -1;
4323     double maxRatio = -1., maxDisplacement = -1.;
4324     set<const SMDS_MeshNode*>::iterator nodeToMove;
4325     for ( it = 0; it < theNbIterations; it++ ) {
4326       maxDisplacement = 0.;
4327       nodeToMove = setMovableNodes.begin();
4328       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4329         const SMDS_MeshNode* node = (*nodeToMove);
4330         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4331
4332         // smooth
4333         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4334         if ( theSmoothMethod == LAPLACIAN )
4335           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4336         else
4337           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4338
4339         // node displacement
4340         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4341         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4342         if ( aDispl > maxDisplacement )
4343           maxDisplacement = aDispl;
4344       }
4345       // no node movement => exit
4346       //if ( maxDisplacement < 1.e-16 ) {
4347       if ( maxDisplacement < disttol ) {
4348         MESSAGE("-- no node movement --");
4349         break;
4350       }
4351
4352       // check elements quality
4353       maxRatio  = 0;
4354       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4355       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4356         const SMDS_MeshElement* elem = (*elemIt);
4357         if ( !elem || elem->GetType() != SMDSAbs_Face )
4358           continue;
4359         SMESH::Controls::TSequenceOfXYZ aPoints;
4360         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4361           double aValue = aQualityFunc.GetValue( aPoints );
4362           if ( aValue > maxRatio )
4363             maxRatio = aValue;
4364         }
4365       }
4366       if ( maxRatio <= theTgtAspectRatio ) {
4367         MESSAGE("-- quality achived --");
4368         break;
4369       }
4370       if (it+1 == theNbIterations) {
4371         MESSAGE("-- Iteration limit exceeded --");
4372       }
4373     } // smoothing iterations
4374
4375     MESSAGE(" Face id: " << *fId <<
4376             " Nb iterstions: " << it <<
4377             " Displacement: " << maxDisplacement <<
4378             " Aspect Ratio " << maxRatio);
4379
4380     // ---------------------------------------
4381     // new nodes positions are computed,
4382     // record movement in DS and set new UV
4383     // ---------------------------------------
4384     nodeToMove = setMovableNodes.begin();
4385     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4386       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4387       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4388       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4389       if ( node_uv != uvMap.end() ) {
4390         gp_XY* uv = node_uv->second;
4391         node->SetPosition
4392           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4393       }
4394     }
4395
4396     // move medium nodes of quadratic elements
4397     if ( isQuadratic )
4398     {
4399       vector<const SMDS_MeshNode*> nodes;
4400       bool checkUV;
4401       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4402       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4403       {
4404         const SMDS_MeshElement* QF = *elemIt;
4405         if ( QF->IsQuadratic() )
4406         {
4407           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4408                         SMDS_MeshElement::iterator() );
4409           nodes.push_back( nodes[0] );
4410           gp_Pnt xyz;
4411           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4412           {
4413             if ( !surface.IsNull() )
4414             {
4415               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4416               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4417               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4418               xyz = surface->Value( uv.X(), uv.Y() );
4419             }
4420             else {
4421               xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4422             }
4423             if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4424               // we have to move a medium node
4425               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4426           }
4427         }
4428       }
4429     }
4430
4431   } // loop on face ids
4432
4433 }
4434
4435 namespace
4436 {
4437   //=======================================================================
4438   //function : isReverse
4439   //purpose  : Return true if normal of prevNodes is not co-directied with
4440   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4441   //           iNotSame is where prevNodes and nextNodes are different.
4442   //           If result is true then future volume orientation is OK
4443   //=======================================================================
4444
4445   bool isReverse(const SMDS_MeshElement*             face,
4446                  const vector<const SMDS_MeshNode*>& prevNodes,
4447                  const vector<const SMDS_MeshNode*>& nextNodes,
4448                  const int                           iNotSame)
4449   {
4450
4451     SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4452     SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4453     gp_XYZ extrDir( pN - pP ), faceNorm;
4454     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4455
4456     return faceNorm * extrDir < 0.0;
4457   }
4458
4459   //================================================================================
4460   /*!
4461    * \brief Assure that theElemSets[0] holds elements, not nodes
4462    */
4463   //================================================================================
4464
4465   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4466   {
4467     if ( !theElemSets[0].empty() &&
4468          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4469     {
4470       std::swap( theElemSets[0], theElemSets[1] );
4471     }
4472     else if ( !theElemSets[1].empty() &&
4473               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4474     {
4475       std::swap( theElemSets[0], theElemSets[1] );
4476     }
4477   }
4478 }
4479
4480 //=======================================================================
4481 /*!
4482  * \brief Create elements by sweeping an element
4483  * \param elem - element to sweep
4484  * \param newNodesItVec - nodes generated from each node of the element
4485  * \param newElems - generated elements
4486  * \param nbSteps - number of sweeping steps
4487  * \param srcElements - to append elem for each generated element
4488  */
4489 //=======================================================================
4490
4491 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4492                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4493                                     list<const SMDS_MeshElement*>&        newElems,
4494                                     const size_t                          nbSteps,
4495                                     SMESH_SequenceOfElemPtr&              srcElements)
4496 {
4497   //MESSAGE("sweepElement " << nbSteps);
4498   SMESHDS_Mesh* aMesh = GetMeshDS();
4499
4500   const int           nbNodes = elem->NbNodes();
4501   const int         nbCorners = elem->NbCornerNodes();
4502   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4503                                                           polyhedron creation !!! */
4504   // Loop on elem nodes:
4505   // find new nodes and detect same nodes indices
4506   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4507   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4508   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4509   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4510
4511   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4512   vector<int> sames(nbNodes);
4513   vector<bool> isSingleNode(nbNodes);
4514
4515   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4516     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4517     const SMDS_MeshNode*                         node = nnIt->first;
4518     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4519     if ( listNewNodes.empty() )
4520       return;
4521
4522     itNN   [ iNode ] = listNewNodes.begin();
4523     prevNod[ iNode ] = node;
4524     nextNod[ iNode ] = listNewNodes.front();
4525
4526     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4527                                                              corner node of linear */
4528     if ( prevNod[ iNode ] != nextNod [ iNode ])
4529       nbDouble += !isSingleNode[iNode];
4530
4531     if( iNode < nbCorners ) { // check corners only
4532       if ( prevNod[ iNode ] == nextNod [ iNode ])
4533         sames[nbSame++] = iNode;
4534       else
4535         iNotSameNode = iNode;
4536     }
4537   }
4538
4539   if ( nbSame == nbNodes || nbSame > 2) {
4540     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4541     return;
4542   }
4543
4544   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4545   {
4546     // fix nodes order to have bottom normal external
4547     if ( baseType == SMDSEntity_Polygon )
4548     {
4549       std::reverse( itNN.begin(), itNN.end() );
4550       std::reverse( prevNod.begin(), prevNod.end() );
4551       std::reverse( midlNod.begin(), midlNod.end() );
4552       std::reverse( nextNod.begin(), nextNod.end() );
4553       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4554     }
4555     else
4556     {
4557       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4558       SMDS_MeshCell::applyInterlace( ind, itNN );
4559       SMDS_MeshCell::applyInterlace( ind, prevNod );
4560       SMDS_MeshCell::applyInterlace( ind, nextNod );
4561       SMDS_MeshCell::applyInterlace( ind, midlNod );
4562       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4563       if ( nbSame > 0 )
4564       {
4565         sames[nbSame] = iNotSameNode;
4566         for ( int j = 0; j <= nbSame; ++j )
4567           for ( size_t i = 0; i < ind.size(); ++i )
4568             if ( ind[i] == sames[j] )
4569             {
4570               sames[j] = i;
4571               break;
4572             }
4573         iNotSameNode = sames[nbSame];
4574       }
4575     }
4576   }
4577   else if ( elem->GetType() == SMDSAbs_Edge )
4578   {
4579     // orient a new face same as adjacent one
4580     int i1, i2;
4581     const SMDS_MeshElement* e;
4582     TIDSortedElemSet dummy;
4583     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4584         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4585         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4586     {
4587       // there is an adjacent face, check order of nodes in it
4588       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4589       if ( sameOrder )
4590       {
4591         std::swap( itNN[0],    itNN[1] );
4592         std::swap( prevNod[0], prevNod[1] );
4593         std::swap( nextNod[0], nextNod[1] );
4594         isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4595         if ( nbSame > 0 )
4596           sames[0] = 1 - sames[0];
4597         iNotSameNode = 1 - iNotSameNode;
4598       }
4599     }
4600   }
4601
4602   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4603   if ( nbSame > 0 ) {
4604     iSameNode    = sames[ nbSame-1 ];
4605     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4606     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4607     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4608   }
4609
4610   if ( baseType == SMDSEntity_Polygon )
4611   {
4612     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4613     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4614   }
4615   else if ( baseType == SMDSEntity_Quad_Polygon )
4616   {
4617     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4618     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4619   }
4620
4621   // make new elements
4622   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4623   {
4624     // get next nodes
4625     for ( iNode = 0; iNode < nbNodes; iNode++ )
4626     {
4627       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4628       nextNod[ iNode ] = *itNN[ iNode ]++;
4629     }
4630
4631     SMDS_MeshElement* aNewElem = 0;
4632     /*if(!elem->IsPoly())*/ {
4633       switch ( baseType ) {
4634       case SMDSEntity_0D:
4635       case SMDSEntity_Node: { // sweep NODE
4636         if ( nbSame == 0 ) {
4637           if ( isSingleNode[0] )
4638             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4639           else
4640             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4641         }
4642         else
4643           return;
4644         break;
4645       }
4646       case SMDSEntity_Edge: { // sweep EDGE
4647         if ( nbDouble == 0 )
4648         {
4649           if ( nbSame == 0 ) // ---> quadrangle
4650             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4651                                       nextNod[ 1 ], nextNod[ 0 ] );
4652           else               // ---> triangle
4653             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4654                                       nextNod[ iNotSameNode ] );
4655         }
4656         else                 // ---> polygon
4657         {
4658           vector<const SMDS_MeshNode*> poly_nodes;
4659           poly_nodes.push_back( prevNod[0] );
4660           poly_nodes.push_back( prevNod[1] );
4661           if ( prevNod[1] != nextNod[1] )
4662           {
4663             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4664             poly_nodes.push_back( nextNod[1] );
4665           }
4666           if ( prevNod[0] != nextNod[0] )
4667           {
4668             poly_nodes.push_back( nextNod[0] );
4669             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4670           }
4671           switch ( poly_nodes.size() ) {
4672           case 3:
4673             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4674             break;
4675           case 4:
4676             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4677                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4678             break;
4679           default:
4680             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4681           }
4682         }
4683         break;
4684       }
4685       case SMDSEntity_Triangle: // TRIANGLE --->
4686         {
4687           if ( nbDouble > 0 ) break;
4688           if ( nbSame == 0 )       // ---> pentahedron
4689             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4690                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4691
4692           else if ( nbSame == 1 )  // ---> pyramid
4693             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4694                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4695                                          nextNod[ iSameNode ]);
4696
4697           else // 2 same nodes:       ---> tetrahedron
4698             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4699                                          nextNod[ iNotSameNode ]);
4700           break;
4701         }
4702       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4703         {
4704           if ( nbSame == 2 )
4705             return;
4706           if ( nbDouble+nbSame == 2 )
4707           {
4708             if(nbSame==0) {      // ---> quadratic quadrangle
4709               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4710                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4711             }
4712             else { //(nbSame==1) // ---> quadratic triangle
4713               if(sames[0]==2) {
4714                 return; // medium node on axis
4715               }
4716               else if(sames[0]==0)
4717                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4718                                           prevNod[2], midlNod[1], nextNod[2] );
4719               else // sames[0]==1
4720                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4721                                           prevNod[2], nextNod[2], midlNod[0]);
4722             }
4723           }
4724           else if ( nbDouble == 3 )
4725           {
4726             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4727               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4728                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4729             }
4730           }
4731           else
4732             return;
4733           break;
4734         }
4735       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4736         if ( nbDouble > 0 ) break;
4737
4738         if ( nbSame == 0 )       // ---> hexahedron
4739           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4740                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4741
4742         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4743           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4744                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4745                                        nextNod[ iSameNode ]);
4746           newElems.push_back( aNewElem );
4747           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4748                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4749                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4750         }
4751         else if ( nbSame == 2 ) { // ---> pentahedron
4752           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4753             // iBeforeSame is same too
4754             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4755                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4756                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4757           else
4758             // iAfterSame is same too
4759             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4760                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4761                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4762         }
4763         break;
4764       }
4765       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4766       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4767         if ( nbDouble+nbSame != 3 ) break;
4768         if(nbSame==0) {
4769           // --->  pentahedron with 15 nodes
4770           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4771                                        nextNod[0], nextNod[1], nextNod[2],
4772                                        prevNod[3], prevNod[4], prevNod[5],
4773                                        nextNod[3], nextNod[4], nextNod[5],
4774                                        midlNod[0], midlNod[1], midlNod[2]);
4775         }
4776         else if(nbSame==1) {
4777           // --->  2d order pyramid of 13 nodes
4778           int apex = iSameNode;
4779           int i0 = ( apex + 1 ) % nbCorners;
4780           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4781           int i0a = apex + 3;
4782           int i1a = i1 + 3;
4783           int i01 = i0 + 3;
4784           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4785                                       nextNod[i0], nextNod[i1], prevNod[apex],
4786                                       prevNod[i01], midlNod[i0],
4787                                       nextNod[i01], midlNod[i1],
4788                                       prevNod[i1a], prevNod[i0a],
4789                                       nextNod[i0a], nextNod[i1a]);
4790         }
4791         else if(nbSame==2) {
4792           // --->  2d order tetrahedron of 10 nodes
4793           int n1 = iNotSameNode;
4794           int n2 = ( n1 + 1             ) % nbCorners;
4795           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4796           int n12 = n1 + 3;
4797           int n23 = n2 + 3;
4798           int n31 = n3 + 3;
4799           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4800                                        prevNod[n12], prevNod[n23], prevNod[n31],
4801                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4802         }
4803         break;
4804       }
4805       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4806         if( nbSame == 0 ) {
4807           if ( nbDouble != 4 ) break;
4808           // --->  hexahedron with 20 nodes
4809           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4810                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4811                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4812                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4813                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4814         }
4815         else if(nbSame==1) {
4816           // ---> pyramid + pentahedron - can not be created since it is needed
4817           // additional middle node at the center of face
4818           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4819           return;
4820         }
4821         else if( nbSame == 2 ) {
4822           if ( nbDouble != 2 ) break;
4823           // --->  2d order Pentahedron with 15 nodes
4824           int n1,n2,n4,n5;
4825           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4826             // iBeforeSame is same too
4827             n1 = iBeforeSame;
4828             n2 = iOpposSame;
4829             n4 = iSameNode;
4830             n5 = iAfterSame;
4831           }
4832           else {
4833             // iAfterSame is same too
4834             n1 = iSameNode;
4835             n2 = iBeforeSame;
4836             n4 = iAfterSame;
4837             n5 = iOpposSame;
4838           }
4839           int n12 = n2 + 4;
4840           int n45 = n4 + 4;
4841           int n14 = n1 + 4;
4842           int n25 = n5 + 4;
4843           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4844                                        prevNod[n4], prevNod[n5], nextNod[n5],
4845                                        prevNod[n12], midlNod[n2], nextNod[n12],
4846                                        prevNod[n45], midlNod[n5], nextNod[n45],
4847                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4848         }
4849         break;
4850       }
4851       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4852
4853         if( nbSame == 0 && nbDouble == 9 ) {
4854           // --->  tri-quadratic hexahedron with 27 nodes
4855           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4856                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4857                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4858                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4859                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4860                                        prevNod[8], // bottom center
4861                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4862                                        nextNod[8], // top center
4863                                        midlNod[8]);// elem center
4864         }
4865         else
4866         {
4867           return;
4868         }
4869         break;
4870       }
4871       case SMDSEntity_Polygon: { // sweep POLYGON
4872
4873         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4874           // --->  hexagonal prism
4875           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4876                                        prevNod[3], prevNod[4], prevNod[5],
4877                                        nextNod[0], nextNod[1], nextNod[2],
4878                                        nextNod[3], nextNod[4], nextNod[5]);
4879         }
4880         break;
4881       }
4882       case SMDSEntity_Ball:
4883         return;
4884
4885       default:
4886         break;
4887       } // switch ( baseType )
4888     } // scope
4889
4890     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4891     {
4892       if ( baseType != SMDSEntity_Polygon )
4893       {
4894         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4895         SMDS_MeshCell::applyInterlace( ind, prevNod );
4896         SMDS_MeshCell::applyInterlace( ind, nextNod );
4897         SMDS_MeshCell::applyInterlace( ind, midlNod );
4898         SMDS_MeshCell::applyInterlace( ind, itNN );
4899         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4900         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4901       }
4902       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4903       vector<int> quantities (nbNodes + 2);
4904       polyedre_nodes.clear();
4905       quantities.clear();
4906
4907       // bottom of prism
4908       for (int inode = 0; inode < nbNodes; inode++)
4909         polyedre_nodes.push_back( prevNod[inode] );
4910       quantities.push_back( nbNodes );
4911
4912       // top of prism
4913       polyedre_nodes.push_back( nextNod[0] );
4914       for (int inode = nbNodes; inode-1; --inode )
4915         polyedre_nodes.push_back( nextNod[inode-1] );
4916       quantities.push_back( nbNodes );
4917
4918       // side faces
4919       // 3--6--2
4920       // |     |
4921       // 7     5
4922       // |     |
4923       // 0--4--1
4924       const int iQuad = elem->IsQuadratic();
4925       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4926       {
4927         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4928         int inextface = (iface+1+iQuad) % nbNodes;
4929         int imid      = (iface+1) % nbNodes;
4930         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4931         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4932         polyedre_nodes.push_back( prevNod[iface] );             // 1
4933         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4934         {
4935           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4936           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4937         }
4938         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4939         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4940         {
4941           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4942           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4943         }
4944         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4945         if ( nbFaceNodes > 2 )
4946           quantities.push_back( nbFaceNodes );
4947         else // degenerated face
4948           polyedre_nodes.resize( prevNbNodes );
4949       }
4950       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4951
4952     } // try to create a polyherdal prism
4953
4954     if ( aNewElem ) {
4955       newElems.push_back( aNewElem );
4956       myLastCreatedElems.Append(aNewElem);
4957       srcElements.Append( elem );
4958     }
4959
4960     // set new prev nodes
4961     for ( iNode = 0; iNode < nbNodes; iNode++ )
4962       prevNod[ iNode ] = nextNod[ iNode ];
4963
4964   } // loop on steps
4965 }
4966
4967 //=======================================================================
4968 /*!
4969  * \brief Create 1D and 2D elements around swept elements
4970  * \param mapNewNodes - source nodes and ones generated from them
4971  * \param newElemsMap - source elements and ones generated from them
4972  * \param elemNewNodesMap - nodes generated from each node of each element
4973  * \param elemSet - all swept elements
4974  * \param nbSteps - number of sweeping steps
4975  * \param srcElements - to append elem for each generated element
4976  */
4977 //=======================================================================
4978
4979 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4980                                   TTElemOfElemListMap &    newElemsMap,
4981                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4982                                   TIDSortedElemSet&        elemSet,
4983                                   const int                nbSteps,
4984                                   SMESH_SequenceOfElemPtr& srcElements)
4985 {
4986   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4987   SMESHDS_Mesh* aMesh = GetMeshDS();
4988
4989   // Find nodes belonging to only one initial element - sweep them into edges.
4990
4991   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4992   for ( ; nList != mapNewNodes.end(); nList++ )
4993   {
4994     const SMDS_MeshNode* node =
4995       static_cast<const SMDS_MeshNode*>( nList->first );
4996     if ( newElemsMap.count( node ))
4997       continue; // node was extruded into edge
4998     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4999     int nbInitElems = 0;
5000     const SMDS_MeshElement* el = 0;
5001     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
5002     while ( eIt->more() && nbInitElems < 2 ) {
5003       const SMDS_MeshElement* e = eIt->next();
5004       SMDSAbs_ElementType  type = e->GetType();
5005       if ( type == SMDSAbs_Volume ||
5006            type < highType ||
5007            !elemSet.count(e))
5008         continue;
5009       if ( type > highType ) {
5010         nbInitElems = 0;
5011         highType    = type;
5012       }
5013       el = e;
5014       ++nbInitElems;
5015     }
5016     if ( nbInitElems == 1 ) {
5017       bool NotCreateEdge = el && el->IsMediumNode(node);
5018       if(!NotCreateEdge) {
5019         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5020         list<const SMDS_MeshElement*> newEdges;
5021         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5022       }
5023     }
5024   }
5025
5026   // Make a ceiling for each element ie an equal element of last new nodes.
5027   // Find free links of faces - make edges and sweep them into faces.
5028
5029   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5030
5031   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
5032   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5033   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5034   {
5035     const SMDS_MeshElement* elem = itElem->first;
5036     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5037
5038     if(itElem->second.size()==0) continue;
5039
5040     const bool isQuadratic = elem->IsQuadratic();
5041
5042     if ( elem->GetType() == SMDSAbs_Edge ) {
5043       // create a ceiling edge
5044       if ( !isQuadratic ) {
5045         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5046                                vecNewNodes[ 1 ]->second.back())) {
5047           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5048                                                    vecNewNodes[ 1 ]->second.back()));
5049           srcElements.Append( elem );
5050         }
5051       }
5052       else {
5053         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5054                                vecNewNodes[ 1 ]->second.back(),
5055                                vecNewNodes[ 2 ]->second.back())) {
5056           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5057                                                    vecNewNodes[ 1 ]->second.back(),
5058                                                    vecNewNodes[ 2 ]->second.back()));
5059           srcElements.Append( elem );
5060         }
5061       }
5062     }
5063     if ( elem->GetType() != SMDSAbs_Face )
5064       continue;
5065
5066     bool hasFreeLinks = false;
5067
5068     TIDSortedElemSet avoidSet;
5069     avoidSet.insert( elem );
5070
5071     set<const SMDS_MeshNode*> aFaceLastNodes;
5072     int iNode, nbNodes = vecNewNodes.size();
5073     if ( !isQuadratic ) {
5074       // loop on the face nodes
5075       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5076         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5077         // look for free links of the face
5078         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5079         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5080         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5081         // check if a link n1-n2 is free
5082         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5083           hasFreeLinks = true;
5084           // make a new edge and a ceiling for a new edge
5085           const SMDS_MeshElement* edge;
5086           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5087             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5088             srcElements.Append( myLastCreatedElems.Last() );
5089           }
5090           n1 = vecNewNodes[ iNode ]->second.back();
5091           n2 = vecNewNodes[ iNext ]->second.back();
5092           if ( !aMesh->FindEdge( n1, n2 )) {
5093             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5094             srcElements.Append( edge );
5095           }
5096         }
5097       }
5098     }
5099     else { // elem is quadratic face
5100       int nbn = nbNodes/2;
5101       for ( iNode = 0; iNode < nbn; iNode++ ) {
5102         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5103         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5104         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5105         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5106         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5107         // check if a link is free
5108         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5109              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5110              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5111           hasFreeLinks = true;
5112           // make an edge and a ceiling for a new edge
5113           // find medium node
5114           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5115             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5116             srcElements.Append( elem );
5117           }
5118           n1 = vecNewNodes[ iNode ]->second.back();
5119           n2 = vecNewNodes[ iNext ]->second.back();
5120           n3 = vecNewNodes[ iNode+nbn ]->second.back();
5121           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5122             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5123             srcElements.Append( elem );
5124           }
5125         }
5126       }
5127       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5128         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5129       }
5130     }
5131
5132     // sweep free links into faces
5133
5134     if ( hasFreeLinks ) {
5135       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5136       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5137
5138       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5139       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5140       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5141         initNodeSet.insert( vecNewNodes[ iNode ]->first );
5142         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5143       }
5144       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
5145         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5146         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5147       }
5148       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5149         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5150         std::advance( v, volNb );
5151         // find indices of free faces of a volume and their source edges
5152         list< int > freeInd;
5153         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5154         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5155         int iF, nbF = vTool.NbFaces();
5156         for ( iF = 0; iF < nbF; iF ++ ) {
5157           if (vTool.IsFreeFace( iF ) &&
5158               vTool.GetFaceNodes( iF, faceNodeSet ) &&
5159               initNodeSet != faceNodeSet) // except an initial face
5160           {
5161             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5162               continue;
5163             if ( faceNodeSet == initNodeSetNoCenter )
5164               continue;
5165             freeInd.push_back( iF );
5166             // find source edge of a free face iF
5167             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5168             vector<const SMDS_MeshNode*>::iterator lastCommom;
5169             commonNodes.resize( nbNodes, 0 );
5170             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5171                                                 initNodeSet.begin(), initNodeSet.end(),
5172                                                 commonNodes.begin());
5173             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5174               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5175             else
5176               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5177 #ifdef _DEBUG_
5178             if ( !srcEdges.back() )
5179             {
5180               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5181                    << iF << " of volume #" << vTool.ID() << endl;
5182             }
5183 #endif
5184           }
5185         }
5186         if ( freeInd.empty() )
5187           continue;
5188
5189         // create wall faces for all steps;
5190         // if such a face has been already created by sweep of edge,
5191         // assure that its orientation is OK
5192         for ( int iStep = 0; iStep < nbSteps; iStep++ )
5193         {
5194           vTool.Set( *v, /*ignoreCentralNodes=*/false );
5195           vTool.SetExternalNormal();
5196           const int nextShift = vTool.IsForward() ? +1 : -1;
5197           list< int >::iterator ind = freeInd.begin();
5198           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5199           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5200           {
5201             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5202             int nbn = vTool.NbFaceNodes( *ind );
5203             const SMDS_MeshElement * f = 0;
5204             if ( nbn == 3 )              ///// triangle
5205             {
5206               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5207               if ( !f ||
5208                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5209               {
5210                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5211                                                      nodes[ 1 ],
5212                                                      nodes[ 1 + nextShift ] };
5213                 if ( f )
5214                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5215                 else
5216                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5217                                                             newOrder[ 2 ] ));
5218               }
5219             }
5220             else if ( nbn == 4 )       ///// quadrangle
5221             {
5222               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5223               if ( !f ||
5224                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5225               {
5226                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5227                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
5228                 if ( f )
5229                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5230                 else
5231                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5232                                                             newOrder[ 2 ], newOrder[ 3 ]));
5233               }
5234             }
5235             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5236             {
5237               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5238               if ( !f ||
5239                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5240               {
5241                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5242                                                      nodes[2],
5243                                                      nodes[2 + 2*nextShift],
5244                                                      nodes[3 - 2*nextShift],
5245                                                      nodes[3],
5246                                                      nodes[3 + 2*nextShift]};
5247                 if ( f )
5248                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5249                 else
5250                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5251                                                             newOrder[ 1 ],
5252                                                             newOrder[ 2 ],
5253                                                             newOrder[ 3 ],
5254                                                             newOrder[ 4 ],
5255                                                             newOrder[ 5 ] ));
5256               }
5257             }
5258             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5259             {
5260               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5261                                    nodes[1], nodes[3], nodes[5], nodes[7] );
5262               if ( !f ||
5263                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5264               {
5265                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5266                                                      nodes[4 - 2*nextShift],
5267                                                      nodes[4],
5268                                                      nodes[4 + 2*nextShift],
5269                                                      nodes[1],
5270                                                      nodes[5 - 2*nextShift],
5271                                                      nodes[5],
5272                                                      nodes[5 + 2*nextShift] };
5273                 if ( f )
5274                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5275                 else
5276                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5277                                                            newOrder[ 2 ], newOrder[ 3 ],
5278                                                            newOrder[ 4 ], newOrder[ 5 ],
5279                                                            newOrder[ 6 ], newOrder[ 7 ]));
5280               }
5281             }
5282             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5283             {
5284               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5285                                       SMDSAbs_Face, /*noMedium=*/false);
5286               if ( !f ||
5287                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5288               {
5289                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5290                                                      nodes[4 - 2*nextShift],
5291                                                      nodes[4],
5292                                                      nodes[4 + 2*nextShift],
5293                                                      nodes[1],
5294                                                      nodes[5 - 2*nextShift],
5295                                                      nodes[5],
5296                                                      nodes[5 + 2*nextShift],
5297                                                      nodes[8] };
5298                 if ( f )
5299                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5300                 else
5301                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5302                                                            newOrder[ 2 ], newOrder[ 3 ],
5303                                                            newOrder[ 4 ], newOrder[ 5 ],
5304                                                            newOrder[ 6 ], newOrder[ 7 ],
5305                                                            newOrder[ 8 ]));
5306               }
5307             }
5308             else  //////// polygon
5309             {
5310               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5311               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5312               if ( !f ||
5313                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5314               {
5315                 if ( !vTool.IsForward() )
5316                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5317                 if ( f )
5318                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5319                 else
5320                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5321               }
5322             }
5323
5324             while ( srcElements.Length() < myLastCreatedElems.Length() )
5325               srcElements.Append( *srcEdge );
5326
5327           }  // loop on free faces
5328
5329           // go to the next volume
5330           iVol = 0;
5331           while ( iVol++ < nbVolumesByStep ) v++;
5332
5333         } // loop on steps
5334       } // loop on volumes of one step
5335     } // sweep free links into faces
5336
5337     // Make a ceiling face with a normal external to a volume
5338
5339     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5340     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5341     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5342
5343     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5344       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5345       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5346     }
5347     if ( iF >= 0 )
5348     {
5349       lastVol.SetExternalNormal();
5350       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5351       const               int nbn = lastVol.NbFaceNodes( iF );
5352       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5353       if ( !hasFreeLinks ||
5354            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5355       {
5356         const vector<int>& interlace =
5357           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5358         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5359
5360         AddElement( nodeVec, anyFace.Init( elem ));
5361
5362         while ( srcElements.Length() < myLastCreatedElems.Length() )
5363           srcElements.Append( elem );
5364       }
5365     }
5366   } // loop on swept elements
5367 }
5368
5369 //=======================================================================
5370 //function : RotationSweep
5371 //purpose  :
5372 //=======================================================================
5373
5374 SMESH_MeshEditor::PGroupIDs
5375 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5376                                 const gp_Ax1&      theAxis,
5377                                 const double       theAngle,
5378                                 const int          theNbSteps,
5379                                 const double       theTol,
5380                                 const bool         theMakeGroups,
5381                                 const bool         theMakeWalls)
5382 {
5383   myLastCreatedElems.Clear();
5384   myLastCreatedNodes.Clear();
5385
5386   // source elements for each generated one
5387   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5388
5389   MESSAGE( "RotationSweep()");
5390   gp_Trsf aTrsf;
5391   aTrsf.SetRotation( theAxis, theAngle );
5392   gp_Trsf aTrsf2;
5393   aTrsf2.SetRotation( theAxis, theAngle/2. );
5394
5395   gp_Lin aLine( theAxis );
5396   double aSqTol = theTol * theTol;
5397
5398   SMESHDS_Mesh* aMesh = GetMeshDS();
5399
5400   TNodeOfNodeListMap mapNewNodes;
5401   TElemOfVecOfNnlmiMap mapElemNewNodes;
5402   TTElemOfElemListMap newElemsMap;
5403
5404   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5405                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5406                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5407   // loop on theElemSets
5408   setElemsFirst( theElemSets );
5409   TIDSortedElemSet::iterator itElem;
5410   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5411   {
5412     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5413     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5414       const SMDS_MeshElement* elem = *itElem;
5415       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5416         continue;
5417       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5418       newNodesItVec.reserve( elem->NbNodes() );
5419
5420       // loop on elem nodes
5421       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5422       while ( itN->more() )
5423       {
5424         const SMDS_MeshNode* node = cast2Node( itN->next() );
5425
5426         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5427         double coord[3];
5428         aXYZ.Coord( coord[0], coord[1], coord[2] );
5429         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5430
5431         // check if a node has been already sweeped
5432         TNodeOfNodeListMapItr nIt =
5433           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5434         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5435         if ( listNewNodes.empty() )
5436         {
5437           // check if we are to create medium nodes between corner ones
5438           bool needMediumNodes = false;
5439           if ( isQuadraticMesh )
5440           {
5441             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5442             while (it->more() && !needMediumNodes )
5443             {
5444               const SMDS_MeshElement* invElem = it->next();
5445               if ( invElem != elem && !theElems.count( invElem )) continue;
5446               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5447               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5448                 needMediumNodes = true;
5449             }
5450           }
5451
5452           // make new nodes
5453           const SMDS_MeshNode * newNode = node;
5454           for ( int i = 0; i < theNbSteps; i++ ) {
5455             if ( !isOnAxis ) {
5456               if ( needMediumNodes )  // create a medium node
5457               {
5458                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5459                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5460                 myLastCreatedNodes.Append(newNode);
5461                 srcNodes.Append( node );
5462                 listNewNodes.push_back( newNode );
5463                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5464               }
5465               else {
5466                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5467               }
5468               // create a corner node
5469               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5470               myLastCreatedNodes.Append(newNode);
5471               srcNodes.Append( node );
5472               listNewNodes.push_back( newNode );
5473             }
5474             else {
5475               listNewNodes.push_back( newNode );
5476               // if ( needMediumNodes )
5477               //   listNewNodes.push_back( newNode );
5478             }
5479           }
5480         }
5481         newNodesItVec.push_back( nIt );
5482       }
5483       // make new elements
5484       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5485     }
5486   }
5487
5488   if ( theMakeWalls )
5489     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5490
5491   PGroupIDs newGroupIDs;
5492   if ( theMakeGroups )
5493     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5494
5495   return newGroupIDs;
5496 }
5497
5498 //=======================================================================
5499 //function : ExtrusParam
5500 //purpose  : standard construction
5501 //=======================================================================
5502
5503 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&  theStep,
5504                                             const int      theNbSteps,
5505                                             const int      theFlags,
5506                                             const double   theTolerance):
5507   myDir( theStep ),
5508   myFlags( theFlags ),
5509   myTolerance( theTolerance ),
5510   myElemsToUse( NULL )
5511 {
5512   mySteps = new TColStd_HSequenceOfReal;
5513   const double stepSize = theStep.Magnitude();
5514   for (int i=1; i<=theNbSteps; i++ )
5515     mySteps->Append( stepSize );
5516
5517   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5518       ( theTolerance > 0 ))
5519   {
5520     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5521   }
5522   else
5523   {
5524     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5525   }
5526 }
5527
5528 //=======================================================================
5529 //function : ExtrusParam
5530 //purpose  : steps are given explicitly
5531 //=======================================================================
5532
5533 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5534                                             Handle(TColStd_HSequenceOfReal) theSteps,
5535                                             const int                       theFlags,
5536                                             const double                    theTolerance):
5537   myDir( theDir ),
5538   mySteps( theSteps ),
5539   myFlags( theFlags ),
5540   myTolerance( theTolerance ),
5541   myElemsToUse( NULL )
5542 {
5543   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5544       ( theTolerance > 0 ))
5545   {
5546     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5547   }
5548   else
5549   {
5550     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5551   }
5552 }
5553
5554 //=======================================================================
5555 //function : ExtrusParam
5556 //purpose  : for extrusion by normal
5557 //=======================================================================
5558
5559 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5560                                             const int    theNbSteps,
5561                                             const int    theFlags,
5562                                             const int    theDim ):
5563   myDir( 1,0,0 ),
5564   mySteps( new TColStd_HSequenceOfReal ),
5565   myFlags( theFlags ),
5566   myTolerance( 0 ),
5567   myElemsToUse( NULL )
5568 {
5569   for (int i = 0; i < theNbSteps; i++ )
5570     mySteps->Append( theStepSize );
5571
5572   if ( theDim == 1 )
5573   {
5574     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5575   }
5576   else
5577   {
5578     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5579   }
5580 }
5581
5582 //=======================================================================
5583 //function : ExtrusParam::SetElementsToUse
5584 //purpose  : stores elements to use for extrusion by normal, depending on
5585 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag
5586 //=======================================================================
5587
5588 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems )
5589 {
5590   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5591 }
5592
5593 //=======================================================================
5594 //function : ExtrusParam::beginStepIter
5595 //purpose  : prepare iteration on steps
5596 //=======================================================================
5597
5598 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5599 {
5600   myWithMediumNodes = withMediumNodes;
5601   myNextStep = 1;
5602   myCurSteps.clear();
5603 }
5604 //=======================================================================
5605 //function : ExtrusParam::moreSteps
5606 //purpose  : are there more steps?
5607 //=======================================================================
5608
5609 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5610 {
5611   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5612 }
5613 //=======================================================================
5614 //function : ExtrusParam::nextStep
5615 //purpose  : returns the next step
5616 //=======================================================================
5617
5618 double SMESH_MeshEditor::ExtrusParam::nextStep()
5619 {
5620   double res = 0;
5621   if ( !myCurSteps.empty() )
5622   {
5623     res = myCurSteps.back();
5624     myCurSteps.pop_back();
5625   }
5626   else if ( myNextStep <= mySteps->Length() )
5627   {
5628     myCurSteps.push_back( mySteps->Value( myNextStep ));
5629     ++myNextStep;
5630     if ( myWithMediumNodes )
5631     {
5632       myCurSteps.back() /= 2.;
5633       myCurSteps.push_back( myCurSteps.back() );
5634     }
5635     res = nextStep();
5636   }
5637   return res;
5638 }
5639
5640 //=======================================================================
5641 //function : ExtrusParam::makeNodesByDir
5642 //purpose  : create nodes for standard extrusion
5643 //=======================================================================
5644
5645 int SMESH_MeshEditor::ExtrusParam::
5646 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5647                 const SMDS_MeshNode*              srcNode,
5648                 std::list<const SMDS_MeshNode*> & newNodes,
5649                 const bool                        makeMediumNodes)
5650 {
5651   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5652
5653   int nbNodes = 0;
5654   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5655   {
5656     p += myDir.XYZ() * nextStep();
5657     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5658     newNodes.push_back( newNode );
5659   }
5660   return nbNodes;
5661 }
5662
5663 //=======================================================================
5664 //function : ExtrusParam::makeNodesByDirAndSew
5665 //purpose  : create nodes for standard extrusion with sewing
5666 //=======================================================================
5667
5668 int SMESH_MeshEditor::ExtrusParam::
5669 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5670                       const SMDS_MeshNode*              srcNode,
5671                       std::list<const SMDS_MeshNode*> & newNodes,
5672                       const bool                        makeMediumNodes)
5673 {
5674   gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5675
5676   int nbNodes = 0;
5677   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5678   {
5679     P1 += myDir.XYZ() * nextStep();
5680
5681     // try to search in sequence of existing nodes
5682     // if myNodes.Length()>0 we 'nave to use given sequence
5683     // else - use all nodes of mesh
5684     const SMDS_MeshNode * node = 0;
5685     if ( myNodes.Length() > 0 ) {
5686       int i;
5687       for(i=1; i<=myNodes.Length(); i++) {
5688         gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5689         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5690         {
5691           node = myNodes.Value(i);
5692           break;
5693         }
5694       }
5695     }
5696     else {
5697       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5698       while(itn->more()) {
5699         SMESH_TNodeXYZ P2( itn->next() );
5700         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5701         {
5702           node = P2._node;
5703           break;
5704         }
5705       }
5706     }
5707
5708     if ( !node )
5709       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5710
5711     newNodes.push_back( node );
5712
5713   } // loop on steps
5714
5715   return nbNodes;
5716 }
5717
5718 //=======================================================================
5719 //function : ExtrusParam::makeNodesByNormal2D
5720 //purpose  : create nodes for extrusion using normals of faces
5721 //=======================================================================
5722
5723 int SMESH_MeshEditor::ExtrusParam::
5724 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5725                      const SMDS_MeshNode*              srcNode,
5726                      std::list<const SMDS_MeshNode*> & newNodes,
5727                      const bool                        makeMediumNodes)
5728 {
5729   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5730
5731   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5732
5733   // get normals to faces sharing srcNode
5734   vector< gp_XYZ > norms, baryCenters;
5735   gp_XYZ norm, avgNorm( 0,0,0 );
5736   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5737   while ( faceIt->more() )
5738   {
5739     const SMDS_MeshElement* face = faceIt->next();
5740     if ( myElemsToUse && !myElemsToUse->count( face ))
5741       continue;
5742     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5743     {
5744       norms.push_back( norm );
5745       avgNorm += norm;
5746       if ( !alongAvgNorm )
5747       {
5748         gp_XYZ bc(0,0,0);
5749         int nbN = 0;
5750         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5751           bc += SMESH_TNodeXYZ( nIt->next() );
5752         baryCenters.push_back( bc / nbN );
5753       }
5754     }
5755   }
5756
5757   if ( norms.empty() ) return 0;
5758
5759   double normSize = avgNorm.Modulus();
5760   if ( normSize < std::numeric_limits<double>::min() )
5761     return 0;
5762
5763   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5764   {
5765     myDir = avgNorm;
5766     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5767   }
5768
5769   avgNorm /= normSize;
5770
5771   int nbNodes = 0;
5772   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5773   {
5774     gp_XYZ pNew = p;
5775     double stepSize = nextStep();
5776
5777     if ( norms.size() > 1 )
5778     {
5779       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5780       {
5781         // translate plane of a face
5782         baryCenters[ iF ] += norms[ iF ] * stepSize;
5783
5784         // find point of intersection of the face plane located at baryCenters[ iF ]
5785         // and avgNorm located at pNew
5786         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5787         double dot  = ( norms[ iF ] * avgNorm );
5788         if ( dot < std::numeric_limits<double>::min() )
5789           dot = stepSize * 1e-3;
5790         double step = -( norms[ iF ] * pNew + d ) / dot;
5791         pNew += step * avgNorm;
5792       }
5793     }
5794     else
5795     {
5796       pNew += stepSize * avgNorm;
5797     }
5798     p = pNew;
5799
5800     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5801     newNodes.push_back( newNode );
5802   }
5803   return nbNodes;
5804 }
5805
5806 //=======================================================================
5807 //function : ExtrusParam::makeNodesByNormal1D
5808 //purpose  : create nodes for extrusion using normals of edges
5809 //=======================================================================
5810
5811 int SMESH_MeshEditor::ExtrusParam::
5812 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5813                      const SMDS_MeshNode*              srcNode,
5814                      std::list<const SMDS_MeshNode*> & newNodes,
5815                      const bool                        makeMediumNodes)
5816 {
5817   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5818   return 0;
5819 }
5820
5821 //=======================================================================
5822 //function : ExtrusionSweep
5823 //purpose  :
5824 //=======================================================================
5825
5826 SMESH_MeshEditor::PGroupIDs
5827 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5828                                   const gp_Vec&        theStep,
5829                                   const int            theNbSteps,
5830                                   TTElemOfElemListMap& newElemsMap,
5831                                   const int            theFlags,
5832                                   const double         theTolerance)
5833 {
5834   ExtrusParam aParams( theStep, theNbSteps, theFlags, theTolerance );
5835   return ExtrusionSweep( theElems, aParams, newElemsMap );
5836 }
5837
5838
5839 //=======================================================================
5840 //function : ExtrusionSweep
5841 //purpose  :
5842 //=======================================================================
5843
5844 SMESH_MeshEditor::PGroupIDs
5845 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5846                                   ExtrusParam&         theParams,
5847                                   TTElemOfElemListMap& newElemsMap)
5848 {
5849   myLastCreatedElems.Clear();
5850   myLastCreatedNodes.Clear();
5851
5852   // source elements for each generated one
5853   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5854
5855   //SMESHDS_Mesh* aMesh = GetMeshDS();
5856
5857   setElemsFirst( theElemSets );
5858   const int nbSteps = theParams.NbSteps();
5859   theParams.SetElementsToUse( theElemSets[0] );
5860
5861   TNodeOfNodeListMap mapNewNodes;
5862   //TNodeOfNodeVecMap mapNewNodes;
5863   TElemOfVecOfNnlmiMap mapElemNewNodes;
5864   //TElemOfVecOfMapNodesMap mapElemNewNodes;
5865
5866   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5867                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5868                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5869   // loop on theElems
5870   TIDSortedElemSet::iterator itElem;
5871   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5872   {
5873     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5874     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5875     {
5876       // check element type
5877       const SMDS_MeshElement* elem = *itElem;
5878       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5879         continue;
5880
5881       const size_t nbNodes = elem->NbNodes();
5882       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5883       newNodesItVec.reserve( nbNodes );
5884
5885       // loop on elem nodes
5886       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5887       while ( itN->more() )
5888       {
5889         // check if a node has been already sweeped
5890         const SMDS_MeshNode* node = cast2Node( itN->next() );
5891         TNodeOfNodeListMap::iterator nIt =
5892           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5893         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5894         if ( listNewNodes.empty() )
5895         {
5896           // make new nodes
5897
5898           // check if we are to create medium nodes between corner ones
5899           bool needMediumNodes = false;
5900           if ( isQuadraticMesh )
5901           {
5902             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5903             while (it->more() && !needMediumNodes )
5904             {
5905               const SMDS_MeshElement* invElem = it->next();
5906               if ( invElem != elem && !theElems.count( invElem )) continue;
5907               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5908               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5909                 needMediumNodes = true;
5910             }
5911           }
5912           // create nodes for all steps
5913           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5914           {
5915             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5916             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5917             {
5918               myLastCreatedNodes.Append( *newNodesIt );
5919               srcNodes.Append( node );
5920             }
5921           }
5922           else
5923           {
5924             break; // newNodesItVec will be shorter than nbNodes
5925           }
5926         }
5927         newNodesItVec.push_back( nIt );
5928       }
5929       // make new elements
5930       if ( newNodesItVec.size() == nbNodes )
5931         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5932     }
5933   }
5934
5935   if ( theParams.ToMakeBoundary() ) {
5936     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5937   }
5938   PGroupIDs newGroupIDs;
5939   if ( theParams.ToMakeGroups() )
5940     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5941
5942   return newGroupIDs;
5943 }
5944
5945 //=======================================================================
5946 //function : ExtrusionAlongTrack
5947 //purpose  :
5948 //=======================================================================
5949 SMESH_MeshEditor::Extrusion_Error
5950 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5951                                        SMESH_subMesh*       theTrack,
5952                                        const SMDS_MeshNode* theN1,
5953                                        const bool           theHasAngles,
5954                                        list<double>&        theAngles,
5955                                        const bool           theLinearVariation,
5956                                        const bool           theHasRefPoint,
5957                                        const gp_Pnt&        theRefPoint,
5958                                        const bool           theMakeGroups)
5959 {
5960   MESSAGE("ExtrusionAlongTrack");
5961   myLastCreatedElems.Clear();
5962   myLastCreatedNodes.Clear();
5963
5964   int aNbE;
5965   std::list<double> aPrms;
5966   TIDSortedElemSet::iterator itElem;
5967
5968   gp_XYZ aGC;
5969   TopoDS_Edge aTrackEdge;
5970   TopoDS_Vertex aV1, aV2;
5971
5972   SMDS_ElemIteratorPtr aItE;
5973   SMDS_NodeIteratorPtr aItN;
5974   SMDSAbs_ElementType aTypeE;
5975
5976   TNodeOfNodeListMap mapNewNodes;
5977
5978   // 1. Check data
5979   aNbE = theElements[0].size() + theElements[1].size();
5980   // nothing to do
5981   if ( !aNbE )
5982     return EXTR_NO_ELEMENTS;
5983
5984   // 1.1 Track Pattern
5985   ASSERT( theTrack );
5986
5987   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5988   if ( !pSubMeshDS )
5989     return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
5990                                 theHasAngles, theAngles, theLinearVariation,
5991                                 theHasRefPoint, theRefPoint, theMakeGroups );
5992
5993   aItE = pSubMeshDS->GetElements();
5994   while ( aItE->more() ) {
5995     const SMDS_MeshElement* pE = aItE->next();
5996     aTypeE = pE->GetType();
5997     // Pattern must contain links only
5998     if ( aTypeE != SMDSAbs_Edge )
5999       return EXTR_PATH_NOT_EDGE;
6000   }
6001
6002   list<SMESH_MeshEditor_PathPoint> fullList;
6003
6004   const TopoDS_Shape& aS = theTrack->GetSubShape();
6005   // Sub-shape for the Pattern must be an Edge or Wire
6006   if( aS.ShapeType() == TopAbs_EDGE ) {
6007     aTrackEdge = TopoDS::Edge( aS );
6008     // the Edge must not be degenerated
6009     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6010       return EXTR_BAD_PATH_SHAPE;
6011     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6012     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
6013     const SMDS_MeshNode* aN1 = aItN->next();
6014     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
6015     const SMDS_MeshNode* aN2 = aItN->next();
6016     // starting node must be aN1 or aN2
6017     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6018       return EXTR_BAD_STARTING_NODE;
6019     aItN = pSubMeshDS->GetNodes();
6020     while ( aItN->more() ) {
6021       const SMDS_MeshNode* pNode = aItN->next();
6022       const SMDS_EdgePosition* pEPos =
6023         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6024       double aT = pEPos->GetUParameter();
6025       aPrms.push_back( aT );
6026     }
6027     //Extrusion_Error err =
6028     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6029   } else if( aS.ShapeType() == TopAbs_WIRE ) {
6030     list< SMESH_subMesh* > LSM;
6031     TopTools_SequenceOfShape Edges;
6032     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6033     while(itSM->more()) {
6034       SMESH_subMesh* SM = itSM->next();
6035       LSM.push_back(SM);
6036       const TopoDS_Shape& aS = SM->GetSubShape();
6037       Edges.Append(aS);
6038     }
6039     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6040     int startNid = theN1->GetID();
6041     TColStd_MapOfInteger UsedNums;
6042
6043     int NbEdges = Edges.Length();
6044     int i = 1;
6045     for(; i<=NbEdges; i++) {
6046       int k = 0;
6047       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6048       for(; itLSM!=LSM.end(); itLSM++) {
6049         k++;
6050         if(UsedNums.Contains(k)) continue;
6051         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6052         SMESH_subMesh* locTrack = *itLSM;
6053         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6054         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6055         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6056         const SMDS_MeshNode* aN1 = aItN->next();
6057         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6058         const SMDS_MeshNode* aN2 = aItN->next();
6059         // starting node must be aN1 or aN2
6060         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6061         // 2. Collect parameters on the track edge
6062         aPrms.clear();
6063         aItN = locMeshDS->GetNodes();
6064         while ( aItN->more() ) {
6065           const SMDS_MeshNode* pNode = aItN->next();
6066           const SMDS_EdgePosition* pEPos =
6067             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6068           double aT = pEPos->GetUParameter();
6069           aPrms.push_back( aT );
6070         }
6071         list<SMESH_MeshEditor_PathPoint> LPP;
6072         //Extrusion_Error err =
6073         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6074         LLPPs.push_back(LPP);
6075         UsedNums.Add(k);
6076         // update startN for search following egde
6077         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6078         else startNid = aN1->GetID();
6079         break;
6080       }
6081     }
6082     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6083     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6084     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6085     for(; itPP!=firstList.end(); itPP++) {
6086       fullList.push_back( *itPP );
6087     }
6088     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6089     fullList.pop_back();
6090     itLLPP++;
6091     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6092       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6093       itPP = currList.begin();
6094       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6095       gp_Dir D1 = PP1.Tangent();
6096       gp_Dir D2 = PP2.Tangent();
6097       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6098                            (D1.Z()+D2.Z())/2 ) );
6099       PP1.SetTangent(Dnew);
6100       fullList.push_back(PP1);
6101       itPP++;
6102       for(; itPP!=firstList.end(); itPP++) {
6103         fullList.push_back( *itPP );
6104       }
6105       PP1 = fullList.back();
6106       fullList.pop_back();
6107     }
6108     // if wire not closed
6109     fullList.push_back(PP1);
6110     // else ???
6111   }
6112   else {
6113     return EXTR_BAD_PATH_SHAPE;
6114   }
6115
6116   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6117                           theHasRefPoint, theRefPoint, theMakeGroups);
6118 }
6119
6120
6121 //=======================================================================
6122 //function : ExtrusionAlongTrack
6123 //purpose  :
6124 //=======================================================================
6125 SMESH_MeshEditor::Extrusion_Error
6126 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
6127                                        SMESH_Mesh*          theTrack,
6128                                        const SMDS_MeshNode* theN1,
6129                                        const bool           theHasAngles,
6130                                        list<double>&        theAngles,
6131                                        const bool           theLinearVariation,
6132                                        const bool           theHasRefPoint,
6133                                        const gp_Pnt&        theRefPoint,
6134                                        const bool           theMakeGroups)
6135 {
6136   myLastCreatedElems.Clear();
6137   myLastCreatedNodes.Clear();
6138
6139   int aNbE;
6140   std::list<double> aPrms;
6141   TIDSortedElemSet::iterator itElem;
6142
6143   gp_XYZ aGC;
6144   TopoDS_Edge aTrackEdge;
6145   TopoDS_Vertex aV1, aV2;
6146
6147   SMDS_ElemIteratorPtr aItE;
6148   SMDS_NodeIteratorPtr aItN;
6149   SMDSAbs_ElementType aTypeE;
6150
6151   TNodeOfNodeListMap mapNewNodes;
6152
6153   // 1. Check data
6154   aNbE = theElements[0].size() + theElements[1].size();
6155   // nothing to do
6156   if ( !aNbE )
6157     return EXTR_NO_ELEMENTS;
6158
6159   // 1.1 Track Pattern
6160   ASSERT( theTrack );
6161
6162   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6163
6164   aItE = pMeshDS->elementsIterator();
6165   while ( aItE->more() ) {
6166     const SMDS_MeshElement* pE = aItE->next();
6167     aTypeE = pE->GetType();
6168     // Pattern must contain links only
6169     if ( aTypeE != SMDSAbs_Edge )
6170       return EXTR_PATH_NOT_EDGE;
6171   }
6172
6173   list<SMESH_MeshEditor_PathPoint> fullList;
6174
6175   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6176
6177   if ( !theTrack->HasShapeToMesh() ) {
6178     //Mesh without shape
6179     const SMDS_MeshNode* currentNode = NULL;
6180     const SMDS_MeshNode* prevNode = theN1;
6181     std::vector<const SMDS_MeshNode*> aNodesList;
6182     aNodesList.push_back(theN1);
6183     int nbEdges = 0, conn=0;
6184     const SMDS_MeshElement* prevElem = NULL;
6185     const SMDS_MeshElement* currentElem = NULL;
6186     int totalNbEdges = theTrack->NbEdges();
6187     SMDS_ElemIteratorPtr nIt;
6188
6189     //check start node
6190     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6191       return EXTR_BAD_STARTING_NODE;
6192     }
6193
6194     conn = nbEdgeConnectivity(theN1);
6195     if( conn != 1 )
6196       return EXTR_PATH_NOT_EDGE;
6197
6198     aItE = theN1->GetInverseElementIterator();
6199     prevElem = aItE->next();
6200     currentElem = prevElem;
6201     //Get all nodes
6202     if(totalNbEdges == 1 ) {
6203       nIt = currentElem->nodesIterator();
6204       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6205       if(currentNode == prevNode)
6206         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6207       aNodesList.push_back(currentNode);
6208     } else {
6209       nIt = currentElem->nodesIterator();
6210       while( nIt->more() ) {
6211         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6212         if(currentNode == prevNode)
6213           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6214         aNodesList.push_back(currentNode);
6215
6216         //case of the closed mesh
6217         if(currentNode == theN1) {
6218           nbEdges++;
6219           break;
6220         }
6221
6222         conn = nbEdgeConnectivity(currentNode);
6223         if(conn > 2) {
6224           return EXTR_PATH_NOT_EDGE;
6225         }else if( conn == 1 && nbEdges > 0 ) {
6226           //End of the path
6227           nbEdges++;
6228           break;
6229         }else {
6230           prevNode = currentNode;
6231           aItE = currentNode->GetInverseElementIterator();
6232           currentElem = aItE->next();
6233           if( currentElem  == prevElem)
6234             currentElem = aItE->next();
6235           nIt = currentElem->nodesIterator();
6236           prevElem = currentElem;
6237           nbEdges++;
6238         }
6239       }
6240     }
6241
6242     if(nbEdges != totalNbEdges)
6243       return EXTR_PATH_NOT_EDGE;
6244
6245     TopTools_SequenceOfShape Edges;
6246     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6247     int startNid = theN1->GetID();
6248     for ( size_t i = 1; i < aNodesList.size(); i++ )
6249     {
6250       gp_Pnt     p1 = SMESH_TNodeXYZ( aNodesList[i-1] );
6251       gp_Pnt     p2 = SMESH_TNodeXYZ( aNodesList[i] );
6252       TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6253       list<SMESH_MeshEditor_PathPoint> LPP;
6254       aPrms.clear();
6255       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6256       LLPPs.push_back(LPP);
6257       if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i  ]->GetID();
6258       else                                        startNid = aNodesList[i-1]->GetID();
6259     }
6260
6261     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6262     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6263     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6264     for(; itPP!=firstList.end(); itPP++) {
6265       fullList.push_back( *itPP );
6266     }
6267
6268     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6269     SMESH_MeshEditor_PathPoint PP2;
6270     fullList.pop_back();
6271     itLLPP++;
6272     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6273       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6274       itPP = currList.begin();
6275       PP2 = currList.front();
6276       gp_Dir D1 = PP1.Tangent();
6277       gp_Dir D2 = PP2.Tangent();
6278       gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6279       PP1.SetTangent(Dnew);
6280       fullList.push_back(PP1);
6281       itPP++;
6282       for(; itPP!=currList.end(); itPP++) {
6283         fullList.push_back( *itPP );
6284       }
6285       PP1 = fullList.back();
6286       fullList.pop_back();
6287     }
6288     fullList.push_back(PP1);
6289
6290   } // Sub-shape for the Pattern must be an Edge or Wire
6291   else if ( aS.ShapeType() == TopAbs_EDGE )
6292   {
6293     aTrackEdge = TopoDS::Edge( aS );
6294     // the Edge must not be degenerated
6295     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6296       return EXTR_BAD_PATH_SHAPE;
6297     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6298     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6299     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6300     // starting node must be aN1 or aN2
6301     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6302       return EXTR_BAD_STARTING_NODE;
6303     aItN = pMeshDS->nodesIterator();
6304     while ( aItN->more() ) {
6305       const SMDS_MeshNode* pNode = aItN->next();
6306       if( pNode==aN1 || pNode==aN2 ) continue;
6307       const SMDS_EdgePosition* pEPos =
6308         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6309       double aT = pEPos->GetUParameter();
6310       aPrms.push_back( aT );
6311     }
6312     //Extrusion_Error err =
6313     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6314   }
6315   else if( aS.ShapeType() == TopAbs_WIRE ) {
6316     list< SMESH_subMesh* > LSM;
6317     TopTools_SequenceOfShape Edges;
6318     TopExp_Explorer eExp(aS, TopAbs_EDGE);
6319     for(; eExp.More(); eExp.Next()) {
6320       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6321       if( SMESH_Algo::isDegenerated(E) ) continue;
6322       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6323       if(SM) {
6324         LSM.push_back(SM);
6325         Edges.Append(E);
6326       }
6327     }
6328     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6329     TopoDS_Vertex aVprev;
6330     TColStd_MapOfInteger UsedNums;
6331     int NbEdges = Edges.Length();
6332     int i = 1;
6333     for(; i<=NbEdges; i++) {
6334       int k = 0;
6335       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6336       for(; itLSM!=LSM.end(); itLSM++) {
6337         k++;
6338         if(UsedNums.Contains(k)) continue;
6339         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6340         SMESH_subMesh* locTrack = *itLSM;
6341         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6342         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6343         bool aN1isOK = false, aN2isOK = false;
6344         if ( aVprev.IsNull() ) {
6345           // if previous vertex is not yet defined, it means that we in the beginning of wire
6346           // and we have to find initial vertex corresponding to starting node theN1
6347           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6348           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6349           // starting node must be aN1 or aN2
6350           aN1isOK = ( aN1 && aN1 == theN1 );
6351           aN2isOK = ( aN2 && aN2 == theN1 );
6352         }
6353         else {
6354           // we have specified ending vertex of the previous edge on the previous iteration
6355           // and we have just to check that it corresponds to any vertex in current segment
6356           aN1isOK = aVprev.IsSame( aV1 );
6357           aN2isOK = aVprev.IsSame( aV2 );
6358         }
6359         if ( !aN1isOK && !aN2isOK ) continue;
6360         // 2. Collect parameters on the track edge
6361         aPrms.clear();
6362         aItN = locMeshDS->GetNodes();
6363         while ( aItN->more() ) {
6364           const SMDS_MeshNode*     pNode = aItN->next();
6365           const SMDS_EdgePosition* pEPos =
6366             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6367           double aT = pEPos->GetUParameter();
6368           aPrms.push_back( aT );
6369         }
6370         list<SMESH_MeshEditor_PathPoint> LPP;
6371         //Extrusion_Error err =
6372         MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6373         LLPPs.push_back(LPP);
6374         UsedNums.Add(k);
6375         // update startN for search following egde
6376         if ( aN1isOK ) aVprev = aV2;
6377         else           aVprev = aV1;
6378         break;
6379       }
6380     }
6381     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6382     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6383     fullList.splice( fullList.end(), firstList );
6384
6385     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6386     fullList.pop_back();
6387     itLLPP++;
6388     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6389       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6390       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6391       gp_Dir D1 = PP1.Tangent();
6392       gp_Dir D2 = PP2.Tangent();
6393       gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6394       PP1.SetTangent(Dnew);
6395       fullList.push_back(PP1);
6396       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6397       PP1 = fullList.back();
6398       fullList.pop_back();
6399     }
6400     // if wire not closed
6401     fullList.push_back(PP1);
6402     // else ???
6403   }
6404   else {
6405     return EXTR_BAD_PATH_SHAPE;
6406   }
6407
6408   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6409                           theHasRefPoint, theRefPoint, theMakeGroups);
6410 }
6411
6412
6413 //=======================================================================
6414 //function : MakeEdgePathPoints
6415 //purpose  : auxilary for ExtrusionAlongTrack
6416 //=======================================================================
6417 SMESH_MeshEditor::Extrusion_Error
6418 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>&                aPrms,
6419                                      const TopoDS_Edge&                aTrackEdge,
6420                                      bool                              FirstIsStart,
6421                                      list<SMESH_MeshEditor_PathPoint>& LPP)
6422 {
6423   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6424   aTolVec=1.e-7;
6425   aTolVec2=aTolVec*aTolVec;
6426   double aT1, aT2;
6427   TopoDS_Vertex aV1, aV2;
6428   TopExp::Vertices( aTrackEdge, aV1, aV2 );
6429   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6430   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6431   // 2. Collect parameters on the track edge
6432   aPrms.push_front( aT1 );
6433   aPrms.push_back( aT2 );
6434   // sort parameters
6435   aPrms.sort();
6436   if( FirstIsStart ) {
6437     if ( aT1 > aT2 ) {
6438       aPrms.reverse();
6439     }
6440   }
6441   else {
6442     if ( aT2 > aT1 ) {
6443       aPrms.reverse();
6444     }
6445   }
6446   // 3. Path Points
6447   SMESH_MeshEditor_PathPoint aPP;
6448   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6449   std::list<double>::iterator aItD = aPrms.begin();
6450   for(; aItD != aPrms.end(); ++aItD) {
6451     double aT = *aItD;
6452     gp_Pnt aP3D;
6453     gp_Vec aVec;
6454     aC3D->D1( aT, aP3D, aVec );
6455     aL2 = aVec.SquareMagnitude();
6456     if ( aL2 < aTolVec2 )
6457       return EXTR_CANT_GET_TANGENT;
6458     gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6459     aPP.SetPnt( aP3D );
6460     aPP.SetTangent( aTgt );
6461     aPP.SetParameter( aT );
6462     LPP.push_back(aPP);
6463   }
6464   return EXTR_OK;
6465 }
6466
6467
6468 //=======================================================================
6469 //function : MakeExtrElements
6470 //purpose  : auxilary for ExtrusionAlongTrack
6471 //=======================================================================
6472 SMESH_MeshEditor::Extrusion_Error
6473 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet                  theElemSets[2],
6474                                    list<SMESH_MeshEditor_PathPoint>& fullList,
6475                                    const bool                        theHasAngles,
6476                                    list<double>&                     theAngles,
6477                                    const bool                        theLinearVariation,
6478                                    const bool                        theHasRefPoint,
6479                                    const gp_Pnt&                     theRefPoint,
6480                                    const bool                        theMakeGroups)
6481 {
6482   const int aNbTP = fullList.size();
6483
6484   // Angles
6485   if( theHasAngles && !theAngles.empty() && theLinearVariation )
6486     LinearAngleVariation(aNbTP-1, theAngles);
6487
6488   // fill vector of path points with angles
6489   vector<SMESH_MeshEditor_PathPoint> aPPs;
6490   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6491   list<double>::iterator                 itAngles = theAngles.begin();
6492   aPPs.push_back( *itPP++ );
6493   for( ; itPP != fullList.end(); itPP++) {
6494     aPPs.push_back( *itPP );
6495     if ( theHasAngles && itAngles != theAngles.end() )
6496       aPPs.back().SetAngle( *itAngles++ );
6497   }
6498
6499   TNodeOfNodeListMap   mapNewNodes;
6500   TElemOfVecOfNnlmiMap mapElemNewNodes;
6501   TTElemOfElemListMap  newElemsMap;
6502   TIDSortedElemSet::iterator itElem;
6503   // source elements for each generated one
6504   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6505
6506   // 3. Center of rotation aV0
6507   gp_Pnt aV0 = theRefPoint;
6508   if ( !theHasRefPoint )
6509   {
6510     gp_XYZ aGC( 0.,0.,0. );
6511     TIDSortedElemSet newNodes;
6512
6513     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6514     {
6515       TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6516       itElem = theElements.begin();
6517       for ( ; itElem != theElements.end(); itElem++ )
6518       {
6519         const SMDS_MeshElement* elem = *itElem;
6520         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6521         while ( itN->more() ) {
6522           const SMDS_MeshElement* node = itN->next();
6523           if ( newNodes.insert( node ).second )
6524             aGC += SMESH_TNodeXYZ( node );
6525         }
6526       }
6527     }
6528     aGC /= newNodes.size();
6529     aV0.SetXYZ( aGC );
6530   } // if (!theHasRefPoint) {
6531
6532   // 4. Processing the elements
6533   SMESHDS_Mesh* aMesh = GetMeshDS();
6534   list<const SMDS_MeshNode*> emptyList;
6535
6536   setElemsFirst( theElemSets );
6537   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6538   {
6539     TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6540     for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6541     {
6542       const SMDS_MeshElement* elem = *itElem;
6543
6544       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6545       newNodesItVec.reserve( elem->NbNodes() );
6546
6547       // loop on elem nodes
6548       int nodeIndex = -1;
6549       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6550       while ( itN->more() )
6551       {
6552         ++nodeIndex;
6553         // check if a node has been already processed
6554         const SMDS_MeshNode* node = cast2Node( itN->next() );
6555         TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6556         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6557         if ( listNewNodes.empty() )
6558         {
6559           // make new nodes
6560           Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6561           gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6562           gp_Ax1 anAx1, anAxT1T0;
6563           gp_Dir aDT1x, aDT0x, aDT1T0;
6564
6565           aTolAng=1.e-4;
6566
6567           aV0x = aV0;
6568           aPN0 = SMESH_TNodeXYZ( node );
6569
6570           const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6571           aP0x = aPP0.Pnt();
6572           aDT0x= aPP0.Tangent();
6573
6574           for ( int j = 1; j < aNbTP; ++j ) {
6575             const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6576             aP1x     = aPP1.Pnt();
6577             aDT1x    = aPP1.Tangent();
6578             aAngle1x = aPP1.Angle();
6579
6580             gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6581             // Translation
6582             gp_Vec aV01x( aP0x, aP1x );
6583             aTrsf.SetTranslation( aV01x );
6584
6585             // traslated point
6586             aV1x = aV0x.Transformed( aTrsf );
6587             aPN1 = aPN0.Transformed( aTrsf );
6588
6589             // rotation 1 [ T1,T0 ]
6590             aAngleT1T0=-aDT1x.Angle( aDT0x );
6591             if (fabs(aAngleT1T0) > aTolAng)
6592             {
6593               aDT1T0=aDT1x^aDT0x;
6594               anAxT1T0.SetLocation( aV1x );
6595               anAxT1T0.SetDirection( aDT1T0 );
6596               aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6597
6598               aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6599             }
6600
6601             // rotation 2
6602             if ( theHasAngles ) {
6603               anAx1.SetLocation( aV1x );
6604               anAx1.SetDirection( aDT1x );
6605               aTrsfRot.SetRotation( anAx1, aAngle1x );
6606
6607               aPN1 = aPN1.Transformed( aTrsfRot );
6608             }
6609
6610             // make new node
6611             if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6612             {
6613               // create additional node
6614               gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6615               const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6616               myLastCreatedNodes.Append(newNode);
6617               srcNodes.Append( node );
6618               listNewNodes.push_back( newNode );
6619             }
6620             const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6621             myLastCreatedNodes.Append(newNode);
6622             srcNodes.Append( node );
6623             listNewNodes.push_back( newNode );
6624
6625             aPN0 = aPN1;
6626             aP0x = aP1x;
6627             aV0x = aV1x;
6628             aDT0x = aDT1x;
6629           }
6630         }
6631         else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6632         {
6633           // if current elem is quadratic and current node is not medium
6634           // we have to check - may be it is needed to insert additional nodes
6635           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6636           if ((int) listNewNodes.size() == aNbTP-1 )
6637           {
6638             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6639             gp_XYZ P(node->X(), node->Y(), node->Z());
6640             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6641             int i;
6642             for(i=0; i<aNbTP-1; i++) {
6643               const SMDS_MeshNode* N = *it;
6644               double x = ( N->X() + P.X() )/2.;
6645               double y = ( N->Y() + P.Y() )/2.;
6646               double z = ( N->Z() + P.Z() )/2.;
6647               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6648               srcNodes.Append( node );
6649               myLastCreatedNodes.Append(newN);
6650               aNodes[2*i] = newN;
6651               aNodes[2*i+1] = N;
6652               P = gp_XYZ(N->X(),N->Y(),N->Z());
6653             }
6654             listNewNodes.clear();
6655             for(i=0; i<2*(aNbTP-1); i++) {
6656               listNewNodes.push_back(aNodes[i]);
6657             }
6658           }
6659         }
6660
6661         newNodesItVec.push_back( nIt );
6662       }
6663
6664       // make new elements
6665       sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6666     }
6667   }
6668
6669   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6670
6671   if ( theMakeGroups )
6672     generateGroups( srcNodes, srcElems, "extruded");
6673
6674   return EXTR_OK;
6675 }
6676
6677
6678 //=======================================================================
6679 //function : LinearAngleVariation
6680 //purpose  : auxilary for ExtrusionAlongTrack
6681 //=======================================================================
6682 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6683                                             list<double>& Angles)
6684 {
6685   int nbAngles = Angles.size();
6686   if( nbSteps > nbAngles ) {
6687     vector<double> theAngles(nbAngles);
6688     list<double>::iterator it = Angles.begin();
6689     int i = -1;
6690     for(; it!=Angles.end(); it++) {
6691       i++;
6692       theAngles[i] = (*it);
6693     }
6694     list<double> res;
6695     double rAn2St = double( nbAngles ) / double( nbSteps );
6696     double angPrev = 0, angle;
6697     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6698       double angCur = rAn2St * ( iSt+1 );
6699       double angCurFloor  = floor( angCur );
6700       double angPrevFloor = floor( angPrev );
6701       if ( angPrevFloor == angCurFloor )
6702         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6703       else {
6704         int iP = int( angPrevFloor );
6705         double angPrevCeil = ceil(angPrev);
6706         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6707
6708         int iC = int( angCurFloor );
6709         if ( iC < nbAngles )
6710           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6711
6712         iP = int( angPrevCeil );
6713         while ( iC-- > iP )
6714           angle += theAngles[ iC ];
6715       }
6716       res.push_back(angle);
6717       angPrev = angCur;
6718     }
6719     Angles.clear();
6720     it = res.begin();
6721     for(; it!=res.end(); it++)
6722       Angles.push_back( *it );
6723   }
6724 }
6725
6726
6727 //================================================================================
6728 /*!
6729  * \brief Move or copy theElements applying theTrsf to their nodes
6730  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6731  *  \param theTrsf - transformation to apply
6732  *  \param theCopy - if true, create translated copies of theElems
6733  *  \param theMakeGroups - if true and theCopy, create translated groups
6734  *  \param theTargetMesh - mesh to copy translated elements into
6735  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6736  */
6737 //================================================================================
6738
6739 SMESH_MeshEditor::PGroupIDs
6740 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6741                              const gp_Trsf&     theTrsf,
6742                              const bool         theCopy,
6743                              const bool         theMakeGroups,
6744                              SMESH_Mesh*        theTargetMesh)
6745 {
6746   myLastCreatedElems.Clear();
6747   myLastCreatedNodes.Clear();
6748
6749   bool needReverse = false;
6750   string groupPostfix;
6751   switch ( theTrsf.Form() ) {
6752   case gp_PntMirror:
6753     MESSAGE("gp_PntMirror");
6754     needReverse = true;
6755     groupPostfix = "mirrored";
6756     break;
6757   case gp_Ax1Mirror:
6758     MESSAGE("gp_Ax1Mirror");
6759     groupPostfix = "mirrored";
6760     break;
6761   case gp_Ax2Mirror:
6762     MESSAGE("gp_Ax2Mirror");
6763     needReverse = true;
6764     groupPostfix = "mirrored";
6765     break;
6766   case gp_Rotation:
6767     MESSAGE("gp_Rotation");
6768     groupPostfix = "rotated";
6769     break;
6770   case gp_Translation:
6771     MESSAGE("gp_Translation");
6772     groupPostfix = "translated";
6773     break;
6774   case gp_Scale:
6775     MESSAGE("gp_Scale");
6776     groupPostfix = "scaled";
6777     break;
6778   case gp_CompoundTrsf: // different scale by axis
6779     MESSAGE("gp_CompoundTrsf");
6780     groupPostfix = "scaled";
6781     break;
6782   default:
6783     MESSAGE("default");
6784     needReverse = false;
6785     groupPostfix = "transformed";
6786   }
6787
6788   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6789   SMESHDS_Mesh* aMesh    = GetMeshDS();
6790
6791   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6792   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6793   SMESH_MeshEditor::ElemFeatures elemType;
6794
6795   // map old node to new one
6796   TNodeNodeMap nodeMap;
6797
6798   // elements sharing moved nodes; those of them which have all
6799   // nodes mirrored but are not in theElems are to be reversed
6800   TIDSortedElemSet inverseElemSet;
6801
6802   // source elements for each generated one
6803   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6804
6805   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6806   TIDSortedElemSet orphanNode;
6807
6808   if ( theElems.empty() ) // transform the whole mesh
6809   {
6810     // add all elements
6811     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6812     while ( eIt->more() ) theElems.insert( eIt->next() );
6813     // add orphan nodes
6814     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6815     while ( nIt->more() )
6816     {
6817       const SMDS_MeshNode* node = nIt->next();
6818       if ( node->NbInverseElements() == 0)
6819         orphanNode.insert( node );
6820     }
6821   }
6822
6823   // loop on elements to transform nodes : first orphan nodes then elems
6824   TIDSortedElemSet::iterator itElem;
6825   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6826   for (int i=0; i<2; i++)
6827     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6828     {
6829       const SMDS_MeshElement* elem = *itElem;
6830       if ( !elem )
6831         continue;
6832
6833       // loop on elem nodes
6834       double coord[3];
6835       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6836       while ( itN->more() )
6837       {
6838         const SMDS_MeshNode* node = cast2Node( itN->next() );
6839         // check if a node has been already transformed
6840         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6841           nodeMap.insert( make_pair ( node, node ));
6842         if ( !n2n_isnew.second )
6843           continue;
6844
6845         node->GetXYZ( coord );
6846         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6847         if ( theTargetMesh ) {
6848           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6849           n2n_isnew.first->second = newNode;
6850           myLastCreatedNodes.Append(newNode);
6851           srcNodes.Append( node );
6852         }
6853         else if ( theCopy ) {
6854           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6855           n2n_isnew.first->second = newNode;
6856           myLastCreatedNodes.Append(newNode);
6857           srcNodes.Append( node );
6858         }
6859         else {
6860           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6861           // node position on shape becomes invalid
6862           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6863             ( SMDS_SpacePosition::originSpacePosition() );
6864         }
6865
6866         // keep inverse elements
6867         if ( !theCopy && !theTargetMesh && needReverse ) {
6868           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6869           while ( invElemIt->more() ) {
6870             const SMDS_MeshElement* iel = invElemIt->next();
6871             inverseElemSet.insert( iel );
6872           }
6873         }
6874       }
6875     } // loop on elems in { &orphanNode, &theElems };
6876
6877   // either create new elements or reverse mirrored ones
6878   if ( !theCopy && !needReverse && !theTargetMesh )
6879     return PGroupIDs();
6880
6881   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6882
6883   // Replicate or reverse elements
6884
6885   std::vector<int> iForw;
6886   vector<const SMDS_MeshNode*> nodes;
6887   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6888   {
6889     const SMDS_MeshElement* elem = *itElem;
6890     if ( !elem ) continue;
6891
6892     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6893     size_t               nbNodes  = elem->NbNodes();
6894     if ( geomType == SMDSGeom_NONE ) continue; // node
6895
6896     nodes.resize( nbNodes );
6897
6898     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6899     {
6900       const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6901       if (!aPolyedre)
6902         continue;
6903       nodes.clear();
6904       bool allTransformed = true;
6905       int nbFaces = aPolyedre->NbFaces();
6906       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6907       {
6908         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6909         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6910         {
6911           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6912           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6913           if ( nodeMapIt == nodeMap.end() )
6914             allTransformed = false; // not all nodes transformed
6915           else
6916             nodes.push_back((*nodeMapIt).second);
6917         }
6918         if ( needReverse && allTransformed )
6919           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6920       }
6921       if ( !allTransformed )
6922         continue; // not all nodes transformed
6923     }
6924     else // ----------------------- the rest element types
6925     {
6926       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6927       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6928       const vector<int>&    i = needReverse ? iRev : iForw;
6929
6930       // find transformed nodes
6931       size_t iNode = 0;
6932       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6933       while ( itN->more() ) {
6934         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6935         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6936         if ( nodeMapIt == nodeMap.end() )
6937           break; // not all nodes transformed
6938         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6939       }
6940       if ( iNode != nbNodes )
6941         continue; // not all nodes transformed
6942     }
6943
6944     if ( editor ) {
6945       // copy in this or a new mesh
6946       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6947         srcElems.Append( elem );
6948     }
6949     else {
6950       // reverse element as it was reversed by transformation
6951       if ( nbNodes > 2 )
6952         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6953     }
6954
6955   } // loop on elements
6956
6957   if ( editor && editor != this )
6958     myLastCreatedElems = editor->myLastCreatedElems;
6959
6960   PGroupIDs newGroupIDs;
6961
6962   if ( ( theMakeGroups && theCopy ) ||
6963        ( theMakeGroups && theTargetMesh ) )
6964     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6965
6966   return newGroupIDs;
6967 }
6968
6969 //=======================================================================
6970 /*!
6971  * \brief Create groups of elements made during transformation
6972  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6973  *  \param elemGens - elements making corresponding myLastCreatedElems
6974  *  \param postfix - to append to names of new groups
6975  *  \param targetMesh - mesh to create groups in
6976  *  \param topPresent - is there "top" elements that are created by sweeping
6977  */
6978 //=======================================================================
6979
6980 SMESH_MeshEditor::PGroupIDs
6981 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6982                                  const SMESH_SequenceOfElemPtr& elemGens,
6983                                  const std::string&             postfix,
6984                                  SMESH_Mesh*                    targetMesh,
6985                                  const bool                     topPresent)
6986 {
6987   PGroupIDs newGroupIDs( new list<int> );
6988   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6989
6990   // Sort existing groups by types and collect their names
6991
6992   // containers to store an old group and generated new ones;
6993   // 1st new group is for result elems of different type than a source one;
6994   // 2nd new group is for same type result elems ("top" group at extrusion)
6995   using boost::tuple;
6996   using boost::make_tuple;
6997   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6998   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6999   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
7000   // group names
7001   set< string > groupNames;
7002
7003   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7004   if ( !groupIt->more() ) return newGroupIDs;
7005
7006   int newGroupID = mesh->GetGroupIds().back()+1;
7007   while ( groupIt->more() )
7008   {
7009     SMESH_Group * group = groupIt->next();
7010     if ( !group ) continue;
7011     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7012     if ( !groupDS || groupDS->IsEmpty() ) continue;
7013     groupNames.insert    ( group->GetName() );
7014     groupDS->SetStoreName( group->GetName() );
7015     const SMDSAbs_ElementType type = groupDS->GetType();
7016     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7017     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7018     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7019     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7020   }
7021
7022   // Loop on nodes and elements to add them in new groups
7023
7024   vector< const SMDS_MeshElement* > resultElems;
7025   for ( int isNodes = 0; isNodes < 2; ++isNodes )
7026   {
7027     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
7028     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7029     if ( gens.Length() != elems.Length() )
7030       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7031
7032     // loop on created elements
7033     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7034     {
7035       const SMDS_MeshElement* sourceElem = gens( iElem );
7036       if ( !sourceElem ) {
7037         MESSAGE("generateGroups(): NULL source element");
7038         continue;
7039       }
7040       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7041       if ( groupsOldNew.empty() ) { // no groups of this type at all
7042         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7043           ++iElem; // skip all elements made by sourceElem
7044         continue;
7045       }
7046       // collect all elements made by the iElem-th sourceElem
7047       resultElems.clear();
7048       if ( const SMDS_MeshElement* resElem = elems( iElem ))
7049         if ( resElem != sourceElem )
7050           resultElems.push_back( resElem );
7051       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7052         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7053           if ( resElem != sourceElem )
7054             resultElems.push_back( resElem );
7055
7056       const SMDS_MeshElement* topElem = 0;
7057       if ( isNodes ) // there must be a top element
7058       {
7059         topElem = resultElems.back();
7060         resultElems.pop_back();
7061       }
7062       else
7063       {
7064         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7065         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7066           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7067           {
7068             topElem = *resElemIt;
7069             *resElemIt = 0; // erase *resElemIt
7070             break;
7071           }
7072       }
7073       // add resultElems to groups originted from ones the sourceElem belongs to
7074       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7075       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7076       {
7077         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7078         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7079         {
7080           // fill in a new group
7081           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7082           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7083           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7084             if ( *resElemIt )
7085               newGroup.Add( *resElemIt );
7086
7087           // fill a "top" group
7088           if ( topElem )
7089           {
7090             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7091             newTopGroup.Add( topElem );
7092          }
7093         }
7094       }
7095     } // loop on created elements
7096   }// loop on nodes and elements
7097
7098   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7099
7100   list<int> topGrouIds;
7101   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7102   {
7103     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
7104     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7105                                       orderedOldNewGroups[i]->get<2>() };
7106     for ( int is2nd = 0; is2nd < 2; ++is2nd )
7107     {
7108       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7109       if ( newGroupDS->IsEmpty() )
7110       {
7111         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7112       }
7113       else
7114       {
7115         // set group type
7116         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7117
7118         // make a name
7119         const bool isTop = ( topPresent &&
7120                              newGroupDS->GetType() == oldGroupDS->GetType() &&
7121                              is2nd );
7122
7123         string name = oldGroupDS->GetStoreName();
7124         { // remove trailing whitespaces (issue 22599)
7125           size_t size = name.size();
7126           while ( size > 1 && isspace( name[ size-1 ]))
7127             --size;
7128           if ( size != name.size() )
7129           {
7130             name.resize( size );
7131             oldGroupDS->SetStoreName( name.c_str() );
7132           }
7133         }
7134         if ( !targetMesh ) {
7135           string suffix = ( isTop ? "top": postfix.c_str() );
7136           name += "_";
7137           name += suffix;
7138           int nb = 1;
7139           while ( !groupNames.insert( name ).second ) // name exists
7140             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7141         }
7142         else if ( isTop ) {
7143           name += "_top";
7144         }
7145         newGroupDS->SetStoreName( name.c_str() );
7146
7147         // make a SMESH_Groups
7148         mesh->AddGroup( newGroupDS );
7149         if ( isTop )
7150           topGrouIds.push_back( newGroupDS->GetID() );
7151         else
7152           newGroupIDs->push_back( newGroupDS->GetID() );
7153       }
7154     }
7155   }
7156   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7157
7158   return newGroupIDs;
7159 }
7160
7161 //================================================================================
7162 /*!
7163  *  * \brief Return list of group of nodes close to each other within theTolerance
7164  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
7165  *  *        an Octree algorithm
7166  *  \param [in,out] theNodes - the nodes to treat
7167  *  \param [in]     theTolerance - the tolerance
7168  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
7169  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
7170  *         corner and medium nodes in separate groups
7171  */
7172 //================================================================================
7173
7174 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
7175                                             const double         theTolerance,
7176                                             TListOfListOfNodes & theGroupsOfNodes,
7177                                             bool                 theSeparateCornersAndMedium)
7178 {
7179   myLastCreatedElems.Clear();
7180   myLastCreatedNodes.Clear();
7181
7182   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
7183        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
7184        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7185     theSeparateCornersAndMedium = false;
7186
7187   TIDSortedNodeSet& corners = theNodes;
7188   TIDSortedNodeSet  medium;
7189
7190   if ( theNodes.empty() ) // get all nodes in the mesh
7191   {
7192     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7193     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7194     if ( theSeparateCornersAndMedium )
7195       while ( nIt->more() )
7196       {
7197         const SMDS_MeshNode* n = nIt->next();
7198         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7199         nodeSet->insert( nodeSet->end(), n );
7200       }
7201     else
7202       while ( nIt->more() )
7203         theNodes.insert( theNodes.end(),nIt->next() );
7204   }
7205   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7206   {
7207     TIDSortedNodeSet::iterator nIt = corners.begin();
7208     while ( nIt != corners.end() )
7209       if ( SMESH_MesherHelper::IsMedium( *nIt ))
7210       {
7211         medium.insert( medium.end(), *nIt );
7212         corners.erase( nIt++ );
7213       }
7214       else
7215       {
7216         ++nIt;
7217       }
7218   }
7219
7220   if ( !corners.empty() )
7221     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7222   if ( !medium.empty() )
7223     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7224 }
7225
7226 //=======================================================================
7227 //function : SimplifyFace
7228 //purpose  : split a chain of nodes into several closed chains
7229 //=======================================================================
7230
7231 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7232                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7233                                     vector<int>&                         quantities) const
7234 {
7235   int nbNodes = faceNodes.size();
7236
7237   if (nbNodes < 3)
7238     return 0;
7239
7240   set<const SMDS_MeshNode*> nodeSet;
7241
7242   // get simple seq of nodes
7243   vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7244   int iSimple = 0;
7245
7246   simpleNodes[iSimple++] = faceNodes[0];
7247   for (int iCur = 1; iCur < nbNodes; iCur++) {
7248     if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7249       simpleNodes[iSimple++] = faceNodes[iCur];
7250       nodeSet.insert( faceNodes[iCur] );
7251     }
7252   }
7253   int nbUnique = nodeSet.size();
7254   int nbSimple = iSimple;
7255   if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7256     nbSimple--;
7257     iSimple--;
7258   }
7259
7260   if (nbUnique < 3)
7261     return 0;
7262
7263   // separate loops
7264   int nbNew = 0;
7265   bool foundLoop = (nbSimple > nbUnique);
7266   while (foundLoop) {
7267     foundLoop = false;
7268     set<const SMDS_MeshNode*> loopSet;
7269     for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7270       const SMDS_MeshNode* n = simpleNodes[iSimple];
7271       if (!loopSet.insert( n ).second) {
7272         foundLoop = true;
7273
7274         // separate loop
7275         int iC = 0, curLast = iSimple;
7276         for (; iC < curLast; iC++) {
7277           if (simpleNodes[iC] == n) break;
7278         }
7279         int loopLen = curLast - iC;
7280         if (loopLen > 2) {
7281           // create sub-element
7282           nbNew++;
7283           quantities.push_back(loopLen);
7284           for (; iC < curLast; iC++) {
7285             poly_nodes.push_back(simpleNodes[iC]);
7286           }
7287         }
7288         // shift the rest nodes (place from the first loop position)
7289         for (iC = curLast + 1; iC < nbSimple; iC++) {
7290           simpleNodes[iC - loopLen] = simpleNodes[iC];
7291         }
7292         nbSimple -= loopLen;
7293         iSimple -= loopLen;
7294       }
7295     } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7296   } // while (foundLoop)
7297
7298   if (iSimple > 2) {
7299     nbNew++;
7300     quantities.push_back(iSimple);
7301     for (int i = 0; i < iSimple; i++)
7302       poly_nodes.push_back(simpleNodes[i]);
7303   }
7304
7305   return nbNew;
7306 }
7307
7308 //=======================================================================
7309 //function : MergeNodes
7310 //purpose  : In each group, the cdr of nodes are substituted by the first one
7311 //           in all elements.
7312 //=======================================================================
7313
7314 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7315 {
7316   MESSAGE("MergeNodes");
7317   myLastCreatedElems.Clear();
7318   myLastCreatedNodes.Clear();
7319
7320   SMESHDS_Mesh* aMesh = GetMeshDS();
7321
7322   TNodeNodeMap nodeNodeMap; // node to replace - new node
7323   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7324   list< int > rmElemIds, rmNodeIds;
7325
7326   // Fill nodeNodeMap and elems
7327
7328   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7329   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7330   {
7331     list<const SMDS_MeshNode*>& nodes = *grIt;
7332     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7333     const SMDS_MeshNode* nToKeep = *nIt;
7334     for ( ++nIt; nIt != nodes.end(); nIt++ )
7335     {
7336       const SMDS_MeshNode* nToRemove = *nIt;
7337       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7338       if ( nToRemove != nToKeep )
7339       {
7340         rmNodeIds.push_back( nToRemove->GetID() );
7341         AddToSameGroups( nToKeep, nToRemove, aMesh );
7342         // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7343         // after MergeNodes() w/o creating node in place of merged ones.
7344         const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7345         if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7346           if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7347             sm->SetIsAlwaysComputed( true );
7348       }
7349       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7350       while ( invElemIt->more() ) {
7351         const SMDS_MeshElement* elem = invElemIt->next();
7352         elems.insert(elem);
7353       }
7354     }
7355   }
7356   // Change element nodes or remove an element
7357
7358   set<const SMDS_MeshNode*> nodeSet;
7359   vector< const SMDS_MeshNode*> curNodes, uniqueNodes;
7360   vector<int> iRepl;
7361   ElemFeatures elemType;
7362
7363   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7364   for ( ; eIt != elems.end(); eIt++ )
7365   {
7366     const SMDS_MeshElement* elem = *eIt;
7367     const           int  nbNodes = elem->NbNodes();
7368     const           int aShapeId = FindShape( elem );
7369
7370     nodeSet.clear();
7371     curNodes.resize( nbNodes );
7372     uniqueNodes.resize( nbNodes );
7373     iRepl.resize( nbNodes );
7374     int iUnique = 0, iCur = 0, nbRepl = 0;
7375
7376     // get new seq of nodes
7377     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7378     while ( itN->more() )
7379     {
7380       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7381
7382       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7383       if ( nnIt != nodeNodeMap.end() ) { // n sticks
7384         n = (*nnIt).second;
7385         { ////////// BUG 0020185: begin
7386           bool stopRecur = false;
7387           set<const SMDS_MeshNode*> nodesRecur;
7388           nodesRecur.insert(n);
7389           while (!stopRecur) {
7390             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7391             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7392               n = (*nnIt_i).second;
7393               if (!nodesRecur.insert(n).second) {
7394                 // error: recursive dependancy
7395                 stopRecur = true;
7396               }
7397             }
7398             else
7399               stopRecur = true;
7400           }
7401         } ////////// BUG 0020185: end
7402       }
7403       curNodes[ iCur ] = n;
7404       bool isUnique = nodeSet.insert( n ).second;
7405       if ( isUnique )
7406         uniqueNodes[ iUnique++ ] = n;
7407       else
7408         iRepl[ nbRepl++ ] = iCur;
7409       iCur++;
7410     }
7411
7412     // Analyse element topology after replacement
7413
7414     bool isOk = true;
7415     int nbUniqueNodes = nodeSet.size();
7416     if ( nbNodes != nbUniqueNodes ) // some nodes stick
7417     {
7418       if (elem->IsPoly()) // Polygons and Polyhedral volumes
7419       {
7420         if (elem->GetType() == SMDSAbs_Face) // Polygon
7421         {
7422           elemType.Init( elem );
7423           const bool isQuad = elemType.myIsQuad;
7424           if ( isQuad )
7425             SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7426               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7427
7428           // a polygon can divide into several elements
7429           vector<const SMDS_MeshNode *> polygons_nodes;
7430           vector<int> quantities;
7431           int nbNew = SimplifyFace( curNodes, polygons_nodes, quantities );
7432           if (nbNew > 0)
7433           {
7434             vector<const SMDS_MeshNode *> face_nodes;
7435             int inode = 0;
7436             for (int iface = 0; iface < nbNew; iface++)
7437             {
7438               int nbNewNodes = quantities[iface];
7439               face_nodes.assign( polygons_nodes.begin() + inode,
7440                                  polygons_nodes.begin() + inode + nbNewNodes );
7441               inode += nbNewNodes;
7442               if ( isQuad ) // check if a result elem is a valid quadratic polygon
7443               {
7444                 bool isValid = ( nbNewNodes % 2 == 0 );
7445                 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7446                   isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7447                 elemType.SetQuad( isValid );
7448                 if ( isValid ) // put medium nodes after corners
7449                   SMDS_MeshCell::applyInterlaceRev
7450                     ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7451                                                           nbNewNodes ), face_nodes );
7452               }
7453               elemType.SetPoly(( nbNewNodes / ( elemType.myIsQuad + 1 ) > 4 ));
7454
7455               SMDS_MeshElement* newElem = AddElement( face_nodes, elemType );
7456               if ( aShapeId )
7457                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7458             }
7459           }
7460           rmElemIds.push_back(elem->GetID());
7461
7462         } // Polygon
7463
7464         else if (elem->GetType() == SMDSAbs_Volume) // Polyhedral volume
7465         {
7466           if (nbUniqueNodes < 4) {
7467             rmElemIds.push_back(elem->GetID());
7468           }
7469           else {
7470             // each face has to be analyzed in order to check volume validity
7471             const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
7472             if (aPolyedre)
7473             {
7474               int nbFaces = aPolyedre->NbFaces();
7475
7476               vector<const SMDS_MeshNode *> poly_nodes;
7477               vector<int> quantities;
7478
7479               for (int iface = 1; iface <= nbFaces; iface++) {
7480                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7481                 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7482
7483                 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7484                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7485                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7486                   if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7487                     faceNode = (*nnIt).second;
7488                   }
7489                   faceNodes[inode - 1] = faceNode;
7490                 }
7491
7492                 SimplifyFace(faceNodes, poly_nodes, quantities);
7493               }
7494
7495               if (quantities.size() > 3) {
7496                 // to be done: remove coincident faces
7497               }
7498
7499               if (quantities.size() > 3)
7500               {
7501                 const SMDS_MeshElement* newElem =
7502                   aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7503                 myLastCreatedElems.Append(newElem);
7504                 if ( aShapeId && newElem )
7505                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7506                 rmElemIds.push_back(elem->GetID());
7507               }
7508             }
7509             else {
7510               rmElemIds.push_back(elem->GetID());
7511             }
7512           }
7513         }
7514         else {
7515         }
7516
7517         continue;
7518       } // poly element
7519
7520       // Regular elements
7521       // TODO not all the possible cases are solved. Find something more generic?
7522       switch ( nbNodes ) {
7523       case 2: ///////////////////////////////////// EDGE
7524         isOk = false; break;
7525       case 3: ///////////////////////////////////// TRIANGLE
7526         isOk = false; break;
7527       case 4:
7528         if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7529           isOk = false;
7530         else { //////////////////////////////////// QUADRANGLE
7531           if ( nbUniqueNodes < 3 )
7532             isOk = false;
7533           else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7534             isOk = false; // opposite nodes stick
7535           //MESSAGE("isOk " << isOk);
7536         }
7537         break;
7538       case 6: ///////////////////////////////////// PENTAHEDRON
7539         if ( nbUniqueNodes == 4 ) {
7540           // ---------------------------------> tetrahedron
7541           if (nbRepl == 3 &&
7542               iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7543             // all top nodes stick: reverse a bottom
7544             uniqueNodes[ 0 ] = curNodes [ 1 ];
7545             uniqueNodes[ 1 ] = curNodes [ 0 ];
7546           }
7547           else if (nbRepl == 3 &&
7548                    iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7549             // all bottom nodes stick: set a top before
7550             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7551             uniqueNodes[ 0 ] = curNodes [ 3 ];
7552             uniqueNodes[ 1 ] = curNodes [ 4 ];
7553             uniqueNodes[ 2 ] = curNodes [ 5 ];
7554           }
7555           else if (nbRepl == 4 &&
7556                    iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7557             // a lateral face turns into a line: reverse a bottom
7558             uniqueNodes[ 0 ] = curNodes [ 1 ];
7559             uniqueNodes[ 1 ] = curNodes [ 0 ];
7560           }
7561           else
7562             isOk = false;
7563         }
7564         else if ( nbUniqueNodes == 5 ) {
7565           // PENTAHEDRON --------------------> 2 tetrahedrons
7566           if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7567             // a bottom node sticks with a linked top one
7568             // 1.
7569             SMDS_MeshElement* newElem =
7570               aMesh->AddVolume(curNodes[ 3 ],
7571                                curNodes[ 4 ],
7572                                curNodes[ 5 ],
7573                                curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7574             myLastCreatedElems.Append(newElem);
7575             if ( aShapeId )
7576               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7577             // 2. : reverse a bottom
7578             uniqueNodes[ 0 ] = curNodes [ 1 ];
7579             uniqueNodes[ 1 ] = curNodes [ 0 ];
7580             nbUniqueNodes = 4;
7581           }
7582           else
7583             isOk = false;
7584         }
7585         else
7586           isOk = false;
7587         break;
7588       case 8: {
7589         if(elem->IsQuadratic()) { // Quadratic quadrangle
7590           //   1    5    2
7591           //    +---+---+
7592           //    |       |
7593           //    |       |
7594           //   4+       +6
7595           //    |       |
7596           //    |       |
7597           //    +---+---+
7598           //   0    7    3
7599           isOk = false;
7600           if(nbRepl==2) {
7601             MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7602           }
7603           if(nbRepl==3) {
7604             MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2]);
7605             nbUniqueNodes = 6;
7606             if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7607               uniqueNodes[0] = curNodes[0];
7608               uniqueNodes[1] = curNodes[2];
7609               uniqueNodes[2] = curNodes[3];
7610               uniqueNodes[3] = curNodes[5];
7611               uniqueNodes[4] = curNodes[6];
7612               uniqueNodes[5] = curNodes[7];
7613               isOk = true;
7614             }
7615             if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7616               uniqueNodes[0] = curNodes[0];
7617               uniqueNodes[1] = curNodes[1];
7618               uniqueNodes[2] = curNodes[2];
7619               uniqueNodes[3] = curNodes[4];
7620               uniqueNodes[4] = curNodes[5];
7621               uniqueNodes[5] = curNodes[6];
7622               isOk = true;
7623             }
7624             if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7625               uniqueNodes[0] = curNodes[1];
7626               uniqueNodes[1] = curNodes[2];
7627               uniqueNodes[2] = curNodes[3];
7628               uniqueNodes[3] = curNodes[5];
7629               uniqueNodes[4] = curNodes[6];
7630               uniqueNodes[5] = curNodes[0];
7631               isOk = true;
7632             }
7633             if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7634               uniqueNodes[0] = curNodes[0];
7635               uniqueNodes[1] = curNodes[1];
7636               uniqueNodes[2] = curNodes[3];
7637               uniqueNodes[3] = curNodes[4];
7638               uniqueNodes[4] = curNodes[6];
7639               uniqueNodes[5] = curNodes[7];
7640               isOk = true;
7641             }
7642             if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7643               uniqueNodes[0] = curNodes[0];
7644               uniqueNodes[1] = curNodes[2];
7645               uniqueNodes[2] = curNodes[3];
7646               uniqueNodes[3] = curNodes[1];
7647               uniqueNodes[4] = curNodes[6];
7648               uniqueNodes[5] = curNodes[7];
7649               isOk = true;
7650             }
7651             if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
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[7];
7658               isOk = true;
7659             }
7660             if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7661               uniqueNodes[0] = curNodes[0];
7662               uniqueNodes[1] = curNodes[1];
7663               uniqueNodes[2] = curNodes[3];
7664               uniqueNodes[3] = curNodes[4];
7665               uniqueNodes[4] = curNodes[2];
7666               uniqueNodes[5] = curNodes[7];
7667               isOk = true;
7668             }
7669             if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7670               uniqueNodes[0] = curNodes[0];
7671               uniqueNodes[1] = curNodes[1];
7672               uniqueNodes[2] = curNodes[2];
7673               uniqueNodes[3] = curNodes[4];
7674               uniqueNodes[4] = curNodes[5];
7675               uniqueNodes[5] = curNodes[3];
7676               isOk = true;
7677             }
7678           }
7679           if(nbRepl==4) {
7680             MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3]);
7681           }
7682           if(nbRepl==5) {
7683             MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7684           }
7685           break;
7686         }
7687         //////////////////////////////////// HEXAHEDRON
7688         isOk = false;
7689         SMDS_VolumeTool hexa (elem);
7690         hexa.SetExternalNormal();
7691         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7692           //////////////////////// HEX ---> 1 tetrahedron
7693           for ( int iFace = 0; iFace < 6; iFace++ ) {
7694             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7695             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7696                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7697                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7698               // one face turns into a point ...
7699               int iOppFace = hexa.GetOppFaceIndex( iFace );
7700               ind = hexa.GetFaceNodesIndices( iOppFace );
7701               int nbStick = 0;
7702               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7703                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7704                   nbStick++;
7705               }
7706               if ( nbStick == 1 ) {
7707                 // ... and the opposite one - into a triangle.
7708                 // set a top node
7709                 ind = hexa.GetFaceNodesIndices( iFace );
7710                 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7711                 isOk = true;
7712               }
7713               break;
7714             }
7715           }
7716         }
7717         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7718           //////////////////////// HEX ---> 1 prism
7719           int nbTria = 0, iTria[3];
7720           const int *ind; // indices of face nodes
7721           // look for triangular faces
7722           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7723             ind = hexa.GetFaceNodesIndices( iFace );
7724             TIDSortedNodeSet faceNodes;
7725             for ( iCur = 0; iCur < 4; iCur++ )
7726               faceNodes.insert( curNodes[ind[iCur]] );
7727             if ( faceNodes.size() == 3 )
7728               iTria[ nbTria++ ] = iFace;
7729           }
7730           // check if triangles are opposite
7731           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7732           {
7733             isOk = true;
7734             // set nodes of the bottom triangle
7735             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7736             vector<int> indB;
7737             for ( iCur = 0; iCur < 4; iCur++ )
7738               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7739                 indB.push_back( ind[iCur] );
7740             if ( !hexa.IsForward() )
7741               std::swap( indB[0], indB[2] );
7742             for ( iCur = 0; iCur < 3; iCur++ )
7743               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7744             // set nodes of the top triangle
7745             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7746             for ( iCur = 0; iCur < 3; ++iCur )
7747               for ( int j = 0; j < 4; ++j )
7748                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7749                 {
7750                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7751                   break;
7752                 }
7753           }
7754           break;
7755         }
7756         else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7757           //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7758           for ( int iFace = 0; iFace < 6; iFace++ ) {
7759             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7760             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7761                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7762                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7763               // one face turns into a point ...
7764               int iOppFace = hexa.GetOppFaceIndex( iFace );
7765               ind = hexa.GetFaceNodesIndices( iOppFace );
7766               int nbStick = 0;
7767               iUnique = 2;  // reverse a tetrahedron 1 bottom
7768               for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7769                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7770                   nbStick++;
7771                 else if ( iUnique >= 0 )
7772                   uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7773               }
7774               if ( nbStick == 0 ) {
7775                 // ... and the opposite one is a quadrangle
7776                 // set a top node
7777                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7778                 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7779                 nbUniqueNodes = 4;
7780                 // tetrahedron 2
7781                 SMDS_MeshElement* newElem =
7782                   aMesh->AddVolume(curNodes[ind[ 0 ]],
7783                                    curNodes[ind[ 3 ]],
7784                                    curNodes[ind[ 2 ]],
7785                                    curNodes[indTop[ 0 ]]);
7786                 myLastCreatedElems.Append(newElem);
7787                 if ( aShapeId )
7788                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7789                 isOk = true;
7790               }
7791               break;
7792             }
7793           }
7794         }
7795         else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7796           ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7797           // find indices of quad and tri faces
7798           int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7799           for ( iFace = 0; iFace < 6; iFace++ ) {
7800             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7801             nodeSet.clear();
7802             for ( iCur = 0; iCur < 4; iCur++ )
7803               nodeSet.insert( curNodes[ind[ iCur ]] );
7804             nbUniqueNodes = nodeSet.size();
7805             if ( nbUniqueNodes == 3 )
7806               iTriFace[ nbTri++ ] = iFace;
7807             else if ( nbUniqueNodes == 4 )
7808               iQuadFace[ nbQuad++ ] = iFace;
7809           }
7810           if (nbQuad == 2 && nbTri == 4 &&
7811               hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7812             // 2 opposite quadrangles stuck with a diagonal;
7813             // sample groups of merged indices: (0-4)(2-6)
7814             // --------------------------------------------> 2 tetrahedrons
7815             const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7816             const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7817             int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7818             if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7819                 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7820               // stuck with 0-2 diagonal
7821               i0  = ind1[ 3 ];
7822               i1d = ind1[ 0 ];
7823               i2  = ind1[ 1 ];
7824               i3d = ind1[ 2 ];
7825               i0t = ind2[ 1 ];
7826               i2t = ind2[ 3 ];
7827             }
7828             else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7829                      curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7830               // stuck with 1-3 diagonal
7831               i0  = ind1[ 0 ];
7832               i1d = ind1[ 1 ];
7833               i2  = ind1[ 2 ];
7834               i3d = ind1[ 3 ];
7835               i0t = ind2[ 0 ];
7836               i2t = ind2[ 1 ];
7837             }
7838             else {
7839               ASSERT(0);
7840             }
7841             // tetrahedron 1
7842             uniqueNodes[ 0 ] = curNodes [ i0 ];
7843             uniqueNodes[ 1 ] = curNodes [ i1d ];
7844             uniqueNodes[ 2 ] = curNodes [ i3d ];
7845             uniqueNodes[ 3 ] = curNodes [ i0t ];
7846             nbUniqueNodes = 4;
7847             // tetrahedron 2
7848             SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7849                                                          curNodes[ i2 ],
7850                                                          curNodes[ i3d ],
7851                                                          curNodes[ i2t ]);
7852             myLastCreatedElems.Append(newElem);
7853             if ( aShapeId )
7854               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7855             isOk = true;
7856           }
7857           else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7858                    ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7859             // --------------------------------------------> prism
7860             // find 2 opposite triangles
7861             nbUniqueNodes = 6;
7862             for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7863               if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7864                 // find indices of kept and replaced nodes
7865                 // and fill unique nodes of 2 opposite triangles
7866                 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7867                 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7868                 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7869                 // fill unique nodes
7870                 iUnique = 0;
7871                 isOk = true;
7872                 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7873                   const SMDS_MeshNode* n     = curNodes[ind1[ iCur ]];
7874                   const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7875                   if ( n == nInit ) {
7876                     // iCur of a linked node of the opposite face (make normals co-directed):
7877                     int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7878                     // check that correspondent corners of triangles are linked
7879                     if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7880                       isOk = false;
7881                     else {
7882                       uniqueNodes[ iUnique ] = n;
7883                       uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7884                       iUnique++;
7885                     }
7886                   }
7887                 }
7888                 break;
7889               }
7890             }
7891           }
7892         } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7893         else
7894         {
7895           MESSAGE("MergeNodes() removes hexahedron "<< elem);
7896         }
7897         break;
7898       } // HEXAHEDRON
7899
7900       default:
7901         isOk = false;
7902       } // switch ( nbNodes )
7903
7904     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7905
7906     if ( isOk ) // the non-poly elem remains valid after sticking nodes
7907     {
7908       if ( nbNodes != nbUniqueNodes ||
7909            !aMesh->ChangeElementNodes( elem, & curNodes[0], nbNodes ))
7910       {
7911         elemType.Init( elem ).SetID( elem->GetID() );
7912
7913         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7914         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7915
7916         uniqueNodes.resize(nbUniqueNodes);
7917         SMDS_MeshElement* newElem = this->AddElement( uniqueNodes, elemType );
7918         if ( sm && newElem )
7919           sm->AddElement( newElem );
7920         if ( elem != newElem )
7921           ReplaceElemInGroups( elem, newElem, aMesh );
7922       }
7923     }
7924     else {
7925       // Remove invalid regular element or invalid polygon
7926       rmElemIds.push_back( elem->GetID() );
7927     }
7928
7929   } // loop on elements
7930
7931   // Remove bad elements, then equal nodes (order important)
7932
7933   Remove( rmElemIds, false );
7934   Remove( rmNodeIds, true );
7935
7936   return;
7937 }
7938
7939
7940 // ========================================================
7941 // class   : SortableElement
7942 // purpose : allow sorting elements basing on their nodes
7943 // ========================================================
7944 class SortableElement : public set <const SMDS_MeshElement*>
7945 {
7946 public:
7947
7948   SortableElement( const SMDS_MeshElement* theElem )
7949   {
7950     myElem = theElem;
7951     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7952     while ( nodeIt->more() )
7953       this->insert( nodeIt->next() );
7954   }
7955
7956   const SMDS_MeshElement* Get() const
7957   { return myElem; }
7958
7959 private:
7960   mutable const SMDS_MeshElement* myElem;
7961 };
7962
7963 //=======================================================================
7964 //function : FindEqualElements
7965 //purpose  : Return list of group of elements built on the same nodes.
7966 //           Search among theElements or in the whole mesh if theElements is empty
7967 //=======================================================================
7968
7969 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
7970                                          TListOfListOfElementsID & theGroupsOfElementsID)
7971 {
7972   myLastCreatedElems.Clear();
7973   myLastCreatedNodes.Clear();
7974
7975   typedef map< SortableElement, int > TMapOfNodeSet;
7976   typedef list<int> TGroupOfElems;
7977
7978   if ( theElements.empty() )
7979   { // get all elements in the mesh
7980     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7981     while ( eIt->more() )
7982       theElements.insert( theElements.end(), eIt->next() );
7983   }
7984
7985   vector< TGroupOfElems > arrayOfGroups;
7986   TGroupOfElems groupOfElems;
7987   TMapOfNodeSet mapOfNodeSet;
7988
7989   TIDSortedElemSet::iterator elemIt = theElements.begin();
7990   for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7991   {
7992     const SMDS_MeshElement* curElem = *elemIt;
7993     SortableElement SE(curElem);
7994     // check uniqueness
7995     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7996     if ( !pp.second ) { // one more coincident elem
7997       TMapOfNodeSet::iterator& itSE = pp.first;
7998       int ind = (*itSE).second;
7999       arrayOfGroups[ind].push_back( curElem->GetID() );
8000     }
8001     else {
8002       arrayOfGroups.push_back( groupOfElems );
8003       arrayOfGroups.back().push_back( curElem->GetID() );
8004       i++;
8005     }
8006   }
8007
8008   groupOfElems.clear();
8009   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8010   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8011   {
8012     if ( groupIt->size() > 1 ) {
8013       //groupOfElems.sort(); -- theElements is sorted already
8014       theGroupsOfElementsID.push_back( groupOfElems );
8015       theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
8016     }
8017   }
8018 }
8019
8020 //=======================================================================
8021 //function : MergeElements
8022 //purpose  : In each given group, substitute all elements by the first one.
8023 //=======================================================================
8024
8025 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8026 {
8027   myLastCreatedElems.Clear();
8028   myLastCreatedNodes.Clear();
8029
8030   typedef list<int> TListOfIDs;
8031   TListOfIDs rmElemIds; // IDs of elems to remove
8032
8033   SMESHDS_Mesh* aMesh = GetMeshDS();
8034
8035   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8036   while ( groupsIt != theGroupsOfElementsID.end() ) {
8037     TListOfIDs& aGroupOfElemID = *groupsIt;
8038     aGroupOfElemID.sort();
8039     int elemIDToKeep = aGroupOfElemID.front();
8040     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8041     aGroupOfElemID.pop_front();
8042     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8043     while ( idIt != aGroupOfElemID.end() ) {
8044       int elemIDToRemove = *idIt;
8045       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8046       // add the kept element in groups of removed one (PAL15188)
8047       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8048       rmElemIds.push_back( elemIDToRemove );
8049       ++idIt;
8050     }
8051     ++groupsIt;
8052   }
8053
8054   Remove( rmElemIds, false );
8055 }
8056
8057 //=======================================================================
8058 //function : MergeEqualElements
8059 //purpose  : Remove all but one of elements built on the same nodes.
8060 //=======================================================================
8061
8062 void SMESH_MeshEditor::MergeEqualElements()
8063 {
8064   TIDSortedElemSet aMeshElements; /* empty input ==
8065                                      to merge equal elements in the whole mesh */
8066   TListOfListOfElementsID aGroupsOfElementsID;
8067   FindEqualElements(aMeshElements, aGroupsOfElementsID);
8068   MergeElements(aGroupsOfElementsID);
8069 }
8070
8071 //=======================================================================
8072 //function : findAdjacentFace
8073 //purpose  :
8074 //=======================================================================
8075
8076 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8077                                                 const SMDS_MeshNode* n2,
8078                                                 const SMDS_MeshElement* elem)
8079 {
8080   TIDSortedElemSet elemSet, avoidSet;
8081   if ( elem )
8082     avoidSet.insert ( elem );
8083   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8084 }
8085
8086 //=======================================================================
8087 //function : findSegment
8088 //purpose  : Return a mesh segment by two nodes one of which can be medium
8089 //=======================================================================
8090
8091 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8092                                            const SMDS_MeshNode* n2)
8093 {
8094   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8095   while ( it->more() )
8096   {
8097     const SMDS_MeshElement* seg = it->next();
8098     if ( seg->GetNodeIndex( n2 ) >= 0 )
8099       return seg;
8100   }
8101   return 0;
8102 }
8103
8104 //=======================================================================
8105 //function : FindFreeBorder
8106 //purpose  :
8107 //=======================================================================
8108
8109 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8110
8111 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
8112                                        const SMDS_MeshNode*             theSecondNode,
8113                                        const SMDS_MeshNode*             theLastNode,
8114                                        list< const SMDS_MeshNode* > &   theNodes,
8115                                        list< const SMDS_MeshElement* >& theFaces)
8116 {
8117   if ( !theFirstNode || !theSecondNode )
8118     return false;
8119   // find border face between theFirstNode and theSecondNode
8120   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8121   if ( !curElem )
8122     return false;
8123
8124   theFaces.push_back( curElem );
8125   theNodes.push_back( theFirstNode );
8126   theNodes.push_back( theSecondNode );
8127
8128   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8129   TIDSortedElemSet foundElems;
8130   bool needTheLast = ( theLastNode != 0 );
8131
8132   while ( nStart != theLastNode ) {
8133     if ( nStart == theFirstNode )
8134       return !needTheLast;
8135
8136     // find all free border faces sharing form nStart
8137
8138     list< const SMDS_MeshElement* > curElemList;
8139     list< const SMDS_MeshNode* >    nStartList;
8140     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8141     while ( invElemIt->more() ) {
8142       const SMDS_MeshElement* e = invElemIt->next();
8143       if ( e == curElem || foundElems.insert( e ).second ) {
8144         // get nodes
8145         int iNode = 0, nbNodes = e->NbNodes();
8146         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8147
8148         if ( e->IsQuadratic() ) {
8149           const SMDS_VtkFace* F =
8150             dynamic_cast<const SMDS_VtkFace*>(e);
8151           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8152           // use special nodes iterator
8153           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8154           while( anIter->more() ) {
8155             nodes[ iNode++ ] = cast2Node(anIter->next());
8156           }
8157         }
8158         else {
8159           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8160           while ( nIt->more() )
8161             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8162         }
8163         nodes[ iNode ] = nodes[ 0 ];
8164         // check 2 links
8165         for ( iNode = 0; iNode < nbNodes; iNode++ )
8166           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8167                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8168               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8169           {
8170             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8171             curElemList.push_back( e );
8172           }
8173       }
8174     }
8175     // analyse the found
8176
8177     int nbNewBorders = curElemList.size();
8178     if ( nbNewBorders == 0 ) {
8179       // no free border furthermore
8180       return !needTheLast;
8181     }
8182     else if ( nbNewBorders == 1 ) {
8183       // one more element found
8184       nIgnore = nStart;
8185       nStart = nStartList.front();
8186       curElem = curElemList.front();
8187       theFaces.push_back( curElem );
8188       theNodes.push_back( nStart );
8189     }
8190     else {
8191       // several continuations found
8192       list< const SMDS_MeshElement* >::iterator curElemIt;
8193       list< const SMDS_MeshNode* >::iterator nStartIt;
8194       // check if one of them reached the last node
8195       if ( needTheLast ) {
8196         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8197              curElemIt!= curElemList.end();
8198              curElemIt++, nStartIt++ )
8199           if ( *nStartIt == theLastNode ) {
8200             theFaces.push_back( *curElemIt );
8201             theNodes.push_back( *nStartIt );
8202             return true;
8203           }
8204       }
8205       // find the best free border by the continuations
8206       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8207       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8208       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8209            curElemIt!= curElemList.end();
8210            curElemIt++, nStartIt++ )
8211       {
8212         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8213         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8214         // find one more free border
8215         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8216           cNL->clear();
8217           cFL->clear();
8218         }
8219         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8220           // choice: clear a worse one
8221           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8222           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8223           contNodes[ iWorse ].clear();
8224           contFaces[ iWorse ].clear();
8225         }
8226       }
8227       if ( contNodes[0].empty() && contNodes[1].empty() )
8228         return false;
8229
8230       // append the best free border
8231       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8232       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8233       theNodes.pop_back(); // remove nIgnore
8234       theNodes.pop_back(); // remove nStart
8235       theFaces.pop_back(); // remove curElem
8236       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8237       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8238       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8239       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8240       return true;
8241
8242     } // several continuations found
8243   } // while ( nStart != theLastNode )
8244
8245   return true;
8246 }
8247
8248 //=======================================================================
8249 //function : CheckFreeBorderNodes
8250 //purpose  : Return true if the tree nodes are on a free border
8251 //=======================================================================
8252
8253 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8254                                             const SMDS_MeshNode* theNode2,
8255                                             const SMDS_MeshNode* theNode3)
8256 {
8257   list< const SMDS_MeshNode* > nodes;
8258   list< const SMDS_MeshElement* > faces;
8259   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8260 }
8261
8262 //=======================================================================
8263 //function : SewFreeBorder
8264 //purpose  :
8265 //warning  : for border-to-side sewing theSideSecondNode is considered as
8266 //           the last side node and theSideThirdNode is not used
8267 //=======================================================================
8268
8269 SMESH_MeshEditor::Sew_Error
8270 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8271                                  const SMDS_MeshNode* theBordSecondNode,
8272                                  const SMDS_MeshNode* theBordLastNode,
8273                                  const SMDS_MeshNode* theSideFirstNode,
8274                                  const SMDS_MeshNode* theSideSecondNode,
8275                                  const SMDS_MeshNode* theSideThirdNode,
8276                                  const bool           theSideIsFreeBorder,
8277                                  const bool           toCreatePolygons,
8278                                  const bool           toCreatePolyedrs)
8279 {
8280   myLastCreatedElems.Clear();
8281   myLastCreatedNodes.Clear();
8282
8283   MESSAGE("::SewFreeBorder()");
8284   Sew_Error aResult = SEW_OK;
8285
8286   // ====================================
8287   //    find side nodes and elements
8288   // ====================================
8289
8290   list< const SMDS_MeshNode* >    nSide[ 2 ];
8291   list< const SMDS_MeshElement* > eSide[ 2 ];
8292   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
8293   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8294
8295   // Free border 1
8296   // --------------
8297   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8298                       nSide[0], eSide[0])) {
8299     MESSAGE(" Free Border 1 not found " );
8300     aResult = SEW_BORDER1_NOT_FOUND;
8301   }
8302   if (theSideIsFreeBorder) {
8303     // Free border 2
8304     // --------------
8305     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8306                         nSide[1], eSide[1])) {
8307       MESSAGE(" Free Border 2 not found " );
8308       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8309     }
8310   }
8311   if ( aResult != SEW_OK )
8312     return aResult;
8313
8314   if (!theSideIsFreeBorder) {
8315     // Side 2
8316     // --------------
8317
8318     // -------------------------------------------------------------------------
8319     // Algo:
8320     // 1. If nodes to merge are not coincident, move nodes of the free border
8321     //    from the coord sys defined by the direction from the first to last
8322     //    nodes of the border to the correspondent sys of the side 2
8323     // 2. On the side 2, find the links most co-directed with the correspondent
8324     //    links of the free border
8325     // -------------------------------------------------------------------------
8326
8327     // 1. Since sewing may break if there are volumes to split on the side 2,
8328     //    we wont move nodes but just compute new coordinates for them
8329     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8330     TNodeXYZMap nBordXYZ;
8331     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8332     list< const SMDS_MeshNode* >::iterator nBordIt;
8333
8334     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8335     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8336     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8337     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8338     double tol2 = 1.e-8;
8339     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8340     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8341       // Need node movement.
8342
8343       // find X and Z axes to create trsf
8344       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8345       gp_Vec X = Zs ^ Zb;
8346       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8347         // Zb || Zs
8348         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8349
8350       // coord systems
8351       gp_Ax3 toBordAx( Pb1, Zb, X );
8352       gp_Ax3 fromSideAx( Ps1, Zs, X );
8353       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8354       // set trsf
8355       gp_Trsf toBordSys, fromSide2Sys;
8356       toBordSys.SetTransformation( toBordAx );
8357       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8358       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8359
8360       // move
8361       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8362         const SMDS_MeshNode* n = *nBordIt;
8363         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8364         toBordSys.Transforms( xyz );
8365         fromSide2Sys.Transforms( xyz );
8366         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8367       }
8368     }
8369     else {
8370       // just insert nodes XYZ in the nBordXYZ map
8371       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8372         const SMDS_MeshNode* n = *nBordIt;
8373         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8374       }
8375     }
8376
8377     // 2. On the side 2, find the links most co-directed with the correspondent
8378     //    links of the free border
8379
8380     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8381     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8382     sideNodes.push_back( theSideFirstNode );
8383
8384     bool hasVolumes = false;
8385     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8386     set<long> foundSideLinkIDs, checkedLinkIDs;
8387     SMDS_VolumeTool volume;
8388     //const SMDS_MeshNode* faceNodes[ 4 ];
8389
8390     const SMDS_MeshNode*    sideNode;
8391     const SMDS_MeshElement* sideElem;
8392     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8393     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8394     nBordIt = bordNodes.begin();
8395     nBordIt++;
8396     // border node position and border link direction to compare with
8397     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8398     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8399     // choose next side node by link direction or by closeness to
8400     // the current border node:
8401     bool searchByDir = ( *nBordIt != theBordLastNode );
8402     do {
8403       // find the next node on the Side 2
8404       sideNode = 0;
8405       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8406       long linkID;
8407       checkedLinkIDs.clear();
8408       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8409
8410       // loop on inverse elements of current node (prevSideNode) on the Side 2
8411       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8412       while ( invElemIt->more() )
8413       {
8414         const SMDS_MeshElement* elem = invElemIt->next();
8415         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8416         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8417         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8418         bool isVolume = volume.Set( elem );
8419         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8420         if ( isVolume ) // --volume
8421           hasVolumes = true;
8422         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8423           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8424           if(elem->IsQuadratic()) {
8425             const SMDS_VtkFace* F =
8426               dynamic_cast<const SMDS_VtkFace*>(elem);
8427             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8428             // use special nodes iterator
8429             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8430             while( anIter->more() ) {
8431               nodes[ iNode ] = cast2Node(anIter->next());
8432               if ( nodes[ iNode++ ] == prevSideNode )
8433                 iPrevNode = iNode - 1;
8434             }
8435           }
8436           else {
8437             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8438             while ( nIt->more() ) {
8439               nodes[ iNode ] = cast2Node( nIt->next() );
8440               if ( nodes[ iNode++ ] == prevSideNode )
8441                 iPrevNode = iNode - 1;
8442             }
8443           }
8444           // there are 2 links to check
8445           nbNodes = 2;
8446         }
8447         else // --edge
8448           continue;
8449         // loop on links, to be precise, on the second node of links
8450         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8451           const SMDS_MeshNode* n = nodes[ iNode ];
8452           if ( isVolume ) {
8453             if ( !volume.IsLinked( n, prevSideNode ))
8454               continue;
8455           }
8456           else {
8457             if ( iNode ) // a node before prevSideNode
8458               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8459             else         // a node after prevSideNode
8460               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8461           }
8462           // check if this link was already used
8463           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8464           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8465           if (!isJustChecked &&
8466               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8467           {
8468             // test a link geometrically
8469             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8470             bool linkIsBetter = false;
8471             double dot = 0.0, dist = 0.0;
8472             if ( searchByDir ) { // choose most co-directed link
8473               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8474               linkIsBetter = ( dot > maxDot );
8475             }
8476             else { // choose link with the node closest to bordPos
8477               dist = ( nextXYZ - bordPos ).SquareModulus();
8478               linkIsBetter = ( dist < minDist );
8479             }
8480             if ( linkIsBetter ) {
8481               maxDot = dot;
8482               minDist = dist;
8483               linkID = iLink;
8484               sideNode = n;
8485               sideElem = elem;
8486             }
8487           }
8488         }
8489       } // loop on inverse elements of prevSideNode
8490
8491       if ( !sideNode ) {
8492         MESSAGE(" Cant find path by links of the Side 2 ");
8493         return SEW_BAD_SIDE_NODES;
8494       }
8495       sideNodes.push_back( sideNode );
8496       sideElems.push_back( sideElem );
8497       foundSideLinkIDs.insert ( linkID );
8498       prevSideNode = sideNode;
8499
8500       if ( *nBordIt == theBordLastNode )
8501         searchByDir = false;
8502       else {
8503         // find the next border link to compare with
8504         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8505         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8506         // move to next border node if sideNode is before forward border node (bordPos)
8507         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8508           prevBordNode = *nBordIt;
8509           nBordIt++;
8510           bordPos = nBordXYZ[ *nBordIt ];
8511           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8512           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8513         }
8514       }
8515     }
8516     while ( sideNode != theSideSecondNode );
8517
8518     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8519       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8520       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8521     }
8522   } // end nodes search on the side 2
8523
8524   // ============================
8525   // sew the border to the side 2
8526   // ============================
8527
8528   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8529   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8530
8531   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8532   if ( toMergeConformal && toCreatePolygons )
8533   {
8534     // do not merge quadrangles if polygons are OK (IPAL0052824)
8535     eIt[0] = eSide[0].begin();
8536     eIt[1] = eSide[1].begin();
8537     bool allQuads[2] = { true, true };
8538     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8539       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8540         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8541     }
8542     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8543   }
8544
8545   TListOfListOfNodes nodeGroupsToMerge;
8546   if (( toMergeConformal ) ||
8547       ( theSideIsFreeBorder && !theSideThirdNode )) {
8548
8549     // all nodes are to be merged
8550
8551     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8552          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8553          nIt[0]++, nIt[1]++ )
8554     {
8555       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8556       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8557       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8558     }
8559   }
8560   else {
8561
8562     // insert new nodes into the border and the side to get equal nb of segments
8563
8564     // get normalized parameters of nodes on the borders
8565     vector< double > param[ 2 ];
8566     param[0].resize( maxNbNodes );
8567     param[1].resize( maxNbNodes );
8568     int iNode, iBord;
8569     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8570       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8571       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8572       const SMDS_MeshNode* nPrev = *nIt;
8573       double bordLength = 0;
8574       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8575         const SMDS_MeshNode* nCur = *nIt;
8576         gp_XYZ segment (nCur->X() - nPrev->X(),
8577                         nCur->Y() - nPrev->Y(),
8578                         nCur->Z() - nPrev->Z());
8579         double segmentLen = segment.Modulus();
8580         bordLength += segmentLen;
8581         param[ iBord ][ iNode ] = bordLength;
8582         nPrev = nCur;
8583       }
8584       // normalize within [0,1]
8585       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8586         param[ iBord ][ iNode ] /= bordLength;
8587       }
8588     }
8589
8590     // loop on border segments
8591     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8592     int i[ 2 ] = { 0, 0 };
8593     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8594     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8595
8596     TElemOfNodeListMap insertMap;
8597     TElemOfNodeListMap::iterator insertMapIt;
8598     // insertMap is
8599     // key:   elem to insert nodes into
8600     // value: 2 nodes to insert between + nodes to be inserted
8601     do {
8602       bool next[ 2 ] = { false, false };
8603
8604       // find min adjacent segment length after sewing
8605       double nextParam = 10., prevParam = 0;
8606       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8607         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8608           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8609         if ( i[ iBord ] > 0 )
8610           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8611       }
8612       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8613       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8614       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8615
8616       // choose to insert or to merge nodes
8617       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8618       if ( Abs( du ) <= minSegLen * 0.2 ) {
8619         // merge
8620         // ------
8621         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8622         const SMDS_MeshNode* n0 = *nIt[0];
8623         const SMDS_MeshNode* n1 = *nIt[1];
8624         nodeGroupsToMerge.back().push_back( n1 );
8625         nodeGroupsToMerge.back().push_back( n0 );
8626         // position of node of the border changes due to merge
8627         param[ 0 ][ i[0] ] += du;
8628         // move n1 for the sake of elem shape evaluation during insertion.
8629         // n1 will be removed by MergeNodes() anyway
8630         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8631         next[0] = next[1] = true;
8632       }
8633       else {
8634         // insert
8635         // ------
8636         int intoBord = ( du < 0 ) ? 0 : 1;
8637         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8638         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8639         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8640         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8641         if ( intoBord == 1 ) {
8642           // move node of the border to be on a link of elem of the side
8643           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8644           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8645           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8646           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8647           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8648         }
8649         insertMapIt = insertMap.find( elem );
8650         bool  notFound = ( insertMapIt == insertMap.end() );
8651         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8652         if ( otherLink ) {
8653           // insert into another link of the same element:
8654           // 1. perform insertion into the other link of the elem
8655           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8656           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8657           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8658           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8659           // 2. perform insertion into the link of adjacent faces
8660           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8661             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8662           }
8663           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8664             InsertNodesIntoLink( seg, n12, n22, nodeList );
8665           }
8666           if (toCreatePolyedrs) {
8667             // perform insertion into the links of adjacent volumes
8668             UpdateVolumes(n12, n22, nodeList);
8669           }
8670           // 3. find an element appeared on n1 and n2 after the insertion
8671           insertMap.erase( elem );
8672           elem = findAdjacentFace( n1, n2, 0 );
8673         }
8674         if ( notFound || otherLink ) {
8675           // add element and nodes of the side into the insertMap
8676           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8677           (*insertMapIt).second.push_back( n1 );
8678           (*insertMapIt).second.push_back( n2 );
8679         }
8680         // add node to be inserted into elem
8681         (*insertMapIt).second.push_back( nIns );
8682         next[ 1 - intoBord ] = true;
8683       }
8684
8685       // go to the next segment
8686       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8687         if ( next[ iBord ] ) {
8688           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8689             eIt[ iBord ]++;
8690           nPrev[ iBord ] = *nIt[ iBord ];
8691           nIt[ iBord ]++; i[ iBord ]++;
8692         }
8693       }
8694     }
8695     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8696
8697     // perform insertion of nodes into elements
8698
8699     for (insertMapIt = insertMap.begin();
8700          insertMapIt != insertMap.end();
8701          insertMapIt++ )
8702     {
8703       const SMDS_MeshElement* elem = (*insertMapIt).first;
8704       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8705       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8706       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8707
8708       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8709
8710       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8711         InsertNodesIntoLink( seg, n1, n2, nodeList );
8712       }
8713
8714       if ( !theSideIsFreeBorder ) {
8715         // look for and insert nodes into the faces adjacent to elem
8716         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8717           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8718         }
8719       }
8720       if (toCreatePolyedrs) {
8721         // perform insertion into the links of adjacent volumes
8722         UpdateVolumes(n1, n2, nodeList);
8723       }
8724     }
8725   } // end: insert new nodes
8726
8727   MergeNodes ( nodeGroupsToMerge );
8728
8729
8730   // Remove coincident segments
8731
8732   // get new segments
8733   TIDSortedElemSet segments;
8734   SMESH_SequenceOfElemPtr newFaces;
8735   for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8736   {
8737     if ( !myLastCreatedElems(i) ) continue;
8738     if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8739       segments.insert( segments.end(), myLastCreatedElems(i) );
8740     else
8741       newFaces.Append( myLastCreatedElems(i) );
8742   }
8743   // get segments adjacent to merged nodes
8744   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8745   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8746   {
8747     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8748     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8749     while ( segIt->more() )
8750       segments.insert( segIt->next() );
8751   }
8752
8753   // find coincident
8754   TListOfListOfElementsID equalGroups;
8755   if ( !segments.empty() )
8756     FindEqualElements( segments, equalGroups );
8757   if ( !equalGroups.empty() )
8758   {
8759     // remove from segments those that will be removed
8760     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8761     for ( ; itGroups != equalGroups.end(); ++itGroups )
8762     {
8763       list< int >& group = *itGroups;
8764       list< int >::iterator id = group.begin();
8765       for ( ++id; id != group.end(); ++id )
8766         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8767           segments.erase( seg );
8768     }
8769     // remove equal segments
8770     MergeElements( equalGroups );
8771
8772     // restore myLastCreatedElems
8773     myLastCreatedElems = newFaces;
8774     TIDSortedElemSet::iterator seg = segments.begin();
8775     for ( ; seg != segments.end(); ++seg )
8776       myLastCreatedElems.Append( *seg );
8777   }
8778
8779   return aResult;
8780 }
8781
8782 //=======================================================================
8783 //function : InsertNodesIntoLink
8784 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8785 //           and theBetweenNode2 and split theElement
8786 //=======================================================================
8787
8788 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8789                                            const SMDS_MeshNode*        theBetweenNode1,
8790                                            const SMDS_MeshNode*        theBetweenNode2,
8791                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8792                                            const bool                  toCreatePoly)
8793 {
8794   if ( !theElement ) return;
8795
8796   SMESHDS_Mesh *aMesh = GetMeshDS();
8797   vector<const SMDS_MeshElement*> newElems;
8798
8799   if ( theElement->GetType() == SMDSAbs_Edge )
8800   {
8801     theNodesToInsert.push_front( theBetweenNode1 );
8802     theNodesToInsert.push_back ( theBetweenNode2 );
8803     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8804     const SMDS_MeshNode* n1 = *n;
8805     for ( ++n; n != theNodesToInsert.end(); ++n )
8806     {
8807       const SMDS_MeshNode* n2 = *n;
8808       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8809         AddToSameGroups( seg, theElement, aMesh );
8810       else
8811         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8812       n1 = n2;
8813     }
8814     theNodesToInsert.pop_front();
8815     theNodesToInsert.pop_back();
8816
8817     if ( theElement->IsQuadratic() ) // add a not split part
8818     {
8819       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8820                                           theElement->end_nodes() );
8821       int iOther = 0, nbN = nodes.size();
8822       for ( ; iOther < nbN; ++iOther )
8823         if ( nodes[iOther] != theBetweenNode1 &&
8824              nodes[iOther] != theBetweenNode2 )
8825           break;
8826       if      ( iOther == 0 )
8827       {
8828         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8829           AddToSameGroups( seg, theElement, aMesh );
8830         else
8831           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8832       }
8833       else if ( iOther == 2 )
8834       {
8835         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8836           AddToSameGroups( seg, theElement, aMesh );
8837         else
8838           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8839       }
8840     }
8841     // treat new elements
8842     for ( size_t i = 0; i < newElems.size(); ++i )
8843       if ( newElems[i] )
8844       {
8845         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8846         myLastCreatedElems.Append( newElems[i] );
8847       }
8848     ReplaceElemInGroups( theElement, newElems, aMesh );
8849     aMesh->RemoveElement( theElement );
8850     return;
8851
8852   } // if ( theElement->GetType() == SMDSAbs_Edge )
8853
8854   const SMDS_MeshElement* theFace = theElement;
8855   if ( theFace->GetType() != SMDSAbs_Face ) return;
8856
8857   // find indices of 2 link nodes and of the rest nodes
8858   int iNode = 0, il1, il2, i3, i4;
8859   il1 = il2 = i3 = i4 = -1;
8860   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8861
8862   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8863   while ( nodeIt->more() ) {
8864     const SMDS_MeshNode* n = nodeIt->next();
8865     if ( n == theBetweenNode1 )
8866       il1 = iNode;
8867     else if ( n == theBetweenNode2 )
8868       il2 = iNode;
8869     else if ( i3 < 0 )
8870       i3 = iNode;
8871     else
8872       i4 = iNode;
8873     nodes[ iNode++ ] = n;
8874   }
8875   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8876     return ;
8877
8878   // arrange link nodes to go one after another regarding the face orientation
8879   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8880   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8881   if ( reverse ) {
8882     iNode = il1;
8883     il1 = il2;
8884     il2 = iNode;
8885     aNodesToInsert.reverse();
8886   }
8887   // check that not link nodes of a quadrangles are in good order
8888   int nbFaceNodes = theFace->NbNodes();
8889   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8890     iNode = i3;
8891     i3 = i4;
8892     i4 = iNode;
8893   }
8894
8895   if (toCreatePoly || theFace->IsPoly()) {
8896
8897     iNode = 0;
8898     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8899
8900     // add nodes of face up to first node of link
8901     bool isFLN = false;
8902
8903     if ( theFace->IsQuadratic() ) {
8904       const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8905       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8906       // use special nodes iterator
8907       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8908       while( anIter->more()  && !isFLN ) {
8909         const SMDS_MeshNode* n = cast2Node(anIter->next());
8910         poly_nodes[iNode++] = n;
8911         if (n == nodes[il1]) {
8912           isFLN = true;
8913         }
8914       }
8915       // add nodes to insert
8916       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8917       for (; nIt != aNodesToInsert.end(); nIt++) {
8918         poly_nodes[iNode++] = *nIt;
8919       }
8920       // add nodes of face starting from last node of link
8921       while ( anIter->more() ) {
8922         poly_nodes[iNode++] = cast2Node(anIter->next());
8923       }
8924     }
8925     else {
8926       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8927       while ( nodeIt->more() && !isFLN ) {
8928         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8929         poly_nodes[iNode++] = n;
8930         if (n == nodes[il1]) {
8931           isFLN = true;
8932         }
8933       }
8934       // add nodes to insert
8935       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8936       for (; nIt != aNodesToInsert.end(); nIt++) {
8937         poly_nodes[iNode++] = *nIt;
8938       }
8939       // add nodes of face starting from last node of link
8940       while ( nodeIt->more() ) {
8941         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8942         poly_nodes[iNode++] = n;
8943       }
8944     }
8945
8946     // make a new face
8947     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8948   }
8949
8950   else if ( !theFace->IsQuadratic() )
8951   {
8952     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8953     int nbLinkNodes = 2 + aNodesToInsert.size();
8954     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8955     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8956     linkNodes[ 0 ] = nodes[ il1 ];
8957     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8958     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8959     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8960       linkNodes[ iNode++ ] = *nIt;
8961     }
8962     // decide how to split a quadrangle: compare possible variants
8963     // and choose which of splits to be a quadrangle
8964     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8965     if ( nbFaceNodes == 3 ) {
8966       iBestQuad = nbSplits;
8967       i4 = i3;
8968     }
8969     else if ( nbFaceNodes == 4 ) {
8970       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8971       double aBestRate = DBL_MAX;
8972       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8973         i1 = 0; i2 = 1;
8974         double aBadRate = 0;
8975         // evaluate elements quality
8976         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8977           if ( iSplit == iQuad ) {
8978             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8979                                    linkNodes[ i2++ ],
8980                                    nodes[ i3 ],
8981                                    nodes[ i4 ]);
8982             aBadRate += getBadRate( &quad, aCrit );
8983           }
8984           else {
8985             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8986                                    linkNodes[ i2++ ],
8987                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8988             aBadRate += getBadRate( &tria, aCrit );
8989           }
8990         }
8991         // choice
8992         if ( aBadRate < aBestRate ) {
8993           iBestQuad = iQuad;
8994           aBestRate = aBadRate;
8995         }
8996       }
8997     }
8998
8999     // create new elements
9000     i1 = 0; i2 = 1;
9001     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
9002     {
9003       if ( iSplit == iBestQuad )
9004         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9005                                             linkNodes[ i2++ ],
9006                                             nodes[ i3 ],
9007                                             nodes[ i4 ]));
9008       else
9009         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9010                                             linkNodes[ i2++ ],
9011                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
9012     }
9013
9014     const SMDS_MeshNode* newNodes[ 4 ];
9015     newNodes[ 0 ] = linkNodes[ i1 ];
9016     newNodes[ 1 ] = linkNodes[ i2 ];
9017     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9018     newNodes[ 3 ] = nodes[ i4 ];
9019     if (iSplit == iBestQuad)
9020       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
9021     else
9022       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
9023
9024   } // end if(!theFace->IsQuadratic())
9025
9026   else { // theFace is quadratic
9027     // we have to split theFace on simple triangles and one simple quadrangle
9028     int tmp = il1/2;
9029     int nbshift = tmp*2;
9030     // shift nodes in nodes[] by nbshift
9031     int i,j;
9032     for(i=0; i<nbshift; i++) {
9033       const SMDS_MeshNode* n = nodes[0];
9034       for(j=0; j<nbFaceNodes-1; j++) {
9035         nodes[j] = nodes[j+1];
9036       }
9037       nodes[nbFaceNodes-1] = n;
9038     }
9039     il1 = il1 - nbshift;
9040     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9041     //   n0      n1     n2    n0      n1     n2
9042     //     +-----+-----+        +-----+-----+
9043     //      \         /         |           |
9044     //       \       /          |           |
9045     //      n5+     +n3       n7+           +n3
9046     //         \   /            |           |
9047     //          \ /             |           |
9048     //           +              +-----+-----+
9049     //           n4           n6      n5     n4
9050
9051     // create new elements
9052     int n1,n2,n3;
9053     if ( nbFaceNodes == 6 ) { // quadratic triangle
9054       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9055       if ( theFace->IsMediumNode(nodes[il1]) ) {
9056         // create quadrangle
9057         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
9058         n1 = 1;
9059         n2 = 2;
9060         n3 = 3;
9061       }
9062       else {
9063         // create quadrangle
9064         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
9065         n1 = 0;
9066         n2 = 1;
9067         n3 = 5;
9068       }
9069     }
9070     else { // nbFaceNodes==8 - quadratic quadrangle
9071       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9072       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9073       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9074       if ( theFace->IsMediumNode( nodes[ il1 ])) {
9075         // create quadrangle
9076         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9077         n1 = 1;
9078         n2 = 2;
9079         n3 = 3;
9080       }
9081       else {
9082         // create quadrangle
9083         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9084         n1 = 0;
9085         n2 = 1;
9086         n3 = 7;
9087       }
9088     }
9089     // create needed triangles using n1,n2,n3 and inserted nodes
9090     int nbn = 2 + aNodesToInsert.size();
9091     vector<const SMDS_MeshNode*> aNodes(nbn);
9092     aNodes[0    ] = nodes[n1];
9093     aNodes[nbn-1] = nodes[n2];
9094     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9095     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9096       aNodes[iNode++] = *nIt;
9097     }
9098     for ( i = 1; i < nbn; i++ )
9099       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9100   }
9101
9102   // remove the old face
9103   for ( size_t i = 0; i < newElems.size(); ++i )
9104     if ( newElems[i] )
9105     {
9106       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9107       myLastCreatedElems.Append( newElems[i] );
9108     }
9109   ReplaceElemInGroups( theFace, newElems, aMesh );
9110   aMesh->RemoveElement(theFace);
9111
9112 } // InsertNodesIntoLink()
9113
9114 //=======================================================================
9115 //function : UpdateVolumes
9116 //purpose  :
9117 //=======================================================================
9118
9119 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
9120                                       const SMDS_MeshNode*        theBetweenNode2,
9121                                       list<const SMDS_MeshNode*>& theNodesToInsert)
9122 {
9123   myLastCreatedElems.Clear();
9124   myLastCreatedNodes.Clear();
9125
9126   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9127   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9128     const SMDS_MeshElement* elem = invElemIt->next();
9129
9130     // check, if current volume has link theBetweenNode1 - theBetweenNode2
9131     SMDS_VolumeTool aVolume (elem);
9132     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9133       continue;
9134
9135     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9136     int iface, nbFaces = aVolume.NbFaces();
9137     vector<const SMDS_MeshNode *> poly_nodes;
9138     vector<int> quantities (nbFaces);
9139
9140     for (iface = 0; iface < nbFaces; iface++) {
9141       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9142       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9143       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9144
9145       for (int inode = 0; inode < nbFaceNodes; inode++) {
9146         poly_nodes.push_back(faceNodes[inode]);
9147
9148         if (nbInserted == 0) {
9149           if (faceNodes[inode] == theBetweenNode1) {
9150             if (faceNodes[inode + 1] == theBetweenNode2) {
9151               nbInserted = theNodesToInsert.size();
9152
9153               // add nodes to insert
9154               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9155               for (; nIt != theNodesToInsert.end(); nIt++) {
9156                 poly_nodes.push_back(*nIt);
9157               }
9158             }
9159           }
9160           else if (faceNodes[inode] == theBetweenNode2) {
9161             if (faceNodes[inode + 1] == theBetweenNode1) {
9162               nbInserted = theNodesToInsert.size();
9163
9164               // add nodes to insert in reversed order
9165               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9166               nIt--;
9167               for (; nIt != theNodesToInsert.begin(); nIt--) {
9168                 poly_nodes.push_back(*nIt);
9169               }
9170               poly_nodes.push_back(*nIt);
9171             }
9172           }
9173           else {
9174           }
9175         }
9176       }
9177       quantities[iface] = nbFaceNodes + nbInserted;
9178     }
9179
9180     // Replace the volume
9181     SMESHDS_Mesh *aMesh = GetMeshDS();
9182
9183     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9184     {
9185       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9186       myLastCreatedElems.Append( newElem );
9187       ReplaceElemInGroups( elem, newElem, aMesh );
9188     }
9189     aMesh->RemoveElement( elem );
9190   }
9191 }
9192
9193 namespace
9194 {
9195   //================================================================================
9196   /*!
9197    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9198    */
9199   //================================================================================
9200
9201   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9202                            vector<const SMDS_MeshNode *> & nodes,
9203                            vector<int> &                   nbNodeInFaces )
9204   {
9205     nodes.clear();
9206     nbNodeInFaces.clear();
9207     SMDS_VolumeTool vTool ( elem );
9208     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9209     {
9210       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9211       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9212       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9213     }
9214   }
9215 }
9216
9217 //=======================================================================
9218 /*!
9219  * \brief Convert elements contained in a sub-mesh to quadratic
9220  * \return int - nb of checked elements
9221  */
9222 //=======================================================================
9223
9224 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9225                                              SMESH_MesherHelper& theHelper,
9226                                              const bool          theForce3d)
9227 {
9228   int nbElem = 0;
9229   if( !theSm ) return nbElem;
9230
9231   vector<int> nbNodeInFaces;
9232   vector<const SMDS_MeshNode *> nodes;
9233   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9234   while(ElemItr->more())
9235   {
9236     nbElem++;
9237     const SMDS_MeshElement* elem = ElemItr->next();
9238     if( !elem ) continue;
9239
9240     // analyse a necessity of conversion
9241     const SMDSAbs_ElementType aType = elem->GetType();
9242     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9243       continue;
9244     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9245     bool hasCentralNodes = false;
9246     if ( elem->IsQuadratic() )
9247     {
9248       bool alreadyOK;
9249       switch ( aGeomType ) {
9250       case SMDSEntity_Quad_Triangle:
9251       case SMDSEntity_Quad_Quadrangle:
9252       case SMDSEntity_Quad_Hexa:
9253         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9254
9255       case SMDSEntity_BiQuad_Triangle:
9256       case SMDSEntity_BiQuad_Quadrangle:
9257       case SMDSEntity_TriQuad_Hexa:
9258         alreadyOK = theHelper.GetIsBiQuadratic();
9259         hasCentralNodes = true;
9260         break;
9261       default:
9262         alreadyOK = true;
9263       }
9264       // take into account already present modium nodes
9265       switch ( aType ) {
9266       case SMDSAbs_Volume:
9267         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9268       case SMDSAbs_Face:
9269         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9270       case SMDSAbs_Edge:
9271         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9272       default:;
9273       }
9274       if ( alreadyOK )
9275         continue;
9276     }
9277     // get elem data needed to re-create it
9278     //
9279     const int id      = elem->GetID();
9280     const int nbNodes = elem->NbCornerNodes();
9281     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9282     if ( aGeomType == SMDSEntity_Polyhedra )
9283       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9284     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9285       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9286
9287     // remove a linear element
9288     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9289
9290     // remove central nodes of biquadratic elements (biquad->quad convertion)
9291     if ( hasCentralNodes )
9292       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9293         if ( nodes[i]->NbInverseElements() == 0 )
9294           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9295
9296     const SMDS_MeshElement* NewElem = 0;
9297
9298     switch( aType )
9299     {
9300     case SMDSAbs_Edge :
9301       {
9302         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9303         break;
9304       }
9305     case SMDSAbs_Face :
9306       {
9307         switch(nbNodes)
9308         {
9309         case 3:
9310           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9311           break;
9312         case 4:
9313           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9314           break;
9315         default:
9316           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9317         }
9318         break;
9319       }
9320     case SMDSAbs_Volume :
9321       {
9322         switch( aGeomType )
9323         {
9324         case SMDSEntity_Tetra:
9325           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9326           break;
9327         case SMDSEntity_Pyramid:
9328           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9329           break;
9330         case SMDSEntity_Penta:
9331           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9332           break;
9333         case SMDSEntity_Hexa:
9334         case SMDSEntity_Quad_Hexa:
9335         case SMDSEntity_TriQuad_Hexa:
9336           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9337                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9338           break;
9339         case SMDSEntity_Hexagonal_Prism:
9340         default:
9341           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9342         }
9343         break;
9344       }
9345     default :
9346       continue;
9347     }
9348     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9349     if( NewElem && NewElem->getshapeId() < 1 )
9350       theSm->AddElement( NewElem );
9351   }
9352   return nbElem;
9353 }
9354 //=======================================================================
9355 //function : ConvertToQuadratic
9356 //purpose  :
9357 //=======================================================================
9358
9359 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9360 {
9361   SMESHDS_Mesh* meshDS = GetMeshDS();
9362
9363   SMESH_MesherHelper aHelper(*myMesh);
9364
9365   aHelper.SetIsQuadratic( true );
9366   aHelper.SetIsBiQuadratic( theToBiQuad );
9367   aHelper.SetElementsOnShape(true);
9368   aHelper.ToFixNodeParameters( true );
9369
9370   // convert elements assigned to sub-meshes
9371   int nbCheckedElems = 0;
9372   if ( myMesh->HasShapeToMesh() )
9373   {
9374     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9375     {
9376       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9377       while ( smIt->more() ) {
9378         SMESH_subMesh* sm = smIt->next();
9379         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9380           aHelper.SetSubShape( sm->GetSubShape() );
9381           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9382         }
9383       }
9384     }
9385   }
9386
9387   // convert elements NOT assigned to sub-meshes
9388   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9389   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9390   {
9391     aHelper.SetElementsOnShape(false);
9392     SMESHDS_SubMesh *smDS = 0;
9393
9394     // convert edges
9395     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9396     while( aEdgeItr->more() )
9397     {
9398       const SMDS_MeshEdge* edge = aEdgeItr->next();
9399       if ( !edge->IsQuadratic() )
9400       {
9401         int                  id = edge->GetID();
9402         const SMDS_MeshNode* n1 = edge->GetNode(0);
9403         const SMDS_MeshNode* n2 = edge->GetNode(1);
9404
9405         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9406
9407         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9408         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9409       }
9410       else
9411       {
9412         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9413       }
9414     }
9415
9416     // convert faces
9417     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9418     while( aFaceItr->more() )
9419     {
9420       const SMDS_MeshFace* face = aFaceItr->next();
9421       if ( !face ) continue;
9422       
9423       const SMDSAbs_EntityType type = face->GetEntityType();
9424       bool alreadyOK;
9425       switch( type )
9426       {
9427       case SMDSEntity_Quad_Triangle:
9428       case SMDSEntity_Quad_Quadrangle:
9429         alreadyOK = !theToBiQuad;
9430         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9431         break;
9432       case SMDSEntity_BiQuad_Triangle:
9433       case SMDSEntity_BiQuad_Quadrangle:
9434         alreadyOK = theToBiQuad;
9435         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9436         break;
9437       default: alreadyOK = false;
9438       }
9439       if ( alreadyOK )
9440         continue;
9441
9442       const int id = face->GetID();
9443       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9444
9445       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9446
9447       SMDS_MeshFace * NewFace = 0;
9448       switch( type )
9449       {
9450       case SMDSEntity_Triangle:
9451       case SMDSEntity_Quad_Triangle:
9452       case SMDSEntity_BiQuad_Triangle:
9453         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9454         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9455           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9456         break;
9457
9458       case SMDSEntity_Quadrangle:
9459       case SMDSEntity_Quad_Quadrangle:
9460       case SMDSEntity_BiQuad_Quadrangle:
9461         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9462         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9463           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9464         break;
9465
9466       default:;
9467         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9468       }
9469       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9470     }
9471
9472     // convert volumes
9473     vector<int> nbNodeInFaces;
9474     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9475     while(aVolumeItr->more())
9476     {
9477       const SMDS_MeshVolume* volume = aVolumeItr->next();
9478       if ( !volume ) continue;
9479
9480       const SMDSAbs_EntityType type = volume->GetEntityType();
9481       if ( volume->IsQuadratic() )
9482       {
9483         bool alreadyOK;
9484         switch ( type )
9485         {
9486         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9487         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9488         default:                      alreadyOK = true;
9489         }
9490         if ( alreadyOK )
9491         {
9492           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9493           continue;
9494         }
9495       }
9496       const int id = volume->GetID();
9497       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9498       if ( type == SMDSEntity_Polyhedra )
9499         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9500       else if ( type == SMDSEntity_Hexagonal_Prism )
9501         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9502
9503       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9504
9505       SMDS_MeshVolume * NewVolume = 0;
9506       switch ( type )
9507       {
9508       case SMDSEntity_Tetra:
9509         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9510         break;
9511       case SMDSEntity_Hexa:
9512       case SMDSEntity_Quad_Hexa:
9513       case SMDSEntity_TriQuad_Hexa:
9514         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9515                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9516         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9517           if ( nodes[i]->NbInverseElements() == 0 )
9518             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9519         break;
9520       case SMDSEntity_Pyramid:
9521         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9522                                       nodes[3], nodes[4], id, theForce3d);
9523         break;
9524       case SMDSEntity_Penta:
9525         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9526                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9527         break;
9528       case SMDSEntity_Hexagonal_Prism:
9529       default:
9530         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9531       }
9532       ReplaceElemInGroups(volume, NewVolume, meshDS);
9533     }
9534   }
9535
9536   if ( !theForce3d )
9537   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9538     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9539     // aHelper.FixQuadraticElements(myError);
9540     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9541   }
9542 }
9543
9544 //================================================================================
9545 /*!
9546  * \brief Makes given elements quadratic
9547  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9548  *  \param theElements - elements to make quadratic
9549  */
9550 //================================================================================
9551
9552 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9553                                           TIDSortedElemSet& theElements,
9554                                           const bool        theToBiQuad)
9555 {
9556   if ( theElements.empty() ) return;
9557
9558   // we believe that all theElements are of the same type
9559   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9560
9561   // get all nodes shared by theElements
9562   TIDSortedNodeSet allNodes;
9563   TIDSortedElemSet::iterator eIt = theElements.begin();
9564   for ( ; eIt != theElements.end(); ++eIt )
9565     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9566
9567   // complete theElements with elements of lower dim whose all nodes are in allNodes
9568
9569   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9570   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9571   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9572   for ( ; nIt != allNodes.end(); ++nIt )
9573   {
9574     const SMDS_MeshNode* n = *nIt;
9575     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9576     while ( invIt->more() )
9577     {
9578       const SMDS_MeshElement*      e = invIt->next();
9579       const SMDSAbs_ElementType type = e->GetType();
9580       if ( e->IsQuadratic() )
9581       {
9582         quadAdjacentElems[ type ].insert( e );
9583
9584         bool alreadyOK;
9585         switch ( e->GetEntityType() ) {
9586         case SMDSEntity_Quad_Triangle:
9587         case SMDSEntity_Quad_Quadrangle:
9588         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9589         case SMDSEntity_BiQuad_Triangle:
9590         case SMDSEntity_BiQuad_Quadrangle:
9591         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9592         default:                           alreadyOK = true;
9593         }
9594         if ( alreadyOK )
9595           continue;
9596       }
9597       if ( type >= elemType )
9598         continue; // same type or more complex linear element
9599
9600       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9601         continue; // e is already checked
9602
9603       // check nodes
9604       bool allIn = true;
9605       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9606       while ( nodeIt->more() && allIn )
9607         allIn = allNodes.count( nodeIt->next() );
9608       if ( allIn )
9609         theElements.insert(e );
9610     }
9611   }
9612
9613   SMESH_MesherHelper helper(*myMesh);
9614   helper.SetIsQuadratic( true );
9615   helper.SetIsBiQuadratic( theToBiQuad );
9616
9617   // add links of quadratic adjacent elements to the helper
9618
9619   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9620     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9621           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9622     {
9623       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9624     }
9625   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9626     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9627           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9628     {
9629       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9630     }
9631   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9632     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9633           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9634     {
9635       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9636     }
9637
9638   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9639
9640   SMESHDS_Mesh*  meshDS = GetMeshDS();
9641   SMESHDS_SubMesh* smDS = 0;
9642   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9643   {
9644     const SMDS_MeshElement* elem = *eIt;
9645
9646     bool alreadyOK;
9647     int nbCentralNodes = 0;
9648     switch ( elem->GetEntityType() ) {
9649       // linear convertible
9650     case SMDSEntity_Edge:
9651     case SMDSEntity_Triangle:
9652     case SMDSEntity_Quadrangle:
9653     case SMDSEntity_Tetra:
9654     case SMDSEntity_Pyramid:
9655     case SMDSEntity_Hexa:
9656     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9657       // quadratic that can become bi-quadratic
9658     case SMDSEntity_Quad_Triangle:
9659     case SMDSEntity_Quad_Quadrangle:
9660     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9661       // bi-quadratic
9662     case SMDSEntity_BiQuad_Triangle:
9663     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9664     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9665       // the rest
9666     default:                           alreadyOK = true;
9667     }
9668     if ( alreadyOK ) continue;
9669
9670     const SMDSAbs_ElementType type = elem->GetType();
9671     const int                   id = elem->GetID();
9672     const int              nbNodes = elem->NbCornerNodes();
9673     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9674
9675     helper.SetSubShape( elem->getshapeId() );
9676
9677     if ( !smDS || !smDS->Contains( elem ))
9678       smDS = meshDS->MeshElements( elem->getshapeId() );
9679     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9680
9681     SMDS_MeshElement * newElem = 0;
9682     switch( nbNodes )
9683     {
9684     case 4: // cases for most frequently used element types go first (for optimization)
9685       if ( type == SMDSAbs_Volume )
9686         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9687       else
9688         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9689       break;
9690     case 8:
9691       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9692                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9693       break;
9694     case 3:
9695       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9696       break;
9697     case 2:
9698       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9699       break;
9700     case 5:
9701       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9702                                  nodes[4], id, theForce3d);
9703       break;
9704     case 6:
9705       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9706                                  nodes[4], nodes[5], id, theForce3d);
9707       break;
9708     default:;
9709     }
9710     ReplaceElemInGroups( elem, newElem, meshDS);
9711     if( newElem && smDS )
9712       smDS->AddElement( newElem );
9713
9714      // remove central nodes
9715     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9716       if ( nodes[i]->NbInverseElements() == 0 )
9717         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9718
9719   } // loop on theElements
9720
9721   if ( !theForce3d )
9722   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9723     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9724     // helper.FixQuadraticElements( myError );
9725     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9726   }
9727 }
9728
9729 //=======================================================================
9730 /*!
9731  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9732  * \return int - nb of checked elements
9733  */
9734 //=======================================================================
9735
9736 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9737                                      SMDS_ElemIteratorPtr theItr,
9738                                      const int            theShapeID)
9739 {
9740   int nbElem = 0;
9741   SMESHDS_Mesh* meshDS = GetMeshDS();
9742   ElemFeatures elemType;
9743   vector<const SMDS_MeshNode *> nodes;
9744
9745   while( theItr->more() )
9746   {
9747     const SMDS_MeshElement* elem = theItr->next();
9748     nbElem++;
9749     if( elem && elem->IsQuadratic())
9750     {
9751       // get elem data
9752       int nbCornerNodes = elem->NbCornerNodes();
9753       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9754
9755       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9756
9757       //remove a quadratic element
9758       if ( !theSm || !theSm->Contains( elem ))
9759         theSm = meshDS->MeshElements( elem->getshapeId() );
9760       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9761
9762       // remove medium nodes
9763       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9764         if ( nodes[i]->NbInverseElements() == 0 )
9765           meshDS->RemoveFreeNode( nodes[i], theSm );
9766
9767       // add a linear element
9768       nodes.resize( nbCornerNodes );
9769       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9770       ReplaceElemInGroups(elem, newElem, meshDS);
9771       if( theSm && newElem )
9772         theSm->AddElement( newElem );
9773     }
9774   }
9775   return nbElem;
9776 }
9777
9778 //=======================================================================
9779 //function : ConvertFromQuadratic
9780 //purpose  :
9781 //=======================================================================
9782
9783 bool SMESH_MeshEditor::ConvertFromQuadratic()
9784 {
9785   int nbCheckedElems = 0;
9786   if ( myMesh->HasShapeToMesh() )
9787   {
9788     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9789     {
9790       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9791       while ( smIt->more() ) {
9792         SMESH_subMesh* sm = smIt->next();
9793         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9794           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9795       }
9796     }
9797   }
9798
9799   int totalNbElems =
9800     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9801   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9802   {
9803     SMESHDS_SubMesh *aSM = 0;
9804     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9805   }
9806
9807   return true;
9808 }
9809
9810 namespace
9811 {
9812   //================================================================================
9813   /*!
9814    * \brief Return true if all medium nodes of the element are in the node set
9815    */
9816   //================================================================================
9817
9818   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9819   {
9820     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9821       if ( !nodeSet.count( elem->GetNode(i) ))
9822         return false;
9823     return true;
9824   }
9825 }
9826
9827 //================================================================================
9828 /*!
9829  * \brief Makes given elements linear
9830  */
9831 //================================================================================
9832
9833 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9834 {
9835   if ( theElements.empty() ) return;
9836
9837   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9838   set<int> mediumNodeIDs;
9839   TIDSortedElemSet::iterator eIt = theElements.begin();
9840   for ( ; eIt != theElements.end(); ++eIt )
9841   {
9842     const SMDS_MeshElement* e = *eIt;
9843     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9844       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9845   }
9846
9847   // replace given elements by linear ones
9848   SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9849   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9850
9851   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9852   // except those elements sharing medium nodes of quadratic element whose medium nodes
9853   // are not all in mediumNodeIDs
9854
9855   // get remaining medium nodes
9856   TIDSortedNodeSet mediumNodes;
9857   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9858   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9859     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9860       mediumNodes.insert( mediumNodes.end(), n );
9861
9862   // find more quadratic elements to convert
9863   TIDSortedElemSet moreElemsToConvert;
9864   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9865   for ( ; nIt != mediumNodes.end(); ++nIt )
9866   {
9867     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9868     while ( invIt->more() )
9869     {
9870       const SMDS_MeshElement* e = invIt->next();
9871       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9872       {
9873         // find a more complex element including e and
9874         // whose medium nodes are not in mediumNodes
9875         bool complexFound = false;
9876         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9877         {
9878           SMDS_ElemIteratorPtr invIt2 =
9879             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9880           while ( invIt2->more() )
9881           {
9882             const SMDS_MeshElement* eComplex = invIt2->next();
9883             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9884             {
9885               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9886               if ( nbCommonNodes == e->NbNodes())
9887               {
9888                 complexFound = true;
9889                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9890                 break;
9891               }
9892             }
9893           }
9894         }
9895         if ( !complexFound )
9896           moreElemsToConvert.insert( e );
9897       }
9898     }
9899   }
9900   elemIt = elemSetIterator( moreElemsToConvert );
9901   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9902 }
9903
9904 //=======================================================================
9905 //function : SewSideElements
9906 //purpose  :
9907 //=======================================================================
9908
9909 SMESH_MeshEditor::Sew_Error
9910 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9911                                    TIDSortedElemSet&    theSide2,
9912                                    const SMDS_MeshNode* theFirstNode1,
9913                                    const SMDS_MeshNode* theFirstNode2,
9914                                    const SMDS_MeshNode* theSecondNode1,
9915                                    const SMDS_MeshNode* theSecondNode2)
9916 {
9917   myLastCreatedElems.Clear();
9918   myLastCreatedNodes.Clear();
9919
9920   MESSAGE ("::::SewSideElements()");
9921   if ( theSide1.size() != theSide2.size() )
9922     return SEW_DIFF_NB_OF_ELEMENTS;
9923
9924   Sew_Error aResult = SEW_OK;
9925   // Algo:
9926   // 1. Build set of faces representing each side
9927   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9928   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9929
9930   // =======================================================================
9931   // 1. Build set of faces representing each side:
9932   // =======================================================================
9933   // a. build set of nodes belonging to faces
9934   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9935   // c. create temporary faces representing side of volumes if correspondent
9936   //    face does not exist
9937
9938   SMESHDS_Mesh* aMesh = GetMeshDS();
9939   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9940   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9941   TIDSortedElemSet             faceSet1, faceSet2;
9942   set<const SMDS_MeshElement*> volSet1,  volSet2;
9943   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9944   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9945   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9946   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9947   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9948   int iSide, iFace, iNode;
9949
9950   list<const SMDS_MeshElement* > tempFaceList;
9951   for ( iSide = 0; iSide < 2; iSide++ ) {
9952     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9953     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9954     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9955     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9956     set<const SMDS_MeshElement*>::iterator vIt;
9957     TIDSortedElemSet::iterator eIt;
9958     set<const SMDS_MeshNode*>::iterator    nIt;
9959
9960     // check that given nodes belong to given elements
9961     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9962     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9963     int firstIndex = -1, secondIndex = -1;
9964     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9965       const SMDS_MeshElement* elem = *eIt;
9966       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9967       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9968       if ( firstIndex > -1 && secondIndex > -1 ) break;
9969     }
9970     if ( firstIndex < 0 || secondIndex < 0 ) {
9971       // we can simply return until temporary faces created
9972       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9973     }
9974
9975     // -----------------------------------------------------------
9976     // 1a. Collect nodes of existing faces
9977     //     and build set of face nodes in order to detect missing
9978     //     faces corresponding to sides of volumes
9979     // -----------------------------------------------------------
9980
9981     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9982
9983     // loop on the given element of a side
9984     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9985       //const SMDS_MeshElement* elem = *eIt;
9986       const SMDS_MeshElement* elem = *eIt;
9987       if ( elem->GetType() == SMDSAbs_Face ) {
9988         faceSet->insert( elem );
9989         set <const SMDS_MeshNode*> faceNodeSet;
9990         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9991         while ( nodeIt->more() ) {
9992           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9993           nodeSet->insert( n );
9994           faceNodeSet.insert( n );
9995         }
9996         setOfFaceNodeSet.insert( faceNodeSet );
9997       }
9998       else if ( elem->GetType() == SMDSAbs_Volume )
9999         volSet->insert( elem );
10000     }
10001     // ------------------------------------------------------------------------------
10002     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10003     // ------------------------------------------------------------------------------
10004
10005     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10006       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10007       while ( fIt->more() ) { // loop on faces sharing a node
10008         const SMDS_MeshElement* f = fIt->next();
10009         if ( faceSet->find( f ) == faceSet->end() ) {
10010           // check if all nodes are in nodeSet and
10011           // complete setOfFaceNodeSet if they are
10012           set <const SMDS_MeshNode*> faceNodeSet;
10013           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10014           bool allInSet = true;
10015           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10016             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10017             if ( nodeSet->find( n ) == nodeSet->end() )
10018               allInSet = false;
10019             else
10020               faceNodeSet.insert( n );
10021           }
10022           if ( allInSet ) {
10023             faceSet->insert( f );
10024             setOfFaceNodeSet.insert( faceNodeSet );
10025           }
10026         }
10027       }
10028     }
10029
10030     // -------------------------------------------------------------------------
10031     // 1c. Create temporary faces representing sides of volumes if correspondent
10032     //     face does not exist
10033     // -------------------------------------------------------------------------
10034
10035     if ( !volSet->empty() ) {
10036       //int nodeSetSize = nodeSet->size();
10037
10038       // loop on given volumes
10039       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10040         SMDS_VolumeTool vol (*vIt);
10041         // loop on volume faces: find free faces
10042         // --------------------------------------
10043         list<const SMDS_MeshElement* > freeFaceList;
10044         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10045           if ( !vol.IsFreeFace( iFace ))
10046             continue;
10047           // check if there is already a face with same nodes in a face set
10048           const SMDS_MeshElement* aFreeFace = 0;
10049           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10050           int nbNodes = vol.NbFaceNodes( iFace );
10051           set <const SMDS_MeshNode*> faceNodeSet;
10052           vol.GetFaceNodes( iFace, faceNodeSet );
10053           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10054           if ( isNewFace ) {
10055             // no such a face is given but it still can exist, check it
10056             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10057             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10058           }
10059           if ( !aFreeFace ) {
10060             // create a temporary face
10061             if ( nbNodes == 3 ) {
10062               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10063               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10064             }
10065             else if ( nbNodes == 4 ) {
10066               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10067               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10068             }
10069             else {
10070               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10071               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10072               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10073             }
10074             if ( aFreeFace )
10075               tempFaceList.push_back( aFreeFace );
10076           }
10077
10078           if ( aFreeFace )
10079             freeFaceList.push_back( aFreeFace );
10080
10081         } // loop on faces of a volume
10082
10083         // choose one of several free faces of a volume
10084         // --------------------------------------------
10085         if ( freeFaceList.size() > 1 ) {
10086           // choose a face having max nb of nodes shared by other elems of a side
10087           int maxNbNodes = -1;
10088           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10089           while ( fIt != freeFaceList.end() ) { // loop on free faces
10090             int nbSharedNodes = 0;
10091             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10092             while ( nodeIt->more() ) { // loop on free face nodes
10093               const SMDS_MeshNode* n =
10094                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10095               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10096               while ( invElemIt->more() ) {
10097                 const SMDS_MeshElement* e = invElemIt->next();
10098                 nbSharedNodes += faceSet->count( e );
10099                 nbSharedNodes += elemSet->count( e );
10100               }
10101             }
10102             if ( nbSharedNodes > maxNbNodes ) {
10103               maxNbNodes = nbSharedNodes;
10104               freeFaceList.erase( freeFaceList.begin(), fIt++ );
10105             }
10106             else if ( nbSharedNodes == maxNbNodes ) {
10107               fIt++;
10108             }
10109             else {
10110               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10111             }
10112           }
10113           if ( freeFaceList.size() > 1 )
10114           {
10115             // could not choose one face, use another way
10116             // choose a face most close to the bary center of the opposite side
10117             gp_XYZ aBC( 0., 0., 0. );
10118             set <const SMDS_MeshNode*> addedNodes;
10119             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10120             eIt = elemSet2->begin();
10121             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10122               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10123               while ( nodeIt->more() ) { // loop on free face nodes
10124                 const SMDS_MeshNode* n =
10125                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10126                 if ( addedNodes.insert( n ).second )
10127                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10128               }
10129             }
10130             aBC /= addedNodes.size();
10131             double minDist = DBL_MAX;
10132             fIt = freeFaceList.begin();
10133             while ( fIt != freeFaceList.end() ) { // loop on free faces
10134               double dist = 0;
10135               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10136               while ( nodeIt->more() ) { // loop on free face nodes
10137                 const SMDS_MeshNode* n =
10138                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10139                 gp_XYZ p( n->X(),n->Y(),n->Z() );
10140                 dist += ( aBC - p ).SquareModulus();
10141               }
10142               if ( dist < minDist ) {
10143                 minDist = dist;
10144                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10145               }
10146               else
10147                 fIt = freeFaceList.erase( fIt++ );
10148             }
10149           }
10150         } // choose one of several free faces of a volume
10151
10152         if ( freeFaceList.size() == 1 ) {
10153           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10154           faceSet->insert( aFreeFace );
10155           // complete a node set with nodes of a found free face
10156           //           for ( iNode = 0; iNode < ; iNode++ )
10157           //             nodeSet->insert( fNodes[ iNode ] );
10158         }
10159
10160       } // loop on volumes of a side
10161
10162       //       // complete a set of faces if new nodes in a nodeSet appeared
10163       //       // ----------------------------------------------------------
10164       //       if ( nodeSetSize != nodeSet->size() ) {
10165       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10166       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10167       //           while ( fIt->more() ) { // loop on faces sharing a node
10168       //             const SMDS_MeshElement* f = fIt->next();
10169       //             if ( faceSet->find( f ) == faceSet->end() ) {
10170       //               // check if all nodes are in nodeSet and
10171       //               // complete setOfFaceNodeSet if they are
10172       //               set <const SMDS_MeshNode*> faceNodeSet;
10173       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10174       //               bool allInSet = true;
10175       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10176       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10177       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10178       //                   allInSet = false;
10179       //                 else
10180       //                   faceNodeSet.insert( n );
10181       //               }
10182       //               if ( allInSet ) {
10183       //                 faceSet->insert( f );
10184       //                 setOfFaceNodeSet.insert( faceNodeSet );
10185       //               }
10186       //             }
10187       //           }
10188       //         }
10189       //       }
10190     } // Create temporary faces, if there are volumes given
10191   } // loop on sides
10192
10193   if ( faceSet1.size() != faceSet2.size() ) {
10194     // delete temporary faces: they are in reverseElements of actual nodes
10195 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10196 //    while ( tmpFaceIt->more() )
10197 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10198 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10199 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10200 //      aMesh->RemoveElement(*tmpFaceIt);
10201     MESSAGE("Diff nb of faces");
10202     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10203   }
10204
10205   // ============================================================
10206   // 2. Find nodes to merge:
10207   //              bind a node to remove to a node to put instead
10208   // ============================================================
10209
10210   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10211   if ( theFirstNode1 != theFirstNode2 )
10212     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10213   if ( theSecondNode1 != theSecondNode2 )
10214     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10215
10216   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10217   set< long > linkIdSet; // links to process
10218   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10219
10220   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10221   list< NLink > linkList[2];
10222   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10223   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10224   // loop on links in linkList; find faces by links and append links
10225   // of the found faces to linkList
10226   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10227   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10228   {
10229     NLink link[] = { *linkIt[0], *linkIt[1] };
10230     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10231     if ( !linkIdSet.count( linkID ) )
10232       continue;
10233
10234     // by links, find faces in the face sets,
10235     // and find indices of link nodes in the found faces;
10236     // in a face set, there is only one or no face sharing a link
10237     // ---------------------------------------------------------------
10238
10239     const SMDS_MeshElement* face[] = { 0, 0 };
10240     vector<const SMDS_MeshNode*> fnodes[2];
10241     int iLinkNode[2][2];
10242     TIDSortedElemSet avoidSet;
10243     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10244       const SMDS_MeshNode* n1 = link[iSide].first;
10245       const SMDS_MeshNode* n2 = link[iSide].second;
10246       //cout << "Side " << iSide << " ";
10247       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10248       // find a face by two link nodes
10249       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10250                                                       *faceSetPtr[ iSide ], avoidSet,
10251                                                       &iLinkNode[iSide][0],
10252                                                       &iLinkNode[iSide][1] );
10253       if ( face[ iSide ])
10254       {
10255         //cout << " F " << face[ iSide]->GetID() <<endl;
10256         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10257         // put face nodes to fnodes
10258         if ( face[ iSide ]->IsQuadratic() )
10259         {
10260           // use interlaced nodes iterator
10261           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10262           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10263           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10264           while ( nIter->more() )
10265             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10266         }
10267         else
10268         {
10269           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10270                                   face[ iSide ]->end_nodes() );
10271         }
10272         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10273       }
10274     }
10275
10276     // check similarity of elements of the sides
10277     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10278       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10279       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10280         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10281       }
10282       else {
10283         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10284       }
10285       break; // do not return because it's necessary to remove tmp faces
10286     }
10287
10288     // set nodes to merge
10289     // -------------------
10290
10291     if ( face[0] && face[1] )  {
10292       const int nbNodes = face[0]->NbNodes();
10293       if ( nbNodes != face[1]->NbNodes() ) {
10294         MESSAGE("Diff nb of face nodes");
10295         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10296         break; // do not return because it s necessary to remove tmp faces
10297       }
10298       bool reverse[] = { false, false }; // order of nodes in the link
10299       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10300         // analyse link orientation in faces
10301         int i1 = iLinkNode[ iSide ][ 0 ];
10302         int i2 = iLinkNode[ iSide ][ 1 ];
10303         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10304       }
10305       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10306       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10307       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10308       {
10309         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10310                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10311       }
10312
10313       // add other links of the faces to linkList
10314       // -----------------------------------------
10315
10316       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10317         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10318         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10319         if ( !iter_isnew.second ) { // already in a set: no need to process
10320           linkIdSet.erase( iter_isnew.first );
10321         }
10322         else // new in set == encountered for the first time: add
10323         {
10324           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10325           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10326           linkList[0].push_back ( NLink( n1, n2 ));
10327           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10328         }
10329       }
10330     } // 2 faces found
10331
10332     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10333       break;
10334
10335   } // loop on link lists
10336
10337   if ( aResult == SEW_OK &&
10338        ( //linkIt[0] != linkList[0].end() ||
10339          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10340     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10341              " " << (faceSetPtr[1]->empty()));
10342     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10343   }
10344
10345   // ====================================================================
10346   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10347   // ====================================================================
10348
10349   // delete temporary faces
10350 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10351 //  while ( tmpFaceIt->more() )
10352 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10353   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10354   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10355     aMesh->RemoveElement(*tmpFaceIt);
10356
10357   if ( aResult != SEW_OK)
10358     return aResult;
10359
10360   list< int > nodeIDsToRemove;
10361   vector< const SMDS_MeshNode*> nodes;
10362   ElemFeatures elemType;
10363
10364   // loop on nodes replacement map
10365   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10366   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10367     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10368     {
10369       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10370       nodeIDsToRemove.push_back( nToRemove->GetID() );
10371       // loop on elements sharing nToRemove
10372       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10373       while ( invElemIt->more() ) {
10374         const SMDS_MeshElement* e = invElemIt->next();
10375         // get a new suite of nodes: make replacement
10376         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10377         nodes.resize( nbNodes );
10378         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10379         while ( nIt->more() ) {
10380           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10381           nnIt = nReplaceMap.find( n );
10382           if ( nnIt != nReplaceMap.end() ) {
10383             nbReplaced++;
10384             n = (*nnIt).second;
10385           }
10386           nodes[ i++ ] = n;
10387         }
10388         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10389         //         elemIDsToRemove.push_back( e->GetID() );
10390         //       else
10391         if ( nbReplaced )
10392         {
10393           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10394           aMesh->RemoveElement( e );
10395
10396           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10397           {
10398             AddToSameGroups( newElem, e, aMesh );
10399             if ( int aShapeId = e->getshapeId() )
10400               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10401           }
10402         }
10403       }
10404     }
10405
10406   Remove( nodeIDsToRemove, true );
10407
10408   return aResult;
10409 }
10410
10411 //================================================================================
10412 /*!
10413  * \brief Find corresponding nodes in two sets of faces
10414  * \param theSide1 - first face set
10415  * \param theSide2 - second first face
10416  * \param theFirstNode1 - a boundary node of set 1
10417  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10418  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10419  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10420  * \param nReplaceMap - output map of corresponding nodes
10421  * \return bool  - is a success or not
10422  */
10423 //================================================================================
10424
10425 #ifdef _DEBUG_
10426 //#define DEBUG_MATCHING_NODES
10427 #endif
10428
10429 SMESH_MeshEditor::Sew_Error
10430 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10431                                     set<const SMDS_MeshElement*>& theSide2,
10432                                     const SMDS_MeshNode*          theFirstNode1,
10433                                     const SMDS_MeshNode*          theFirstNode2,
10434                                     const SMDS_MeshNode*          theSecondNode1,
10435                                     const SMDS_MeshNode*          theSecondNode2,
10436                                     TNodeNodeMap &                nReplaceMap)
10437 {
10438   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10439
10440   nReplaceMap.clear();
10441   if ( theFirstNode1 != theFirstNode2 )
10442     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10443   if ( theSecondNode1 != theSecondNode2 )
10444     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10445
10446   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10447   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10448
10449   list< NLink > linkList[2];
10450   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10451   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10452
10453   // loop on links in linkList; find faces by links and append links
10454   // of the found faces to linkList
10455   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10456   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10457     NLink link[] = { *linkIt[0], *linkIt[1] };
10458     if ( linkSet.find( link[0] ) == linkSet.end() )
10459       continue;
10460
10461     // by links, find faces in the face sets,
10462     // and find indices of link nodes in the found faces;
10463     // in a face set, there is only one or no face sharing a link
10464     // ---------------------------------------------------------------
10465
10466     const SMDS_MeshElement* face[] = { 0, 0 };
10467     list<const SMDS_MeshNode*> notLinkNodes[2];
10468     //bool reverse[] = { false, false }; // order of notLinkNodes
10469     int nbNodes[2];
10470     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10471     {
10472       const SMDS_MeshNode* n1 = link[iSide].first;
10473       const SMDS_MeshNode* n2 = link[iSide].second;
10474       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10475       set< const SMDS_MeshElement* > facesOfNode1;
10476       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10477       {
10478         // during a loop of the first node, we find all faces around n1,
10479         // during a loop of the second node, we find one face sharing both n1 and n2
10480         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10481         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10482         while ( fIt->more() ) { // loop on faces sharing a node
10483           const SMDS_MeshElement* f = fIt->next();
10484           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10485               ! facesOfNode1.insert( f ).second ) // f encounters twice
10486           {
10487             if ( face[ iSide ] ) {
10488               MESSAGE( "2 faces per link " );
10489               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10490             }
10491             face[ iSide ] = f;
10492             faceSet->erase( f );
10493
10494             // get not link nodes
10495             int nbN = f->NbNodes();
10496             if ( f->IsQuadratic() )
10497               nbN /= 2;
10498             nbNodes[ iSide ] = nbN;
10499             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10500             int i1 = f->GetNodeIndex( n1 );
10501             int i2 = f->GetNodeIndex( n2 );
10502             int iEnd = nbN, iBeg = -1, iDelta = 1;
10503             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10504             if ( reverse ) {
10505               std::swap( iEnd, iBeg ); iDelta = -1;
10506             }
10507             int i = i2;
10508             while ( true ) {
10509               i += iDelta;
10510               if ( i == iEnd ) i = iBeg + iDelta;
10511               if ( i == i1 ) break;
10512               nodes.push_back ( f->GetNode( i ) );
10513             }
10514           }
10515         }
10516       }
10517     }
10518     // check similarity of elements of the sides
10519     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10520       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10521       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10522         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10523       }
10524       else {
10525         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10526       }
10527     }
10528
10529     // set nodes to merge
10530     // -------------------
10531
10532     if ( face[0] && face[1] )  {
10533       if ( nbNodes[0] != nbNodes[1] ) {
10534         MESSAGE("Diff nb of face nodes");
10535         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10536       }
10537 #ifdef DEBUG_MATCHING_NODES
10538       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10539                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10540                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10541 #endif
10542       int nbN = nbNodes[0];
10543       {
10544         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10545         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10546         for ( int i = 0 ; i < nbN - 2; ++i ) {
10547 #ifdef DEBUG_MATCHING_NODES
10548           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10549 #endif
10550           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10551         }
10552       }
10553
10554       // add other links of the face 1 to linkList
10555       // -----------------------------------------
10556
10557       const SMDS_MeshElement* f0 = face[0];
10558       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10559       for ( int i = 0; i < nbN; i++ )
10560       {
10561         const SMDS_MeshNode* n2 = f0->GetNode( i );
10562         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10563           linkSet.insert( SMESH_TLink( n1, n2 ));
10564         if ( !iter_isnew.second ) { // already in a set: no need to process
10565           linkSet.erase( iter_isnew.first );
10566         }
10567         else // new in set == encountered for the first time: add
10568         {
10569 #ifdef DEBUG_MATCHING_NODES
10570           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10571                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10572 #endif
10573           linkList[0].push_back ( NLink( n1, n2 ));
10574           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10575         }
10576         n1 = n2;
10577       }
10578     } // 2 faces found
10579   } // loop on link lists
10580
10581   return SEW_OK;
10582 }
10583
10584 //================================================================================
10585 /*!
10586  * \brief Create elements equal (on same nodes) to given ones
10587  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10588  *              elements of the uppest dimension are duplicated.
10589  */
10590 //================================================================================
10591
10592 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10593 {
10594   ClearLastCreated();
10595   SMESHDS_Mesh* mesh = GetMeshDS();
10596
10597   // get an element type and an iterator over elements
10598
10599   SMDSAbs_ElementType type;
10600   SMDS_ElemIteratorPtr elemIt;
10601   vector< const SMDS_MeshElement* > allElems;
10602   if ( theElements.empty() )
10603   {
10604     if ( mesh->NbNodes() == 0 )
10605       return;
10606     // get most complex type
10607     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10608       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10609       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10610     };
10611     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10612       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10613       {
10614         type = types[i];
10615         break;
10616       }
10617     // put all elements in the vector <allElems>
10618     allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10619     elemIt = mesh->elementsIterator( type );
10620     while ( elemIt->more() )
10621       allElems.push_back( elemIt->next());
10622     elemIt = elemSetIterator( allElems );
10623   }
10624   else
10625   {
10626     type = (*theElements.begin())->GetType();
10627     elemIt = elemSetIterator( theElements );
10628   }
10629
10630   // duplicate elements
10631
10632   ElemFeatures elemType;
10633
10634   vector< const SMDS_MeshNode* > nodes;
10635   while ( elemIt->more() )
10636   {
10637     const SMDS_MeshElement* elem = elemIt->next();
10638     if ( elem->GetType() != type )
10639       continue;
10640
10641     elemType.Init( elem, /*basicOnly=*/false );
10642     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10643
10644     AddElement( nodes, elemType );
10645   }
10646 }
10647
10648 //================================================================================
10649 /*!
10650   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10651   \param theElems - the list of elements (edges or faces) to be replicated
10652   The nodes for duplication could be found from these elements
10653   \param theNodesNot - list of nodes to NOT replicate
10654   \param theAffectedElems - the list of elements (cells and edges) to which the
10655   replicated nodes should be associated to.
10656   \return TRUE if operation has been completed successfully, FALSE otherwise
10657 */
10658 //================================================================================
10659
10660 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10661                                     const TIDSortedElemSet& theNodesNot,
10662                                     const TIDSortedElemSet& theAffectedElems )
10663 {
10664   myLastCreatedElems.Clear();
10665   myLastCreatedNodes.Clear();
10666
10667   if ( theElems.size() == 0 )
10668     return false;
10669
10670   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10671   if ( !aMeshDS )
10672     return false;
10673
10674   bool res = false;
10675   TNodeNodeMap anOldNodeToNewNode;
10676   // duplicate elements and nodes
10677   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10678   // replce nodes by duplications
10679   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10680   return res;
10681 }
10682
10683 //================================================================================
10684 /*!
10685   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10686   \param theMeshDS - mesh instance
10687   \param theElems - the elements replicated or modified (nodes should be changed)
10688   \param theNodesNot - nodes to NOT replicate
10689   \param theNodeNodeMap - relation of old node to new created node
10690   \param theIsDoubleElem - flag os to replicate element or modify
10691   \return TRUE if operation has been completed successfully, FALSE otherwise
10692 */
10693 //================================================================================
10694
10695 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10696                                    const TIDSortedElemSet& theElems,
10697                                    const TIDSortedElemSet& theNodesNot,
10698                                    TNodeNodeMap&           theNodeNodeMap,
10699                                    const bool              theIsDoubleElem )
10700 {
10701   MESSAGE("doubleNodes");
10702   // iterate through element and duplicate them (by nodes duplication)
10703   bool res = false;
10704   std::vector<const SMDS_MeshNode*> newNodes;
10705   ElemFeatures elemType;
10706
10707   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10708   for ( ;  elemItr != theElems.end(); ++elemItr )
10709   {
10710     const SMDS_MeshElement* anElem = *elemItr;
10711     if (!anElem)
10712       continue;
10713
10714     // duplicate nodes to duplicate element
10715     bool isDuplicate = false;
10716     newNodes.resize( anElem->NbNodes() );
10717     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10718     int ind = 0;
10719     while ( anIter->more() )
10720     {
10721       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10722       const SMDS_MeshNode*  aNewNode = aCurrNode;
10723       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10724       if ( n2n != theNodeNodeMap.end() )
10725       {
10726         aNewNode = n2n->second;
10727       }
10728       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10729       {
10730         // duplicate node
10731         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10732         copyPosition( aCurrNode, aNewNode );
10733         theNodeNodeMap[ aCurrNode ] = aNewNode;
10734         myLastCreatedNodes.Append( aNewNode );
10735       }
10736       isDuplicate |= (aCurrNode != aNewNode);
10737       newNodes[ ind++ ] = aNewNode;
10738     }
10739     if ( !isDuplicate )
10740       continue;
10741
10742     if ( theIsDoubleElem )
10743       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10744     else
10745       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10746
10747     res = true;
10748   }
10749   return res;
10750 }
10751
10752 //================================================================================
10753 /*!
10754   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10755   \param theNodes - identifiers of nodes to be doubled
10756   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10757   nodes. If list of element identifiers is empty then nodes are doubled but
10758   they not assigned to elements
10759   \return TRUE if operation has been completed successfully, FALSE otherwise
10760 */
10761 //================================================================================
10762
10763 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10764                                     const std::list< int >& theListOfModifiedElems )
10765 {
10766   MESSAGE("DoubleNodes");
10767   myLastCreatedElems.Clear();
10768   myLastCreatedNodes.Clear();
10769
10770   if ( theListOfNodes.size() == 0 )
10771     return false;
10772
10773   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10774   if ( !aMeshDS )
10775     return false;
10776
10777   // iterate through nodes and duplicate them
10778
10779   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10780
10781   std::list< int >::const_iterator aNodeIter;
10782   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10783   {
10784     int aCurr = *aNodeIter;
10785     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10786     if ( !aNode )
10787       continue;
10788
10789     // duplicate node
10790
10791     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10792     if ( aNewNode )
10793     {
10794       copyPosition( aNode, aNewNode );
10795       anOldNodeToNewNode[ aNode ] = aNewNode;
10796       myLastCreatedNodes.Append( aNewNode );
10797     }
10798   }
10799
10800   // Create map of new nodes for modified elements
10801
10802   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10803
10804   std::list< int >::const_iterator anElemIter;
10805   for ( anElemIter = theListOfModifiedElems.begin();
10806         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10807   {
10808     int aCurr = *anElemIter;
10809     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10810     if ( !anElem )
10811       continue;
10812
10813     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10814
10815     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10816     int ind = 0;
10817     while ( anIter->more() )
10818     {
10819       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10820       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10821       {
10822         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10823         aNodeArr[ ind++ ] = aNewNode;
10824       }
10825       else
10826         aNodeArr[ ind++ ] = aCurrNode;
10827     }
10828     anElemToNodes[ anElem ] = aNodeArr;
10829   }
10830
10831   // Change nodes of elements
10832
10833   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10834     anElemToNodesIter = anElemToNodes.begin();
10835   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10836   {
10837     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10838     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10839     if ( anElem )
10840       {
10841       MESSAGE("ChangeElementNodes");
10842       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10843       }
10844   }
10845
10846   return true;
10847 }
10848
10849 namespace {
10850
10851   //================================================================================
10852   /*!
10853   \brief Check if element located inside shape
10854   \return TRUE if IN or ON shape, FALSE otherwise
10855   */
10856   //================================================================================
10857
10858   template<class Classifier>
10859   bool isInside(const SMDS_MeshElement* theElem,
10860                 Classifier&             theClassifier,
10861                 const double            theTol)
10862   {
10863     gp_XYZ centerXYZ (0, 0, 0);
10864     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10865     while (aNodeItr->more())
10866       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10867
10868     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10869     theClassifier.Perform(aPnt, theTol);
10870     TopAbs_State aState = theClassifier.State();
10871     return (aState == TopAbs_IN || aState == TopAbs_ON );
10872   }
10873
10874   //================================================================================
10875   /*!
10876    * \brief Classifier of the 3D point on the TopoDS_Face
10877    *        with interaface suitable for isInside()
10878    */
10879   //================================================================================
10880
10881   struct _FaceClassifier
10882   {
10883     Extrema_ExtPS       _extremum;
10884     BRepAdaptor_Surface _surface;
10885     TopAbs_State        _state;
10886
10887     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10888     {
10889       _extremum.Initialize( _surface,
10890                             _surface.FirstUParameter(), _surface.LastUParameter(),
10891                             _surface.FirstVParameter(), _surface.LastVParameter(),
10892                             _surface.Tolerance(), _surface.Tolerance() );
10893     }
10894     void Perform(const gp_Pnt& aPnt, double theTol)
10895     {
10896       theTol *= theTol;
10897       _state = TopAbs_OUT;
10898       _extremum.Perform(aPnt);
10899       if ( _extremum.IsDone() )
10900         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10901           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10902     }
10903     TopAbs_State State() const
10904     {
10905       return _state;
10906     }
10907   };
10908 }
10909
10910 //================================================================================
10911 /*!
10912   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10913   This method is the first step of DoubleNodeElemGroupsInRegion.
10914   \param theElems - list of groups of elements (edges or faces) to be replicated
10915   \param theNodesNot - list of groups of nodes not to replicated
10916   \param theShape - shape to detect affected elements (element which geometric center
10917          located on or inside shape). If the shape is null, detection is done on faces orientations
10918          (select elements with a gravity center on the side given by faces normals).
10919          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10920          The replicated nodes should be associated to affected elements.
10921   \return groups of affected elements
10922   \sa DoubleNodeElemGroupsInRegion()
10923  */
10924 //================================================================================
10925
10926 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10927                                                    const TIDSortedElemSet& theNodesNot,
10928                                                    const TopoDS_Shape&     theShape,
10929                                                    TIDSortedElemSet&       theAffectedElems)
10930 {
10931   if ( theShape.IsNull() )
10932   {
10933     std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10934     std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10935     std::set<const SMDS_MeshElement*> edgesToCheck;
10936     alreadyCheckedNodes.clear();
10937     alreadyCheckedElems.clear();
10938     edgesToCheck.clear();
10939
10940     // --- iterates on elements to be replicated and get elements by back references from their nodes
10941
10942     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10943     int ielem;
10944     for ( ielem=1;  elemItr != theElems.end(); ++elemItr )
10945     {
10946       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10947       if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10948         continue;
10949       gp_XYZ normal;
10950       SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10951       MESSAGE("element " << ielem++ <<  " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10952       std::set<const SMDS_MeshNode*> nodesElem;
10953       nodesElem.clear();
10954       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10955       while ( nodeItr->more() )
10956       {
10957         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10958         nodesElem.insert(aNode);
10959       }
10960       std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10961       for (; nodit != nodesElem.end(); nodit++)
10962       {
10963         MESSAGE("  noeud ");
10964         const SMDS_MeshNode* aNode = *nodit;
10965         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10966           continue;
10967         if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10968           continue;
10969         alreadyCheckedNodes.insert(aNode);
10970         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10971         while ( backElemItr->more() )
10972         {
10973           MESSAGE("    backelem ");
10974           const SMDS_MeshElement* curElem = backElemItr->next();
10975           if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10976             continue;
10977           if (theElems.find(curElem) != theElems.end())
10978             continue;
10979           alreadyCheckedElems.insert(curElem);
10980           double x=0, y=0, z=0;
10981           int nb = 0;
10982           SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10983           while ( nodeItr2->more() )
10984           {
10985             const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10986             x += anotherNode->X();
10987             y += anotherNode->Y();
10988             z += anotherNode->Z();
10989             nb++;
10990           }
10991           gp_XYZ p;
10992           p.SetCoord( x/nb -aNode->X(),
10993                       y/nb -aNode->Y(),
10994                       z/nb -aNode->Z() );
10995           MESSAGE("      check " << p.X() << " " << p.Y() << " " << p.Z());
10996           if (normal*p > 0)
10997           {
10998             MESSAGE("    --- inserted")
10999             theAffectedElems.insert( curElem );
11000           }
11001           else if (curElem->GetType() == SMDSAbs_Edge)
11002             edgesToCheck.insert(curElem);
11003         }
11004       }
11005     }
11006     // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
11007     std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
11008     for( ; eit != edgesToCheck.end(); eit++)
11009     {
11010       bool onside = true;
11011       const SMDS_MeshElement* anEdge = *eit;
11012       SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
11013       while ( nodeItr->more() )
11014       {
11015         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11016         if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
11017         {
11018           onside = false;
11019           break;
11020         }
11021       }
11022       if (onside)
11023       {
11024         MESSAGE("    --- edge onside inserted")
11025         theAffectedElems.insert(anEdge);
11026       }
11027     }
11028   }
11029   else
11030   {
11031     const double aTol = Precision::Confusion();
11032     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11033     auto_ptr<_FaceClassifier>              aFaceClassifier;
11034     if ( theShape.ShapeType() == TopAbs_SOLID )
11035     {
11036       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11037       bsc3d->PerformInfinitePoint(aTol);
11038     }
11039     else if (theShape.ShapeType() == TopAbs_FACE )
11040     {
11041       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11042     }
11043
11044     // iterates on indicated elements and get elements by back references from their nodes
11045     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11046     int ielem;
11047     for ( ielem = 1;  elemItr != theElems.end(); ++elemItr )
11048     {
11049       MESSAGE("element " << ielem++);
11050       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11051       if (!anElem)
11052         continue;
11053       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11054       while ( nodeItr->more() )
11055       {
11056         MESSAGE("  noeud ");
11057         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11058         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11059           continue;
11060         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11061         while ( backElemItr->more() )
11062         {
11063           MESSAGE("    backelem ");
11064           const SMDS_MeshElement* curElem = backElemItr->next();
11065           if ( curElem && theElems.find(curElem) == theElems.end() &&
11066               ( bsc3d.get() ?
11067                 isInside( curElem, *bsc3d, aTol ) :
11068                 isInside( curElem, *aFaceClassifier, aTol )))
11069             theAffectedElems.insert( curElem );
11070         }
11071       }
11072     }
11073   }
11074   return true;
11075 }
11076
11077 //================================================================================
11078 /*!
11079   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11080   \param theElems - group of of elements (edges or faces) to be replicated
11081   \param theNodesNot - group of nodes not to replicate
11082   \param theShape - shape to detect affected elements (element which geometric center
11083   located on or inside shape).
11084   The replicated nodes should be associated to affected elements.
11085   \return TRUE if operation has been completed successfully, FALSE otherwise
11086 */
11087 //================================================================================
11088
11089 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11090                                             const TIDSortedElemSet& theNodesNot,
11091                                             const TopoDS_Shape&     theShape )
11092 {
11093   if ( theShape.IsNull() )
11094     return false;
11095
11096   const double aTol = Precision::Confusion();
11097   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11098   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
11099   if ( theShape.ShapeType() == TopAbs_SOLID )
11100   {
11101     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11102     bsc3d->PerformInfinitePoint(aTol);
11103   }
11104   else if (theShape.ShapeType() == TopAbs_FACE )
11105   {
11106     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11107   }
11108
11109   // iterates on indicated elements and get elements by back references from their nodes
11110   TIDSortedElemSet anAffected;
11111   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11112   for ( ;  elemItr != theElems.end(); ++elemItr )
11113   {
11114     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11115     if (!anElem)
11116       continue;
11117
11118     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11119     while ( nodeItr->more() )
11120     {
11121       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11122       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11123         continue;
11124       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11125       while ( backElemItr->more() )
11126       {
11127         const SMDS_MeshElement* curElem = backElemItr->next();
11128         if ( curElem && theElems.find(curElem) == theElems.end() &&
11129              ( bsc3d ?
11130                isInside( curElem, *bsc3d, aTol ) :
11131                isInside( curElem, *aFaceClassifier, aTol )))
11132           anAffected.insert( curElem );
11133       }
11134     }
11135   }
11136   return DoubleNodes( theElems, theNodesNot, anAffected );
11137 }
11138
11139 /*!
11140  *  \brief compute an oriented angle between two planes defined by four points.
11141  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11142  *  @param p0 base of the rotation axe
11143  *  @param p1 extremity of the rotation axe
11144  *  @param g1 belongs to the first plane
11145  *  @param g2 belongs to the second plane
11146  */
11147 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11148 {
11149 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11150 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11151 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11152 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11153   gp_Vec vref(p0, p1);
11154   gp_Vec v1(p0, g1);
11155   gp_Vec v2(p0, g2);
11156   gp_Vec n1 = vref.Crossed(v1);
11157   gp_Vec n2 = vref.Crossed(v2);
11158   try {
11159     return n2.AngleWithRef(n1, vref);
11160   }
11161   catch ( Standard_Failure ) {
11162   }
11163   return Max( v1.Magnitude(), v2.Magnitude() );
11164 }
11165
11166 /*!
11167  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11168  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11169  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11170  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11171  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11172  * 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.
11173  * 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.
11174  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11175  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11176  * \param theElems - list of groups of volumes, where a group of volume is a set of
11177  *        SMDS_MeshElements sorted by Id.
11178  * \param createJointElems - if TRUE, create the elements
11179  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11180  *        the boundary between \a theDomains and the rest mesh
11181  * \return TRUE if operation has been completed successfully, FALSE otherwise
11182  */
11183 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11184                                                      bool                                 createJointElems,
11185                                                      bool                                 onAllBoundaries)
11186 {
11187   MESSAGE("----------------------------------------------");
11188   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11189   MESSAGE("----------------------------------------------");
11190
11191   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11192   meshDS->BuildDownWardConnectivity(true);
11193   CHRONO(50);
11194   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11195
11196   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11197   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11198   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11199
11200   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11201   std::map<int,int>celldom; // cell vtkId --> domain
11202   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11203   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11204   faceDomains.clear();
11205   celldom.clear();
11206   cellDomains.clear();
11207   nodeDomains.clear();
11208   std::map<int,int> emptyMap;
11209   std::set<int> emptySet;
11210   emptyMap.clear();
11211
11212   MESSAGE(".. Number of domains :"<<theElems.size());
11213
11214   TIDSortedElemSet theRestDomElems;
11215   const int iRestDom  = -1;
11216   const int idom0     = onAllBoundaries ? iRestDom : 0;
11217   const int nbDomains = theElems.size();
11218
11219   // Check if the domains do not share an element
11220   for (int idom = 0; idom < nbDomains-1; idom++)
11221   {
11222     //       MESSAGE("... Check of domain #" << idom);
11223     const TIDSortedElemSet& domain = theElems[idom];
11224     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11225     for (; elemItr != domain.end(); ++elemItr)
11226     {
11227       const SMDS_MeshElement* anElem = *elemItr;
11228       int idombisdeb = idom + 1 ;
11229       // check if the element belongs to a domain further in the list
11230       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11231       {
11232         const TIDSortedElemSet& domainbis = theElems[idombis];
11233         if ( domainbis.count( anElem ))
11234         {
11235           MESSAGE(".... Domain #" << idom);
11236           MESSAGE(".... Domain #" << idombis);
11237           throw SALOME_Exception("The domains are not disjoint.");
11238           return false ;
11239         }
11240       }
11241     }
11242   }
11243
11244   for (int idom = 0; idom < nbDomains; idom++)
11245   {
11246
11247     // --- build a map (face to duplicate --> volume to modify)
11248     //     with all the faces shared by 2 domains (group of elements)
11249     //     and corresponding volume of this domain, for each shared face.
11250     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11251
11252     MESSAGE("... Neighbors of domain #" << idom);
11253     const TIDSortedElemSet& domain = theElems[idom];
11254     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11255     for (; elemItr != domain.end(); ++elemItr)
11256     {
11257       const SMDS_MeshElement* anElem = *elemItr;
11258       if (!anElem)
11259         continue;
11260       int vtkId = anElem->getVtkId();
11261       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11262       int neighborsVtkIds[NBMAXNEIGHBORS];
11263       int downIds[NBMAXNEIGHBORS];
11264       unsigned char downTypes[NBMAXNEIGHBORS];
11265       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11266       for (int n = 0; n < nbNeighbors; n++)
11267       {
11268         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11269         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11270         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11271         {
11272           bool ok = false;
11273           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11274           {
11275             // MESSAGE("Domain " << idombis);
11276             const TIDSortedElemSet& domainbis = theElems[idombis];
11277             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11278           }
11279           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11280           {
11281             DownIdType face(downIds[n], downTypes[n]);
11282             if (!faceDomains[face].count(idom))
11283             {
11284               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11285               celldom[vtkId] = idom;
11286               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11287             }
11288             if ( !ok )
11289             {
11290               theRestDomElems.insert( elem );
11291               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11292               celldom[neighborsVtkIds[n]] = iRestDom;
11293             }
11294           }
11295         }
11296       }
11297     }
11298   }
11299
11300   //MESSAGE("Number of shared faces " << faceDomains.size());
11301   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11302
11303   // --- explore the shared faces domain by domain,
11304   //     explore the nodes of the face and see if they belong to a cell in the domain,
11305   //     which has only a node or an edge on the border (not a shared face)
11306
11307   for (int idomain = idom0; idomain < nbDomains; idomain++)
11308   {
11309     //MESSAGE("Domain " << idomain);
11310     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11311     itface = faceDomains.begin();
11312     for (; itface != faceDomains.end(); ++itface)
11313     {
11314       const std::map<int, int>& domvol = itface->second;
11315       if (!domvol.count(idomain))
11316         continue;
11317       DownIdType face = itface->first;
11318       //MESSAGE(" --- face " << face.cellId);
11319       std::set<int> oldNodes;
11320       oldNodes.clear();
11321       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11322       std::set<int>::iterator itn = oldNodes.begin();
11323       for (; itn != oldNodes.end(); ++itn)
11324       {
11325         int oldId = *itn;
11326         //MESSAGE("     node " << oldId);
11327         vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11328         for (int i=0; i<l.ncells; i++)
11329         {
11330           int vtkId = l.cells[i];
11331           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11332           if (!domain.count(anElem))
11333             continue;
11334           int vtkType = grid->GetCellType(vtkId);
11335           int downId = grid->CellIdToDownId(vtkId);
11336           if (downId < 0)
11337           {
11338             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11339             continue; // not OK at this stage of the algorithm:
11340             //no cells created after BuildDownWardConnectivity
11341           }
11342           DownIdType aCell(downId, vtkType);
11343           cellDomains[aCell][idomain] = vtkId;
11344           celldom[vtkId] = idomain;
11345           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11346         }
11347       }
11348     }
11349   }
11350
11351   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11352   //     for each shared face, get the nodes
11353   //     for each node, for each domain of the face, create a clone of the node
11354
11355   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11356   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11357   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11358
11359   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11360   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11361   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11362
11363   MESSAGE(".. Duplication of the nodes");
11364   for (int idomain = idom0; idomain < nbDomains; idomain++)
11365   {
11366     itface = faceDomains.begin();
11367     for (; itface != faceDomains.end(); ++itface)
11368     {
11369       const std::map<int, int>& domvol = itface->second;
11370       if (!domvol.count(idomain))
11371         continue;
11372       DownIdType face = itface->first;
11373       //MESSAGE(" --- face " << face.cellId);
11374       std::set<int> oldNodes;
11375       oldNodes.clear();
11376       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11377       std::set<int>::iterator itn = oldNodes.begin();
11378       for (; itn != oldNodes.end(); ++itn)
11379       {
11380         int oldId = *itn;
11381         if (nodeDomains[oldId].empty())
11382         {
11383           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11384           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11385         }
11386         std::map<int, int>::const_iterator itdom = domvol.begin();
11387         for (; itdom != domvol.end(); ++itdom)
11388         {
11389           int idom = itdom->first;
11390           //MESSAGE("         domain " << idom);
11391           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11392           {
11393             if (nodeDomains[oldId].size() >= 2) // a multiple node
11394             {
11395               vector<int> orderedDoms;
11396               //MESSAGE("multiple node " << oldId);
11397               if (mutipleNodes.count(oldId))
11398                 orderedDoms = mutipleNodes[oldId];
11399               else
11400               {
11401                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11402                 for (; it != nodeDomains[oldId].end(); ++it)
11403                   orderedDoms.push_back(it->first);
11404               }
11405               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11406               //stringstream txt;
11407               //for (int i=0; i<orderedDoms.size(); i++)
11408               //  txt << orderedDoms[i] << " ";
11409               //MESSAGE("orderedDoms " << txt.str());
11410               mutipleNodes[oldId] = orderedDoms;
11411             }
11412             double *coords = grid->GetPoint(oldId);
11413             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11414             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11415             int newId = newNode->getVtkId();
11416             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11417             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11418           }
11419         }
11420       }
11421     }
11422   }
11423
11424   MESSAGE(".. Creation of elements");
11425   for (int idomain = idom0; idomain < nbDomains; idomain++)
11426   {
11427     itface = faceDomains.begin();
11428     for (; itface != faceDomains.end(); ++itface)
11429     {
11430       std::map<int, int> domvol = itface->second;
11431       if (!domvol.count(idomain))
11432         continue;
11433       DownIdType face = itface->first;
11434       //MESSAGE(" --- face " << face.cellId);
11435       std::set<int> oldNodes;
11436       oldNodes.clear();
11437       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11438       int nbMultipleNodes = 0;
11439       std::set<int>::iterator itn = oldNodes.begin();
11440       for (; itn != oldNodes.end(); ++itn)
11441       {
11442         int oldId = *itn;
11443         if (mutipleNodes.count(oldId))
11444           nbMultipleNodes++;
11445       }
11446       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11447       {
11448         //MESSAGE("multiple Nodes detected on a shared face");
11449         int downId = itface->first.cellId;
11450         unsigned char cellType = itface->first.cellType;
11451         // --- shared edge or shared face ?
11452         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11453         {
11454           int nodes[3];
11455           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11456           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11457             if (mutipleNodes.count(nodes[i]))
11458               if (!mutipleNodesToFace.count(nodes[i]))
11459                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11460         }
11461         else // shared face (between two volumes)
11462         {
11463           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11464           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11465           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11466           for (int ie =0; ie < nbEdges; ie++)
11467           {
11468             int nodes[3];
11469             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11470             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11471             {
11472               vector<int> vn0 = mutipleNodes[nodes[0]];
11473               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11474               vector<int> doms;
11475               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11476                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11477                   if ( vn0[i0] == vn1[i1] )
11478                     doms.push_back( vn0[ i0 ]);
11479               if ( doms.size() > 2 )
11480               {
11481                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11482                 double *coords = grid->GetPoint(nodes[0]);
11483                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11484                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11485                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11486                 gp_Pnt gref;
11487                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11488                 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11489                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11490                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11491                 for ( size_t id = 0; id < doms.size(); id++ )
11492                 {
11493                   int idom = doms[id];
11494                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11495                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11496                   {
11497                     int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11498                     SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11499                     if (domain.count(elem))
11500                     {
11501                       SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11502                       domvol[idom] = svol;
11503                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11504                       double values[3];
11505                       vtkIdType npts = 0;
11506                       vtkIdType* pts = 0;
11507                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11508                       SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11509                       if (id ==0)
11510                       {
11511                         gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11512                         angleDom[idom] = 0;
11513                       }
11514                       else
11515                       {
11516                         gp_Pnt g(values[0], values[1], values[2]);
11517                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11518                         //MESSAGE("  angle=" << angleDom[idom]);
11519                       }
11520                       break;
11521                     }
11522                   }
11523                 }
11524                 map<double, int> sortedDom; // sort domains by angle
11525                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11526                   sortedDom[ia->second] = ia->first;
11527                 vector<int> vnodes;
11528                 vector<int> vdom;
11529                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11530                 {
11531                   vdom.push_back(ib->second);
11532                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11533                 }
11534                 for (int ino = 0; ino < nbNodes; ino++)
11535                   vnodes.push_back(nodes[ino]);
11536                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11537               }
11538             }
11539           }
11540         }
11541       }
11542     }
11543   }
11544
11545   // --- iterate on shared faces (volumes to modify, face to extrude)
11546   //     get node id's of the face (id SMDS = id VTK)
11547   //     create flat element with old and new nodes if requested
11548
11549   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11550   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11551
11552   std::map<int, std::map<long,int> > nodeQuadDomains;
11553   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11554
11555   MESSAGE(".. Creation of elements: simple junction");
11556   if (createJointElems)
11557   {
11558     int idg;
11559     string joints2DName = "joints2D";
11560     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11561     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11562     string joints3DName = "joints3D";
11563     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11564     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11565
11566     itface = faceDomains.begin();
11567     for (; itface != faceDomains.end(); ++itface)
11568     {
11569       DownIdType face = itface->first;
11570       std::set<int> oldNodes;
11571       std::set<int>::iterator itn;
11572       oldNodes.clear();
11573       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11574
11575       std::map<int, int> domvol = itface->second;
11576       std::map<int, int>::iterator itdom = domvol.begin();
11577       int dom1 = itdom->first;
11578       int vtkVolId = itdom->second;
11579       itdom++;
11580       int dom2 = itdom->first;
11581       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11582                                                        nodeQuadDomains);
11583       stringstream grpname;
11584       grpname << "j_";
11585       if (dom1 < dom2)
11586         grpname << dom1 << "_" << dom2;
11587       else
11588         grpname << dom2 << "_" << dom1;
11589       string namegrp = grpname.str();
11590       if (!mapOfJunctionGroups.count(namegrp))
11591         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11592       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11593       if (sgrp)
11594         sgrp->Add(vol->GetID());
11595       if (vol->GetType() == SMDSAbs_Volume)
11596         joints3DGrp->Add(vol->GetID());
11597       else if (vol->GetType() == SMDSAbs_Face)
11598         joints2DGrp->Add(vol->GetID());
11599     }
11600   }
11601
11602   // --- create volumes on multiple domain intersection if requested
11603   //     iterate on mutipleNodesToFace
11604   //     iterate on edgesMultiDomains
11605
11606   MESSAGE(".. Creation of elements: multiple junction");
11607   if (createJointElems)
11608   {
11609     // --- iterate on mutipleNodesToFace
11610
11611     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11612     for (; itn != mutipleNodesToFace.end(); ++itn)
11613     {
11614       int node = itn->first;
11615       vector<int> orderDom = itn->second;
11616       vector<vtkIdType> orderedNodes;
11617       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11618         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11619       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11620
11621       stringstream grpname;
11622       grpname << "m2j_";
11623       grpname << 0 << "_" << 0;
11624       int idg;
11625       string namegrp = grpname.str();
11626       if (!mapOfJunctionGroups.count(namegrp))
11627         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11628       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11629       if (sgrp)
11630         sgrp->Add(face->GetID());
11631     }
11632
11633     // --- iterate on edgesMultiDomains
11634
11635     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11636     for (; ite != edgesMultiDomains.end(); ++ite)
11637     {
11638       vector<int> nodes = ite->first;
11639       vector<int> orderDom = ite->second;
11640       vector<vtkIdType> orderedNodes;
11641       if (nodes.size() == 2)
11642       {
11643         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11644         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11645           if ( orderDom.size() == 3 )
11646             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11647               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11648           else
11649             for (int idom = orderDom.size()-1; idom >=0; idom--)
11650               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11651         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11652
11653         int idg;
11654         string namegrp = "jointsMultiples";
11655         if (!mapOfJunctionGroups.count(namegrp))
11656           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11657         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11658         if (sgrp)
11659           sgrp->Add(vol->GetID());
11660       }
11661       else
11662       {
11663         //INFOS("Quadratic multiple joints not implemented");
11664         // TODO quadratic nodes
11665       }
11666     }
11667   }
11668
11669   // --- list the explicit faces and edges of the mesh that need to be modified,
11670   //     i.e. faces and edges built with one or more duplicated nodes.
11671   //     associate these faces or edges to their corresponding domain.
11672   //     only the first domain found is kept when a face or edge is shared
11673
11674   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11675   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11676   faceOrEdgeDom.clear();
11677   feDom.clear();
11678
11679   MESSAGE(".. Modification of elements");
11680   for (int idomain = idom0; idomain < nbDomains; idomain++)
11681   {
11682     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11683     for (; itnod != nodeDomains.end(); ++itnod)
11684     {
11685       int oldId = itnod->first;
11686       //MESSAGE("     node " << oldId);
11687       vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11688       for (int i = 0; i < l.ncells; i++)
11689       {
11690         int vtkId = l.cells[i];
11691         int vtkType = grid->GetCellType(vtkId);
11692         int downId = grid->CellIdToDownId(vtkId);
11693         if (downId < 0)
11694           continue; // new cells: not to be modified
11695         DownIdType aCell(downId, vtkType);
11696         int volParents[1000];
11697         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11698         for (int j = 0; j < nbvol; j++)
11699           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11700             if (!feDom.count(vtkId))
11701             {
11702               feDom[vtkId] = idomain;
11703               faceOrEdgeDom[aCell] = emptyMap;
11704               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11705               //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11706               //        << " type " << vtkType << " downId " << downId);
11707             }
11708       }
11709     }
11710   }
11711
11712   // --- iterate on shared faces (volumes to modify, face to extrude)
11713   //     get node id's of the face
11714   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11715
11716   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11717   for (int m=0; m<3; m++)
11718   {
11719     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11720     itface = (*amap).begin();
11721     for (; itface != (*amap).end(); ++itface)
11722     {
11723       DownIdType face = itface->first;
11724       std::set<int> oldNodes;
11725       std::set<int>::iterator itn;
11726       oldNodes.clear();
11727       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11728       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11729       std::map<int, int> localClonedNodeIds;
11730
11731       std::map<int, int> domvol = itface->second;
11732       std::map<int, int>::iterator itdom = domvol.begin();
11733       for (; itdom != domvol.end(); ++itdom)
11734       {
11735         int idom = itdom->first;
11736         int vtkVolId = itdom->second;
11737         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11738         localClonedNodeIds.clear();
11739         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11740         {
11741           int oldId = *itn;
11742           if (nodeDomains[oldId].count(idom))
11743           {
11744             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11745             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11746           }
11747         }
11748         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11749       }
11750     }
11751   }
11752
11753   // Remove empty groups (issue 0022812)
11754   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11755   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11756   {
11757     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11758       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11759   }
11760
11761   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11762   grid->BuildLinks();
11763
11764   CHRONOSTOP(50);
11765   counters::stats();
11766   return true;
11767 }
11768
11769 /*!
11770  * \brief Double nodes on some external faces and create flat elements.
11771  * Flat elements are mainly used by some types of mechanic calculations.
11772  *
11773  * Each group of the list must be constituted of faces.
11774  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11775  * @param theElems - list of groups of faces, where a group of faces is a set of
11776  * SMDS_MeshElements sorted by Id.
11777  * @return TRUE if operation has been completed successfully, FALSE otherwise
11778  */
11779 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11780 {
11781   MESSAGE("-------------------------------------------------");
11782   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11783   MESSAGE("-------------------------------------------------");
11784
11785   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11786
11787   // --- For each group of faces
11788   //     duplicate the nodes, create a flat element based on the face
11789   //     replace the nodes of the faces by their clones
11790
11791   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11792   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11793   clonedNodes.clear();
11794   intermediateNodes.clear();
11795   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11796   mapOfJunctionGroups.clear();
11797
11798   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11799   {
11800     const TIDSortedElemSet&           domain = theElems[idom];
11801     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11802     for ( ; elemItr != domain.end(); ++elemItr )
11803     {
11804       SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11805       SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11806       if (!aFace)
11807         continue;
11808       // MESSAGE("aFace=" << aFace->GetID());
11809       bool isQuad = aFace->IsQuadratic();
11810       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11811
11812       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11813
11814       SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11815       while (nodeIt->more())
11816       {
11817         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11818         bool isMedium = isQuad && (aFace->IsMediumNode(node));
11819         if (isMedium)
11820           ln2.push_back(node);
11821         else
11822           ln0.push_back(node);
11823
11824         const SMDS_MeshNode* clone = 0;
11825         if (!clonedNodes.count(node))
11826         {
11827           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11828           copyPosition( node, clone );
11829           clonedNodes[node] = clone;
11830         }
11831         else
11832           clone = clonedNodes[node];
11833
11834         if (isMedium)
11835           ln3.push_back(clone);
11836         else
11837           ln1.push_back(clone);
11838
11839         const SMDS_MeshNode* inter = 0;
11840         if (isQuad && (!isMedium))
11841         {
11842           if (!intermediateNodes.count(node))
11843           {
11844             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11845             copyPosition( node, inter );
11846             intermediateNodes[node] = inter;
11847           }
11848           else
11849             inter = intermediateNodes[node];
11850           ln4.push_back(inter);
11851         }
11852       }
11853
11854       // --- extrude the face
11855
11856       vector<const SMDS_MeshNode*> ln;
11857       SMDS_MeshVolume* vol = 0;
11858       vtkIdType aType = aFace->GetVtkType();
11859       switch (aType)
11860       {
11861       case VTK_TRIANGLE:
11862         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11863         // MESSAGE("vol prism " << vol->GetID());
11864         ln.push_back(ln1[0]);
11865         ln.push_back(ln1[1]);
11866         ln.push_back(ln1[2]);
11867         break;
11868       case VTK_QUAD:
11869         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11870         // MESSAGE("vol hexa " << vol->GetID());
11871         ln.push_back(ln1[0]);
11872         ln.push_back(ln1[1]);
11873         ln.push_back(ln1[2]);
11874         ln.push_back(ln1[3]);
11875         break;
11876       case VTK_QUADRATIC_TRIANGLE:
11877         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11878                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11879         // MESSAGE("vol quad prism " << vol->GetID());
11880         ln.push_back(ln1[0]);
11881         ln.push_back(ln1[1]);
11882         ln.push_back(ln1[2]);
11883         ln.push_back(ln3[0]);
11884         ln.push_back(ln3[1]);
11885         ln.push_back(ln3[2]);
11886         break;
11887       case VTK_QUADRATIC_QUAD:
11888         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11889         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11890         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11891         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11892                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11893                                 ln4[0], ln4[1], ln4[2], ln4[3]);
11894         // MESSAGE("vol quad hexa " << vol->GetID());
11895         ln.push_back(ln1[0]);
11896         ln.push_back(ln1[1]);
11897         ln.push_back(ln1[2]);
11898         ln.push_back(ln1[3]);
11899         ln.push_back(ln3[0]);
11900         ln.push_back(ln3[1]);
11901         ln.push_back(ln3[2]);
11902         ln.push_back(ln3[3]);
11903         break;
11904       case VTK_POLYGON:
11905         break;
11906       default:
11907         break;
11908       }
11909
11910       if (vol)
11911       {
11912         stringstream grpname;
11913         grpname << "jf_";
11914         grpname << idom;
11915         int idg;
11916         string namegrp = grpname.str();
11917         if (!mapOfJunctionGroups.count(namegrp))
11918           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11919         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11920         if (sgrp)
11921           sgrp->Add(vol->GetID());
11922       }
11923
11924       // --- modify the face
11925
11926       aFace->ChangeNodes(&ln[0], ln.size());
11927     }
11928   }
11929   return true;
11930 }
11931
11932 /*!
11933  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11934  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11935  *  groups of faces to remove inside the object, (idem edges).
11936  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11937  */
11938 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
11939                                       const TopoDS_Shape&             theShape,
11940                                       SMESH_NodeSearcher*             theNodeSearcher,
11941                                       const char*                     groupName,
11942                                       std::vector<double>&            nodesCoords,
11943                                       std::vector<std::vector<int> >& listOfListOfNodes)
11944 {
11945   MESSAGE("--------------------------------");
11946   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11947   MESSAGE("--------------------------------");
11948
11949   // --- zone of volumes to remove is given :
11950   //     1 either by a geom shape (one or more vertices) and a radius,
11951   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11952   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11953   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11954   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11955   //     defined by it's name.
11956
11957   SMESHDS_GroupBase* groupDS = 0;
11958   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11959   while ( groupIt->more() )
11960   {
11961     groupDS = 0;
11962     SMESH_Group * group = groupIt->next();
11963     if ( !group ) continue;
11964     groupDS = group->GetGroupDS();
11965     if ( !groupDS || groupDS->IsEmpty() ) continue;
11966     std::string grpName = group->GetName();
11967     //MESSAGE("grpName=" << grpName);
11968     if (grpName == groupName)
11969       break;
11970     else
11971       groupDS = 0;
11972   }
11973
11974   bool isNodeGroup = false;
11975   bool isNodeCoords = false;
11976   if (groupDS)
11977   {
11978     if (groupDS->GetType() != SMDSAbs_Node)
11979       return;
11980     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11981   }
11982
11983   if (nodesCoords.size() > 0)
11984     isNodeCoords = true; // a list o nodes given by their coordinates
11985   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11986
11987   // --- define groups to build
11988
11989   int idg; // --- group of SMDS volumes
11990   string grpvName = groupName;
11991   grpvName += "_vol";
11992   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11993   if (!grp)
11994   {
11995     MESSAGE("group not created " << grpvName);
11996     return;
11997   }
11998   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11999
12000   int idgs; // --- group of SMDS faces on the skin
12001   string grpsName = groupName;
12002   grpsName += "_skin";
12003   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12004   if (!grps)
12005   {
12006     MESSAGE("group not created " << grpsName);
12007     return;
12008   }
12009   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12010
12011   int idgi; // --- group of SMDS faces internal (several shapes)
12012   string grpiName = groupName;
12013   grpiName += "_internalFaces";
12014   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12015   if (!grpi)
12016   {
12017     MESSAGE("group not created " << grpiName);
12018     return;
12019   }
12020   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12021
12022   int idgei; // --- group of SMDS faces internal (several shapes)
12023   string grpeiName = groupName;
12024   grpeiName += "_internalEdges";
12025   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12026   if (!grpei)
12027   {
12028     MESSAGE("group not created " << grpeiName);
12029     return;
12030   }
12031   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12032
12033   // --- build downward connectivity
12034
12035   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12036   meshDS->BuildDownWardConnectivity(true);
12037   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12038
12039   // --- set of volumes detected inside
12040
12041   std::set<int> setOfInsideVol;
12042   std::set<int> setOfVolToCheck;
12043
12044   std::vector<gp_Pnt> gpnts;
12045   gpnts.clear();
12046
12047   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12048   {
12049     MESSAGE("group of nodes provided");
12050     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12051     while ( elemIt->more() )
12052     {
12053       const SMDS_MeshElement* elem = elemIt->next();
12054       if (!elem)
12055         continue;
12056       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12057       if (!node)
12058         continue;
12059       SMDS_MeshElement* vol = 0;
12060       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12061       while (volItr->more())
12062       {
12063         vol = (SMDS_MeshElement*)volItr->next();
12064         setOfInsideVol.insert(vol->getVtkId());
12065         sgrp->Add(vol->GetID());
12066       }
12067     }
12068   }
12069   else if (isNodeCoords)
12070   {
12071     MESSAGE("list of nodes coordinates provided");
12072     size_t i = 0;
12073     int k = 0;
12074     while ( i < nodesCoords.size()-2 )
12075     {
12076       double x = nodesCoords[i++];
12077       double y = nodesCoords[i++];
12078       double z = nodesCoords[i++];
12079       gp_Pnt p = gp_Pnt(x, y ,z);
12080       gpnts.push_back(p);
12081       MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12082       k++;
12083     }
12084   }
12085   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12086   {
12087     MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12088     TopTools_IndexedMapOfShape vertexMap;
12089     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12090     gp_Pnt p = gp_Pnt(0,0,0);
12091     if (vertexMap.Extent() < 1)
12092       return;
12093
12094     for ( int i = 1; i <= vertexMap.Extent(); ++i )
12095     {
12096       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12097       p = BRep_Tool::Pnt(vertex);
12098       gpnts.push_back(p);
12099       MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12100     }
12101   }
12102
12103   if (gpnts.size() > 0)
12104   {
12105     int nodeId = 0;
12106     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12107     if (startNode)
12108       nodeId = startNode->GetID();
12109     MESSAGE("nodeId " << nodeId);
12110
12111     double radius2 = radius*radius;
12112     MESSAGE("radius2 " << radius2);
12113
12114     // --- volumes on start node
12115
12116     setOfVolToCheck.clear();
12117     SMDS_MeshElement* startVol = 0;
12118     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12119     while (volItr->more())
12120     {
12121       startVol = (SMDS_MeshElement*)volItr->next();
12122       setOfVolToCheck.insert(startVol->getVtkId());
12123     }
12124     if (setOfVolToCheck.empty())
12125     {
12126       MESSAGE("No volumes found");
12127       return;
12128     }
12129
12130     // --- starting with central volumes then their neighbors, check if they are inside
12131     //     or outside the domain, until no more new neighbor volume is inside.
12132     //     Fill the group of inside volumes
12133
12134     std::map<int, double> mapOfNodeDistance2;
12135     mapOfNodeDistance2.clear();
12136     std::set<int> setOfOutsideVol;
12137     while (!setOfVolToCheck.empty())
12138     {
12139       std::set<int>::iterator it = setOfVolToCheck.begin();
12140       int vtkId = *it;
12141       MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12142       bool volInside = false;
12143       vtkIdType npts = 0;
12144       vtkIdType* pts = 0;
12145       grid->GetCellPoints(vtkId, npts, pts);
12146       for (int i=0; i<npts; i++)
12147       {
12148         double distance2 = 0;
12149         if (mapOfNodeDistance2.count(pts[i]))
12150         {
12151           distance2 = mapOfNodeDistance2[pts[i]];
12152           MESSAGE("point " << pts[i] << " distance2 " << distance2);
12153         }
12154         else
12155         {
12156           double *coords = grid->GetPoint(pts[i]);
12157           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12158           distance2 = 1.E40;
12159           for ( size_t j = 0; j < gpnts.size(); j++ )
12160           {
12161             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12162             if (d2 < distance2)
12163             {
12164               distance2 = d2;
12165               if (distance2 < radius2)
12166                 break;
12167             }
12168           }
12169           mapOfNodeDistance2[pts[i]] = distance2;
12170           MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12171         }
12172         if (distance2 < radius2)
12173         {
12174           volInside = true; // one or more nodes inside the domain
12175           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12176           break;
12177         }
12178       }
12179       if (volInside)
12180       {
12181         setOfInsideVol.insert(vtkId);
12182         MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12183         int neighborsVtkIds[NBMAXNEIGHBORS];
12184         int downIds[NBMAXNEIGHBORS];
12185         unsigned char downTypes[NBMAXNEIGHBORS];
12186         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12187         for (int n = 0; n < nbNeighbors; n++)
12188           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12189             setOfVolToCheck.insert(neighborsVtkIds[n]);
12190       }
12191       else
12192       {
12193         setOfOutsideVol.insert(vtkId);
12194         MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12195       }
12196       setOfVolToCheck.erase(vtkId);
12197     }
12198   }
12199
12200   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12201   //     If yes, add the volume to the inside set
12202
12203   bool addedInside = true;
12204   std::set<int> setOfVolToReCheck;
12205   while (addedInside)
12206   {
12207     MESSAGE(" --------------------------- re check");
12208     addedInside = false;
12209     std::set<int>::iterator itv = setOfInsideVol.begin();
12210     for (; itv != setOfInsideVol.end(); ++itv)
12211     {
12212       int vtkId = *itv;
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           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12220     }
12221     setOfVolToCheck = setOfVolToReCheck;
12222     setOfVolToReCheck.clear();
12223     while  (!setOfVolToCheck.empty())
12224     {
12225       std::set<int>::iterator it = setOfVolToCheck.begin();
12226       int vtkId = *it;
12227       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12228       {
12229         MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12230         int countInside = 0;
12231         int neighborsVtkIds[NBMAXNEIGHBORS];
12232         int downIds[NBMAXNEIGHBORS];
12233         unsigned char downTypes[NBMAXNEIGHBORS];
12234         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12235         for (int n = 0; n < nbNeighbors; n++)
12236           if (setOfInsideVol.count(neighborsVtkIds[n]))
12237             countInside++;
12238         MESSAGE("countInside " << countInside);
12239         if (countInside > 1)
12240         {
12241           MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12242           setOfInsideVol.insert(vtkId);
12243           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12244           addedInside = true;
12245         }
12246         else
12247           setOfVolToReCheck.insert(vtkId);
12248       }
12249       setOfVolToCheck.erase(vtkId);
12250     }
12251   }
12252
12253   // --- map of Downward faces at the boundary, inside the global volume
12254   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12255   //     fill group of SMDS faces inside the volume (when several volume shapes)
12256   //     fill group of SMDS faces on the skin of the global volume (if skin)
12257
12258   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12259   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12260   std::set<int>::iterator it = setOfInsideVol.begin();
12261   for (; it != setOfInsideVol.end(); ++it)
12262   {
12263     int vtkId = *it;
12264     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12265     int neighborsVtkIds[NBMAXNEIGHBORS];
12266     int downIds[NBMAXNEIGHBORS];
12267     unsigned char downTypes[NBMAXNEIGHBORS];
12268     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12269     for (int n = 0; n < nbNeighbors; n++)
12270     {
12271       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12272       if (neighborDim == 3)
12273       {
12274         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12275         {
12276           DownIdType face(downIds[n], downTypes[n]);
12277           boundaryFaces[face] = vtkId;
12278         }
12279         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12280         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12281         if (vtkFaceId >= 0)
12282         {
12283           sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12284           // find also the smds edges on this face
12285           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12286           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12287           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12288           for (int i = 0; i < nbEdges; i++)
12289           {
12290             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12291             if (vtkEdgeId >= 0)
12292               sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12293           }
12294         }
12295       }
12296       else if (neighborDim == 2) // skin of the volume
12297       {
12298         DownIdType face(downIds[n], downTypes[n]);
12299         skinFaces[face] = vtkId;
12300         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12301         if (vtkFaceId >= 0)
12302           sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12303       }
12304     }
12305   }
12306
12307   // --- identify the edges constituting the wire of each subshape on the skin
12308   //     define polylines with the nodes of edges, equivalent to wires
12309   //     project polylines on subshapes, and partition, to get geom faces
12310
12311   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12312   std::set<int> emptySet;
12313   emptySet.clear();
12314   std::set<int> shapeIds;
12315
12316   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12317   while (itelem->more())
12318   {
12319     const SMDS_MeshElement *elem = itelem->next();
12320     int shapeId = elem->getshapeId();
12321     int vtkId = elem->getVtkId();
12322     if (!shapeIdToVtkIdSet.count(shapeId))
12323     {
12324       shapeIdToVtkIdSet[shapeId] = emptySet;
12325       shapeIds.insert(shapeId);
12326     }
12327     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12328   }
12329
12330   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12331   std::set<DownIdType, DownIdCompare> emptyEdges;
12332   emptyEdges.clear();
12333
12334   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12335   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12336   {
12337     int shapeId = itShape->first;
12338     MESSAGE(" --- Shape ID --- "<< shapeId);
12339     shapeIdToEdges[shapeId] = emptyEdges;
12340
12341     std::vector<int> nodesEdges;
12342
12343     std::set<int>::iterator its = itShape->second.begin();
12344     for (; its != itShape->second.end(); ++its)
12345     {
12346       int vtkId = *its;
12347       MESSAGE("     " << vtkId);
12348       int neighborsVtkIds[NBMAXNEIGHBORS];
12349       int downIds[NBMAXNEIGHBORS];
12350       unsigned char downTypes[NBMAXNEIGHBORS];
12351       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12352       for (int n = 0; n < nbNeighbors; n++)
12353       {
12354         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12355           continue;
12356         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12357         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12358         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12359         {
12360           DownIdType edge(downIds[n], downTypes[n]);
12361           if (!shapeIdToEdges[shapeId].count(edge))
12362           {
12363             shapeIdToEdges[shapeId].insert(edge);
12364             int vtkNodeId[3];
12365             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12366             nodesEdges.push_back(vtkNodeId[0]);
12367             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12368             MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12369           }
12370         }
12371       }
12372     }
12373
12374     std::list<int> order;
12375     order.clear();
12376     if (nodesEdges.size() > 0)
12377     {
12378       order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12379       nodesEdges[0] = -1;
12380       order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
12381       nodesEdges[1] = -1; // do not reuse this edge
12382       bool found = true;
12383       while (found)
12384       {
12385         int nodeTofind = order.back(); // try first to push back
12386         int i = 0;
12387         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12388           if (nodesEdges[i] == nodeTofind)
12389             break;
12390         if ( i == (int) nodesEdges.size() )
12391           found = false; // no follower found on back
12392         else
12393         {
12394           if (i%2) // odd ==> use the previous one
12395             if (nodesEdges[i-1] < 0)
12396               found = false;
12397             else
12398             {
12399               order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
12400               nodesEdges[i-1] = -1;
12401             }
12402           else // even ==> use the next one
12403             if (nodesEdges[i+1] < 0)
12404               found = false;
12405             else
12406             {
12407               order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
12408               nodesEdges[i+1] = -1;
12409             }
12410         }
12411         if (found)
12412           continue;
12413         // try to push front
12414         found = true;
12415         nodeTofind = order.front(); // try to push front
12416         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12417           if ( nodesEdges[i] == nodeTofind )
12418             break;
12419         if ( i == (int)nodesEdges.size() )
12420         {
12421           found = false; // no predecessor found on front
12422           continue;
12423         }
12424         if (i%2) // odd ==> use the previous one
12425           if (nodesEdges[i-1] < 0)
12426             found = false;
12427           else
12428           {
12429             order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
12430             nodesEdges[i-1] = -1;
12431           }
12432         else // even ==> use the next one
12433           if (nodesEdges[i+1] < 0)
12434             found = false;
12435           else
12436           {
12437             order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
12438             nodesEdges[i+1] = -1;
12439           }
12440       }
12441     }
12442
12443
12444     std::vector<int> nodes;
12445     nodes.push_back(shapeId);
12446     std::list<int>::iterator itl = order.begin();
12447     for (; itl != order.end(); itl++)
12448     {
12449       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12450       MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12451     }
12452     listOfListOfNodes.push_back(nodes);
12453   }
12454
12455   //     partition geom faces with blocFissure
12456   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12457   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12458
12459   return;
12460 }
12461
12462
12463 //================================================================================
12464 /*!
12465  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12466  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12467  * \return TRUE if operation has been completed successfully, FALSE otherwise
12468  */
12469 //================================================================================
12470
12471 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12472 {
12473   // iterates on volume elements and detect all free faces on them
12474   SMESHDS_Mesh* aMesh = GetMeshDS();
12475   if (!aMesh)
12476     return false;
12477
12478   ElemFeatures faceType( SMDSAbs_Face );
12479   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12480   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12481   while(vIt->more())
12482   {
12483     const SMDS_MeshVolume* volume = vIt->next();
12484     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12485     vTool.SetExternalNormal();
12486     const int iQuad = volume->IsQuadratic();
12487     faceType.SetQuad( iQuad );
12488     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12489     {
12490       if (!vTool.IsFreeFace(iface))
12491         continue;
12492       nbFree++;
12493       vector<const SMDS_MeshNode *> nodes;
12494       int nbFaceNodes = vTool.NbFaceNodes(iface);
12495       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12496       int inode = 0;
12497       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12498         nodes.push_back(faceNodes[inode]);
12499
12500       if (iQuad) // add medium nodes
12501       {
12502         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12503           nodes.push_back(faceNodes[inode]);
12504         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12505           nodes.push_back(faceNodes[8]);
12506       }
12507       // add new face based on volume nodes
12508       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12509       {
12510         nbExisted++; // face already exsist
12511       }
12512       else
12513       {
12514         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12515         nbCreated++;
12516       }
12517     }
12518   }
12519   return ( nbFree == ( nbExisted + nbCreated ));
12520 }
12521
12522 namespace
12523 {
12524   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12525   {
12526     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12527       return n;
12528     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12529   }
12530 }
12531 //================================================================================
12532 /*!
12533  * \brief Creates missing boundary elements
12534  *  \param elements - elements whose boundary is to be checked
12535  *  \param dimension - defines type of boundary elements to create
12536  *  \param group - a group to store created boundary elements in
12537  *  \param targetMesh - a mesh to store created boundary elements in
12538  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12539  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12540  *                                boundary elements will be copied into the targetMesh
12541  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12542  *                                boundary elements will be added into the new group
12543  *  \param aroundElements - if true, elements will be created on boundary of given
12544  *                          elements else, on boundary of the whole mesh.
12545  * \return nb of added boundary elements
12546  */
12547 //================================================================================
12548
12549 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12550                                        Bnd_Dimension           dimension,
12551                                        SMESH_Group*            group/*=0*/,
12552                                        SMESH_Mesh*             targetMesh/*=0*/,
12553                                        bool                    toCopyElements/*=false*/,
12554                                        bool                    toCopyExistingBoundary/*=false*/,
12555                                        bool                    toAddExistingBondary/*= false*/,
12556                                        bool                    aroundElements/*= false*/)
12557 {
12558   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12559   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12560   // hope that all elements are of the same type, do not check them all
12561   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12562     throw SALOME_Exception(LOCALIZED("wrong element type"));
12563
12564   if ( !targetMesh )
12565     toCopyElements = toCopyExistingBoundary = false;
12566
12567   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12568   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12569   int nbAddedBnd = 0;
12570
12571   // editor adding present bnd elements and optionally holding elements to add to the group
12572   SMESH_MeshEditor* presentEditor;
12573   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12574   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12575
12576   SMESH_MesherHelper helper( *myMesh );
12577   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12578   SMDS_VolumeTool vTool;
12579   TIDSortedElemSet avoidSet;
12580   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12581   size_t inode;
12582
12583   typedef vector<const SMDS_MeshNode*> TConnectivity;
12584   TConnectivity tgtNodes;
12585   ElemFeatures elemKind( missType ), elemToCopy;
12586
12587   vector<const SMDS_MeshElement*> presentBndElems;
12588   vector<TConnectivity>           missingBndElems;
12589   vector<int>                     freeFacets;
12590   TConnectivity nodes, elemNodes;
12591
12592   SMDS_ElemIteratorPtr eIt;
12593   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12594   else                  eIt = elemSetIterator( elements );
12595
12596   while (eIt->more())
12597   {
12598     const SMDS_MeshElement* elem = eIt->next();
12599     const int              iQuad = elem->IsQuadratic();
12600     elemKind.SetQuad( iQuad );
12601
12602     // ------------------------------------------------------------------------------------
12603     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12604     // ------------------------------------------------------------------------------------
12605     presentBndElems.clear();
12606     missingBndElems.clear();
12607     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12608     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12609     {
12610       const SMDS_MeshElement* otherVol = 0;
12611       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12612       {
12613         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12614              ( !aroundElements || elements.count( otherVol )))
12615           continue;
12616         freeFacets.push_back( iface );
12617       }
12618       if ( missType == SMDSAbs_Face )
12619         vTool.SetExternalNormal();
12620       for ( size_t i = 0; i < freeFacets.size(); ++i )
12621       {
12622         int                iface = freeFacets[i];
12623         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12624         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12625         if ( missType == SMDSAbs_Edge ) // boundary edges
12626         {
12627           nodes.resize( 2+iQuad );
12628           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12629           {
12630             for ( size_t j = 0; j < nodes.size(); ++j )
12631               nodes[ j ] = nn[ i+j ];
12632             if ( const SMDS_MeshElement* edge =
12633                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12634               presentBndElems.push_back( edge );
12635             else
12636               missingBndElems.push_back( nodes );
12637           }
12638         }
12639         else // boundary face
12640         {
12641           nodes.clear();
12642           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12643             nodes.push_back( nn[inode] ); // add corner nodes
12644           if (iQuad)
12645             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12646               nodes.push_back( nn[inode] ); // add medium nodes
12647           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12648           if ( iCenter > 0 )
12649             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12650
12651           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12652                                                                SMDSAbs_Face, /*noMedium=*/false ))
12653             presentBndElems.push_back( f );
12654           else
12655             missingBndElems.push_back( nodes );
12656
12657           if ( targetMesh != myMesh )
12658           {
12659             // add 1D elements on face boundary to be added to a new mesh
12660             const SMDS_MeshElement* edge;
12661             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12662             {
12663               if ( iQuad )
12664                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12665               else
12666                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12667               if ( edge && avoidSet.insert( edge ).second )
12668                 presentBndElems.push_back( edge );
12669             }
12670           }
12671         }
12672       }
12673     }
12674     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12675     {
12676       avoidSet.clear(), avoidSet.insert( elem );
12677       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12678                         SMDS_MeshElement::iterator() );
12679       elemNodes.push_back( elemNodes[0] );
12680       nodes.resize( 2 + iQuad );
12681       const int nbLinks = elem->NbCornerNodes();
12682       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12683       {
12684         nodes[0] = elemNodes[iN];
12685         nodes[1] = elemNodes[iN+1+iQuad];
12686         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12687           continue; // not free link
12688
12689         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12690         if ( const SMDS_MeshElement* edge =
12691              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12692           presentBndElems.push_back( edge );
12693         else
12694           missingBndElems.push_back( nodes );
12695       }
12696     }
12697
12698     // ---------------------------------
12699     // 2. Add missing boundary elements
12700     // ---------------------------------
12701     if ( targetMesh != myMesh )
12702       // instead of making a map of nodes in this mesh and targetMesh,
12703       // we create nodes with same IDs.
12704       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12705       {
12706         TConnectivity& srcNodes = missingBndElems[i];
12707         tgtNodes.resize( srcNodes.size() );
12708         for ( inode = 0; inode < srcNodes.size(); ++inode )
12709           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12710         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12711                                                                    missType,
12712                                                                    /*noMedium=*/false))
12713           continue;
12714         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12715         ++nbAddedBnd;
12716       }
12717     else
12718       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12719       {
12720         TConnectivity& nodes = missingBndElems[ i ];
12721         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12722                                                                    missType,
12723                                                                    /*noMedium=*/false))
12724           continue;
12725         SMDS_MeshElement* newElem =
12726           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12727         nbAddedBnd += bool( newElem );
12728
12729         // try to set a new element to a shape
12730         if ( myMesh->HasShapeToMesh() )
12731         {
12732           bool ok = true;
12733           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12734           const size_t nbN = nodes.size() / (iQuad+1 );
12735           for ( inode = 0; inode < nbN && ok; ++inode )
12736           {
12737             pair<int, TopAbs_ShapeEnum> i_stype =
12738               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12739             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12740               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12741           }
12742           if ( ok && mediumShapes.size() > 1 )
12743           {
12744             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12745             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12746             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12747             {
12748               if (( ok = ( stype_i->first != stype_i_0.first )))
12749                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12750                                         aMesh->IndexToShape( stype_i_0.second ));
12751             }
12752           }
12753           if ( ok && mediumShapes.begin()->first == missShapeType )
12754             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12755         }
12756       }
12757
12758     // ----------------------------------
12759     // 3. Copy present boundary elements
12760     // ----------------------------------
12761     if ( toCopyExistingBoundary )
12762       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12763       {
12764         const SMDS_MeshElement* e = presentBndElems[i];
12765         tgtNodes.resize( e->NbNodes() );
12766         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12767           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12768         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12769       }
12770     else // store present elements to add them to a group
12771       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12772       {
12773         presentEditor->myLastCreatedElems.Append( presentBndElems[ i ]);
12774       }
12775
12776   } // loop on given elements
12777
12778   // ---------------------------------------------
12779   // 4. Fill group with boundary elements
12780   // ---------------------------------------------
12781   if ( group )
12782   {
12783     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12784       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12785         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12786   }
12787   tgtEditor.myLastCreatedElems.Clear();
12788   tgtEditor2.myLastCreatedElems.Clear();
12789
12790   // -----------------------
12791   // 5. Copy given elements
12792   // -----------------------
12793   if ( toCopyElements && targetMesh != myMesh )
12794   {
12795     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12796     else                  eIt = elemSetIterator( elements );
12797     while (eIt->more())
12798     {
12799       const SMDS_MeshElement* elem = eIt->next();
12800       tgtNodes.resize( elem->NbNodes() );
12801       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12802         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12803       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12804
12805       tgtEditor.myLastCreatedElems.Clear();
12806     }
12807   }
12808   return nbAddedBnd;
12809 }
12810
12811 //================================================================================
12812 /*!
12813  * \brief Copy node position and set \a to node on the same geometry
12814  */
12815 //================================================================================
12816
12817 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12818                                      const SMDS_MeshNode* to )
12819 {
12820   if ( !from || !to ) return;
12821
12822   SMDS_PositionPtr pos = from->GetPosition();
12823   if ( !pos || from->getshapeId() < 1 ) return;
12824
12825   switch ( pos->GetTypeOfPosition() )
12826   {
12827   case SMDS_TOP_3DSPACE: break;
12828
12829   case SMDS_TOP_FACE:
12830   {
12831     const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12832     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12833                                 fPos->GetUParameter(), fPos->GetVParameter() );
12834     break;
12835   }
12836   case SMDS_TOP_EDGE:
12837   {
12838     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12839     const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12840     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12841     break;
12842   }
12843   case SMDS_TOP_VERTEX:
12844   {
12845     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12846     break;
12847   }
12848   case SMDS_TOP_UNSPEC:
12849   default:;
12850   }
12851 }