Salome HOME
79ecf615d3fef24f0c866aff2679531677b2ca08
[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     helper.SetIsQuadratic  ( nodes.size() > 4 );
1605     helper.SetIsBiQuadratic( nodes.size() == 9 );
1606     if ( helper.GetIsQuadratic() )
1607       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1608
1609     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1610
1611     for ( int i = 0; i < 4; ++i )
1612     {
1613       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1614                                                nodes[(i+1)%4],
1615                                                nCentral );
1616       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1617       myLastCreatedElems.Append( tria );
1618     }
1619   }
1620 }
1621
1622 //=======================================================================
1623 //function : BestSplit
1624 //purpose  : Find better diagonal for cutting.
1625 //=======================================================================
1626
1627 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1628                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1629 {
1630   myLastCreatedElems.Clear();
1631   myLastCreatedNodes.Clear();
1632
1633   if (!theCrit.get())
1634     return -1;
1635
1636   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1637     return -1;
1638
1639   if( theQuad->NbNodes()==4 ||
1640       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1641
1642     // retrieve element nodes
1643     const SMDS_MeshNode* aNodes [4];
1644     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1645     int i = 0;
1646     //while (itN->more())
1647     while (i<4) {
1648       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1649     }
1650     // compare two sets of possible triangles
1651     double aBadRate1, aBadRate2; // to what extent a set is bad
1652     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1653     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1654     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1655
1656     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1657     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1658     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1659     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1660     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1661     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1662       return 1; // diagonal 1-3
1663
1664     return 2; // diagonal 2-4
1665   }
1666   return -1;
1667 }
1668
1669 namespace
1670 {
1671   // Methods of splitting volumes into tetra
1672
1673   const int theHexTo5_1[5*4+1] =
1674     {
1675       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1676     };
1677   const int theHexTo5_2[5*4+1] =
1678     {
1679       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1680     };
1681   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1682
1683   const int theHexTo6_1[6*4+1] =
1684     {
1685       1, 5, 6, 0,    0, 1, 2, 6,     0, 4, 5, 6,    0, 4, 6, 7,     0, 2, 3, 6,   0, 3, 7, 6,  -1
1686     };
1687   const int theHexTo6_2[6*4+1] =
1688     {
1689       2, 6, 7, 1,    1, 2, 3, 7,     1, 5, 6, 7,    1, 5, 7, 4,     1, 3, 0, 7,   1, 0, 4, 7,  -1
1690     };
1691   const int theHexTo6_3[6*4+1] =
1692     {
1693       3, 7, 4, 2,    2, 3, 0, 4,     2, 6, 7, 4,    2, 6, 4, 5,     2, 0, 1, 4,   2, 1, 5, 4,  -1
1694     };
1695   const int theHexTo6_4[6*4+1] =
1696     {
1697       0, 4, 5, 3,    3, 0, 1, 5,     3, 7, 4, 5,    3, 7, 5, 6,     3, 1, 2, 5,   3, 2, 6, 5,  -1
1698     };
1699   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1700
1701   const int thePyraTo2_1[2*4+1] =
1702     {
1703       0, 1, 2, 4,    0, 2, 3, 4,   -1
1704     };
1705   const int thePyraTo2_2[2*4+1] =
1706     {
1707       1, 2, 3, 4,    1, 3, 0, 4,   -1
1708     };
1709   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1710
1711   const int thePentaTo3_1[3*4+1] =
1712     {
1713       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1714     };
1715   const int thePentaTo3_2[3*4+1] =
1716     {
1717       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1718     };
1719   const int thePentaTo3_3[3*4+1] =
1720     {
1721       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1722     };
1723   const int thePentaTo3_4[3*4+1] =
1724     {
1725       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1726     };
1727   const int thePentaTo3_5[3*4+1] =
1728     {
1729       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1730     };
1731   const int thePentaTo3_6[3*4+1] =
1732     {
1733       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1734     };
1735   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1736                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1737
1738   // Methods of splitting hexahedron into prisms
1739
1740   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1741     {
1742       0, 1, 8, 4, 5, 9,    1, 2, 8, 5, 6, 9,    2, 3, 8, 6, 7, 9,   3, 0, 8, 7, 4, 9,    -1
1743     };
1744   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1745     {
1746       1, 0, 8, 2, 3, 9,    0, 4, 8, 3, 7, 9,    4, 5, 8, 7, 6, 9,   5, 1, 8, 6, 2, 9,    -1
1747     };
1748   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1749     {
1750       0, 3, 9, 1, 2, 8,    3, 7, 9, 2, 6, 8,    7, 4, 9, 6, 5, 8,   4, 0, 9, 5, 1, 8,    -1
1751     };
1752
1753   const int theHexTo2Prisms_BT_1[6*2+1] =
1754     {
1755       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1756     };
1757   const int theHexTo2Prisms_BT_2[6*2+1] =
1758     {
1759       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1760     };
1761   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1762
1763   const int theHexTo2Prisms_LR_1[6*2+1] =
1764     {
1765       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1766     };
1767   const int theHexTo2Prisms_LR_2[6*2+1] =
1768     {
1769       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1770     };
1771   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1772
1773   const int theHexTo2Prisms_FB_1[6*2+1] =
1774     {
1775       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1776     };
1777   const int theHexTo2Prisms_FB_2[6*2+1] =
1778     {
1779       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1780     };
1781   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1782
1783
1784   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1785   {
1786     int _n1, _n2, _n3;
1787     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1788     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1789     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1790                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1791   };
1792   struct TSplitMethod
1793   {
1794     int        _nbSplits;
1795     int        _nbCorners;
1796     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1797     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1798     bool       _ownConn;      //!< to delete _connectivity in destructor
1799     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1800
1801     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1802       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1803     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1804     bool hasFacet( const TTriangleFacet& facet ) const
1805     {
1806       if ( _nbCorners == 4 )
1807       {
1808         const int* tetConn = _connectivity;
1809         for ( ; tetConn[0] >= 0; tetConn += 4 )
1810           if (( facet.contains( tetConn[0] ) +
1811                 facet.contains( tetConn[1] ) +
1812                 facet.contains( tetConn[2] ) +
1813                 facet.contains( tetConn[3] )) == 3 )
1814             return true;
1815       }
1816       else // prism, _nbCorners == 6
1817       {
1818         const int* prismConn = _connectivity;
1819         for ( ; prismConn[0] >= 0; prismConn += 6 )
1820         {
1821           if (( facet.contains( prismConn[0] ) &&
1822                 facet.contains( prismConn[1] ) &&
1823                 facet.contains( prismConn[2] ))
1824               ||
1825               ( facet.contains( prismConn[3] ) &&
1826                 facet.contains( prismConn[4] ) &&
1827                 facet.contains( prismConn[5] )))
1828             return true;
1829         }
1830       }
1831       return false;
1832     }
1833   };
1834
1835   //=======================================================================
1836   /*!
1837    * \brief return TSplitMethod for the given element to split into tetrahedra
1838    */
1839   //=======================================================================
1840
1841   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1842   {
1843     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1844
1845     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1846     // an edge and a face barycenter; tertaherdons are based on triangles and
1847     // a volume barycenter
1848     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1849
1850     // Find out how adjacent volumes are split
1851
1852     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1853     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1854     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1855     {
1856       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1857       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1858       if ( nbNodes < 4 ) continue;
1859
1860       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1861       const int* nInd = vol.GetFaceNodesIndices( iF );
1862       if ( nbNodes == 4 )
1863       {
1864         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1865         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1866         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1867         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1868       }
1869       else
1870       {
1871         int iCom = 0; // common node of triangle faces to split into
1872         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1873         {
1874           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1875                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1876                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1877           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1878                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1879                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1880           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1881           {
1882             triaSplits.push_back( t012 );
1883             triaSplits.push_back( t023 );
1884             break;
1885           }
1886         }
1887       }
1888       if ( !triaSplits.empty() )
1889         hasAdjacentSplits = true;
1890     }
1891
1892     // Among variants of split method select one compliant with adjacent volumes
1893
1894     TSplitMethod method;
1895     if ( !vol.Element()->IsPoly() && !is24TetMode )
1896     {
1897       int nbVariants = 2, nbTet = 0;
1898       const int** connVariants = 0;
1899       switch ( vol.Element()->GetEntityType() )
1900       {
1901       case SMDSEntity_Hexa:
1902       case SMDSEntity_Quad_Hexa:
1903       case SMDSEntity_TriQuad_Hexa:
1904         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1905           connVariants = theHexTo5, nbTet = 5;
1906         else
1907           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1908         break;
1909       case SMDSEntity_Pyramid:
1910       case SMDSEntity_Quad_Pyramid:
1911         connVariants = thePyraTo2;  nbTet = 2;
1912         break;
1913       case SMDSEntity_Penta:
1914       case SMDSEntity_Quad_Penta:
1915         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1916         break;
1917       default:
1918         nbVariants = 0;
1919       }
1920       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1921       {
1922         // check method compliancy with adjacent tetras,
1923         // all found splits must be among facets of tetras described by this method
1924         method = TSplitMethod( nbTet, connVariants[variant] );
1925         if ( hasAdjacentSplits && method._nbSplits > 0 )
1926         {
1927           bool facetCreated = true;
1928           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1929           {
1930             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1931             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1932               facetCreated = method.hasFacet( *facet );
1933           }
1934           if ( !facetCreated )
1935             method = TSplitMethod(0); // incompatible method
1936         }
1937       }
1938     }
1939     if ( method._nbSplits < 1 )
1940     {
1941       // No standard method is applicable, use a generic solution:
1942       // each facet of a volume is split into triangles and
1943       // each of triangles and a volume barycenter form a tetrahedron.
1944
1945       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1946
1947       int* connectivity = new int[ maxTetConnSize + 1 ];
1948       method._connectivity = connectivity;
1949       method._ownConn = true;
1950       method._baryNode = !isHex27; // to create central node or not
1951
1952       int connSize = 0;
1953       int baryCenInd = vol.NbNodes() - int( isHex27 );
1954       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1955       {
1956         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1957         const int*   nInd = vol.GetFaceNodesIndices( iF );
1958         // find common node of triangle facets of tetra to create
1959         int iCommon = 0; // index in linear numeration
1960         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1961         if ( !triaSplits.empty() )
1962         {
1963           // by found facets
1964           const TTriangleFacet* facet = &triaSplits.front();
1965           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1966             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1967                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1968               break;
1969         }
1970         else if ( nbNodes > 3 && !is24TetMode )
1971         {
1972           // find the best method of splitting into triangles by aspect ratio
1973           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1974           map< double, int > badness2iCommon;
1975           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1976           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1977           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1978           {
1979             double badness = 0;
1980             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1981             {
1982               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1983                                       nodes[ iQ*((iLast-1)%nbNodes)],
1984                                       nodes[ iQ*((iLast  )%nbNodes)]);
1985               badness += getBadRate( &tria, aspectRatio );
1986             }
1987             badness2iCommon.insert( make_pair( badness, iCommon ));
1988           }
1989           // use iCommon with lowest badness
1990           iCommon = badness2iCommon.begin()->second;
1991         }
1992         if ( iCommon >= nbNodes )
1993           iCommon = 0; // something wrong
1994
1995         // fill connectivity of tetrahedra based on a current face
1996         int nbTet = nbNodes - 2;
1997         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1998         {
1999           int faceBaryCenInd;
2000           if ( isHex27 )
2001           {
2002             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2003             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2004           }
2005           else
2006           {
2007             method._faceBaryNode[ iF ] = 0;
2008             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2009           }
2010           nbTet = nbNodes;
2011           for ( int i = 0; i < nbTet; ++i )
2012           {
2013             int i1 = i, i2 = (i+1) % nbNodes;
2014             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2015             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2016             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2017             connectivity[ connSize++ ] = faceBaryCenInd;
2018             connectivity[ connSize++ ] = baryCenInd;
2019           }
2020         }
2021         else
2022         {
2023           for ( int i = 0; i < nbTet; ++i )
2024           {
2025             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2026             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2027             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2028             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2029             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2030             connectivity[ connSize++ ] = baryCenInd;
2031           }
2032         }
2033         method._nbSplits += nbTet;
2034
2035       } // loop on volume faces
2036
2037       connectivity[ connSize++ ] = -1;
2038
2039     } // end of generic solution
2040
2041     return method;
2042   }
2043   //=======================================================================
2044   /*!
2045    * \brief return TSplitMethod to split haxhedron into prisms
2046    */
2047   //=======================================================================
2048
2049   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2050                                     const int        methodFlags,
2051                                     const int        facetToSplit)
2052   {
2053     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2054     // B, T, L, B, R, F
2055     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2056
2057     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2058     {
2059       static TSplitMethod to4methods[4]; // order BT, LR, FB
2060       if ( to4methods[iF]._nbSplits == 0 )
2061       {
2062         switch ( iF ) {
2063         case 0:
2064           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2065           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2066           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2067           break;
2068         case 1:
2069           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2070           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2071           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2072           break;
2073         case 2:
2074           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2075           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2076           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2077           break;
2078         default: return to4methods[3];
2079         }
2080         to4methods[iF]._nbSplits  = 4;
2081         to4methods[iF]._nbCorners = 6;
2082       }
2083       return to4methods[iF];
2084     }
2085     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2086
2087     TSplitMethod method;
2088
2089     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2090
2091     const int nbVariants = 2, nbSplits = 2;
2092     const int** connVariants = 0;
2093     switch ( iF ) {
2094     case 0: connVariants = theHexTo2Prisms_BT; break;
2095     case 1: connVariants = theHexTo2Prisms_LR; break;
2096     case 2: connVariants = theHexTo2Prisms_FB; break;
2097     default: return method;
2098     }
2099
2100     // look for prisms adjacent via facetToSplit and an opposite one
2101     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2102     {
2103       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2104       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2105       if ( nbNodes != 4 ) return method;
2106
2107       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2108       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2109       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2110       TTriangleFacet* t;
2111       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2112         t = &t012;
2113       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2114         t = &t123;
2115       else
2116         continue;
2117
2118       // there are adjacent prism
2119       for ( int variant = 0; variant < nbVariants; ++variant )
2120       {
2121         // check method compliancy with adjacent prisms,
2122         // the found prism facets must be among facets of prisms described by current method
2123         method._nbSplits     = nbSplits;
2124         method._nbCorners    = 6;
2125         method._connectivity = connVariants[ variant ];
2126         if ( method.hasFacet( *t ))
2127           return method;
2128       }
2129     }
2130
2131     // No adjacent prisms. Select a variant with a best aspect ratio.
2132
2133     double badness[2] = { 0, 0 };
2134     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2135     const SMDS_MeshNode** nodes = vol.GetNodes();
2136     for ( int variant = 0; variant < nbVariants; ++variant )
2137       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2138       {
2139         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2140         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2141
2142         method._connectivity = connVariants[ variant ];
2143         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2144         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2145         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2146
2147         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2148                                 nodes[ t->_n2 ],
2149                                 nodes[ t->_n3 ] );
2150         badness[ variant ] += getBadRate( &tria, aspectRatio );
2151       }
2152     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2153
2154     method._nbSplits     = nbSplits;
2155     method._nbCorners    = 6;
2156     method._connectivity = connVariants[ iBetter ];
2157
2158     return method;
2159   }
2160
2161   //================================================================================
2162   /*!
2163    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2164    */
2165   //================================================================================
2166
2167   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2168                                        const SMDSAbs_GeometryType geom ) const
2169   {
2170     // find the tetrahedron including the three nodes of facet
2171     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2172     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2173     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2174     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2175     while ( volIt1->more() )
2176     {
2177       const SMDS_MeshElement* v = volIt1->next();
2178       if ( v->GetGeomType() != geom )
2179         continue;
2180       const int lastCornerInd = v->NbCornerNodes() - 1;
2181       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2182         continue; // medium node not allowed
2183       const int ind2 = v->GetNodeIndex( n2 );
2184       if ( ind2 < 0 || lastCornerInd < ind2 )
2185         continue;
2186       const int ind3 = v->GetNodeIndex( n3 );
2187       if ( ind3 < 0 || lastCornerInd < ind3 )
2188         continue;
2189       return true;
2190     }
2191     return false;
2192   }
2193
2194   //=======================================================================
2195   /*!
2196    * \brief A key of a face of volume
2197    */
2198   //=======================================================================
2199
2200   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2201   {
2202     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2203     {
2204       TIDSortedNodeSet sortedNodes;
2205       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2206       int nbNodes = vol.NbFaceNodes( iF );
2207       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2208       for ( int i = 0; i < nbNodes; i += iQ )
2209         sortedNodes.insert( fNodes[i] );
2210       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2211       first.first   = (*(n++))->GetID();
2212       first.second  = (*(n++))->GetID();
2213       second.first  = (*(n++))->GetID();
2214       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2215     }
2216   };
2217 } // namespace
2218
2219 //=======================================================================
2220 //function : SplitVolumes
2221 //purpose  : Split volume elements into tetrahedra or prisms.
2222 //           If facet ID < 0, element is split into tetrahedra,
2223 //           else a hexahedron is split into prisms so that the given facet is
2224 //           split into triangles
2225 //=======================================================================
2226
2227 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2228                                      const int            theMethodFlags)
2229 {
2230   SMDS_VolumeTool    volTool;
2231   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2232   fHelper.ToFixNodeParameters( true );
2233
2234   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2235   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2236
2237   SMESH_SequenceOfElemPtr newNodes, newElems;
2238
2239   // map face of volume to it's baricenrtic node
2240   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2241   double bc[3];
2242   vector<const SMDS_MeshElement* > splitVols;
2243
2244   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2245   for ( ; elem2facet != theElems.end(); ++elem2facet )
2246   {
2247     const SMDS_MeshElement* elem = elem2facet->first;
2248     const int       facetToSplit = elem2facet->second;
2249     if ( elem->GetType() != SMDSAbs_Volume )
2250       continue;
2251     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2252     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2253       continue;
2254
2255     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2256
2257     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2258                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2259                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2260     if ( splitMethod._nbSplits < 1 ) continue;
2261
2262     // find submesh to add new tetras to
2263     if ( !subMesh || !subMesh->Contains( elem ))
2264     {
2265       int shapeID = FindShape( elem );
2266       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2267       subMesh = GetMeshDS()->MeshElements( shapeID );
2268     }
2269     int iQ;
2270     if ( elem->IsQuadratic() )
2271     {
2272       iQ = 2;
2273       // add quadratic links to the helper
2274       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2275       {
2276         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2277         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2278         for ( int iN = 0; iN < nbN; iN += iQ )
2279           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2280       }
2281       helper.SetIsQuadratic( true );
2282     }
2283     else
2284     {
2285       iQ = 1;
2286       helper.SetIsQuadratic( false );
2287     }
2288     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2289                                         volTool.GetNodes() + elem->NbNodes() );
2290     helper.SetElementsOnShape( true );
2291     if ( splitMethod._baryNode )
2292     {
2293       // make a node at barycenter
2294       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2295       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2296       nodes.push_back( gcNode );
2297       newNodes.Append( gcNode );
2298     }
2299     if ( !splitMethod._faceBaryNode.empty() )
2300     {
2301       // make or find baricentric nodes of faces
2302       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2303       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2304       {
2305         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2306           volFace2BaryNode.insert
2307           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2308         if ( !f_n->second )
2309         {
2310           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2311           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2312         }
2313         nodes.push_back( iF_n->second = f_n->second );
2314       }
2315     }
2316
2317     // make new volumes
2318     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2319     const int* volConn = splitMethod._connectivity;
2320     if ( splitMethod._nbCorners == 4 ) // tetra
2321       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2322         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2323                                                             nodes[ volConn[1] ],
2324                                                             nodes[ volConn[2] ],
2325                                                             nodes[ volConn[3] ]));
2326     else // prisms
2327       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2328         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2329                                                             nodes[ volConn[1] ],
2330                                                             nodes[ volConn[2] ],
2331                                                             nodes[ volConn[3] ],
2332                                                             nodes[ volConn[4] ],
2333                                                             nodes[ volConn[5] ]));
2334
2335     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2336
2337     // Split faces on sides of the split volume
2338
2339     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2340     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2341     {
2342       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2343       if ( nbNodes < 4 ) continue;
2344
2345       // find an existing face
2346       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2347                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2348       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2349                                                                        /*noMedium=*/false))
2350       {
2351         // make triangles
2352         helper.SetElementsOnShape( false );
2353         vector< const SMDS_MeshElement* > triangles;
2354
2355         // find submesh to add new triangles in
2356         if ( !fSubMesh || !fSubMesh->Contains( face ))
2357         {
2358           int shapeID = FindShape( face );
2359           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2360         }
2361         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2362         if ( iF_n != splitMethod._faceBaryNode.end() )
2363         {
2364           const SMDS_MeshNode *baryNode = iF_n->second;
2365           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2366           {
2367             const SMDS_MeshNode* n1 = fNodes[iN];
2368             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2369             const SMDS_MeshNode *n3 = baryNode;
2370             if ( !volTool.IsFaceExternal( iF ))
2371               swap( n2, n3 );
2372             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2373           }
2374           if ( fSubMesh ) // update position of the bary node on geometry
2375           {
2376             if ( subMesh )
2377               subMesh->RemoveNode( baryNode, false );
2378             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2379             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2380             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2381             {
2382               fHelper.SetSubShape( s );
2383               gp_XY uv( 1e100, 1e100 );
2384               double distXYZ[4];
2385               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2386                                         uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2387                    uv.X() < 1e100 )
2388               {
2389                 // node is too far from the surface
2390                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2391                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2392                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2393               }
2394             }
2395           }
2396         }
2397         else
2398         {
2399           // among possible triangles create ones discribed by split method
2400           const int* nInd = volTool.GetFaceNodesIndices( iF );
2401           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2402           int iCom = 0; // common node of triangle faces to split into
2403           list< TTriangleFacet > facets;
2404           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2405           {
2406             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2407                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2408                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2409             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2410                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2411                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2412             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2413             {
2414               facets.push_back( t012 );
2415               facets.push_back( t023 );
2416               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2417                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2418                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2419                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2420               break;
2421             }
2422           }
2423           list< TTriangleFacet >::iterator facet = facets.begin();
2424           if ( facet == facets.end() )
2425             break;
2426           for ( ; facet != facets.end(); ++facet )
2427           {
2428             if ( !volTool.IsFaceExternal( iF ))
2429               swap( facet->_n2, facet->_n3 );
2430             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2431                                                  volNodes[ facet->_n2 ],
2432                                                  volNodes[ facet->_n3 ]));
2433           }
2434         }
2435         for ( size_t i = 0; i < triangles.size(); ++i )
2436         {
2437           if ( !triangles[ i ]) continue;
2438           if ( fSubMesh )
2439             fSubMesh->AddElement( triangles[ i ]);
2440           newElems.Append( triangles[ i ]);
2441         }
2442         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2443         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2444
2445       } // while a face based on facet nodes exists
2446     } // loop on volume faces to split them into triangles
2447
2448     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2449
2450     if ( geomType == SMDSEntity_TriQuad_Hexa )
2451     {
2452       // remove medium nodes that could become free
2453       for ( int i = 20; i < volTool.NbNodes(); ++i )
2454         if ( volNodes[i]->NbInverseElements() == 0 )
2455           GetMeshDS()->RemoveNode( volNodes[i] );
2456     }
2457   } // loop on volumes to split
2458
2459   myLastCreatedNodes = newNodes;
2460   myLastCreatedElems = newElems;
2461 }
2462
2463 //=======================================================================
2464 //function : GetHexaFacetsToSplit
2465 //purpose  : For hexahedra that will be split into prisms, finds facets to
2466 //           split into triangles. Only hexahedra adjacent to the one closest
2467 //           to theFacetNormal.Location() are returned.
2468 //param [in,out] theHexas - the hexahedra
2469 //param [in]     theFacetNormal - facet normal
2470 //param [out]    theFacets - the hexahedra and found facet IDs
2471 //=======================================================================
2472
2473 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2474                                              const gp_Ax1&     theFacetNormal,
2475                                              TFacetOfElem &    theFacets)
2476 {
2477   #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2478
2479   // Find a hexa closest to the location of theFacetNormal
2480
2481   const SMDS_MeshElement* startHex;
2482   {
2483     // get SMDS_ElemIteratorPtr on theHexas
2484     typedef const SMDS_MeshElement*                                      TValue;
2485     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2486     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2487     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2488     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2489     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2490       ( new TElemSetIter( theHexas.begin(),
2491                           theHexas.end(),
2492                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2493
2494     SMESH_ElementSearcher* searcher =
2495       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2496
2497     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2498
2499     delete searcher;
2500
2501     if ( !startHex )
2502       throw SALOME_Exception( THIS_METHOD "startHex not found");
2503   }
2504
2505   // Select a facet of startHex by theFacetNormal
2506
2507   SMDS_VolumeTool vTool( startHex );
2508   double norm[3], dot, maxDot = 0;
2509   int facetID = -1;
2510   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2511     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2512     {
2513       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2514       if ( dot > maxDot )
2515       {
2516         facetID = iF;
2517         maxDot = dot;
2518       }
2519     }
2520   if ( facetID < 0 )
2521     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2522
2523   // Fill theFacets starting from facetID of startHex
2524
2525   // facets used for seach of volumes adjacent to already treated ones
2526   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2527   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2528   TFacetMap facetsToCheck;
2529
2530   set<const SMDS_MeshNode*> facetNodes;
2531   const SMDS_MeshElement*   curHex;
2532
2533   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2534
2535   while ( startHex )
2536   {
2537     // move in two directions from startHex via facetID
2538     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2539     {
2540       curHex       = startHex;
2541       int curFacet = facetID;
2542       if ( is2nd ) // do not treat startHex twice
2543       {
2544         vTool.Set( curHex );
2545         if ( vTool.IsFreeFace( curFacet, &curHex ))
2546         {
2547           curHex = 0;
2548         }
2549         else
2550         {
2551           vTool.GetFaceNodes( curFacet, facetNodes );
2552           vTool.Set( curHex );
2553           curFacet = vTool.GetFaceIndex( facetNodes );
2554         }
2555       }
2556       while ( curHex )
2557       {
2558         // store a facet to split
2559         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2560         {
2561           theFacets.insert( make_pair( curHex, -1 ));
2562           break;
2563         }
2564         if ( !allHex && !theHexas.count( curHex ))
2565           break;
2566
2567         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2568           theFacets.insert( make_pair( curHex, curFacet ));
2569         if ( !facetIt2isNew.second )
2570           break;
2571
2572         // remember not-to-split facets in facetsToCheck
2573         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2574         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2575         {
2576           if ( iF == curFacet && iF == oppFacet )
2577             continue;
2578           TVolumeFaceKey facetKey ( vTool, iF );
2579           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2580           pair< TFacetMap::iterator, bool > it2isnew =
2581             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2582           if ( !it2isnew.second )
2583             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2584         }
2585         // pass to a volume adjacent via oppFacet
2586         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2587         {
2588           curHex = 0;
2589         }
2590         else
2591         {
2592           // get a new curFacet
2593           vTool.GetFaceNodes( oppFacet, facetNodes );
2594           vTool.Set( curHex );
2595           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2596         }
2597       }
2598     } // move in two directions from startHex via facetID
2599
2600     // Find a new startHex by facetsToCheck
2601
2602     startHex = 0;
2603     facetID  = -1;
2604     TFacetMap::iterator fIt = facetsToCheck.begin();
2605     while ( !startHex && fIt != facetsToCheck.end() )
2606     {
2607       const TElemFacets&  elemFacets = fIt->second;
2608       const SMDS_MeshElement*    hex = elemFacets.first->first;
2609       int                 splitFacet = elemFacets.first->second;
2610       int               lateralFacet = elemFacets.second;
2611       facetsToCheck.erase( fIt );
2612       fIt = facetsToCheck.begin();
2613
2614       vTool.Set( hex );
2615       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2616            curHex->GetGeomType() != SMDSGeom_HEXA )
2617         continue;
2618       if ( !allHex && !theHexas.count( curHex ))
2619         continue;
2620
2621       startHex = curHex;
2622
2623       // find a facet of startHex to split
2624
2625       set<const SMDS_MeshNode*> lateralNodes;
2626       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2627       vTool.GetFaceNodes( splitFacet,   facetNodes );
2628       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2629       vTool.Set( startHex );
2630       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2631
2632       // look for a facet of startHex having common nodes with facetNodes
2633       // but not lateralFacet
2634       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2635       {
2636         if ( iF == lateralFacet )
2637           continue;
2638         int nbCommonNodes = 0;
2639         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2640         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2641           nbCommonNodes += facetNodes.count( nn[ iN ]);
2642
2643         if ( nbCommonNodes >= 2 )
2644         {
2645           facetID = iF;
2646           break;
2647         }
2648       }
2649       if ( facetID < 0 )
2650         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2651     }
2652   } //   while ( startHex )
2653
2654   return;
2655 }
2656
2657 namespace
2658 {
2659   //================================================================================
2660   /*!
2661    * \brief Selects nodes of several elements according to a given interlace
2662    *  \param [in] srcNodes - nodes to select from
2663    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2664    *  \param [in] interlace - indices of nodes for all elements
2665    *  \param [in] nbElems - nb of elements
2666    *  \param [in] nbNodes - nb of nodes in each element
2667    *  \param [in] mesh - the mesh
2668    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2669    *  \param [in] type - type of elements to look for
2670    */
2671   //================================================================================
2672
2673   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2674                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2675                     const int*                            interlace,
2676                     const int                             nbElems,
2677                     const int                             nbNodes,
2678                     SMESHDS_Mesh*                         mesh = 0,
2679                     list< const SMDS_MeshElement* >*      elemQueue=0,
2680                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2681   {
2682     for ( int iE = 0; iE < nbElems; ++iE )
2683     {
2684       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2685       const int*                         select = & interlace[iE*nbNodes];
2686       elemNodes.resize( nbNodes );
2687       for ( int iN = 0; iN < nbNodes; ++iN )
2688         elemNodes[iN] = srcNodes[ select[ iN ]];
2689     }
2690     const SMDS_MeshElement* e;
2691     if ( elemQueue )
2692       for ( int iE = 0; iE < nbElems; ++iE )
2693         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2694           elemQueue->push_back( e );
2695   }
2696 }
2697
2698 //=======================================================================
2699 /*
2700  * Split bi-quadratic elements into linear ones without creation of additional nodes
2701  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2702  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2703  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2704  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2705  *   will be split in order to keep the mesh conformal.
2706  *  \param elems - elements to split
2707  */
2708 //=======================================================================
2709
2710 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2711 {
2712   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2713   vector<const SMDS_MeshElement* > splitElems;
2714   list< const SMDS_MeshElement* > elemQueue;
2715   list< const SMDS_MeshElement* >::iterator elemIt;
2716
2717   SMESHDS_Mesh * mesh = GetMeshDS();
2718   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2719   int nbElems, nbNodes;
2720
2721   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2722   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2723   {
2724     elemQueue.clear();
2725     elemQueue.push_back( *elemSetIt );
2726     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2727     {
2728       const SMDS_MeshElement* elem = *elemIt;
2729       switch( elem->GetEntityType() )
2730       {
2731       case SMDSEntity_TriQuad_Hexa: // HEX27
2732       {
2733         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2734         nbElems  = nbNodes = 8;
2735         elemType = & hexaType;
2736
2737         // get nodes for new elements
2738         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2739                                  { 1,9,20,8,    17,22,26,21 },
2740                                  { 2,10,20,9,   18,23,26,22 },
2741                                  { 3,11,20,10,  19,24,26,23 },
2742                                  { 16,21,26,24, 4,12,25,15  },
2743                                  { 17,22,26,21, 5,13,25,12  },
2744                                  { 18,23,26,22, 6,14,25,13  },
2745                                  { 19,24,26,23, 7,15,25,14  }};
2746         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2747
2748         // add boundary faces to elemQueue
2749         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2750                                  { 4,5,6,7, 12,13,14,15, 25 },
2751                                  { 0,1,5,4, 8,17,12,16,  21 },
2752                                  { 1,2,6,5, 9,18,13,17,  22 },
2753                                  { 2,3,7,6, 10,19,14,18, 23 },
2754                                  { 3,0,4,7, 11,16,15,19, 24 }};
2755         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2756
2757         // add boundary segments to elemQueue
2758         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2759                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2760                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2761         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2762         break;
2763       }
2764       case SMDSEntity_BiQuad_Triangle: // TRIA7
2765       {
2766         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2767         nbElems = 3;
2768         nbNodes = 4;
2769         elemType = & quadType;
2770
2771         // get nodes for new elements
2772         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2773         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2774
2775         // add boundary segments to elemQueue
2776         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2777         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2778         break;
2779       }
2780       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2781       {
2782         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2783         nbElems = 4;
2784         nbNodes = 4;
2785         elemType = & quadType;
2786
2787         // get nodes for new elements
2788         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2789         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2790
2791         // add boundary segments to elemQueue
2792         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2793         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2794         break;
2795       }
2796       case SMDSEntity_Quad_Edge:
2797       {
2798         if ( elemIt == elemQueue.begin() )
2799           continue; // an elem is in theElems
2800         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2801         nbElems = 2;
2802         nbNodes = 2;
2803         elemType = & segType;
2804
2805         // get nodes for new elements
2806         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2807         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2808         break;
2809       }
2810       default: continue;
2811       } // switch( elem->GetEntityType() )
2812
2813       // Create new elements
2814
2815       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2816
2817       splitElems.clear();
2818
2819       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2820       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2821       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2822       //elemType->SetID( -1 );
2823
2824       for ( int iE = 0; iE < nbElems; ++iE )
2825         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2826
2827
2828       ReplaceElemInGroups( elem, splitElems, mesh );
2829
2830       if ( subMesh )
2831         for ( size_t i = 0; i < splitElems.size(); ++i )
2832           subMesh->AddElement( splitElems[i] );
2833     }
2834   }
2835 }
2836
2837 //=======================================================================
2838 //function : AddToSameGroups
2839 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2840 //=======================================================================
2841
2842 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2843                                         const SMDS_MeshElement* elemInGroups,
2844                                         SMESHDS_Mesh *          aMesh)
2845 {
2846   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2847   if (!groups.empty()) {
2848     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2849     for ( ; grIt != groups.end(); grIt++ ) {
2850       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2851       if ( group && group->Contains( elemInGroups ))
2852         group->SMDSGroup().Add( elemToAdd );
2853     }
2854   }
2855 }
2856
2857
2858 //=======================================================================
2859 //function : RemoveElemFromGroups
2860 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2861 //=======================================================================
2862 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2863                                              SMESHDS_Mesh *          aMesh)
2864 {
2865   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2866   if (!groups.empty())
2867   {
2868     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2869     for (; GrIt != groups.end(); GrIt++)
2870     {
2871       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2872       if (!grp || grp->IsEmpty()) continue;
2873       grp->SMDSGroup().Remove(removeelem);
2874     }
2875   }
2876 }
2877
2878 //================================================================================
2879 /*!
2880  * \brief Replace elemToRm by elemToAdd in the all groups
2881  */
2882 //================================================================================
2883
2884 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2885                                             const SMDS_MeshElement* elemToAdd,
2886                                             SMESHDS_Mesh *          aMesh)
2887 {
2888   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2889   if (!groups.empty()) {
2890     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2891     for ( ; grIt != groups.end(); grIt++ ) {
2892       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2893       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2894         group->SMDSGroup().Add( elemToAdd );
2895     }
2896   }
2897 }
2898
2899 //================================================================================
2900 /*!
2901  * \brief Replace elemToRm by elemToAdd in the all groups
2902  */
2903 //================================================================================
2904
2905 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2906                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2907                                             SMESHDS_Mesh *                         aMesh)
2908 {
2909   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2910   if (!groups.empty())
2911   {
2912     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2913     for ( ; grIt != groups.end(); grIt++ ) {
2914       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2915       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2916         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2917           group->SMDSGroup().Add( elemToAdd[ i ] );
2918     }
2919   }
2920 }
2921
2922 //=======================================================================
2923 //function : QuadToTri
2924 //purpose  : Cut quadrangles into triangles.
2925 //           theCrit is used to select a diagonal to cut
2926 //=======================================================================
2927
2928 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2929                                   const bool         the13Diag)
2930 {
2931   myLastCreatedElems.Clear();
2932   myLastCreatedNodes.Clear();
2933
2934   MESSAGE( "::QuadToTri()" );
2935
2936   SMESHDS_Mesh * aMesh = GetMeshDS();
2937
2938   Handle(Geom_Surface) surface;
2939   SMESH_MesherHelper   helper( *GetMesh() );
2940
2941   TIDSortedElemSet::iterator itElem;
2942   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2943   {
2944     const SMDS_MeshElement* elem = *itElem;
2945     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2946       continue;
2947
2948     if ( elem->NbNodes() == 4 ) {
2949       // retrieve element nodes
2950       const SMDS_MeshNode* aNodes [4];
2951       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2952       int i = 0;
2953       while ( itN->more() )
2954         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2955
2956       int aShapeId = FindShape( elem );
2957       const SMDS_MeshElement* newElem1 = 0;
2958       const SMDS_MeshElement* newElem2 = 0;
2959       if ( the13Diag ) {
2960         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2961         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2962       }
2963       else {
2964         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2965         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2966       }
2967       myLastCreatedElems.Append(newElem1);
2968       myLastCreatedElems.Append(newElem2);
2969       // put a new triangle on the same shape and add to the same groups
2970       if ( aShapeId )
2971       {
2972         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2973         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2974       }
2975       AddToSameGroups( newElem1, elem, aMesh );
2976       AddToSameGroups( newElem2, elem, aMesh );
2977       aMesh->RemoveElement( elem );
2978     }
2979
2980     // Quadratic quadrangle
2981
2982     else if ( elem->NbNodes() == 8 )
2983     {
2984       // get surface elem is on
2985       int aShapeId = FindShape( elem );
2986       if ( aShapeId != helper.GetSubShapeID() ) {
2987         surface.Nullify();
2988         TopoDS_Shape shape;
2989         if ( aShapeId > 0 )
2990           shape = aMesh->IndexToShape( aShapeId );
2991         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2992           TopoDS_Face face = TopoDS::Face( shape );
2993           surface = BRep_Tool::Surface( face );
2994           if ( !surface.IsNull() )
2995             helper.SetSubShape( shape );
2996         }
2997       }
2998
2999       const SMDS_MeshNode* aNodes [8];
3000       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3001       for ( int i = 0; itN->more(); ++i )
3002         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3003
3004       const SMDS_MeshNode* inFaceNode = 0;
3005       if ( helper.GetNodeUVneedInFaceNode() )
3006         for ( int i = 0; i < 8 &&  !inFaceNode; ++i )
3007           if ( aNodes[ i ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
3008             inFaceNode = aNodes[ i ];
3009
3010       // find middle point for (0,1,2,3)
3011       // and create a node in this point;
3012       gp_XYZ p( 0,0,0 );
3013       if ( surface.IsNull() ) {
3014         for ( int i = 0; i < 4; i++ ) p += SMESH_TNodeXYZ( aNodes[i] );
3015         p /= 4;
3016       }
3017       else {
3018         TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
3019         gp_XY uv( 0,0 );
3020         for ( int i = 0; i < 4; i++ )
3021           uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
3022         uv /= 4.;
3023         p = surface->Value( uv.X(), uv.Y() ).XYZ();
3024       }
3025       const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
3026       myLastCreatedNodes.Append(newN);
3027
3028       // create a new element
3029       const SMDS_MeshElement* newElem1 = 0;
3030       const SMDS_MeshElement* newElem2 = 0;
3031       if ( the13Diag ) {
3032         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3033                                   aNodes[6], aNodes[7], newN );
3034         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3035                                   newN,      aNodes[4], aNodes[5] );
3036       }
3037       else {
3038         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3039                                   aNodes[7], aNodes[4], newN );
3040         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3041                                   newN,      aNodes[5], aNodes[6] );
3042       }
3043       myLastCreatedElems.Append(newElem1);
3044       myLastCreatedElems.Append(newElem2);
3045       // put a new triangle on the same shape and add to the same groups
3046       if ( aShapeId )
3047       {
3048         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3049         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3050       }
3051       AddToSameGroups( newElem1, elem, aMesh );
3052       AddToSameGroups( newElem2, elem, aMesh );
3053       aMesh->RemoveElement( elem );
3054     }
3055   }
3056
3057   return true;
3058 }
3059
3060 //=======================================================================
3061 //function : getAngle
3062 //purpose  :
3063 //=======================================================================
3064
3065 double getAngle(const SMDS_MeshElement * tr1,
3066                 const SMDS_MeshElement * tr2,
3067                 const SMDS_MeshNode *    n1,
3068                 const SMDS_MeshNode *    n2)
3069 {
3070   double angle = 2. * M_PI; // bad angle
3071
3072   // get normals
3073   SMESH::Controls::TSequenceOfXYZ P1, P2;
3074   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3075        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3076     return angle;
3077   gp_Vec N1,N2;
3078   if(!tr1->IsQuadratic())
3079     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3080   else
3081     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3082   if ( N1.SquareMagnitude() <= gp::Resolution() )
3083     return angle;
3084   if(!tr2->IsQuadratic())
3085     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3086   else
3087     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3088   if ( N2.SquareMagnitude() <= gp::Resolution() )
3089     return angle;
3090
3091   // find the first diagonal node n1 in the triangles:
3092   // take in account a diagonal link orientation
3093   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3094   for ( int t = 0; t < 2; t++ ) {
3095     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3096     int i = 0, iDiag = -1;
3097     while ( it->more()) {
3098       const SMDS_MeshElement *n = it->next();
3099       if ( n == n1 || n == n2 ) {
3100         if ( iDiag < 0)
3101           iDiag = i;
3102         else {
3103           if ( i - iDiag == 1 )
3104             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3105           else
3106             nFirst[ t ] = n;
3107           break;
3108         }
3109       }
3110       i++;
3111     }
3112   }
3113   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3114     N2.Reverse();
3115
3116   angle = N1.Angle( N2 );
3117   //SCRUTE( angle );
3118   return angle;
3119 }
3120
3121 // =================================================
3122 // class generating a unique ID for a pair of nodes
3123 // and able to return nodes by that ID
3124 // =================================================
3125 class LinkID_Gen {
3126 public:
3127
3128   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3129     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3130   {}
3131
3132   long GetLinkID (const SMDS_MeshNode * n1,
3133                   const SMDS_MeshNode * n2) const
3134   {
3135     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3136   }
3137
3138   bool GetNodes (const long             theLinkID,
3139                  const SMDS_MeshNode* & theNode1,
3140                  const SMDS_MeshNode* & theNode2) const
3141   {
3142     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3143     if ( !theNode1 ) return false;
3144     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3145     if ( !theNode2 ) return false;
3146     return true;
3147   }
3148
3149 private:
3150   LinkID_Gen();
3151   const SMESHDS_Mesh* myMesh;
3152   long                myMaxID;
3153 };
3154
3155
3156 //=======================================================================
3157 //function : TriToQuad
3158 //purpose  : Fuse neighbour triangles into quadrangles.
3159 //           theCrit is used to select a neighbour to fuse with.
3160 //           theMaxAngle is a max angle between element normals at which
3161 //           fusion is still performed.
3162 //=======================================================================
3163
3164 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3165                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3166                                   const double                         theMaxAngle)
3167 {
3168   myLastCreatedElems.Clear();
3169   myLastCreatedNodes.Clear();
3170
3171   MESSAGE( "::TriToQuad()" );
3172
3173   if ( !theCrit.get() )
3174     return false;
3175
3176   SMESHDS_Mesh * aMesh = GetMeshDS();
3177
3178   // Prepare data for algo: build
3179   // 1. map of elements with their linkIDs
3180   // 2. map of linkIDs with their elements
3181
3182   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3183   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3184   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3185   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3186
3187   TIDSortedElemSet::iterator itElem;
3188   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3189   {
3190     const SMDS_MeshElement* elem = *itElem;
3191     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3192     bool IsTria = ( elem->NbCornerNodes()==3 );
3193     if (!IsTria) continue;
3194
3195     // retrieve element nodes
3196     const SMDS_MeshNode* aNodes [4];
3197     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3198     int i = 0;
3199     while ( i < 3 )
3200       aNodes[ i++ ] = itN->next();
3201     aNodes[ 3 ] = aNodes[ 0 ];
3202
3203     // fill maps
3204     for ( i = 0; i < 3; i++ ) {
3205       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3206       // check if elements sharing a link can be fused
3207       itLE = mapLi_listEl.find( link );
3208       if ( itLE != mapLi_listEl.end() ) {
3209         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3210           continue;
3211         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3212         //if ( FindShape( elem ) != FindShape( elem2 ))
3213         //  continue; // do not fuse triangles laying on different shapes
3214         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3215           continue; // avoid making badly shaped quads
3216         (*itLE).second.push_back( elem );
3217       }
3218       else {
3219         mapLi_listEl[ link ].push_back( elem );
3220       }
3221       mapEl_setLi [ elem ].insert( link );
3222     }
3223   }
3224   // Clean the maps from the links shared by a sole element, ie
3225   // links to which only one element is bound in mapLi_listEl
3226
3227   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3228     int nbElems = (*itLE).second.size();
3229     if ( nbElems < 2  ) {
3230       const SMDS_MeshElement* elem = (*itLE).second.front();
3231       SMESH_TLink link = (*itLE).first;
3232       mapEl_setLi[ elem ].erase( link );
3233       if ( mapEl_setLi[ elem ].empty() )
3234         mapEl_setLi.erase( elem );
3235     }
3236   }
3237
3238   // Algo: fuse triangles into quadrangles
3239
3240   while ( ! mapEl_setLi.empty() ) {
3241     // Look for the start element:
3242     // the element having the least nb of shared links
3243     const SMDS_MeshElement* startElem = 0;
3244     int minNbLinks = 4;
3245     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3246       int nbLinks = (*itEL).second.size();
3247       if ( nbLinks < minNbLinks ) {
3248         startElem = (*itEL).first;
3249         minNbLinks = nbLinks;
3250         if ( minNbLinks == 1 )
3251           break;
3252       }
3253     }
3254
3255     // search elements to fuse starting from startElem or links of elements
3256     // fused earlyer - startLinks
3257     list< SMESH_TLink > startLinks;
3258     while ( startElem || !startLinks.empty() ) {
3259       while ( !startElem && !startLinks.empty() ) {
3260         // Get an element to start, by a link
3261         SMESH_TLink linkId = startLinks.front();
3262         startLinks.pop_front();
3263         itLE = mapLi_listEl.find( linkId );
3264         if ( itLE != mapLi_listEl.end() ) {
3265           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3266           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3267           for ( ; itE != listElem.end() ; itE++ )
3268             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3269               startElem = (*itE);
3270           mapLi_listEl.erase( itLE );
3271         }
3272       }
3273
3274       if ( startElem ) {
3275         // Get candidates to be fused
3276         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3277         const SMESH_TLink *link12, *link13;
3278         startElem = 0;
3279         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3280         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3281         ASSERT( !setLi.empty() );
3282         set< SMESH_TLink >::iterator itLi;
3283         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3284         {
3285           const SMESH_TLink & link = (*itLi);
3286           itLE = mapLi_listEl.find( link );
3287           if ( itLE == mapLi_listEl.end() )
3288             continue;
3289
3290           const SMDS_MeshElement* elem = (*itLE).second.front();
3291           if ( elem == tr1 )
3292             elem = (*itLE).second.back();
3293           mapLi_listEl.erase( itLE );
3294           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3295             continue;
3296           if ( tr2 ) {
3297             tr3 = elem;
3298             link13 = &link;
3299           }
3300           else {
3301             tr2 = elem;
3302             link12 = &link;
3303           }
3304
3305           // add other links of elem to list of links to re-start from
3306           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3307           set< SMESH_TLink >::iterator it;
3308           for ( it = links.begin(); it != links.end(); it++ ) {
3309             const SMESH_TLink& link2 = (*it);
3310             if ( link2 != link )
3311               startLinks.push_back( link2 );
3312           }
3313         }
3314
3315         // Get nodes of possible quadrangles
3316         const SMDS_MeshNode *n12 [4], *n13 [4];
3317         bool Ok12 = false, Ok13 = false;
3318         const SMDS_MeshNode *linkNode1, *linkNode2;
3319         if(tr2) {
3320           linkNode1 = link12->first;
3321           linkNode2 = link12->second;
3322           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3323             Ok12 = true;
3324         }
3325         if(tr3) {
3326           linkNode1 = link13->first;
3327           linkNode2 = link13->second;
3328           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3329             Ok13 = true;
3330         }
3331
3332         // Choose a pair to fuse
3333         if ( Ok12 && Ok13 ) {
3334           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3335           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3336           double aBadRate12 = getBadRate( &quad12, theCrit );
3337           double aBadRate13 = getBadRate( &quad13, theCrit );
3338           if (  aBadRate13 < aBadRate12 )
3339             Ok12 = false;
3340           else
3341             Ok13 = false;
3342         }
3343
3344         // Make quadrangles
3345         // and remove fused elems and remove links from the maps
3346         mapEl_setLi.erase( tr1 );
3347         if ( Ok12 )
3348         {
3349           mapEl_setLi.erase( tr2 );
3350           mapLi_listEl.erase( *link12 );
3351           if ( tr1->NbNodes() == 3 )
3352           {
3353             const SMDS_MeshElement* newElem = 0;
3354             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3355             myLastCreatedElems.Append(newElem);
3356             AddToSameGroups( newElem, tr1, aMesh );
3357             int aShapeId = tr1->getshapeId();
3358             if ( aShapeId )
3359               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3360             aMesh->RemoveElement( tr1 );
3361             aMesh->RemoveElement( tr2 );
3362           }
3363           else {
3364             vector< const SMDS_MeshNode* > N1;
3365             vector< const SMDS_MeshNode* > N2;
3366             getNodesFromTwoTria(tr1,tr2,N1,N2);
3367             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3368             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3369             // i.e. first nodes from both arrays form a new diagonal
3370             const SMDS_MeshNode* aNodes[8];
3371             aNodes[0] = N1[0];
3372             aNodes[1] = N1[1];
3373             aNodes[2] = N2[0];
3374             aNodes[3] = N2[1];
3375             aNodes[4] = N1[3];
3376             aNodes[5] = N2[5];
3377             aNodes[6] = N2[3];
3378             aNodes[7] = N1[5];
3379             const SMDS_MeshElement* newElem = 0;
3380             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3381               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3382                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3383             else
3384               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3385                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3386             myLastCreatedElems.Append(newElem);
3387             AddToSameGroups( newElem, tr1, aMesh );
3388             int aShapeId = tr1->getshapeId();
3389             if ( aShapeId )
3390               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3391             aMesh->RemoveElement( tr1 );
3392             aMesh->RemoveElement( tr2 );
3393             // remove middle node (9)
3394             if ( N1[4]->NbInverseElements() == 0 )
3395               aMesh->RemoveNode( N1[4] );
3396             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3397               aMesh->RemoveNode( N1[6] );
3398             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3399               aMesh->RemoveNode( N2[6] );
3400           }
3401         }
3402         else if ( Ok13 )
3403         {
3404           mapEl_setLi.erase( tr3 );
3405           mapLi_listEl.erase( *link13 );
3406           if ( tr1->NbNodes() == 3 ) {
3407             const SMDS_MeshElement* newElem = 0;
3408             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3409             myLastCreatedElems.Append(newElem);
3410             AddToSameGroups( newElem, tr1, aMesh );
3411             int aShapeId = tr1->getshapeId();
3412             if ( aShapeId )
3413               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3414             aMesh->RemoveElement( tr1 );
3415             aMesh->RemoveElement( tr3 );
3416           }
3417           else {
3418             vector< const SMDS_MeshNode* > N1;
3419             vector< const SMDS_MeshNode* > N2;
3420             getNodesFromTwoTria(tr1,tr3,N1,N2);
3421             // now we receive following N1 and N2 (using numeration as above image)
3422             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3423             // i.e. first nodes from both arrays form a new diagonal
3424             const SMDS_MeshNode* aNodes[8];
3425             aNodes[0] = N1[0];
3426             aNodes[1] = N1[1];
3427             aNodes[2] = N2[0];
3428             aNodes[3] = N2[1];
3429             aNodes[4] = N1[3];
3430             aNodes[5] = N2[5];
3431             aNodes[6] = N2[3];
3432             aNodes[7] = N1[5];
3433             const SMDS_MeshElement* newElem = 0;
3434             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3435               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3436                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3437             else
3438               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3439                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3440             myLastCreatedElems.Append(newElem);
3441             AddToSameGroups( newElem, tr1, aMesh );
3442             int aShapeId = tr1->getshapeId();
3443             if ( aShapeId )
3444               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3445             aMesh->RemoveElement( tr1 );
3446             aMesh->RemoveElement( tr3 );
3447             // remove middle node (9)
3448             if ( N1[4]->NbInverseElements() == 0 )
3449               aMesh->RemoveNode( N1[4] );
3450             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3451               aMesh->RemoveNode( N1[6] );
3452             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3453               aMesh->RemoveNode( N2[6] );
3454           }
3455         }
3456
3457         // Next element to fuse: the rejected one
3458         if ( tr3 )
3459           startElem = Ok12 ? tr3 : tr2;
3460
3461       } // if ( startElem )
3462     } // while ( startElem || !startLinks.empty() )
3463   } // while ( ! mapEl_setLi.empty() )
3464
3465   return true;
3466 }
3467
3468
3469 /*#define DUMPSO(txt) \
3470 //  cout << txt << endl;
3471 //=============================================================================
3472 //
3473 //
3474 //
3475 //=============================================================================
3476 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3477 {
3478 if ( i1 == i2 )
3479 return;
3480 int tmp = idNodes[ i1 ];
3481 idNodes[ i1 ] = idNodes[ i2 ];
3482 idNodes[ i2 ] = tmp;
3483 gp_Pnt Ptmp = P[ i1 ];
3484 P[ i1 ] = P[ i2 ];
3485 P[ i2 ] = Ptmp;
3486 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3487 }
3488
3489 //=======================================================================
3490 //function : SortQuadNodes
3491 //purpose  : Set 4 nodes of a quadrangle face in a good order.
3492 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
3493 //           1 or 2 else 0.
3494 //=======================================================================
3495
3496 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3497 int               idNodes[] )
3498 {
3499   gp_Pnt P[4];
3500   int i;
3501   for ( i = 0; i < 4; i++ ) {
3502     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3503     if ( !n ) return 0;
3504     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3505   }
3506
3507   gp_Vec V1(P[0], P[1]);
3508   gp_Vec V2(P[0], P[2]);
3509   gp_Vec V3(P[0], P[3]);
3510
3511   gp_Vec Cross1 = V1 ^ V2;
3512   gp_Vec Cross2 = V2 ^ V3;
3513
3514   i = 0;
3515   if (Cross1.Dot(Cross2) < 0)
3516   {
3517     Cross1 = V2 ^ V1;
3518     Cross2 = V1 ^ V3;
3519
3520     if (Cross1.Dot(Cross2) < 0)
3521       i = 2;
3522     else
3523       i = 1;
3524     swap ( i, i + 1, idNodes, P );
3525
3526     //     for ( int ii = 0; ii < 4; ii++ ) {
3527     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3528     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3529     //     }
3530   }
3531   return i;
3532 }
3533
3534 //=======================================================================
3535 //function : SortHexaNodes
3536 //purpose  : Set 8 nodes of a hexahedron in a good order.
3537 //           Return success status
3538 //=======================================================================
3539
3540 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3541                                       int               idNodes[] )
3542 {
3543   gp_Pnt P[8];
3544   int i;
3545   DUMPSO( "INPUT: ========================================");
3546   for ( i = 0; i < 8; i++ ) {
3547     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3548     if ( !n ) return false;
3549     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3550     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3551   }
3552   DUMPSO( "========================================");
3553
3554
3555   set<int> faceNodes;  // ids of bottom face nodes, to be found
3556   set<int> checkedId1; // ids of tried 2-nd nodes
3557   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3558   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
3559   int iMin, iLoop1 = 0;
3560
3561   // Loop to try the 2-nd nodes
3562
3563   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3564   {
3565     // Find not checked 2-nd node
3566     for ( i = 1; i < 8; i++ )
3567       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3568         int id1 = idNodes[i];
3569         swap ( 1, i, idNodes, P );
3570         checkedId1.insert ( id1 );
3571         break;
3572       }
3573
3574     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3575     // ie that all but meybe one (id3 which is on the same face) nodes
3576     // lay on the same side from the triangle plane.
3577
3578     bool manyInPlane = false; // more than 4 nodes lay in plane
3579     int iLoop2 = 0;
3580     while ( ++iLoop2 < 6 ) {
3581
3582       // get 1-2-3 plane coeffs
3583       Standard_Real A, B, C, D;
3584       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3585       if ( N.SquareMagnitude() > gp::Resolution() )
3586       {
3587         gp_Pln pln ( P[0], N );
3588         pln.Coefficients( A, B, C, D );
3589
3590         // find the node (iMin) closest to pln
3591         Standard_Real dist[ 8 ], minDist = DBL_MAX;
3592         set<int> idInPln;
3593         for ( i = 3; i < 8; i++ ) {
3594           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3595           if ( fabs( dist[i] ) < minDist ) {
3596             minDist = fabs( dist[i] );
3597             iMin = i;
3598           }
3599           if ( fabs( dist[i] ) <= tol )
3600             idInPln.insert( idNodes[i] );
3601         }
3602
3603         // there should not be more than 4 nodes in bottom plane
3604         if ( idInPln.size() > 1 )
3605         {
3606           DUMPSO( "### idInPln.size() = " << idInPln.size());
3607           // idInPlane does not contain the first 3 nodes
3608           if ( manyInPlane || idInPln.size() == 5)
3609             return false; // all nodes in one plane
3610           manyInPlane = true;
3611
3612           // set the 1-st node to be not in plane
3613           for ( i = 3; i < 8; i++ ) {
3614             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3615               DUMPSO( "### Reset 0-th node");
3616               swap( 0, i, idNodes, P );
3617               break;
3618             }
3619           }
3620
3621           // reset to re-check second nodes
3622           leastDist = DBL_MAX;
3623           faceNodes.clear();
3624           checkedId1.clear();
3625           iLoop1 = 0;
3626           break; // from iLoop2;
3627         }
3628
3629         // check that the other 4 nodes are on the same side
3630         bool sameSide = true;
3631         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3632         for ( i = 3; sameSide && i < 8; i++ ) {
3633           if ( i != iMin )
3634             sameSide = ( isNeg == dist[i] <= 0.);
3635         }
3636
3637         // keep best solution
3638         if ( sameSide && minDist < leastDist ) {
3639           leastDist = minDist;
3640           faceNodes.clear();
3641           faceNodes.insert( idNodes[ 1 ] );
3642           faceNodes.insert( idNodes[ 2 ] );
3643           faceNodes.insert( idNodes[ iMin ] );
3644           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3645                   << " leastDist = " << leastDist);
3646           if ( leastDist <= DBL_MIN )
3647             break;
3648         }
3649       }
3650
3651       // set next 3-d node to check
3652       int iNext = 2 + iLoop2;
3653       if ( iNext < 8 ) {
3654         DUMPSO( "Try 2-nd");
3655         swap ( 2, iNext, idNodes, P );
3656       }
3657     } // while ( iLoop2 < 6 )
3658   } // iLoop1
3659
3660   if ( faceNodes.empty() ) return false;
3661
3662   // Put the faceNodes in proper places
3663   for ( i = 4; i < 8; i++ ) {
3664     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3665       // find a place to put
3666       int iTo = 1;
3667       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3668         iTo++;
3669       DUMPSO( "Set faceNodes");
3670       swap ( iTo, i, idNodes, P );
3671     }
3672   }
3673
3674
3675   // Set nodes of the found bottom face in good order
3676   DUMPSO( " Found bottom face: ");
3677   i = SortQuadNodes( theMesh, idNodes );
3678   if ( i ) {
3679     gp_Pnt Ptmp = P[ i ];
3680     P[ i ] = P[ i+1 ];
3681     P[ i+1 ] = Ptmp;
3682   }
3683   //   else
3684   //     for ( int ii = 0; ii < 4; ii++ ) {
3685   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3686   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3687   //    }
3688
3689   // Gravity center of the top and bottom faces
3690   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3691   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3692
3693   // Get direction from the bottom to the top face
3694   gp_Vec upDir ( aGCb, aGCt );
3695   Standard_Real upDirSize = upDir.Magnitude();
3696   if ( upDirSize <= gp::Resolution() ) return false;
3697   upDir / upDirSize;
3698
3699   // Assure that the bottom face normal points up
3700   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3701   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3702   if ( Nb.Dot( upDir ) < 0 ) {
3703     DUMPSO( "Reverse bottom face");
3704     swap( 1, 3, idNodes, P );
3705   }
3706
3707   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3708   Standard_Real minDist = DBL_MAX;
3709   for ( i = 4; i < 8; i++ ) {
3710     // projection of P[i] to the plane defined by P[0] and upDir
3711     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3712     Standard_Real sqDist = P[0].SquareDistance( Pp );
3713     if ( sqDist < minDist ) {
3714       minDist = sqDist;
3715       iMin = i;
3716     }
3717   }
3718   DUMPSO( "Set 4-th");
3719   swap ( 4, iMin, idNodes, P );
3720
3721   // Set nodes of the top face in good order
3722   DUMPSO( "Sort top face");
3723   i = SortQuadNodes( theMesh, &idNodes[4] );
3724   if ( i ) {
3725     i += 4;
3726     gp_Pnt Ptmp = P[ i ];
3727     P[ i ] = P[ i+1 ];
3728     P[ i+1 ] = Ptmp;
3729   }
3730
3731   // Assure that direction of the top face normal is from the bottom face
3732   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3733   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3734   if ( Nt.Dot( upDir ) < 0 ) {
3735     DUMPSO( "Reverse top face");
3736     swap( 5, 7, idNodes, P );
3737   }
3738
3739   //   DUMPSO( "OUTPUT: ========================================");
3740   //   for ( i = 0; i < 8; i++ ) {
3741   //     float *p = ugrid->GetPoint(idNodes[i]);
3742   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3743   //   }
3744
3745   return true;
3746 }*/
3747
3748 //================================================================================
3749 /*!
3750  * \brief Return nodes linked to the given one
3751  * \param theNode - the node
3752  * \param linkedNodes - the found nodes
3753  * \param type - the type of elements to check
3754  *
3755  * Medium nodes are ignored
3756  */
3757 //================================================================================
3758
3759 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3760                                        TIDSortedElemSet &   linkedNodes,
3761                                        SMDSAbs_ElementType  type )
3762 {
3763   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3764   while ( elemIt->more() )
3765   {
3766     const SMDS_MeshElement* elem = elemIt->next();
3767     if(elem->GetType() == SMDSAbs_0DElement)
3768       continue;
3769
3770     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3771     if ( elem->GetType() == SMDSAbs_Volume )
3772     {
3773       SMDS_VolumeTool vol( elem );
3774       while ( nodeIt->more() ) {
3775         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3776         if ( theNode != n && vol.IsLinked( theNode, n ))
3777           linkedNodes.insert( n );
3778       }
3779     }
3780     else
3781     {
3782       for ( int i = 0; nodeIt->more(); ++i ) {
3783         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3784         if ( n == theNode ) {
3785           int iBefore = i - 1;
3786           int iAfter  = i + 1;
3787           if ( elem->IsQuadratic() ) {
3788             int nb = elem->NbNodes() / 2;
3789             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3790             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3791           }
3792           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3793           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3794         }
3795       }
3796     }
3797   }
3798 }
3799
3800 //=======================================================================
3801 //function : laplacianSmooth
3802 //purpose  : pulls theNode toward the center of surrounding nodes directly
3803 //           connected to that node along an element edge
3804 //=======================================================================
3805
3806 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3807                      const Handle(Geom_Surface)&          theSurface,
3808                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3809 {
3810   // find surrounding nodes
3811
3812   TIDSortedElemSet nodeSet;
3813   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3814
3815   // compute new coodrs
3816
3817   double coord[] = { 0., 0., 0. };
3818   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3819   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3820     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3821     if ( theSurface.IsNull() ) { // smooth in 3D
3822       coord[0] += node->X();
3823       coord[1] += node->Y();
3824       coord[2] += node->Z();
3825     }
3826     else { // smooth in 2D
3827       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3828       gp_XY* uv = theUVMap[ node ];
3829       coord[0] += uv->X();
3830       coord[1] += uv->Y();
3831     }
3832   }
3833   int nbNodes = nodeSet.size();
3834   if ( !nbNodes )
3835     return;
3836   coord[0] /= nbNodes;
3837   coord[1] /= nbNodes;
3838
3839   if ( !theSurface.IsNull() ) {
3840     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3841     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3842     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3843     coord[0] = p3d.X();
3844     coord[1] = p3d.Y();
3845     coord[2] = p3d.Z();
3846   }
3847   else
3848     coord[2] /= nbNodes;
3849
3850   // move node
3851
3852   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3853 }
3854
3855 //=======================================================================
3856 //function : centroidalSmooth
3857 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3858 //           surrounding elements
3859 //=======================================================================
3860
3861 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3862                       const Handle(Geom_Surface)&          theSurface,
3863                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3864 {
3865   gp_XYZ aNewXYZ(0.,0.,0.);
3866   SMESH::Controls::Area anAreaFunc;
3867   double totalArea = 0.;
3868   int nbElems = 0;
3869
3870   // compute new XYZ
3871
3872   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3873   while ( elemIt->more() )
3874   {
3875     const SMDS_MeshElement* elem = elemIt->next();
3876     nbElems++;
3877
3878     gp_XYZ elemCenter(0.,0.,0.);
3879     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3880     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3881     int nn = elem->NbNodes();
3882     if(elem->IsQuadratic()) nn = nn/2;
3883     int i=0;
3884     //while ( itN->more() ) {
3885     while ( i<nn ) {
3886       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3887       i++;
3888       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3889       aNodePoints.push_back( aP );
3890       if ( !theSurface.IsNull() ) { // smooth in 2D
3891         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3892         gp_XY* uv = theUVMap[ aNode ];
3893         aP.SetCoord( uv->X(), uv->Y(), 0. );
3894       }
3895       elemCenter += aP;
3896     }
3897     double elemArea = anAreaFunc.GetValue( aNodePoints );
3898     totalArea += elemArea;
3899     elemCenter /= nn;
3900     aNewXYZ += elemCenter * elemArea;
3901   }
3902   aNewXYZ /= totalArea;
3903   if ( !theSurface.IsNull() ) {
3904     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3905     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3906   }
3907
3908   // move node
3909
3910   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3911 }
3912
3913 //=======================================================================
3914 //function : getClosestUV
3915 //purpose  : return UV of closest projection
3916 //=======================================================================
3917
3918 static bool getClosestUV (Extrema_GenExtPS& projector,
3919                           const gp_Pnt&     point,
3920                           gp_XY &           result)
3921 {
3922   projector.Perform( point );
3923   if ( projector.IsDone() ) {
3924     double u, v, minVal = DBL_MAX;
3925     for ( int i = projector.NbExt(); i > 0; i-- )
3926       if ( projector.SquareDistance( i ) < minVal ) {
3927         minVal = projector.SquareDistance( i );
3928         projector.Point( i ).Parameter( u, v );
3929       }
3930     result.SetCoord( u, v );
3931     return true;
3932   }
3933   return false;
3934 }
3935
3936 //=======================================================================
3937 //function : Smooth
3938 //purpose  : Smooth theElements during theNbIterations or until a worst
3939 //           element has aspect ratio <= theTgtAspectRatio.
3940 //           Aspect Ratio varies in range [1.0, inf].
3941 //           If theElements is empty, the whole mesh is smoothed.
3942 //           theFixedNodes contains additionally fixed nodes. Nodes built
3943 //           on edges and boundary nodes are always fixed.
3944 //=======================================================================
3945
3946 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3947                                set<const SMDS_MeshNode*> & theFixedNodes,
3948                                const SmoothMethod          theSmoothMethod,
3949                                const int                   theNbIterations,
3950                                double                      theTgtAspectRatio,
3951                                const bool                  the2D)
3952 {
3953   myLastCreatedElems.Clear();
3954   myLastCreatedNodes.Clear();
3955
3956   MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3957
3958   if ( theTgtAspectRatio < 1.0 )
3959     theTgtAspectRatio = 1.0;
3960
3961   const double disttol = 1.e-16;
3962
3963   SMESH::Controls::AspectRatio aQualityFunc;
3964
3965   SMESHDS_Mesh* aMesh = GetMeshDS();
3966
3967   if ( theElems.empty() ) {
3968     // add all faces to theElems
3969     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3970     while ( fIt->more() ) {
3971       const SMDS_MeshElement* face = fIt->next();
3972       theElems.insert( theElems.end(), face );
3973     }
3974   }
3975   // get all face ids theElems are on
3976   set< int > faceIdSet;
3977   TIDSortedElemSet::iterator itElem;
3978   if ( the2D )
3979     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3980       int fId = FindShape( *itElem );
3981       // check that corresponding submesh exists and a shape is face
3982       if (fId &&
3983           faceIdSet.find( fId ) == faceIdSet.end() &&
3984           aMesh->MeshElements( fId )) {
3985         TopoDS_Shape F = aMesh->IndexToShape( fId );
3986         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3987           faceIdSet.insert( fId );
3988       }
3989     }
3990   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3991
3992   // ===============================================
3993   // smooth elements on each TopoDS_Face separately
3994   // ===============================================
3995
3996   SMESH_MesherHelper helper( *GetMesh() );
3997
3998   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3999   for ( ; fId != faceIdSet.rend(); ++fId )
4000   {
4001     // get face surface and submesh
4002     Handle(Geom_Surface) surface;
4003     SMESHDS_SubMesh* faceSubMesh = 0;
4004     TopoDS_Face face;
4005     double fToler2 = 0;
4006     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
4007     bool isUPeriodic = false, isVPeriodic = false;
4008     if ( *fId )
4009     {
4010       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
4011       surface = BRep_Tool::Surface( face );
4012       faceSubMesh = aMesh->MeshElements( *fId );
4013       fToler2 = BRep_Tool::Tolerance( face );
4014       fToler2 *= fToler2 * 10.;
4015       isUPeriodic = surface->IsUPeriodic();
4016       if ( isUPeriodic )
4017         surface->UPeriod();
4018       isVPeriodic = surface->IsVPeriodic();
4019       if ( isVPeriodic )
4020         surface->VPeriod();
4021       surface->Bounds( u1, u2, v1, v2 );
4022       helper.SetSubShape( face );
4023     }
4024     // ---------------------------------------------------------
4025     // for elements on a face, find movable and fixed nodes and
4026     // compute UV for them
4027     // ---------------------------------------------------------
4028     bool checkBoundaryNodes = false;
4029     bool isQuadratic = false;
4030     set<const SMDS_MeshNode*> setMovableNodes;
4031     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4032     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4033     list< const SMDS_MeshElement* > elemsOnFace;
4034
4035     Extrema_GenExtPS projector;
4036     GeomAdaptor_Surface surfAdaptor;
4037     if ( !surface.IsNull() ) {
4038       surfAdaptor.Load( surface );
4039       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4040     }
4041     int nbElemOnFace = 0;
4042     itElem = theElems.begin();
4043     // loop on not yet smoothed elements: look for elems on a face
4044     while ( itElem != theElems.end() )
4045     {
4046       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4047         break; // all elements found
4048
4049       const SMDS_MeshElement* elem = *itElem;
4050       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4051            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4052         ++itElem;
4053         continue;
4054       }
4055       elemsOnFace.push_back( elem );
4056       theElems.erase( itElem++ );
4057       nbElemOnFace++;
4058
4059       if ( !isQuadratic )
4060         isQuadratic = elem->IsQuadratic();
4061
4062       // get movable nodes of elem
4063       const SMDS_MeshNode* node;
4064       SMDS_TypeOfPosition posType;
4065       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4066       int nn = 0, nbn =  elem->NbNodes();
4067       if(elem->IsQuadratic())
4068         nbn = nbn/2;
4069       while ( nn++ < nbn ) {
4070         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4071         const SMDS_PositionPtr& pos = node->GetPosition();
4072         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4073         if (posType != SMDS_TOP_EDGE &&
4074             posType != SMDS_TOP_VERTEX &&
4075             theFixedNodes.find( node ) == theFixedNodes.end())
4076         {
4077           // check if all faces around the node are on faceSubMesh
4078           // because a node on edge may be bound to face
4079           SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4080           bool all = true;
4081           if ( faceSubMesh ) {
4082             while ( eIt->more() && all ) {
4083               const SMDS_MeshElement* e = eIt->next();
4084               all = faceSubMesh->Contains( e );
4085             }
4086           }
4087           if ( all )
4088             setMovableNodes.insert( node );
4089           else
4090             checkBoundaryNodes = true;
4091         }
4092         if ( posType == SMDS_TOP_3DSPACE )
4093           checkBoundaryNodes = true;
4094       }
4095
4096       if ( surface.IsNull() )
4097         continue;
4098
4099       // get nodes to check UV
4100       list< const SMDS_MeshNode* > uvCheckNodes;
4101       const SMDS_MeshNode* nodeInFace = 0;
4102       itN = elem->nodesIterator();
4103       nn = 0; nbn =  elem->NbNodes();
4104       if(elem->IsQuadratic())
4105         nbn = nbn/2;
4106       while ( nn++ < nbn ) {
4107         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4108         if ( node->GetPosition()->GetDim() == 2 )
4109           nodeInFace = node;
4110         if ( uvMap.find( node ) == uvMap.end() )
4111           uvCheckNodes.push_back( node );
4112         // add nodes of elems sharing node
4113         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4114         //         while ( eIt->more() ) {
4115         //           const SMDS_MeshElement* e = eIt->next();
4116         //           if ( e != elem ) {
4117         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4118         //             while ( nIt->more() ) {
4119         //               const SMDS_MeshNode* n =
4120         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4121         //               if ( uvMap.find( n ) == uvMap.end() )
4122         //                 uvCheckNodes.push_back( n );
4123         //             }
4124         //           }
4125         //         }
4126       }
4127       // check UV on face
4128       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4129       for ( ; n != uvCheckNodes.end(); ++n ) {
4130         node = *n;
4131         gp_XY uv( 0, 0 );
4132         const SMDS_PositionPtr& pos = node->GetPosition();
4133         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4134         // get existing UV
4135         if ( pos )
4136         {
4137           bool toCheck = true;
4138           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4139         }
4140         // compute not existing UV
4141         bool project = ( posType == SMDS_TOP_3DSPACE );
4142         // double dist1 = DBL_MAX, dist2 = 0;
4143         // if ( posType != SMDS_TOP_3DSPACE ) {
4144         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4145         //   project = dist1 > fToler2;
4146         // }
4147         if ( project ) { // compute new UV
4148           gp_XY newUV;
4149           gp_Pnt pNode = SMESH_TNodeXYZ( node );
4150           if ( !getClosestUV( projector, pNode, newUV )) {
4151             MESSAGE("Node Projection Failed " << node);
4152           }
4153           else {
4154             if ( isUPeriodic )
4155               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4156             if ( isVPeriodic )
4157               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4158             // check new UV
4159             // if ( posType != SMDS_TOP_3DSPACE )
4160             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4161             // if ( dist2 < dist1 )
4162               uv = newUV;
4163           }
4164         }
4165         // store UV in the map
4166         listUV.push_back( uv );
4167         uvMap.insert( make_pair( node, &listUV.back() ));
4168       }
4169     } // loop on not yet smoothed elements
4170
4171     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4172       checkBoundaryNodes = true;
4173
4174     // fix nodes on mesh boundary
4175
4176     if ( checkBoundaryNodes ) {
4177       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4178       map< SMESH_TLink, int >::iterator link_nb;
4179       // put all elements links to linkNbMap
4180       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4181       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4182         const SMDS_MeshElement* elem = (*elemIt);
4183         int nbn =  elem->NbCornerNodes();
4184         // loop on elem links: insert them in linkNbMap
4185         for ( int iN = 0; iN < nbn; ++iN ) {
4186           const SMDS_MeshNode* n1 = elem->GetNode( iN );
4187           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4188           SMESH_TLink link( n1, n2 );
4189           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4190           link_nb->second++;
4191         }
4192       }
4193       // remove nodes that are in links encountered only once from setMovableNodes
4194       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4195         if ( link_nb->second == 1 ) {
4196           setMovableNodes.erase( link_nb->first.node1() );
4197           setMovableNodes.erase( link_nb->first.node2() );
4198         }
4199       }
4200     }
4201
4202     // -----------------------------------------------------
4203     // for nodes on seam edge, compute one more UV ( uvMap2 );
4204     // find movable nodes linked to nodes on seam and which
4205     // are to be smoothed using the second UV ( uvMap2 )
4206     // -----------------------------------------------------
4207
4208     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4209     if ( !surface.IsNull() ) {
4210       TopExp_Explorer eExp( face, TopAbs_EDGE );
4211       for ( ; eExp.More(); eExp.Next() ) {
4212         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4213         if ( !BRep_Tool::IsClosed( edge, face ))
4214           continue;
4215         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4216         if ( !sm ) continue;
4217         // find out which parameter varies for a node on seam
4218         double f,l;
4219         gp_Pnt2d uv1, uv2;
4220         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4221         if ( pcurve.IsNull() ) continue;
4222         uv1 = pcurve->Value( f );
4223         edge.Reverse();
4224         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4225         if ( pcurve.IsNull() ) continue;
4226         uv2 = pcurve->Value( f );
4227         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4228         // assure uv1 < uv2
4229         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4230           std::swap( uv1, uv2 );
4231         // get nodes on seam and its vertices
4232         list< const SMDS_MeshNode* > seamNodes;
4233         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4234         while ( nSeamIt->more() ) {
4235           const SMDS_MeshNode* node = nSeamIt->next();
4236           if ( !isQuadratic || !IsMedium( node ))
4237             seamNodes.push_back( node );
4238         }
4239         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4240         for ( ; vExp.More(); vExp.Next() ) {
4241           sm = aMesh->MeshElements( vExp.Current() );
4242           if ( sm ) {
4243             nSeamIt = sm->GetNodes();
4244             while ( nSeamIt->more() )
4245               seamNodes.push_back( nSeamIt->next() );
4246           }
4247         }
4248         // loop on nodes on seam
4249         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4250         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4251           const SMDS_MeshNode* nSeam = *noSeIt;
4252           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4253           if ( n_uv == uvMap.end() )
4254             continue;
4255           // set the first UV
4256           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4257           // set the second UV
4258           listUV.push_back( *n_uv->second );
4259           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4260           if ( uvMap2.empty() )
4261             uvMap2 = uvMap; // copy the uvMap contents
4262           uvMap2[ nSeam ] = &listUV.back();
4263
4264           // collect movable nodes linked to ones on seam in nodesNearSeam
4265           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4266           while ( eIt->more() ) {
4267             const SMDS_MeshElement* e = eIt->next();
4268             int nbUseMap1 = 0, nbUseMap2 = 0;
4269             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4270             int nn = 0, nbn =  e->NbNodes();
4271             if(e->IsQuadratic()) nbn = nbn/2;
4272             while ( nn++ < nbn )
4273             {
4274               const SMDS_MeshNode* n =
4275                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4276               if (n == nSeam ||
4277                   setMovableNodes.find( n ) == setMovableNodes.end() )
4278                 continue;
4279               // add only nodes being closer to uv2 than to uv1
4280               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4281               //              0.5 * ( n->Y() + nSeam->Y() ),
4282               //              0.5 * ( n->Z() + nSeam->Z() ));
4283               // gp_XY uv;
4284               // getClosestUV( projector, pMid, uv );
4285               double x = uvMap[ n ]->Coord( iPar );
4286               if ( Abs( uv1.Coord( iPar ) - x ) >
4287                    Abs( uv2.Coord( iPar ) - x )) {
4288                 nodesNearSeam.insert( n );
4289                 nbUseMap2++;
4290               }
4291               else
4292                 nbUseMap1++;
4293             }
4294             // for centroidalSmooth all element nodes must
4295             // be on one side of a seam
4296             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4297               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4298               nn = 0;
4299               while ( nn++ < nbn ) {
4300                 const SMDS_MeshNode* n =
4301                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4302                 setMovableNodes.erase( n );
4303               }
4304             }
4305           }
4306         } // loop on nodes on seam
4307       } // loop on edge of a face
4308     } // if ( !face.IsNull() )
4309
4310     if ( setMovableNodes.empty() ) {
4311       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4312       continue; // goto next face
4313     }
4314
4315     // -------------
4316     // SMOOTHING //
4317     // -------------
4318
4319     int it = -1;
4320     double maxRatio = -1., maxDisplacement = -1.;
4321     set<const SMDS_MeshNode*>::iterator nodeToMove;
4322     for ( it = 0; it < theNbIterations; it++ ) {
4323       maxDisplacement = 0.;
4324       nodeToMove = setMovableNodes.begin();
4325       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4326         const SMDS_MeshNode* node = (*nodeToMove);
4327         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4328
4329         // smooth
4330         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4331         if ( theSmoothMethod == LAPLACIAN )
4332           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4333         else
4334           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4335
4336         // node displacement
4337         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4338         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4339         if ( aDispl > maxDisplacement )
4340           maxDisplacement = aDispl;
4341       }
4342       // no node movement => exit
4343       //if ( maxDisplacement < 1.e-16 ) {
4344       if ( maxDisplacement < disttol ) {
4345         MESSAGE("-- no node movement --");
4346         break;
4347       }
4348
4349       // check elements quality
4350       maxRatio  = 0;
4351       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4352       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4353         const SMDS_MeshElement* elem = (*elemIt);
4354         if ( !elem || elem->GetType() != SMDSAbs_Face )
4355           continue;
4356         SMESH::Controls::TSequenceOfXYZ aPoints;
4357         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4358           double aValue = aQualityFunc.GetValue( aPoints );
4359           if ( aValue > maxRatio )
4360             maxRatio = aValue;
4361         }
4362       }
4363       if ( maxRatio <= theTgtAspectRatio ) {
4364         MESSAGE("-- quality achived --");
4365         break;
4366       }
4367       if (it+1 == theNbIterations) {
4368         MESSAGE("-- Iteration limit exceeded --");
4369       }
4370     } // smoothing iterations
4371
4372     MESSAGE(" Face id: " << *fId <<
4373             " Nb iterstions: " << it <<
4374             " Displacement: " << maxDisplacement <<
4375             " Aspect Ratio " << maxRatio);
4376
4377     // ---------------------------------------
4378     // new nodes positions are computed,
4379     // record movement in DS and set new UV
4380     // ---------------------------------------
4381     nodeToMove = setMovableNodes.begin();
4382     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4383       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4384       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4385       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4386       if ( node_uv != uvMap.end() ) {
4387         gp_XY* uv = node_uv->second;
4388         node->SetPosition
4389           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4390       }
4391     }
4392
4393     // move medium nodes of quadratic elements
4394     if ( isQuadratic )
4395     {
4396       vector<const SMDS_MeshNode*> nodes;
4397       bool checkUV;
4398       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4399       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4400       {
4401         const SMDS_MeshElement* QF = *elemIt;
4402         if ( QF->IsQuadratic() )
4403         {
4404           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4405                         SMDS_MeshElement::iterator() );
4406           nodes.push_back( nodes[0] );
4407           gp_Pnt xyz;
4408           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4409           {
4410             if ( !surface.IsNull() )
4411             {
4412               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4413               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4414               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4415               xyz = surface->Value( uv.X(), uv.Y() );
4416             }
4417             else {
4418               xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4419             }
4420             if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4421               // we have to move a medium node
4422               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4423           }
4424         }
4425       }
4426     }
4427
4428   } // loop on face ids
4429
4430 }
4431
4432 namespace
4433 {
4434   //=======================================================================
4435   //function : isReverse
4436   //purpose  : Return true if normal of prevNodes is not co-directied with
4437   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4438   //           iNotSame is where prevNodes and nextNodes are different.
4439   //           If result is true then future volume orientation is OK
4440   //=======================================================================
4441
4442   bool isReverse(const SMDS_MeshElement*             face,
4443                  const vector<const SMDS_MeshNode*>& prevNodes,
4444                  const vector<const SMDS_MeshNode*>& nextNodes,
4445                  const int                           iNotSame)
4446   {
4447
4448     SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4449     SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4450     gp_XYZ extrDir( pN - pP ), faceNorm;
4451     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4452
4453     return faceNorm * extrDir < 0.0;
4454   }
4455
4456   //================================================================================
4457   /*!
4458    * \brief Assure that theElemSets[0] holds elements, not nodes
4459    */
4460   //================================================================================
4461
4462   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4463   {
4464     if ( !theElemSets[0].empty() &&
4465          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4466     {
4467       std::swap( theElemSets[0], theElemSets[1] );
4468     }
4469     else if ( !theElemSets[1].empty() &&
4470               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4471     {
4472       std::swap( theElemSets[0], theElemSets[1] );
4473     }
4474   }
4475 }
4476
4477 //=======================================================================
4478 /*!
4479  * \brief Create elements by sweeping an element
4480  * \param elem - element to sweep
4481  * \param newNodesItVec - nodes generated from each node of the element
4482  * \param newElems - generated elements
4483  * \param nbSteps - number of sweeping steps
4484  * \param srcElements - to append elem for each generated element
4485  */
4486 //=======================================================================
4487
4488 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4489                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4490                                     list<const SMDS_MeshElement*>&        newElems,
4491                                     const size_t                          nbSteps,
4492                                     SMESH_SequenceOfElemPtr&              srcElements)
4493 {
4494   //MESSAGE("sweepElement " << nbSteps);
4495   SMESHDS_Mesh* aMesh = GetMeshDS();
4496
4497   const int           nbNodes = elem->NbNodes();
4498   const int         nbCorners = elem->NbCornerNodes();
4499   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4500                                                           polyhedron creation !!! */
4501   // Loop on elem nodes:
4502   // find new nodes and detect same nodes indices
4503   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4504   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4505   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4506   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4507
4508   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4509   vector<int> sames(nbNodes);
4510   vector<bool> isSingleNode(nbNodes);
4511
4512   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4513     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4514     const SMDS_MeshNode*                         node = nnIt->first;
4515     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4516     if ( listNewNodes.empty() )
4517       return;
4518
4519     itNN   [ iNode ] = listNewNodes.begin();
4520     prevNod[ iNode ] = node;
4521     nextNod[ iNode ] = listNewNodes.front();
4522
4523     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4524                                                              corner node of linear */
4525     if ( prevNod[ iNode ] != nextNod [ iNode ])
4526       nbDouble += !isSingleNode[iNode];
4527
4528     if( iNode < nbCorners ) { // check corners only
4529       if ( prevNod[ iNode ] == nextNod [ iNode ])
4530         sames[nbSame++] = iNode;
4531       else
4532         iNotSameNode = iNode;
4533     }
4534   }
4535
4536   if ( nbSame == nbNodes || nbSame > 2) {
4537     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4538     return;
4539   }
4540
4541   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4542   {
4543     // fix nodes order to have bottom normal external
4544     if ( baseType == SMDSEntity_Polygon )
4545     {
4546       std::reverse( itNN.begin(), itNN.end() );
4547       std::reverse( prevNod.begin(), prevNod.end() );
4548       std::reverse( midlNod.begin(), midlNod.end() );
4549       std::reverse( nextNod.begin(), nextNod.end() );
4550       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4551     }
4552     else
4553     {
4554       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4555       SMDS_MeshCell::applyInterlace( ind, itNN );
4556       SMDS_MeshCell::applyInterlace( ind, prevNod );
4557       SMDS_MeshCell::applyInterlace( ind, nextNod );
4558       SMDS_MeshCell::applyInterlace( ind, midlNod );
4559       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4560       if ( nbSame > 0 )
4561       {
4562         sames[nbSame] = iNotSameNode;
4563         for ( int j = 0; j <= nbSame; ++j )
4564           for ( size_t i = 0; i < ind.size(); ++i )
4565             if ( ind[i] == sames[j] )
4566             {
4567               sames[j] = i;
4568               break;
4569             }
4570         iNotSameNode = sames[nbSame];
4571       }
4572     }
4573   }
4574   else if ( elem->GetType() == SMDSAbs_Edge )
4575   {
4576     // orient a new face same as adjacent one
4577     int i1, i2;
4578     const SMDS_MeshElement* e;
4579     TIDSortedElemSet dummy;
4580     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4581         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4582         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4583     {
4584       // there is an adjacent face, check order of nodes in it
4585       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4586       if ( sameOrder )
4587       {
4588         std::swap( itNN[0],    itNN[1] );
4589         std::swap( prevNod[0], prevNod[1] );
4590         std::swap( nextNod[0], nextNod[1] );
4591         isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4592         if ( nbSame > 0 )
4593           sames[0] = 1 - sames[0];
4594         iNotSameNode = 1 - iNotSameNode;
4595       }
4596     }
4597   }
4598
4599   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4600   if ( nbSame > 0 ) {
4601     iSameNode    = sames[ nbSame-1 ];
4602     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4603     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4604     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4605   }
4606
4607   if ( baseType == SMDSEntity_Polygon )
4608   {
4609     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4610     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4611   }
4612   else if ( baseType == SMDSEntity_Quad_Polygon )
4613   {
4614     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4615     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4616   }
4617
4618   // make new elements
4619   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4620   {
4621     // get next nodes
4622     for ( iNode = 0; iNode < nbNodes; iNode++ )
4623     {
4624       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4625       nextNod[ iNode ] = *itNN[ iNode ]++;
4626     }
4627
4628     SMDS_MeshElement* aNewElem = 0;
4629     /*if(!elem->IsPoly())*/ {
4630       switch ( baseType ) {
4631       case SMDSEntity_0D:
4632       case SMDSEntity_Node: { // sweep NODE
4633         if ( nbSame == 0 ) {
4634           if ( isSingleNode[0] )
4635             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4636           else
4637             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4638         }
4639         else
4640           return;
4641         break;
4642       }
4643       case SMDSEntity_Edge: { // sweep EDGE
4644         if ( nbDouble == 0 )
4645         {
4646           if ( nbSame == 0 ) // ---> quadrangle
4647             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4648                                       nextNod[ 1 ], nextNod[ 0 ] );
4649           else               // ---> triangle
4650             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4651                                       nextNod[ iNotSameNode ] );
4652         }
4653         else                 // ---> polygon
4654         {
4655           vector<const SMDS_MeshNode*> poly_nodes;
4656           poly_nodes.push_back( prevNod[0] );
4657           poly_nodes.push_back( prevNod[1] );
4658           if ( prevNod[1] != nextNod[1] )
4659           {
4660             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4661             poly_nodes.push_back( nextNod[1] );
4662           }
4663           if ( prevNod[0] != nextNod[0] )
4664           {
4665             poly_nodes.push_back( nextNod[0] );
4666             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4667           }
4668           switch ( poly_nodes.size() ) {
4669           case 3:
4670             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4671             break;
4672           case 4:
4673             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4674                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4675             break;
4676           default:
4677             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4678           }
4679         }
4680         break;
4681       }
4682       case SMDSEntity_Triangle: // TRIANGLE --->
4683         {
4684           if ( nbDouble > 0 ) break;
4685           if ( nbSame == 0 )       // ---> pentahedron
4686             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4687                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4688
4689           else if ( nbSame == 1 )  // ---> pyramid
4690             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4691                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4692                                          nextNod[ iSameNode ]);
4693
4694           else // 2 same nodes:       ---> tetrahedron
4695             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4696                                          nextNod[ iNotSameNode ]);
4697           break;
4698         }
4699       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4700         {
4701           if ( nbSame == 2 )
4702             return;
4703           if ( nbDouble+nbSame == 2 )
4704           {
4705             if(nbSame==0) {      // ---> quadratic quadrangle
4706               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4707                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4708             }
4709             else { //(nbSame==1) // ---> quadratic triangle
4710               if(sames[0]==2) {
4711                 return; // medium node on axis
4712               }
4713               else if(sames[0]==0)
4714                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4715                                           prevNod[2], midlNod[1], nextNod[2] );
4716               else // sames[0]==1
4717                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4718                                           prevNod[2], nextNod[2], midlNod[0]);
4719             }
4720           }
4721           else if ( nbDouble == 3 )
4722           {
4723             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4724               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4725                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4726             }
4727           }
4728           else
4729             return;
4730           break;
4731         }
4732       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4733         if ( nbDouble > 0 ) break;
4734
4735         if ( nbSame == 0 )       // ---> hexahedron
4736           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4737                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4738
4739         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4740           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4741                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4742                                        nextNod[ iSameNode ]);
4743           newElems.push_back( aNewElem );
4744           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4745                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4746                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4747         }
4748         else if ( nbSame == 2 ) { // ---> pentahedron
4749           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4750             // iBeforeSame is same too
4751             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4752                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4753                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4754           else
4755             // iAfterSame is same too
4756             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4757                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4758                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4759         }
4760         break;
4761       }
4762       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4763       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4764         if ( nbDouble+nbSame != 3 ) break;
4765         if(nbSame==0) {
4766           // --->  pentahedron with 15 nodes
4767           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4768                                        nextNod[0], nextNod[1], nextNod[2],
4769                                        prevNod[3], prevNod[4], prevNod[5],
4770                                        nextNod[3], nextNod[4], nextNod[5],
4771                                        midlNod[0], midlNod[1], midlNod[2]);
4772         }
4773         else if(nbSame==1) {
4774           // --->  2d order pyramid of 13 nodes
4775           int apex = iSameNode;
4776           int i0 = ( apex + 1 ) % nbCorners;
4777           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4778           int i0a = apex + 3;
4779           int i1a = i1 + 3;
4780           int i01 = i0 + 3;
4781           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4782                                       nextNod[i0], nextNod[i1], prevNod[apex],
4783                                       prevNod[i01], midlNod[i0],
4784                                       nextNod[i01], midlNod[i1],
4785                                       prevNod[i1a], prevNod[i0a],
4786                                       nextNod[i0a], nextNod[i1a]);
4787         }
4788         else if(nbSame==2) {
4789           // --->  2d order tetrahedron of 10 nodes
4790           int n1 = iNotSameNode;
4791           int n2 = ( n1 + 1             ) % nbCorners;
4792           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4793           int n12 = n1 + 3;
4794           int n23 = n2 + 3;
4795           int n31 = n3 + 3;
4796           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4797                                        prevNod[n12], prevNod[n23], prevNod[n31],
4798                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4799         }
4800         break;
4801       }
4802       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4803         if( nbSame == 0 ) {
4804           if ( nbDouble != 4 ) break;
4805           // --->  hexahedron with 20 nodes
4806           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4807                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4808                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4809                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4810                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4811         }
4812         else if(nbSame==1) {
4813           // ---> pyramid + pentahedron - can not be created since it is needed
4814           // additional middle node at the center of face
4815           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4816           return;
4817         }
4818         else if( nbSame == 2 ) {
4819           if ( nbDouble != 2 ) break;
4820           // --->  2d order Pentahedron with 15 nodes
4821           int n1,n2,n4,n5;
4822           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4823             // iBeforeSame is same too
4824             n1 = iBeforeSame;
4825             n2 = iOpposSame;
4826             n4 = iSameNode;
4827             n5 = iAfterSame;
4828           }
4829           else {
4830             // iAfterSame is same too
4831             n1 = iSameNode;
4832             n2 = iBeforeSame;
4833             n4 = iAfterSame;
4834             n5 = iOpposSame;
4835           }
4836           int n12 = n2 + 4;
4837           int n45 = n4 + 4;
4838           int n14 = n1 + 4;
4839           int n25 = n5 + 4;
4840           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4841                                        prevNod[n4], prevNod[n5], nextNod[n5],
4842                                        prevNod[n12], midlNod[n2], nextNod[n12],
4843                                        prevNod[n45], midlNod[n5], nextNod[n45],
4844                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4845         }
4846         break;
4847       }
4848       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4849
4850         if( nbSame == 0 && nbDouble == 9 ) {
4851           // --->  tri-quadratic hexahedron with 27 nodes
4852           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4853                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4854                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4855                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4856                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4857                                        prevNod[8], // bottom center
4858                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4859                                        nextNod[8], // top center
4860                                        midlNod[8]);// elem center
4861         }
4862         else
4863         {
4864           return;
4865         }
4866         break;
4867       }
4868       case SMDSEntity_Polygon: { // sweep POLYGON
4869
4870         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4871           // --->  hexagonal prism
4872           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4873                                        prevNod[3], prevNod[4], prevNod[5],
4874                                        nextNod[0], nextNod[1], nextNod[2],
4875                                        nextNod[3], nextNod[4], nextNod[5]);
4876         }
4877         break;
4878       }
4879       case SMDSEntity_Ball:
4880         return;
4881
4882       default:
4883         break;
4884       } // switch ( baseType )
4885     } // scope
4886
4887     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4888     {
4889       if ( baseType != SMDSEntity_Polygon )
4890       {
4891         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4892         SMDS_MeshCell::applyInterlace( ind, prevNod );
4893         SMDS_MeshCell::applyInterlace( ind, nextNod );
4894         SMDS_MeshCell::applyInterlace( ind, midlNod );
4895         SMDS_MeshCell::applyInterlace( ind, itNN );
4896         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4897         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4898       }
4899       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4900       vector<int> quantities (nbNodes + 2);
4901       polyedre_nodes.clear();
4902       quantities.clear();
4903
4904       // bottom of prism
4905       for (int inode = 0; inode < nbNodes; inode++)
4906         polyedre_nodes.push_back( prevNod[inode] );
4907       quantities.push_back( nbNodes );
4908
4909       // top of prism
4910       polyedre_nodes.push_back( nextNod[0] );
4911       for (int inode = nbNodes; inode-1; --inode )
4912         polyedre_nodes.push_back( nextNod[inode-1] );
4913       quantities.push_back( nbNodes );
4914
4915       // side faces
4916       // 3--6--2
4917       // |     |
4918       // 7     5
4919       // |     |
4920       // 0--4--1
4921       const int iQuad = elem->IsQuadratic();
4922       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4923       {
4924         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4925         int inextface = (iface+1+iQuad) % nbNodes;
4926         int imid      = (iface+1) % nbNodes;
4927         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4928         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4929         polyedre_nodes.push_back( prevNod[iface] );             // 1
4930         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4931         {
4932           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4933           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4934         }
4935         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4936         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4937         {
4938           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4939           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4940         }
4941         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4942         if ( nbFaceNodes > 2 )
4943           quantities.push_back( nbFaceNodes );
4944         else // degenerated face
4945           polyedre_nodes.resize( prevNbNodes );
4946       }
4947       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4948
4949     } // try to create a polyherdal prism
4950
4951     if ( aNewElem ) {
4952       newElems.push_back( aNewElem );
4953       myLastCreatedElems.Append(aNewElem);
4954       srcElements.Append( elem );
4955     }
4956
4957     // set new prev nodes
4958     for ( iNode = 0; iNode < nbNodes; iNode++ )
4959       prevNod[ iNode ] = nextNod[ iNode ];
4960
4961   } // loop on steps
4962 }
4963
4964 //=======================================================================
4965 /*!
4966  * \brief Create 1D and 2D elements around swept elements
4967  * \param mapNewNodes - source nodes and ones generated from them
4968  * \param newElemsMap - source elements and ones generated from them
4969  * \param elemNewNodesMap - nodes generated from each node of each element
4970  * \param elemSet - all swept elements
4971  * \param nbSteps - number of sweeping steps
4972  * \param srcElements - to append elem for each generated element
4973  */
4974 //=======================================================================
4975
4976 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4977                                   TTElemOfElemListMap &    newElemsMap,
4978                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4979                                   TIDSortedElemSet&        elemSet,
4980                                   const int                nbSteps,
4981                                   SMESH_SequenceOfElemPtr& srcElements)
4982 {
4983   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4984   SMESHDS_Mesh* aMesh = GetMeshDS();
4985
4986   // Find nodes belonging to only one initial element - sweep them into edges.
4987
4988   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4989   for ( ; nList != mapNewNodes.end(); nList++ )
4990   {
4991     const SMDS_MeshNode* node =
4992       static_cast<const SMDS_MeshNode*>( nList->first );
4993     if ( newElemsMap.count( node ))
4994       continue; // node was extruded into edge
4995     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4996     int nbInitElems = 0;
4997     const SMDS_MeshElement* el = 0;
4998     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4999     while ( eIt->more() && nbInitElems < 2 ) {
5000       const SMDS_MeshElement* e = eIt->next();
5001       SMDSAbs_ElementType  type = e->GetType();
5002       if ( type == SMDSAbs_Volume ||
5003            type < highType ||
5004            !elemSet.count(e))
5005         continue;
5006       if ( type > highType ) {
5007         nbInitElems = 0;
5008         highType    = type;
5009       }
5010       el = e;
5011       ++nbInitElems;
5012     }
5013     if ( nbInitElems == 1 ) {
5014       bool NotCreateEdge = el && el->IsMediumNode(node);
5015       if(!NotCreateEdge) {
5016         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5017         list<const SMDS_MeshElement*> newEdges;
5018         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5019       }
5020     }
5021   }
5022
5023   // Make a ceiling for each element ie an equal element of last new nodes.
5024   // Find free links of faces - make edges and sweep them into faces.
5025
5026   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5027
5028   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
5029   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5030   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5031   {
5032     const SMDS_MeshElement* elem = itElem->first;
5033     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5034
5035     if(itElem->second.size()==0) continue;
5036
5037     const bool isQuadratic = elem->IsQuadratic();
5038
5039     if ( elem->GetType() == SMDSAbs_Edge ) {
5040       // create a ceiling edge
5041       if ( !isQuadratic ) {
5042         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5043                                vecNewNodes[ 1 ]->second.back())) {
5044           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5045                                                    vecNewNodes[ 1 ]->second.back()));
5046           srcElements.Append( elem );
5047         }
5048       }
5049       else {
5050         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5051                                vecNewNodes[ 1 ]->second.back(),
5052                                vecNewNodes[ 2 ]->second.back())) {
5053           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5054                                                    vecNewNodes[ 1 ]->second.back(),
5055                                                    vecNewNodes[ 2 ]->second.back()));
5056           srcElements.Append( elem );
5057         }
5058       }
5059     }
5060     if ( elem->GetType() != SMDSAbs_Face )
5061       continue;
5062
5063     bool hasFreeLinks = false;
5064
5065     TIDSortedElemSet avoidSet;
5066     avoidSet.insert( elem );
5067
5068     set<const SMDS_MeshNode*> aFaceLastNodes;
5069     int iNode, nbNodes = vecNewNodes.size();
5070     if ( !isQuadratic ) {
5071       // loop on the face nodes
5072       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5073         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5074         // look for free links of the face
5075         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5076         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5077         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5078         // check if a link n1-n2 is free
5079         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5080           hasFreeLinks = true;
5081           // make a new edge and a ceiling for a new edge
5082           const SMDS_MeshElement* edge;
5083           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5084             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5085             srcElements.Append( myLastCreatedElems.Last() );
5086           }
5087           n1 = vecNewNodes[ iNode ]->second.back();
5088           n2 = vecNewNodes[ iNext ]->second.back();
5089           if ( !aMesh->FindEdge( n1, n2 )) {
5090             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5091             srcElements.Append( edge );
5092           }
5093         }
5094       }
5095     }
5096     else { // elem is quadratic face
5097       int nbn = nbNodes/2;
5098       for ( iNode = 0; iNode < nbn; iNode++ ) {
5099         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5100         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5101         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5102         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5103         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5104         // check if a link is free
5105         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5106              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5107              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5108           hasFreeLinks = true;
5109           // make an edge and a ceiling for a new edge
5110           // find medium node
5111           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5112             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5113             srcElements.Append( elem );
5114           }
5115           n1 = vecNewNodes[ iNode ]->second.back();
5116           n2 = vecNewNodes[ iNext ]->second.back();
5117           n3 = vecNewNodes[ iNode+nbn ]->second.back();
5118           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5119             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5120             srcElements.Append( elem );
5121           }
5122         }
5123       }
5124       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5125         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5126       }
5127     }
5128
5129     // sweep free links into faces
5130
5131     if ( hasFreeLinks ) {
5132       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5133       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5134
5135       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5136       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5137       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5138         initNodeSet.insert( vecNewNodes[ iNode ]->first );
5139         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5140       }
5141       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
5142         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5143         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5144       }
5145       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5146         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5147         std::advance( v, volNb );
5148         // find indices of free faces of a volume and their source edges
5149         list< int > freeInd;
5150         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5151         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5152         int iF, nbF = vTool.NbFaces();
5153         for ( iF = 0; iF < nbF; iF ++ ) {
5154           if (vTool.IsFreeFace( iF ) &&
5155               vTool.GetFaceNodes( iF, faceNodeSet ) &&
5156               initNodeSet != faceNodeSet) // except an initial face
5157           {
5158             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5159               continue;
5160             if ( faceNodeSet == initNodeSetNoCenter )
5161               continue;
5162             freeInd.push_back( iF );
5163             // find source edge of a free face iF
5164             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5165             vector<const SMDS_MeshNode*>::iterator lastCommom;
5166             commonNodes.resize( nbNodes, 0 );
5167             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5168                                                 initNodeSet.begin(), initNodeSet.end(),
5169                                                 commonNodes.begin());
5170             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5171               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5172             else
5173               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5174 #ifdef _DEBUG_
5175             if ( !srcEdges.back() )
5176             {
5177               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5178                    << iF << " of volume #" << vTool.ID() << endl;
5179             }
5180 #endif
5181           }
5182         }
5183         if ( freeInd.empty() )
5184           continue;
5185
5186         // create wall faces for all steps;
5187         // if such a face has been already created by sweep of edge,
5188         // assure that its orientation is OK
5189         for ( int iStep = 0; iStep < nbSteps; iStep++ )
5190         {
5191           vTool.Set( *v, /*ignoreCentralNodes=*/false );
5192           vTool.SetExternalNormal();
5193           const int nextShift = vTool.IsForward() ? +1 : -1;
5194           list< int >::iterator ind = freeInd.begin();
5195           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5196           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5197           {
5198             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5199             int nbn = vTool.NbFaceNodes( *ind );
5200             const SMDS_MeshElement * f = 0;
5201             if ( nbn == 3 )              ///// triangle
5202             {
5203               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5204               if ( !f ||
5205                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5206               {
5207                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5208                                                      nodes[ 1 ],
5209                                                      nodes[ 1 + nextShift ] };
5210                 if ( f )
5211                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5212                 else
5213                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5214                                                             newOrder[ 2 ] ));
5215               }
5216             }
5217             else if ( nbn == 4 )       ///// quadrangle
5218             {
5219               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5220               if ( !f ||
5221                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5222               {
5223                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5224                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
5225                 if ( f )
5226                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5227                 else
5228                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5229                                                             newOrder[ 2 ], newOrder[ 3 ]));
5230               }
5231             }
5232             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5233             {
5234               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5235               if ( !f ||
5236                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5237               {
5238                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5239                                                      nodes[2],
5240                                                      nodes[2 + 2*nextShift],
5241                                                      nodes[3 - 2*nextShift],
5242                                                      nodes[3],
5243                                                      nodes[3 + 2*nextShift]};
5244                 if ( f )
5245                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5246                 else
5247                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5248                                                             newOrder[ 1 ],
5249                                                             newOrder[ 2 ],
5250                                                             newOrder[ 3 ],
5251                                                             newOrder[ 4 ],
5252                                                             newOrder[ 5 ] ));
5253               }
5254             }
5255             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5256             {
5257               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5258                                    nodes[1], nodes[3], nodes[5], nodes[7] );
5259               if ( !f ||
5260                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5261               {
5262                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5263                                                      nodes[4 - 2*nextShift],
5264                                                      nodes[4],
5265                                                      nodes[4 + 2*nextShift],
5266                                                      nodes[1],
5267                                                      nodes[5 - 2*nextShift],
5268                                                      nodes[5],
5269                                                      nodes[5 + 2*nextShift] };
5270                 if ( f )
5271                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5272                 else
5273                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5274                                                            newOrder[ 2 ], newOrder[ 3 ],
5275                                                            newOrder[ 4 ], newOrder[ 5 ],
5276                                                            newOrder[ 6 ], newOrder[ 7 ]));
5277               }
5278             }
5279             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5280             {
5281               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5282                                       SMDSAbs_Face, /*noMedium=*/false);
5283               if ( !f ||
5284                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5285               {
5286                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5287                                                      nodes[4 - 2*nextShift],
5288                                                      nodes[4],
5289                                                      nodes[4 + 2*nextShift],
5290                                                      nodes[1],
5291                                                      nodes[5 - 2*nextShift],
5292                                                      nodes[5],
5293                                                      nodes[5 + 2*nextShift],
5294                                                      nodes[8] };
5295                 if ( f )
5296                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5297                 else
5298                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5299                                                            newOrder[ 2 ], newOrder[ 3 ],
5300                                                            newOrder[ 4 ], newOrder[ 5 ],
5301                                                            newOrder[ 6 ], newOrder[ 7 ],
5302                                                            newOrder[ 8 ]));
5303               }
5304             }
5305             else  //////// polygon
5306             {
5307               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5308               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5309               if ( !f ||
5310                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5311               {
5312                 if ( !vTool.IsForward() )
5313                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5314                 if ( f )
5315                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5316                 else
5317                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5318               }
5319             }
5320
5321             while ( srcElements.Length() < myLastCreatedElems.Length() )
5322               srcElements.Append( *srcEdge );
5323
5324           }  // loop on free faces
5325
5326           // go to the next volume
5327           iVol = 0;
5328           while ( iVol++ < nbVolumesByStep ) v++;
5329
5330         } // loop on steps
5331       } // loop on volumes of one step
5332     } // sweep free links into faces
5333
5334     // Make a ceiling face with a normal external to a volume
5335
5336     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5337     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5338     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5339
5340     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5341       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5342       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5343     }
5344     if ( iF >= 0 )
5345     {
5346       lastVol.SetExternalNormal();
5347       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5348       const               int nbn = lastVol.NbFaceNodes( iF );
5349       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5350       if ( !hasFreeLinks ||
5351            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5352       {
5353         const vector<int>& interlace =
5354           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5355         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5356
5357         AddElement( nodeVec, anyFace.Init( elem ));
5358
5359         while ( srcElements.Length() < myLastCreatedElems.Length() )
5360           srcElements.Append( elem );
5361       }
5362     }
5363   } // loop on swept elements
5364 }
5365
5366 //=======================================================================
5367 //function : RotationSweep
5368 //purpose  :
5369 //=======================================================================
5370
5371 SMESH_MeshEditor::PGroupIDs
5372 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5373                                 const gp_Ax1&      theAxis,
5374                                 const double       theAngle,
5375                                 const int          theNbSteps,
5376                                 const double       theTol,
5377                                 const bool         theMakeGroups,
5378                                 const bool         theMakeWalls)
5379 {
5380   myLastCreatedElems.Clear();
5381   myLastCreatedNodes.Clear();
5382
5383   // source elements for each generated one
5384   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5385
5386   MESSAGE( "RotationSweep()");
5387   gp_Trsf aTrsf;
5388   aTrsf.SetRotation( theAxis, theAngle );
5389   gp_Trsf aTrsf2;
5390   aTrsf2.SetRotation( theAxis, theAngle/2. );
5391
5392   gp_Lin aLine( theAxis );
5393   double aSqTol = theTol * theTol;
5394
5395   SMESHDS_Mesh* aMesh = GetMeshDS();
5396
5397   TNodeOfNodeListMap mapNewNodes;
5398   TElemOfVecOfNnlmiMap mapElemNewNodes;
5399   TTElemOfElemListMap newElemsMap;
5400
5401   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5402                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5403                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5404   // loop on theElemSets
5405   setElemsFirst( theElemSets );
5406   TIDSortedElemSet::iterator itElem;
5407   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5408   {
5409     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5410     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5411       const SMDS_MeshElement* elem = *itElem;
5412       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5413         continue;
5414       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5415       newNodesItVec.reserve( elem->NbNodes() );
5416
5417       // loop on elem nodes
5418       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5419       while ( itN->more() )
5420       {
5421         const SMDS_MeshNode* node = cast2Node( itN->next() );
5422
5423         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5424         double coord[3];
5425         aXYZ.Coord( coord[0], coord[1], coord[2] );
5426         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5427
5428         // check if a node has been already sweeped
5429         TNodeOfNodeListMapItr nIt =
5430           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5431         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5432         if ( listNewNodes.empty() )
5433         {
5434           // check if we are to create medium nodes between corner ones
5435           bool needMediumNodes = false;
5436           if ( isQuadraticMesh )
5437           {
5438             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5439             while (it->more() && !needMediumNodes )
5440             {
5441               const SMDS_MeshElement* invElem = it->next();
5442               if ( invElem != elem && !theElems.count( invElem )) continue;
5443               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5444               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5445                 needMediumNodes = true;
5446             }
5447           }
5448
5449           // make new nodes
5450           const SMDS_MeshNode * newNode = node;
5451           for ( int i = 0; i < theNbSteps; i++ ) {
5452             if ( !isOnAxis ) {
5453               if ( needMediumNodes )  // create a medium node
5454               {
5455                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5456                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5457                 myLastCreatedNodes.Append(newNode);
5458                 srcNodes.Append( node );
5459                 listNewNodes.push_back( newNode );
5460                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5461               }
5462               else {
5463                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5464               }
5465               // create a corner node
5466               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5467               myLastCreatedNodes.Append(newNode);
5468               srcNodes.Append( node );
5469               listNewNodes.push_back( newNode );
5470             }
5471             else {
5472               listNewNodes.push_back( newNode );
5473               // if ( needMediumNodes )
5474               //   listNewNodes.push_back( newNode );
5475             }
5476           }
5477         }
5478         newNodesItVec.push_back( nIt );
5479       }
5480       // make new elements
5481       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5482     }
5483   }
5484
5485   if ( theMakeWalls )
5486     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5487
5488   PGroupIDs newGroupIDs;
5489   if ( theMakeGroups )
5490     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5491
5492   return newGroupIDs;
5493 }
5494
5495 //=======================================================================
5496 //function : ExtrusParam
5497 //purpose  : standard construction
5498 //=======================================================================
5499
5500 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&  theStep,
5501                                             const int      theNbSteps,
5502                                             const int      theFlags,
5503                                             const double   theTolerance):
5504   myDir( theStep ),
5505   myFlags( theFlags ),
5506   myTolerance( theTolerance ),
5507   myElemsToUse( NULL )
5508 {
5509   mySteps = new TColStd_HSequenceOfReal;
5510   const double stepSize = theStep.Magnitude();
5511   for (int i=1; i<=theNbSteps; i++ )
5512     mySteps->Append( stepSize );
5513
5514   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5515       ( theTolerance > 0 ))
5516   {
5517     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5518   }
5519   else
5520   {
5521     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5522   }
5523 }
5524
5525 //=======================================================================
5526 //function : ExtrusParam
5527 //purpose  : steps are given explicitly
5528 //=======================================================================
5529
5530 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5531                                             Handle(TColStd_HSequenceOfReal) theSteps,
5532                                             const int                       theFlags,
5533                                             const double                    theTolerance):
5534   myDir( theDir ),
5535   mySteps( theSteps ),
5536   myFlags( theFlags ),
5537   myTolerance( theTolerance ),
5538   myElemsToUse( NULL )
5539 {
5540   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5541       ( theTolerance > 0 ))
5542   {
5543     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5544   }
5545   else
5546   {
5547     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5548   }
5549 }
5550
5551 //=======================================================================
5552 //function : ExtrusParam
5553 //purpose  : for extrusion by normal
5554 //=======================================================================
5555
5556 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5557                                             const int    theNbSteps,
5558                                             const int    theFlags,
5559                                             const int    theDim ):
5560   myDir( 1,0,0 ),
5561   mySteps( new TColStd_HSequenceOfReal ),
5562   myFlags( theFlags ),
5563   myTolerance( 0 ),
5564   myElemsToUse( NULL )
5565 {
5566   for (int i = 0; i < theNbSteps; i++ )
5567     mySteps->Append( theStepSize );
5568
5569   if ( theDim == 1 )
5570   {
5571     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5572   }
5573   else
5574   {
5575     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5576   }
5577 }
5578
5579 //=======================================================================
5580 //function : ExtrusParam::SetElementsToUse
5581 //purpose  : stores elements to use for extrusion by normal, depending on
5582 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag
5583 //=======================================================================
5584
5585 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems )
5586 {
5587   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5588 }
5589
5590 //=======================================================================
5591 //function : ExtrusParam::beginStepIter
5592 //purpose  : prepare iteration on steps
5593 //=======================================================================
5594
5595 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5596 {
5597   myWithMediumNodes = withMediumNodes;
5598   myNextStep = 1;
5599   myCurSteps.clear();
5600 }
5601 //=======================================================================
5602 //function : ExtrusParam::moreSteps
5603 //purpose  : are there more steps?
5604 //=======================================================================
5605
5606 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5607 {
5608   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5609 }
5610 //=======================================================================
5611 //function : ExtrusParam::nextStep
5612 //purpose  : returns the next step
5613 //=======================================================================
5614
5615 double SMESH_MeshEditor::ExtrusParam::nextStep()
5616 {
5617   double res = 0;
5618   if ( !myCurSteps.empty() )
5619   {
5620     res = myCurSteps.back();
5621     myCurSteps.pop_back();
5622   }
5623   else if ( myNextStep <= mySteps->Length() )
5624   {
5625     myCurSteps.push_back( mySteps->Value( myNextStep ));
5626     ++myNextStep;
5627     if ( myWithMediumNodes )
5628     {
5629       myCurSteps.back() /= 2.;
5630       myCurSteps.push_back( myCurSteps.back() );
5631     }
5632     res = nextStep();
5633   }
5634   return res;
5635 }
5636
5637 //=======================================================================
5638 //function : ExtrusParam::makeNodesByDir
5639 //purpose  : create nodes for standard extrusion
5640 //=======================================================================
5641
5642 int SMESH_MeshEditor::ExtrusParam::
5643 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5644                 const SMDS_MeshNode*              srcNode,
5645                 std::list<const SMDS_MeshNode*> & newNodes,
5646                 const bool                        makeMediumNodes)
5647 {
5648   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5649
5650   int nbNodes = 0;
5651   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5652   {
5653     p += myDir.XYZ() * nextStep();
5654     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5655     newNodes.push_back( newNode );
5656   }
5657   return nbNodes;
5658 }
5659
5660 //=======================================================================
5661 //function : ExtrusParam::makeNodesByDirAndSew
5662 //purpose  : create nodes for standard extrusion with sewing
5663 //=======================================================================
5664
5665 int SMESH_MeshEditor::ExtrusParam::
5666 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5667                       const SMDS_MeshNode*              srcNode,
5668                       std::list<const SMDS_MeshNode*> & newNodes,
5669                       const bool                        makeMediumNodes)
5670 {
5671   gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5672
5673   int nbNodes = 0;
5674   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5675   {
5676     P1 += myDir.XYZ() * nextStep();
5677
5678     // try to search in sequence of existing nodes
5679     // if myNodes.Length()>0 we 'nave to use given sequence
5680     // else - use all nodes of mesh
5681     const SMDS_MeshNode * node = 0;
5682     if ( myNodes.Length() > 0 ) {
5683       int i;
5684       for(i=1; i<=myNodes.Length(); i++) {
5685         gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5686         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5687         {
5688           node = myNodes.Value(i);
5689           break;
5690         }
5691       }
5692     }
5693     else {
5694       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5695       while(itn->more()) {
5696         SMESH_TNodeXYZ P2( itn->next() );
5697         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5698         {
5699           node = P2._node;
5700           break;
5701         }
5702       }
5703     }
5704
5705     if ( !node )
5706       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5707
5708     newNodes.push_back( node );
5709
5710   } // loop on steps
5711
5712   return nbNodes;
5713 }
5714
5715 //=======================================================================
5716 //function : ExtrusParam::makeNodesByNormal2D
5717 //purpose  : create nodes for extrusion using normals of faces
5718 //=======================================================================
5719
5720 int SMESH_MeshEditor::ExtrusParam::
5721 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5722                      const SMDS_MeshNode*              srcNode,
5723                      std::list<const SMDS_MeshNode*> & newNodes,
5724                      const bool                        makeMediumNodes)
5725 {
5726   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5727
5728   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5729
5730   // get normals to faces sharing srcNode
5731   vector< gp_XYZ > norms, baryCenters;
5732   gp_XYZ norm, avgNorm( 0,0,0 );
5733   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5734   while ( faceIt->more() )
5735   {
5736     const SMDS_MeshElement* face = faceIt->next();
5737     if ( myElemsToUse && !myElemsToUse->count( face ))
5738       continue;
5739     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5740     {
5741       norms.push_back( norm );
5742       avgNorm += norm;
5743       if ( !alongAvgNorm )
5744       {
5745         gp_XYZ bc(0,0,0);
5746         int nbN = 0;
5747         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5748           bc += SMESH_TNodeXYZ( nIt->next() );
5749         baryCenters.push_back( bc / nbN );
5750       }
5751     }
5752   }
5753
5754   if ( norms.empty() ) return 0;
5755
5756   double normSize = avgNorm.Modulus();
5757   if ( normSize < std::numeric_limits<double>::min() )
5758     return 0;
5759
5760   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5761   {
5762     myDir = avgNorm;
5763     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5764   }
5765
5766   avgNorm /= normSize;
5767
5768   int nbNodes = 0;
5769   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5770   {
5771     gp_XYZ pNew = p;
5772     double stepSize = nextStep();
5773
5774     if ( norms.size() > 1 )
5775     {
5776       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5777       {
5778         // translate plane of a face
5779         baryCenters[ iF ] += norms[ iF ] * stepSize;
5780
5781         // find point of intersection of the face plane located at baryCenters[ iF ]
5782         // and avgNorm located at pNew
5783         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5784         double dot  = ( norms[ iF ] * avgNorm );
5785         if ( dot < std::numeric_limits<double>::min() )
5786           dot = stepSize * 1e-3;
5787         double step = -( norms[ iF ] * pNew + d ) / dot;
5788         pNew += step * avgNorm;
5789       }
5790     }
5791     else
5792     {
5793       pNew += stepSize * avgNorm;
5794     }
5795     p = pNew;
5796
5797     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5798     newNodes.push_back( newNode );
5799   }
5800   return nbNodes;
5801 }
5802
5803 //=======================================================================
5804 //function : ExtrusParam::makeNodesByNormal1D
5805 //purpose  : create nodes for extrusion using normals of edges
5806 //=======================================================================
5807
5808 int SMESH_MeshEditor::ExtrusParam::
5809 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5810                      const SMDS_MeshNode*              srcNode,
5811                      std::list<const SMDS_MeshNode*> & newNodes,
5812                      const bool                        makeMediumNodes)
5813 {
5814   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5815   return 0;
5816 }
5817
5818 //=======================================================================
5819 //function : ExtrusionSweep
5820 //purpose  :
5821 //=======================================================================
5822
5823 SMESH_MeshEditor::PGroupIDs
5824 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5825                                   const gp_Vec&        theStep,
5826                                   const int            theNbSteps,
5827                                   TTElemOfElemListMap& newElemsMap,
5828                                   const int            theFlags,
5829                                   const double         theTolerance)
5830 {
5831   ExtrusParam aParams( theStep, theNbSteps, theFlags, theTolerance );
5832   return ExtrusionSweep( theElems, aParams, newElemsMap );
5833 }
5834
5835
5836 //=======================================================================
5837 //function : ExtrusionSweep
5838 //purpose  :
5839 //=======================================================================
5840
5841 SMESH_MeshEditor::PGroupIDs
5842 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5843                                   ExtrusParam&         theParams,
5844                                   TTElemOfElemListMap& newElemsMap)
5845 {
5846   myLastCreatedElems.Clear();
5847   myLastCreatedNodes.Clear();
5848
5849   // source elements for each generated one
5850   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5851
5852   //SMESHDS_Mesh* aMesh = GetMeshDS();
5853
5854   setElemsFirst( theElemSets );
5855   const int nbSteps = theParams.NbSteps();
5856   theParams.SetElementsToUse( theElemSets[0] );
5857
5858   TNodeOfNodeListMap mapNewNodes;
5859   //TNodeOfNodeVecMap mapNewNodes;
5860   TElemOfVecOfNnlmiMap mapElemNewNodes;
5861   //TElemOfVecOfMapNodesMap mapElemNewNodes;
5862
5863   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5864                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5865                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5866   // loop on theElems
5867   TIDSortedElemSet::iterator itElem;
5868   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5869   {
5870     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5871     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5872     {
5873       // check element type
5874       const SMDS_MeshElement* elem = *itElem;
5875       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5876         continue;
5877
5878       const size_t nbNodes = elem->NbNodes();
5879       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5880       newNodesItVec.reserve( nbNodes );
5881
5882       // loop on elem nodes
5883       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5884       while ( itN->more() )
5885       {
5886         // check if a node has been already sweeped
5887         const SMDS_MeshNode* node = cast2Node( itN->next() );
5888         TNodeOfNodeListMap::iterator nIt =
5889           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5890         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5891         if ( listNewNodes.empty() )
5892         {
5893           // make new nodes
5894
5895           // check if we are to create medium nodes between corner ones
5896           bool needMediumNodes = false;
5897           if ( isQuadraticMesh )
5898           {
5899             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5900             while (it->more() && !needMediumNodes )
5901             {
5902               const SMDS_MeshElement* invElem = it->next();
5903               if ( invElem != elem && !theElems.count( invElem )) continue;
5904               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5905               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5906                 needMediumNodes = true;
5907             }
5908           }
5909           // create nodes for all steps
5910           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5911           {
5912             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5913             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5914             {
5915               myLastCreatedNodes.Append( *newNodesIt );
5916               srcNodes.Append( node );
5917             }
5918           }
5919           else
5920           {
5921             break; // newNodesItVec will be shorter than nbNodes
5922           }
5923         }
5924         newNodesItVec.push_back( nIt );
5925       }
5926       // make new elements
5927       if ( newNodesItVec.size() == nbNodes )
5928         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5929     }
5930   }
5931
5932   if ( theParams.ToMakeBoundary() ) {
5933     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5934   }
5935   PGroupIDs newGroupIDs;
5936   if ( theParams.ToMakeGroups() )
5937     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5938
5939   return newGroupIDs;
5940 }
5941
5942 //=======================================================================
5943 //function : ExtrusionAlongTrack
5944 //purpose  :
5945 //=======================================================================
5946 SMESH_MeshEditor::Extrusion_Error
5947 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5948                                        SMESH_subMesh*       theTrack,
5949                                        const SMDS_MeshNode* theN1,
5950                                        const bool           theHasAngles,
5951                                        list<double>&        theAngles,
5952                                        const bool           theLinearVariation,
5953                                        const bool           theHasRefPoint,
5954                                        const gp_Pnt&        theRefPoint,
5955                                        const bool           theMakeGroups)
5956 {
5957   MESSAGE("ExtrusionAlongTrack");
5958   myLastCreatedElems.Clear();
5959   myLastCreatedNodes.Clear();
5960
5961   int aNbE;
5962   std::list<double> aPrms;
5963   TIDSortedElemSet::iterator itElem;
5964
5965   gp_XYZ aGC;
5966   TopoDS_Edge aTrackEdge;
5967   TopoDS_Vertex aV1, aV2;
5968
5969   SMDS_ElemIteratorPtr aItE;
5970   SMDS_NodeIteratorPtr aItN;
5971   SMDSAbs_ElementType aTypeE;
5972
5973   TNodeOfNodeListMap mapNewNodes;
5974
5975   // 1. Check data
5976   aNbE = theElements[0].size() + theElements[1].size();
5977   // nothing to do
5978   if ( !aNbE )
5979     return EXTR_NO_ELEMENTS;
5980
5981   // 1.1 Track Pattern
5982   ASSERT( theTrack );
5983
5984   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5985   if ( !pSubMeshDS )
5986     return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
5987                                 theHasAngles, theAngles, theLinearVariation,
5988                                 theHasRefPoint, theRefPoint, theMakeGroups );
5989
5990   aItE = pSubMeshDS->GetElements();
5991   while ( aItE->more() ) {
5992     const SMDS_MeshElement* pE = aItE->next();
5993     aTypeE = pE->GetType();
5994     // Pattern must contain links only
5995     if ( aTypeE != SMDSAbs_Edge )
5996       return EXTR_PATH_NOT_EDGE;
5997   }
5998
5999   list<SMESH_MeshEditor_PathPoint> fullList;
6000
6001   const TopoDS_Shape& aS = theTrack->GetSubShape();
6002   // Sub-shape for the Pattern must be an Edge or Wire
6003   if( aS.ShapeType() == TopAbs_EDGE ) {
6004     aTrackEdge = TopoDS::Edge( aS );
6005     // the Edge must not be degenerated
6006     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6007       return EXTR_BAD_PATH_SHAPE;
6008     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6009     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
6010     const SMDS_MeshNode* aN1 = aItN->next();
6011     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
6012     const SMDS_MeshNode* aN2 = aItN->next();
6013     // starting node must be aN1 or aN2
6014     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6015       return EXTR_BAD_STARTING_NODE;
6016     aItN = pSubMeshDS->GetNodes();
6017     while ( aItN->more() ) {
6018       const SMDS_MeshNode* pNode = aItN->next();
6019       const SMDS_EdgePosition* pEPos =
6020         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6021       double aT = pEPos->GetUParameter();
6022       aPrms.push_back( aT );
6023     }
6024     //Extrusion_Error err =
6025     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6026   } else if( aS.ShapeType() == TopAbs_WIRE ) {
6027     list< SMESH_subMesh* > LSM;
6028     TopTools_SequenceOfShape Edges;
6029     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6030     while(itSM->more()) {
6031       SMESH_subMesh* SM = itSM->next();
6032       LSM.push_back(SM);
6033       const TopoDS_Shape& aS = SM->GetSubShape();
6034       Edges.Append(aS);
6035     }
6036     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6037     int startNid = theN1->GetID();
6038     TColStd_MapOfInteger UsedNums;
6039
6040     int NbEdges = Edges.Length();
6041     int i = 1;
6042     for(; i<=NbEdges; i++) {
6043       int k = 0;
6044       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6045       for(; itLSM!=LSM.end(); itLSM++) {
6046         k++;
6047         if(UsedNums.Contains(k)) continue;
6048         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6049         SMESH_subMesh* locTrack = *itLSM;
6050         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6051         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6052         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6053         const SMDS_MeshNode* aN1 = aItN->next();
6054         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6055         const SMDS_MeshNode* aN2 = aItN->next();
6056         // starting node must be aN1 or aN2
6057         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6058         // 2. Collect parameters on the track edge
6059         aPrms.clear();
6060         aItN = locMeshDS->GetNodes();
6061         while ( aItN->more() ) {
6062           const SMDS_MeshNode* pNode = aItN->next();
6063           const SMDS_EdgePosition* pEPos =
6064             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6065           double aT = pEPos->GetUParameter();
6066           aPrms.push_back( aT );
6067         }
6068         list<SMESH_MeshEditor_PathPoint> LPP;
6069         //Extrusion_Error err =
6070         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6071         LLPPs.push_back(LPP);
6072         UsedNums.Add(k);
6073         // update startN for search following egde
6074         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6075         else startNid = aN1->GetID();
6076         break;
6077       }
6078     }
6079     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6080     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6081     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6082     for(; itPP!=firstList.end(); itPP++) {
6083       fullList.push_back( *itPP );
6084     }
6085     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6086     fullList.pop_back();
6087     itLLPP++;
6088     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6089       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6090       itPP = currList.begin();
6091       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6092       gp_Dir D1 = PP1.Tangent();
6093       gp_Dir D2 = PP2.Tangent();
6094       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6095                            (D1.Z()+D2.Z())/2 ) );
6096       PP1.SetTangent(Dnew);
6097       fullList.push_back(PP1);
6098       itPP++;
6099       for(; itPP!=firstList.end(); itPP++) {
6100         fullList.push_back( *itPP );
6101       }
6102       PP1 = fullList.back();
6103       fullList.pop_back();
6104     }
6105     // if wire not closed
6106     fullList.push_back(PP1);
6107     // else ???
6108   }
6109   else {
6110     return EXTR_BAD_PATH_SHAPE;
6111   }
6112
6113   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6114                           theHasRefPoint, theRefPoint, theMakeGroups);
6115 }
6116
6117
6118 //=======================================================================
6119 //function : ExtrusionAlongTrack
6120 //purpose  :
6121 //=======================================================================
6122 SMESH_MeshEditor::Extrusion_Error
6123 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
6124                                        SMESH_Mesh*          theTrack,
6125                                        const SMDS_MeshNode* theN1,
6126                                        const bool           theHasAngles,
6127                                        list<double>&        theAngles,
6128                                        const bool           theLinearVariation,
6129                                        const bool           theHasRefPoint,
6130                                        const gp_Pnt&        theRefPoint,
6131                                        const bool           theMakeGroups)
6132 {
6133   myLastCreatedElems.Clear();
6134   myLastCreatedNodes.Clear();
6135
6136   int aNbE;
6137   std::list<double> aPrms;
6138   TIDSortedElemSet::iterator itElem;
6139
6140   gp_XYZ aGC;
6141   TopoDS_Edge aTrackEdge;
6142   TopoDS_Vertex aV1, aV2;
6143
6144   SMDS_ElemIteratorPtr aItE;
6145   SMDS_NodeIteratorPtr aItN;
6146   SMDSAbs_ElementType aTypeE;
6147
6148   TNodeOfNodeListMap mapNewNodes;
6149
6150   // 1. Check data
6151   aNbE = theElements[0].size() + theElements[1].size();
6152   // nothing to do
6153   if ( !aNbE )
6154     return EXTR_NO_ELEMENTS;
6155
6156   // 1.1 Track Pattern
6157   ASSERT( theTrack );
6158
6159   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6160
6161   aItE = pMeshDS->elementsIterator();
6162   while ( aItE->more() ) {
6163     const SMDS_MeshElement* pE = aItE->next();
6164     aTypeE = pE->GetType();
6165     // Pattern must contain links only
6166     if ( aTypeE != SMDSAbs_Edge )
6167       return EXTR_PATH_NOT_EDGE;
6168   }
6169
6170   list<SMESH_MeshEditor_PathPoint> fullList;
6171
6172   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6173
6174   if ( !theTrack->HasShapeToMesh() ) {
6175     //Mesh without shape
6176     const SMDS_MeshNode* currentNode = NULL;
6177     const SMDS_MeshNode* prevNode = theN1;
6178     std::vector<const SMDS_MeshNode*> aNodesList;
6179     aNodesList.push_back(theN1);
6180     int nbEdges = 0, conn=0;
6181     const SMDS_MeshElement* prevElem = NULL;
6182     const SMDS_MeshElement* currentElem = NULL;
6183     int totalNbEdges = theTrack->NbEdges();
6184     SMDS_ElemIteratorPtr nIt;
6185
6186     //check start node
6187     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6188       return EXTR_BAD_STARTING_NODE;
6189     }
6190
6191     conn = nbEdgeConnectivity(theN1);
6192     if( conn != 1 )
6193       return EXTR_PATH_NOT_EDGE;
6194
6195     aItE = theN1->GetInverseElementIterator();
6196     prevElem = aItE->next();
6197     currentElem = prevElem;
6198     //Get all nodes
6199     if(totalNbEdges == 1 ) {
6200       nIt = currentElem->nodesIterator();
6201       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6202       if(currentNode == prevNode)
6203         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6204       aNodesList.push_back(currentNode);
6205     } else {
6206       nIt = currentElem->nodesIterator();
6207       while( nIt->more() ) {
6208         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6209         if(currentNode == prevNode)
6210           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6211         aNodesList.push_back(currentNode);
6212
6213         //case of the closed mesh
6214         if(currentNode == theN1) {
6215           nbEdges++;
6216           break;
6217         }
6218
6219         conn = nbEdgeConnectivity(currentNode);
6220         if(conn > 2) {
6221           return EXTR_PATH_NOT_EDGE;
6222         }else if( conn == 1 && nbEdges > 0 ) {
6223           //End of the path
6224           nbEdges++;
6225           break;
6226         }else {
6227           prevNode = currentNode;
6228           aItE = currentNode->GetInverseElementIterator();
6229           currentElem = aItE->next();
6230           if( currentElem  == prevElem)
6231             currentElem = aItE->next();
6232           nIt = currentElem->nodesIterator();
6233           prevElem = currentElem;
6234           nbEdges++;
6235         }
6236       }
6237     }
6238
6239     if(nbEdges != totalNbEdges)
6240       return EXTR_PATH_NOT_EDGE;
6241
6242     TopTools_SequenceOfShape Edges;
6243     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6244     int startNid = theN1->GetID();
6245     for ( size_t i = 1; i < aNodesList.size(); i++ )
6246     {
6247       gp_Pnt     p1 = SMESH_TNodeXYZ( aNodesList[i-1] );
6248       gp_Pnt     p2 = SMESH_TNodeXYZ( aNodesList[i] );
6249       TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6250       list<SMESH_MeshEditor_PathPoint> LPP;
6251       aPrms.clear();
6252       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6253       LLPPs.push_back(LPP);
6254       if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i  ]->GetID();
6255       else                                        startNid = aNodesList[i-1]->GetID();
6256     }
6257
6258     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6259     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6260     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6261     for(; itPP!=firstList.end(); itPP++) {
6262       fullList.push_back( *itPP );
6263     }
6264
6265     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6266     SMESH_MeshEditor_PathPoint PP2;
6267     fullList.pop_back();
6268     itLLPP++;
6269     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6270       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6271       itPP = currList.begin();
6272       PP2 = currList.front();
6273       gp_Dir D1 = PP1.Tangent();
6274       gp_Dir D2 = PP2.Tangent();
6275       gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6276       PP1.SetTangent(Dnew);
6277       fullList.push_back(PP1);
6278       itPP++;
6279       for(; itPP!=currList.end(); itPP++) {
6280         fullList.push_back( *itPP );
6281       }
6282       PP1 = fullList.back();
6283       fullList.pop_back();
6284     }
6285     fullList.push_back(PP1);
6286
6287   } // Sub-shape for the Pattern must be an Edge or Wire
6288   else if ( aS.ShapeType() == TopAbs_EDGE )
6289   {
6290     aTrackEdge = TopoDS::Edge( aS );
6291     // the Edge must not be degenerated
6292     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6293       return EXTR_BAD_PATH_SHAPE;
6294     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6295     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6296     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6297     // starting node must be aN1 or aN2
6298     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6299       return EXTR_BAD_STARTING_NODE;
6300     aItN = pMeshDS->nodesIterator();
6301     while ( aItN->more() ) {
6302       const SMDS_MeshNode* pNode = aItN->next();
6303       if( pNode==aN1 || pNode==aN2 ) continue;
6304       const SMDS_EdgePosition* pEPos =
6305         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6306       double aT = pEPos->GetUParameter();
6307       aPrms.push_back( aT );
6308     }
6309     //Extrusion_Error err =
6310     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6311   }
6312   else if( aS.ShapeType() == TopAbs_WIRE ) {
6313     list< SMESH_subMesh* > LSM;
6314     TopTools_SequenceOfShape Edges;
6315     TopExp_Explorer eExp(aS, TopAbs_EDGE);
6316     for(; eExp.More(); eExp.Next()) {
6317       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6318       if( SMESH_Algo::isDegenerated(E) ) continue;
6319       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6320       if(SM) {
6321         LSM.push_back(SM);
6322         Edges.Append(E);
6323       }
6324     }
6325     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6326     TopoDS_Vertex aVprev;
6327     TColStd_MapOfInteger UsedNums;
6328     int NbEdges = Edges.Length();
6329     int i = 1;
6330     for(; i<=NbEdges; i++) {
6331       int k = 0;
6332       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6333       for(; itLSM!=LSM.end(); itLSM++) {
6334         k++;
6335         if(UsedNums.Contains(k)) continue;
6336         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6337         SMESH_subMesh* locTrack = *itLSM;
6338         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6339         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6340         bool aN1isOK = false, aN2isOK = false;
6341         if ( aVprev.IsNull() ) {
6342           // if previous vertex is not yet defined, it means that we in the beginning of wire
6343           // and we have to find initial vertex corresponding to starting node theN1
6344           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6345           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6346           // starting node must be aN1 or aN2
6347           aN1isOK = ( aN1 && aN1 == theN1 );
6348           aN2isOK = ( aN2 && aN2 == theN1 );
6349         }
6350         else {
6351           // we have specified ending vertex of the previous edge on the previous iteration
6352           // and we have just to check that it corresponds to any vertex in current segment
6353           aN1isOK = aVprev.IsSame( aV1 );
6354           aN2isOK = aVprev.IsSame( aV2 );
6355         }
6356         if ( !aN1isOK && !aN2isOK ) continue;
6357         // 2. Collect parameters on the track edge
6358         aPrms.clear();
6359         aItN = locMeshDS->GetNodes();
6360         while ( aItN->more() ) {
6361           const SMDS_MeshNode*     pNode = aItN->next();
6362           const SMDS_EdgePosition* pEPos =
6363             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6364           double aT = pEPos->GetUParameter();
6365           aPrms.push_back( aT );
6366         }
6367         list<SMESH_MeshEditor_PathPoint> LPP;
6368         //Extrusion_Error err =
6369         MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6370         LLPPs.push_back(LPP);
6371         UsedNums.Add(k);
6372         // update startN for search following egde
6373         if ( aN1isOK ) aVprev = aV2;
6374         else           aVprev = aV1;
6375         break;
6376       }
6377     }
6378     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6379     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6380     fullList.splice( fullList.end(), firstList );
6381
6382     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6383     fullList.pop_back();
6384     itLLPP++;
6385     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6386       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6387       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6388       gp_Dir D1 = PP1.Tangent();
6389       gp_Dir D2 = PP2.Tangent();
6390       gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6391       PP1.SetTangent(Dnew);
6392       fullList.push_back(PP1);
6393       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6394       PP1 = fullList.back();
6395       fullList.pop_back();
6396     }
6397     // if wire not closed
6398     fullList.push_back(PP1);
6399     // else ???
6400   }
6401   else {
6402     return EXTR_BAD_PATH_SHAPE;
6403   }
6404
6405   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6406                           theHasRefPoint, theRefPoint, theMakeGroups);
6407 }
6408
6409
6410 //=======================================================================
6411 //function : MakeEdgePathPoints
6412 //purpose  : auxilary for ExtrusionAlongTrack
6413 //=======================================================================
6414 SMESH_MeshEditor::Extrusion_Error
6415 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>&                aPrms,
6416                                      const TopoDS_Edge&                aTrackEdge,
6417                                      bool                              FirstIsStart,
6418                                      list<SMESH_MeshEditor_PathPoint>& LPP)
6419 {
6420   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6421   aTolVec=1.e-7;
6422   aTolVec2=aTolVec*aTolVec;
6423   double aT1, aT2;
6424   TopoDS_Vertex aV1, aV2;
6425   TopExp::Vertices( aTrackEdge, aV1, aV2 );
6426   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6427   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6428   // 2. Collect parameters on the track edge
6429   aPrms.push_front( aT1 );
6430   aPrms.push_back( aT2 );
6431   // sort parameters
6432   aPrms.sort();
6433   if( FirstIsStart ) {
6434     if ( aT1 > aT2 ) {
6435       aPrms.reverse();
6436     }
6437   }
6438   else {
6439     if ( aT2 > aT1 ) {
6440       aPrms.reverse();
6441     }
6442   }
6443   // 3. Path Points
6444   SMESH_MeshEditor_PathPoint aPP;
6445   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6446   std::list<double>::iterator aItD = aPrms.begin();
6447   for(; aItD != aPrms.end(); ++aItD) {
6448     double aT = *aItD;
6449     gp_Pnt aP3D;
6450     gp_Vec aVec;
6451     aC3D->D1( aT, aP3D, aVec );
6452     aL2 = aVec.SquareMagnitude();
6453     if ( aL2 < aTolVec2 )
6454       return EXTR_CANT_GET_TANGENT;
6455     gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6456     aPP.SetPnt( aP3D );
6457     aPP.SetTangent( aTgt );
6458     aPP.SetParameter( aT );
6459     LPP.push_back(aPP);
6460   }
6461   return EXTR_OK;
6462 }
6463
6464
6465 //=======================================================================
6466 //function : MakeExtrElements
6467 //purpose  : auxilary for ExtrusionAlongTrack
6468 //=======================================================================
6469 SMESH_MeshEditor::Extrusion_Error
6470 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet                  theElemSets[2],
6471                                    list<SMESH_MeshEditor_PathPoint>& fullList,
6472                                    const bool                        theHasAngles,
6473                                    list<double>&                     theAngles,
6474                                    const bool                        theLinearVariation,
6475                                    const bool                        theHasRefPoint,
6476                                    const gp_Pnt&                     theRefPoint,
6477                                    const bool                        theMakeGroups)
6478 {
6479   const int aNbTP = fullList.size();
6480
6481   // Angles
6482   if( theHasAngles && !theAngles.empty() && theLinearVariation )
6483     LinearAngleVariation(aNbTP-1, theAngles);
6484
6485   // fill vector of path points with angles
6486   vector<SMESH_MeshEditor_PathPoint> aPPs;
6487   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6488   list<double>::iterator                 itAngles = theAngles.begin();
6489   aPPs.push_back( *itPP++ );
6490   for( ; itPP != fullList.end(); itPP++) {
6491     aPPs.push_back( *itPP );
6492     if ( theHasAngles && itAngles != theAngles.end() )
6493       aPPs.back().SetAngle( *itAngles++ );
6494   }
6495
6496   TNodeOfNodeListMap   mapNewNodes;
6497   TElemOfVecOfNnlmiMap mapElemNewNodes;
6498   TTElemOfElemListMap  newElemsMap;
6499   TIDSortedElemSet::iterator itElem;
6500   // source elements for each generated one
6501   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6502
6503   // 3. Center of rotation aV0
6504   gp_Pnt aV0 = theRefPoint;
6505   if ( !theHasRefPoint )
6506   {
6507     gp_XYZ aGC( 0.,0.,0. );
6508     TIDSortedElemSet newNodes;
6509
6510     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6511     {
6512       TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6513       itElem = theElements.begin();
6514       for ( ; itElem != theElements.end(); itElem++ )
6515       {
6516         const SMDS_MeshElement* elem = *itElem;
6517         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6518         while ( itN->more() ) {
6519           const SMDS_MeshElement* node = itN->next();
6520           if ( newNodes.insert( node ).second )
6521             aGC += SMESH_TNodeXYZ( node );
6522         }
6523       }
6524     }
6525     aGC /= newNodes.size();
6526     aV0.SetXYZ( aGC );
6527   } // if (!theHasRefPoint) {
6528
6529   // 4. Processing the elements
6530   SMESHDS_Mesh* aMesh = GetMeshDS();
6531   list<const SMDS_MeshNode*> emptyList;
6532
6533   setElemsFirst( theElemSets );
6534   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6535   {
6536     TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6537     for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6538     {
6539       const SMDS_MeshElement* elem = *itElem;
6540
6541       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6542       newNodesItVec.reserve( elem->NbNodes() );
6543
6544       // loop on elem nodes
6545       int nodeIndex = -1;
6546       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6547       while ( itN->more() )
6548       {
6549         ++nodeIndex;
6550         // check if a node has been already processed
6551         const SMDS_MeshNode* node = cast2Node( itN->next() );
6552         TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6553         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6554         if ( listNewNodes.empty() )
6555         {
6556           // make new nodes
6557           Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6558           gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6559           gp_Ax1 anAx1, anAxT1T0;
6560           gp_Dir aDT1x, aDT0x, aDT1T0;
6561
6562           aTolAng=1.e-4;
6563
6564           aV0x = aV0;
6565           aPN0 = SMESH_TNodeXYZ( node );
6566
6567           const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6568           aP0x = aPP0.Pnt();
6569           aDT0x= aPP0.Tangent();
6570
6571           for ( int j = 1; j < aNbTP; ++j ) {
6572             const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6573             aP1x     = aPP1.Pnt();
6574             aDT1x    = aPP1.Tangent();
6575             aAngle1x = aPP1.Angle();
6576
6577             gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6578             // Translation
6579             gp_Vec aV01x( aP0x, aP1x );
6580             aTrsf.SetTranslation( aV01x );
6581
6582             // traslated point
6583             aV1x = aV0x.Transformed( aTrsf );
6584             aPN1 = aPN0.Transformed( aTrsf );
6585
6586             // rotation 1 [ T1,T0 ]
6587             aAngleT1T0=-aDT1x.Angle( aDT0x );
6588             if (fabs(aAngleT1T0) > aTolAng)
6589             {
6590               aDT1T0=aDT1x^aDT0x;
6591               anAxT1T0.SetLocation( aV1x );
6592               anAxT1T0.SetDirection( aDT1T0 );
6593               aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6594
6595               aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6596             }
6597
6598             // rotation 2
6599             if ( theHasAngles ) {
6600               anAx1.SetLocation( aV1x );
6601               anAx1.SetDirection( aDT1x );
6602               aTrsfRot.SetRotation( anAx1, aAngle1x );
6603
6604               aPN1 = aPN1.Transformed( aTrsfRot );
6605             }
6606
6607             // make new node
6608             if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6609             {
6610               // create additional node
6611               gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6612               const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6613               myLastCreatedNodes.Append(newNode);
6614               srcNodes.Append( node );
6615               listNewNodes.push_back( newNode );
6616             }
6617             const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6618             myLastCreatedNodes.Append(newNode);
6619             srcNodes.Append( node );
6620             listNewNodes.push_back( newNode );
6621
6622             aPN0 = aPN1;
6623             aP0x = aP1x;
6624             aV0x = aV1x;
6625             aDT0x = aDT1x;
6626           }
6627         }
6628         else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6629         {
6630           // if current elem is quadratic and current node is not medium
6631           // we have to check - may be it is needed to insert additional nodes
6632           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6633           if ((int) listNewNodes.size() == aNbTP-1 )
6634           {
6635             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6636             gp_XYZ P(node->X(), node->Y(), node->Z());
6637             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6638             int i;
6639             for(i=0; i<aNbTP-1; i++) {
6640               const SMDS_MeshNode* N = *it;
6641               double x = ( N->X() + P.X() )/2.;
6642               double y = ( N->Y() + P.Y() )/2.;
6643               double z = ( N->Z() + P.Z() )/2.;
6644               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6645               srcNodes.Append( node );
6646               myLastCreatedNodes.Append(newN);
6647               aNodes[2*i] = newN;
6648               aNodes[2*i+1] = N;
6649               P = gp_XYZ(N->X(),N->Y(),N->Z());
6650             }
6651             listNewNodes.clear();
6652             for(i=0; i<2*(aNbTP-1); i++) {
6653               listNewNodes.push_back(aNodes[i]);
6654             }
6655           }
6656         }
6657
6658         newNodesItVec.push_back( nIt );
6659       }
6660
6661       // make new elements
6662       sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6663     }
6664   }
6665
6666   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6667
6668   if ( theMakeGroups )
6669     generateGroups( srcNodes, srcElems, "extruded");
6670
6671   return EXTR_OK;
6672 }
6673
6674
6675 //=======================================================================
6676 //function : LinearAngleVariation
6677 //purpose  : auxilary for ExtrusionAlongTrack
6678 //=======================================================================
6679 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6680                                             list<double>& Angles)
6681 {
6682   int nbAngles = Angles.size();
6683   if( nbSteps > nbAngles ) {
6684     vector<double> theAngles(nbAngles);
6685     list<double>::iterator it = Angles.begin();
6686     int i = -1;
6687     for(; it!=Angles.end(); it++) {
6688       i++;
6689       theAngles[i] = (*it);
6690     }
6691     list<double> res;
6692     double rAn2St = double( nbAngles ) / double( nbSteps );
6693     double angPrev = 0, angle;
6694     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6695       double angCur = rAn2St * ( iSt+1 );
6696       double angCurFloor  = floor( angCur );
6697       double angPrevFloor = floor( angPrev );
6698       if ( angPrevFloor == angCurFloor )
6699         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6700       else {
6701         int iP = int( angPrevFloor );
6702         double angPrevCeil = ceil(angPrev);
6703         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6704
6705         int iC = int( angCurFloor );
6706         if ( iC < nbAngles )
6707           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6708
6709         iP = int( angPrevCeil );
6710         while ( iC-- > iP )
6711           angle += theAngles[ iC ];
6712       }
6713       res.push_back(angle);
6714       angPrev = angCur;
6715     }
6716     Angles.clear();
6717     it = res.begin();
6718     for(; it!=res.end(); it++)
6719       Angles.push_back( *it );
6720   }
6721 }
6722
6723
6724 //================================================================================
6725 /*!
6726  * \brief Move or copy theElements applying theTrsf to their nodes
6727  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6728  *  \param theTrsf - transformation to apply
6729  *  \param theCopy - if true, create translated copies of theElems
6730  *  \param theMakeGroups - if true and theCopy, create translated groups
6731  *  \param theTargetMesh - mesh to copy translated elements into
6732  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6733  */
6734 //================================================================================
6735
6736 SMESH_MeshEditor::PGroupIDs
6737 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6738                              const gp_Trsf&     theTrsf,
6739                              const bool         theCopy,
6740                              const bool         theMakeGroups,
6741                              SMESH_Mesh*        theTargetMesh)
6742 {
6743   myLastCreatedElems.Clear();
6744   myLastCreatedNodes.Clear();
6745
6746   bool needReverse = false;
6747   string groupPostfix;
6748   switch ( theTrsf.Form() ) {
6749   case gp_PntMirror:
6750     MESSAGE("gp_PntMirror");
6751     needReverse = true;
6752     groupPostfix = "mirrored";
6753     break;
6754   case gp_Ax1Mirror:
6755     MESSAGE("gp_Ax1Mirror");
6756     groupPostfix = "mirrored";
6757     break;
6758   case gp_Ax2Mirror:
6759     MESSAGE("gp_Ax2Mirror");
6760     needReverse = true;
6761     groupPostfix = "mirrored";
6762     break;
6763   case gp_Rotation:
6764     MESSAGE("gp_Rotation");
6765     groupPostfix = "rotated";
6766     break;
6767   case gp_Translation:
6768     MESSAGE("gp_Translation");
6769     groupPostfix = "translated";
6770     break;
6771   case gp_Scale:
6772     MESSAGE("gp_Scale");
6773     groupPostfix = "scaled";
6774     break;
6775   case gp_CompoundTrsf: // different scale by axis
6776     MESSAGE("gp_CompoundTrsf");
6777     groupPostfix = "scaled";
6778     break;
6779   default:
6780     MESSAGE("default");
6781     needReverse = false;
6782     groupPostfix = "transformed";
6783   }
6784
6785   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6786   SMESHDS_Mesh* aMesh    = GetMeshDS();
6787
6788   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6789   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6790   SMESH_MeshEditor::ElemFeatures elemType;
6791
6792   // map old node to new one
6793   TNodeNodeMap nodeMap;
6794
6795   // elements sharing moved nodes; those of them which have all
6796   // nodes mirrored but are not in theElems are to be reversed
6797   TIDSortedElemSet inverseElemSet;
6798
6799   // source elements for each generated one
6800   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6801
6802   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6803   TIDSortedElemSet orphanNode;
6804
6805   if ( theElems.empty() ) // transform the whole mesh
6806   {
6807     // add all elements
6808     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6809     while ( eIt->more() ) theElems.insert( eIt->next() );
6810     // add orphan nodes
6811     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6812     while ( nIt->more() )
6813     {
6814       const SMDS_MeshNode* node = nIt->next();
6815       if ( node->NbInverseElements() == 0)
6816         orphanNode.insert( node );
6817     }
6818   }
6819
6820   // loop on elements to transform nodes : first orphan nodes then elems
6821   TIDSortedElemSet::iterator itElem;
6822   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6823   for (int i=0; i<2; i++)
6824     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6825     {
6826       const SMDS_MeshElement* elem = *itElem;
6827       if ( !elem )
6828         continue;
6829
6830       // loop on elem nodes
6831       double coord[3];
6832       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6833       while ( itN->more() )
6834       {
6835         const SMDS_MeshNode* node = cast2Node( itN->next() );
6836         // check if a node has been already transformed
6837         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6838           nodeMap.insert( make_pair ( node, node ));
6839         if ( !n2n_isnew.second )
6840           continue;
6841
6842         node->GetXYZ( coord );
6843         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6844         if ( theTargetMesh ) {
6845           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6846           n2n_isnew.first->second = newNode;
6847           myLastCreatedNodes.Append(newNode);
6848           srcNodes.Append( node );
6849         }
6850         else if ( theCopy ) {
6851           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6852           n2n_isnew.first->second = newNode;
6853           myLastCreatedNodes.Append(newNode);
6854           srcNodes.Append( node );
6855         }
6856         else {
6857           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6858           // node position on shape becomes invalid
6859           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6860             ( SMDS_SpacePosition::originSpacePosition() );
6861         }
6862
6863         // keep inverse elements
6864         if ( !theCopy && !theTargetMesh && needReverse ) {
6865           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6866           while ( invElemIt->more() ) {
6867             const SMDS_MeshElement* iel = invElemIt->next();
6868             inverseElemSet.insert( iel );
6869           }
6870         }
6871       }
6872     } // loop on elems in { &orphanNode, &theElems };
6873
6874   // either create new elements or reverse mirrored ones
6875   if ( !theCopy && !needReverse && !theTargetMesh )
6876     return PGroupIDs();
6877
6878   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6879
6880   // Replicate or reverse elements
6881
6882   std::vector<int> iForw;
6883   vector<const SMDS_MeshNode*> nodes;
6884   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6885   {
6886     const SMDS_MeshElement* elem = *itElem;
6887     if ( !elem ) continue;
6888
6889     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6890     size_t               nbNodes  = elem->NbNodes();
6891     if ( geomType == SMDSGeom_NONE ) continue; // node
6892
6893     nodes.resize( nbNodes );
6894
6895     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6896     {
6897       const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6898       if (!aPolyedre)
6899         continue;
6900       nodes.clear();
6901       bool allTransformed = true;
6902       int nbFaces = aPolyedre->NbFaces();
6903       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6904       {
6905         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6906         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6907         {
6908           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6909           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6910           if ( nodeMapIt == nodeMap.end() )
6911             allTransformed = false; // not all nodes transformed
6912           else
6913             nodes.push_back((*nodeMapIt).second);
6914         }
6915         if ( needReverse && allTransformed )
6916           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6917       }
6918       if ( !allTransformed )
6919         continue; // not all nodes transformed
6920     }
6921     else // ----------------------- the rest element types
6922     {
6923       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6924       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6925       const vector<int>&    i = needReverse ? iRev : iForw;
6926
6927       // find transformed nodes
6928       size_t iNode = 0;
6929       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6930       while ( itN->more() ) {
6931         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6932         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6933         if ( nodeMapIt == nodeMap.end() )
6934           break; // not all nodes transformed
6935         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6936       }
6937       if ( iNode != nbNodes )
6938         continue; // not all nodes transformed
6939     }
6940
6941     if ( editor ) {
6942       // copy in this or a new mesh
6943       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6944         srcElems.Append( elem );
6945     }
6946     else {
6947       // reverse element as it was reversed by transformation
6948       if ( nbNodes > 2 )
6949         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6950     }
6951
6952   } // loop on elements
6953
6954   if ( editor && editor != this )
6955     myLastCreatedElems = editor->myLastCreatedElems;
6956
6957   PGroupIDs newGroupIDs;
6958
6959   if ( ( theMakeGroups && theCopy ) ||
6960        ( theMakeGroups && theTargetMesh ) )
6961     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6962
6963   return newGroupIDs;
6964 }
6965
6966 //=======================================================================
6967 /*!
6968  * \brief Create groups of elements made during transformation
6969  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6970  *  \param elemGens - elements making corresponding myLastCreatedElems
6971  *  \param postfix - to append to names of new groups
6972  *  \param targetMesh - mesh to create groups in
6973  *  \param topPresent - is there "top" elements that are created by sweeping
6974  */
6975 //=======================================================================
6976
6977 SMESH_MeshEditor::PGroupIDs
6978 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6979                                  const SMESH_SequenceOfElemPtr& elemGens,
6980                                  const std::string&             postfix,
6981                                  SMESH_Mesh*                    targetMesh,
6982                                  const bool                     topPresent)
6983 {
6984   PGroupIDs newGroupIDs( new list<int> );
6985   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6986
6987   // Sort existing groups by types and collect their names
6988
6989   // containers to store an old group and generated new ones;
6990   // 1st new group is for result elems of different type than a source one;
6991   // 2nd new group is for same type result elems ("top" group at extrusion)
6992   using boost::tuple;
6993   using boost::make_tuple;
6994   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6995   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6996   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6997   // group names
6998   set< string > groupNames;
6999
7000   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7001   if ( !groupIt->more() ) return newGroupIDs;
7002
7003   int newGroupID = mesh->GetGroupIds().back()+1;
7004   while ( groupIt->more() )
7005   {
7006     SMESH_Group * group = groupIt->next();
7007     if ( !group ) continue;
7008     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7009     if ( !groupDS || groupDS->IsEmpty() ) continue;
7010     groupNames.insert    ( group->GetName() );
7011     groupDS->SetStoreName( group->GetName() );
7012     const SMDSAbs_ElementType type = groupDS->GetType();
7013     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7014     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7015     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7016     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7017   }
7018
7019   // Loop on nodes and elements to add them in new groups
7020
7021   vector< const SMDS_MeshElement* > resultElems;
7022   for ( int isNodes = 0; isNodes < 2; ++isNodes )
7023   {
7024     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
7025     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7026     if ( gens.Length() != elems.Length() )
7027       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7028
7029     // loop on created elements
7030     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7031     {
7032       const SMDS_MeshElement* sourceElem = gens( iElem );
7033       if ( !sourceElem ) {
7034         MESSAGE("generateGroups(): NULL source element");
7035         continue;
7036       }
7037       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7038       if ( groupsOldNew.empty() ) { // no groups of this type at all
7039         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7040           ++iElem; // skip all elements made by sourceElem
7041         continue;
7042       }
7043       // collect all elements made by the iElem-th sourceElem
7044       resultElems.clear();
7045       if ( const SMDS_MeshElement* resElem = elems( iElem ))
7046         if ( resElem != sourceElem )
7047           resultElems.push_back( resElem );
7048       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7049         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7050           if ( resElem != sourceElem )
7051             resultElems.push_back( resElem );
7052
7053       const SMDS_MeshElement* topElem = 0;
7054       if ( isNodes ) // there must be a top element
7055       {
7056         topElem = resultElems.back();
7057         resultElems.pop_back();
7058       }
7059       else
7060       {
7061         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7062         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7063           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7064           {
7065             topElem = *resElemIt;
7066             *resElemIt = 0; // erase *resElemIt
7067             break;
7068           }
7069       }
7070       // add resultElems to groups originted from ones the sourceElem belongs to
7071       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7072       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7073       {
7074         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7075         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7076         {
7077           // fill in a new group
7078           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7079           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7080           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7081             if ( *resElemIt )
7082               newGroup.Add( *resElemIt );
7083
7084           // fill a "top" group
7085           if ( topElem )
7086           {
7087             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7088             newTopGroup.Add( topElem );
7089          }
7090         }
7091       }
7092     } // loop on created elements
7093   }// loop on nodes and elements
7094
7095   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7096
7097   list<int> topGrouIds;
7098   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7099   {
7100     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
7101     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7102                                       orderedOldNewGroups[i]->get<2>() };
7103     for ( int is2nd = 0; is2nd < 2; ++is2nd )
7104     {
7105       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7106       if ( newGroupDS->IsEmpty() )
7107       {
7108         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7109       }
7110       else
7111       {
7112         // set group type
7113         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7114
7115         // make a name
7116         const bool isTop = ( topPresent &&
7117                              newGroupDS->GetType() == oldGroupDS->GetType() &&
7118                              is2nd );
7119
7120         string name = oldGroupDS->GetStoreName();
7121         { // remove trailing whitespaces (issue 22599)
7122           size_t size = name.size();
7123           while ( size > 1 && isspace( name[ size-1 ]))
7124             --size;
7125           if ( size != name.size() )
7126           {
7127             name.resize( size );
7128             oldGroupDS->SetStoreName( name.c_str() );
7129           }
7130         }
7131         if ( !targetMesh ) {
7132           string suffix = ( isTop ? "top": postfix.c_str() );
7133           name += "_";
7134           name += suffix;
7135           int nb = 1;
7136           while ( !groupNames.insert( name ).second ) // name exists
7137             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7138         }
7139         else if ( isTop ) {
7140           name += "_top";
7141         }
7142         newGroupDS->SetStoreName( name.c_str() );
7143
7144         // make a SMESH_Groups
7145         mesh->AddGroup( newGroupDS );
7146         if ( isTop )
7147           topGrouIds.push_back( newGroupDS->GetID() );
7148         else
7149           newGroupIDs->push_back( newGroupDS->GetID() );
7150       }
7151     }
7152   }
7153   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7154
7155   return newGroupIDs;
7156 }
7157
7158 //================================================================================
7159 /*!
7160  *  * \brief Return list of group of nodes close to each other within theTolerance
7161  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
7162  *  *        an Octree algorithm
7163  *  \param [in,out] theNodes - the nodes to treat
7164  *  \param [in]     theTolerance - the tolerance
7165  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
7166  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
7167  *         corner and medium nodes in separate groups
7168  */
7169 //================================================================================
7170
7171 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
7172                                             const double         theTolerance,
7173                                             TListOfListOfNodes & theGroupsOfNodes,
7174                                             bool                 theSeparateCornersAndMedium)
7175 {
7176   myLastCreatedElems.Clear();
7177   myLastCreatedNodes.Clear();
7178
7179   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
7180        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
7181        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7182     theSeparateCornersAndMedium = false;
7183
7184   TIDSortedNodeSet& corners = theNodes;
7185   TIDSortedNodeSet  medium;
7186
7187   if ( theNodes.empty() ) // get all nodes in the mesh
7188   {
7189     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7190     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7191     if ( theSeparateCornersAndMedium )
7192       while ( nIt->more() )
7193       {
7194         const SMDS_MeshNode* n = nIt->next();
7195         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7196         nodeSet->insert( nodeSet->end(), n );
7197       }
7198     else
7199       while ( nIt->more() )
7200         theNodes.insert( theNodes.end(),nIt->next() );
7201   }
7202   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7203   {
7204     TIDSortedNodeSet::iterator nIt = corners.begin();
7205     while ( nIt != corners.end() )
7206       if ( SMESH_MesherHelper::IsMedium( *nIt ))
7207       {
7208         medium.insert( medium.end(), *nIt );
7209         corners.erase( nIt++ );
7210       }
7211       else
7212       {
7213         ++nIt;
7214       }
7215   }
7216
7217   if ( !corners.empty() )
7218     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7219   if ( !medium.empty() )
7220     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7221 }
7222
7223 //=======================================================================
7224 //function : SimplifyFace
7225 //purpose  : split a chain of nodes into several closed chains
7226 //=======================================================================
7227
7228 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7229                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7230                                     vector<int>&                         quantities) const
7231 {
7232   int nbNodes = faceNodes.size();
7233
7234   if (nbNodes < 3)
7235     return 0;
7236
7237   set<const SMDS_MeshNode*> nodeSet;
7238
7239   // get simple seq of nodes
7240   vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7241   int iSimple = 0;
7242
7243   simpleNodes[iSimple++] = faceNodes[0];
7244   for (int iCur = 1; iCur < nbNodes; iCur++) {
7245     if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7246       simpleNodes[iSimple++] = faceNodes[iCur];
7247       nodeSet.insert( faceNodes[iCur] );
7248     }
7249   }
7250   int nbUnique = nodeSet.size();
7251   int nbSimple = iSimple;
7252   if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7253     nbSimple--;
7254     iSimple--;
7255   }
7256
7257   if (nbUnique < 3)
7258     return 0;
7259
7260   // separate loops
7261   int nbNew = 0;
7262   bool foundLoop = (nbSimple > nbUnique);
7263   while (foundLoop) {
7264     foundLoop = false;
7265     set<const SMDS_MeshNode*> loopSet;
7266     for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7267       const SMDS_MeshNode* n = simpleNodes[iSimple];
7268       if (!loopSet.insert( n ).second) {
7269         foundLoop = true;
7270
7271         // separate loop
7272         int iC = 0, curLast = iSimple;
7273         for (; iC < curLast; iC++) {
7274           if (simpleNodes[iC] == n) break;
7275         }
7276         int loopLen = curLast - iC;
7277         if (loopLen > 2) {
7278           // create sub-element
7279           nbNew++;
7280           quantities.push_back(loopLen);
7281           for (; iC < curLast; iC++) {
7282             poly_nodes.push_back(simpleNodes[iC]);
7283           }
7284         }
7285         // shift the rest nodes (place from the first loop position)
7286         for (iC = curLast + 1; iC < nbSimple; iC++) {
7287           simpleNodes[iC - loopLen] = simpleNodes[iC];
7288         }
7289         nbSimple -= loopLen;
7290         iSimple -= loopLen;
7291       }
7292     } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7293   } // while (foundLoop)
7294
7295   if (iSimple > 2) {
7296     nbNew++;
7297     quantities.push_back(iSimple);
7298     for (int i = 0; i < iSimple; i++)
7299       poly_nodes.push_back(simpleNodes[i]);
7300   }
7301
7302   return nbNew;
7303 }
7304
7305 //=======================================================================
7306 //function : MergeNodes
7307 //purpose  : In each group, the cdr of nodes are substituted by the first one
7308 //           in all elements.
7309 //=======================================================================
7310
7311 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7312 {
7313   MESSAGE("MergeNodes");
7314   myLastCreatedElems.Clear();
7315   myLastCreatedNodes.Clear();
7316
7317   SMESHDS_Mesh* aMesh = GetMeshDS();
7318
7319   TNodeNodeMap nodeNodeMap; // node to replace - new node
7320   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7321   list< int > rmElemIds, rmNodeIds;
7322
7323   // Fill nodeNodeMap and elems
7324
7325   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7326   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7327   {
7328     list<const SMDS_MeshNode*>& nodes = *grIt;
7329     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7330     const SMDS_MeshNode* nToKeep = *nIt;
7331     for ( ++nIt; nIt != nodes.end(); nIt++ )
7332     {
7333       const SMDS_MeshNode* nToRemove = *nIt;
7334       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7335       if ( nToRemove != nToKeep )
7336       {
7337         rmNodeIds.push_back( nToRemove->GetID() );
7338         AddToSameGroups( nToKeep, nToRemove, aMesh );
7339         // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7340         // after MergeNodes() w/o creating node in place of merged ones.
7341         const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7342         if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7343           if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7344             sm->SetIsAlwaysComputed( true );
7345       }
7346       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7347       while ( invElemIt->more() ) {
7348         const SMDS_MeshElement* elem = invElemIt->next();
7349         elems.insert(elem);
7350       }
7351     }
7352   }
7353   // Change element nodes or remove an element
7354
7355   set<const SMDS_MeshNode*> nodeSet;
7356   vector< const SMDS_MeshNode*> curNodes, uniqueNodes;
7357   vector<int> iRepl;
7358   ElemFeatures elemType;
7359
7360   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7361   for ( ; eIt != elems.end(); eIt++ )
7362   {
7363     const SMDS_MeshElement* elem = *eIt;
7364     const           int  nbNodes = elem->NbNodes();
7365     const           int aShapeId = FindShape( elem );
7366
7367     nodeSet.clear();
7368     curNodes.resize( nbNodes );
7369     uniqueNodes.resize( nbNodes );
7370     iRepl.resize( nbNodes );
7371     int iUnique = 0, iCur = 0, nbRepl = 0;
7372
7373     // get new seq of nodes
7374     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7375     while ( itN->more() )
7376     {
7377       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7378
7379       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7380       if ( nnIt != nodeNodeMap.end() ) { // n sticks
7381         n = (*nnIt).second;
7382         { ////////// BUG 0020185: begin
7383           bool stopRecur = false;
7384           set<const SMDS_MeshNode*> nodesRecur;
7385           nodesRecur.insert(n);
7386           while (!stopRecur) {
7387             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7388             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7389               n = (*nnIt_i).second;
7390               if (!nodesRecur.insert(n).second) {
7391                 // error: recursive dependancy
7392                 stopRecur = true;
7393               }
7394             }
7395             else
7396               stopRecur = true;
7397           }
7398         } ////////// BUG 0020185: end
7399       }
7400       curNodes[ iCur ] = n;
7401       bool isUnique = nodeSet.insert( n ).second;
7402       if ( isUnique )
7403         uniqueNodes[ iUnique++ ] = n;
7404       else
7405         iRepl[ nbRepl++ ] = iCur;
7406       iCur++;
7407     }
7408
7409     // Analyse element topology after replacement
7410
7411     bool isOk = true;
7412     int nbUniqueNodes = nodeSet.size();
7413     if ( nbNodes != nbUniqueNodes ) // some nodes stick
7414     {
7415       if (elem->IsPoly()) // Polygons and Polyhedral volumes
7416       {
7417         if (elem->GetType() == SMDSAbs_Face) // Polygon
7418         {
7419           elemType.Init( elem );
7420           const bool isQuad = elemType.myIsQuad;
7421           if ( isQuad )
7422             SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7423               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7424
7425           // a polygon can divide into several elements
7426           vector<const SMDS_MeshNode *> polygons_nodes;
7427           vector<int> quantities;
7428           int nbNew = SimplifyFace( curNodes, polygons_nodes, quantities );
7429           if (nbNew > 0)
7430           {
7431             vector<const SMDS_MeshNode *> face_nodes;
7432             int inode = 0;
7433             for (int iface = 0; iface < nbNew; iface++)
7434             {
7435               int nbNewNodes = quantities[iface];
7436               face_nodes.assign( polygons_nodes.begin() + inode,
7437                                  polygons_nodes.begin() + inode + nbNewNodes );
7438               inode += nbNewNodes;
7439               if ( isQuad ) // check if a result elem is a valid quadratic polygon
7440               {
7441                 bool isValid = ( nbNewNodes % 2 == 0 );
7442                 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7443                   isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7444                 elemType.SetQuad( isValid );
7445                 if ( isValid ) // put medium nodes after corners
7446                   SMDS_MeshCell::applyInterlaceRev
7447                     ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7448                                                           nbNewNodes ), face_nodes );
7449               }
7450               elemType.SetPoly(( nbNewNodes / ( elemType.myIsQuad + 1 ) > 4 ));
7451
7452               SMDS_MeshElement* newElem = AddElement( face_nodes, elemType );
7453               if ( aShapeId )
7454                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7455             }
7456           }
7457           rmElemIds.push_back(elem->GetID());
7458
7459         } // Polygon
7460
7461         else if (elem->GetType() == SMDSAbs_Volume) // Polyhedral volume
7462         {
7463           if (nbUniqueNodes < 4) {
7464             rmElemIds.push_back(elem->GetID());
7465           }
7466           else {
7467             // each face has to be analyzed in order to check volume validity
7468             const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
7469             if (aPolyedre)
7470             {
7471               int nbFaces = aPolyedre->NbFaces();
7472
7473               vector<const SMDS_MeshNode *> poly_nodes;
7474               vector<int> quantities;
7475
7476               for (int iface = 1; iface <= nbFaces; iface++) {
7477                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7478                 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7479
7480                 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7481                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7482                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7483                   if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7484                     faceNode = (*nnIt).second;
7485                   }
7486                   faceNodes[inode - 1] = faceNode;
7487                 }
7488
7489                 SimplifyFace(faceNodes, poly_nodes, quantities);
7490               }
7491
7492               if (quantities.size() > 3) {
7493                 // to be done: remove coincident faces
7494               }
7495
7496               if (quantities.size() > 3)
7497               {
7498                 const SMDS_MeshElement* newElem =
7499                   aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7500                 myLastCreatedElems.Append(newElem);
7501                 if ( aShapeId && newElem )
7502                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7503                 rmElemIds.push_back(elem->GetID());
7504               }
7505             }
7506             else {
7507               rmElemIds.push_back(elem->GetID());
7508             }
7509           }
7510         }
7511         else {
7512         }
7513
7514         continue;
7515       } // poly element
7516
7517       // Regular elements
7518       // TODO not all the possible cases are solved. Find something more generic?
7519       switch ( nbNodes ) {
7520       case 2: ///////////////////////////////////// EDGE
7521         isOk = false; break;
7522       case 3: ///////////////////////////////////// TRIANGLE
7523         isOk = false; break;
7524       case 4:
7525         if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7526           isOk = false;
7527         else { //////////////////////////////////// QUADRANGLE
7528           if ( nbUniqueNodes < 3 )
7529             isOk = false;
7530           else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7531             isOk = false; // opposite nodes stick
7532           //MESSAGE("isOk " << isOk);
7533         }
7534         break;
7535       case 6: ///////////////////////////////////// PENTAHEDRON
7536         if ( nbUniqueNodes == 4 ) {
7537           // ---------------------------------> tetrahedron
7538           if (nbRepl == 3 &&
7539               iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7540             // all top nodes stick: reverse a bottom
7541             uniqueNodes[ 0 ] = curNodes [ 1 ];
7542             uniqueNodes[ 1 ] = curNodes [ 0 ];
7543           }
7544           else if (nbRepl == 3 &&
7545                    iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7546             // all bottom nodes stick: set a top before
7547             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7548             uniqueNodes[ 0 ] = curNodes [ 3 ];
7549             uniqueNodes[ 1 ] = curNodes [ 4 ];
7550             uniqueNodes[ 2 ] = curNodes [ 5 ];
7551           }
7552           else if (nbRepl == 4 &&
7553                    iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7554             // a lateral face turns into a line: reverse a bottom
7555             uniqueNodes[ 0 ] = curNodes [ 1 ];
7556             uniqueNodes[ 1 ] = curNodes [ 0 ];
7557           }
7558           else
7559             isOk = false;
7560         }
7561         else if ( nbUniqueNodes == 5 ) {
7562           // PENTAHEDRON --------------------> 2 tetrahedrons
7563           if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7564             // a bottom node sticks with a linked top one
7565             // 1.
7566             SMDS_MeshElement* newElem =
7567               aMesh->AddVolume(curNodes[ 3 ],
7568                                curNodes[ 4 ],
7569                                curNodes[ 5 ],
7570                                curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7571             myLastCreatedElems.Append(newElem);
7572             if ( aShapeId )
7573               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7574             // 2. : reverse a bottom
7575             uniqueNodes[ 0 ] = curNodes [ 1 ];
7576             uniqueNodes[ 1 ] = curNodes [ 0 ];
7577             nbUniqueNodes = 4;
7578           }
7579           else
7580             isOk = false;
7581         }
7582         else
7583           isOk = false;
7584         break;
7585       case 8: {
7586         if(elem->IsQuadratic()) { // Quadratic quadrangle
7587           //   1    5    2
7588           //    +---+---+
7589           //    |       |
7590           //    |       |
7591           //   4+       +6
7592           //    |       |
7593           //    |       |
7594           //    +---+---+
7595           //   0    7    3
7596           isOk = false;
7597           if(nbRepl==2) {
7598             MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7599           }
7600           if(nbRepl==3) {
7601             MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2]);
7602             nbUniqueNodes = 6;
7603             if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7604               uniqueNodes[0] = curNodes[0];
7605               uniqueNodes[1] = curNodes[2];
7606               uniqueNodes[2] = curNodes[3];
7607               uniqueNodes[3] = curNodes[5];
7608               uniqueNodes[4] = curNodes[6];
7609               uniqueNodes[5] = curNodes[7];
7610               isOk = true;
7611             }
7612             if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7613               uniqueNodes[0] = curNodes[0];
7614               uniqueNodes[1] = curNodes[1];
7615               uniqueNodes[2] = curNodes[2];
7616               uniqueNodes[3] = curNodes[4];
7617               uniqueNodes[4] = curNodes[5];
7618               uniqueNodes[5] = curNodes[6];
7619               isOk = true;
7620             }
7621             if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7622               uniqueNodes[0] = curNodes[1];
7623               uniqueNodes[1] = curNodes[2];
7624               uniqueNodes[2] = curNodes[3];
7625               uniqueNodes[3] = curNodes[5];
7626               uniqueNodes[4] = curNodes[6];
7627               uniqueNodes[5] = curNodes[0];
7628               isOk = true;
7629             }
7630             if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7631               uniqueNodes[0] = curNodes[0];
7632               uniqueNodes[1] = curNodes[1];
7633               uniqueNodes[2] = curNodes[3];
7634               uniqueNodes[3] = curNodes[4];
7635               uniqueNodes[4] = curNodes[6];
7636               uniqueNodes[5] = curNodes[7];
7637               isOk = true;
7638             }
7639             if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7640               uniqueNodes[0] = curNodes[0];
7641               uniqueNodes[1] = curNodes[2];
7642               uniqueNodes[2] = curNodes[3];
7643               uniqueNodes[3] = curNodes[1];
7644               uniqueNodes[4] = curNodes[6];
7645               uniqueNodes[5] = curNodes[7];
7646               isOk = true;
7647             }
7648             if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7649               uniqueNodes[0] = curNodes[0];
7650               uniqueNodes[1] = curNodes[1];
7651               uniqueNodes[2] = curNodes[2];
7652               uniqueNodes[3] = curNodes[4];
7653               uniqueNodes[4] = curNodes[5];
7654               uniqueNodes[5] = curNodes[7];
7655               isOk = true;
7656             }
7657             if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7658               uniqueNodes[0] = curNodes[0];
7659               uniqueNodes[1] = curNodes[1];
7660               uniqueNodes[2] = curNodes[3];
7661               uniqueNodes[3] = curNodes[4];
7662               uniqueNodes[4] = curNodes[2];
7663               uniqueNodes[5] = curNodes[7];
7664               isOk = true;
7665             }
7666             if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7667               uniqueNodes[0] = curNodes[0];
7668               uniqueNodes[1] = curNodes[1];
7669               uniqueNodes[2] = curNodes[2];
7670               uniqueNodes[3] = curNodes[4];
7671               uniqueNodes[4] = curNodes[5];
7672               uniqueNodes[5] = curNodes[3];
7673               isOk = true;
7674             }
7675           }
7676           if(nbRepl==4) {
7677             MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3]);
7678           }
7679           if(nbRepl==5) {
7680             MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7681           }
7682           break;
7683         }
7684         //////////////////////////////////// HEXAHEDRON
7685         isOk = false;
7686         SMDS_VolumeTool hexa (elem);
7687         hexa.SetExternalNormal();
7688         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7689           //////////////////////// HEX ---> 1 tetrahedron
7690           for ( int iFace = 0; iFace < 6; iFace++ ) {
7691             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7692             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7693                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7694                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7695               // one face turns into a point ...
7696               int iOppFace = hexa.GetOppFaceIndex( iFace );
7697               ind = hexa.GetFaceNodesIndices( iOppFace );
7698               int nbStick = 0;
7699               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7700                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7701                   nbStick++;
7702               }
7703               if ( nbStick == 1 ) {
7704                 // ... and the opposite one - into a triangle.
7705                 // set a top node
7706                 ind = hexa.GetFaceNodesIndices( iFace );
7707                 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7708                 isOk = true;
7709               }
7710               break;
7711             }
7712           }
7713         }
7714         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7715           //////////////////////// HEX ---> 1 prism
7716           int nbTria = 0, iTria[3];
7717           const int *ind; // indices of face nodes
7718           // look for triangular faces
7719           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7720             ind = hexa.GetFaceNodesIndices( iFace );
7721             TIDSortedNodeSet faceNodes;
7722             for ( iCur = 0; iCur < 4; iCur++ )
7723               faceNodes.insert( curNodes[ind[iCur]] );
7724             if ( faceNodes.size() == 3 )
7725               iTria[ nbTria++ ] = iFace;
7726           }
7727           // check if triangles are opposite
7728           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7729           {
7730             isOk = true;
7731             // set nodes of the bottom triangle
7732             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7733             vector<int> indB;
7734             for ( iCur = 0; iCur < 4; iCur++ )
7735               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7736                 indB.push_back( ind[iCur] );
7737             if ( !hexa.IsForward() )
7738               std::swap( indB[0], indB[2] );
7739             for ( iCur = 0; iCur < 3; iCur++ )
7740               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7741             // set nodes of the top triangle
7742             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7743             for ( iCur = 0; iCur < 3; ++iCur )
7744               for ( int j = 0; j < 4; ++j )
7745                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7746                 {
7747                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7748                   break;
7749                 }
7750           }
7751           break;
7752         }
7753         else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7754           //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7755           for ( int iFace = 0; iFace < 6; iFace++ ) {
7756             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7757             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7758                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7759                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7760               // one face turns into a point ...
7761               int iOppFace = hexa.GetOppFaceIndex( iFace );
7762               ind = hexa.GetFaceNodesIndices( iOppFace );
7763               int nbStick = 0;
7764               iUnique = 2;  // reverse a tetrahedron 1 bottom
7765               for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7766                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7767                   nbStick++;
7768                 else if ( iUnique >= 0 )
7769                   uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7770               }
7771               if ( nbStick == 0 ) {
7772                 // ... and the opposite one is a quadrangle
7773                 // set a top node
7774                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7775                 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7776                 nbUniqueNodes = 4;
7777                 // tetrahedron 2
7778                 SMDS_MeshElement* newElem =
7779                   aMesh->AddVolume(curNodes[ind[ 0 ]],
7780                                    curNodes[ind[ 3 ]],
7781                                    curNodes[ind[ 2 ]],
7782                                    curNodes[indTop[ 0 ]]);
7783                 myLastCreatedElems.Append(newElem);
7784                 if ( aShapeId )
7785                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7786                 isOk = true;
7787               }
7788               break;
7789             }
7790           }
7791         }
7792         else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7793           ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7794           // find indices of quad and tri faces
7795           int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7796           for ( iFace = 0; iFace < 6; iFace++ ) {
7797             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7798             nodeSet.clear();
7799             for ( iCur = 0; iCur < 4; iCur++ )
7800               nodeSet.insert( curNodes[ind[ iCur ]] );
7801             nbUniqueNodes = nodeSet.size();
7802             if ( nbUniqueNodes == 3 )
7803               iTriFace[ nbTri++ ] = iFace;
7804             else if ( nbUniqueNodes == 4 )
7805               iQuadFace[ nbQuad++ ] = iFace;
7806           }
7807           if (nbQuad == 2 && nbTri == 4 &&
7808               hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7809             // 2 opposite quadrangles stuck with a diagonal;
7810             // sample groups of merged indices: (0-4)(2-6)
7811             // --------------------------------------------> 2 tetrahedrons
7812             const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7813             const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7814             int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7815             if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7816                 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7817               // stuck with 0-2 diagonal
7818               i0  = ind1[ 3 ];
7819               i1d = ind1[ 0 ];
7820               i2  = ind1[ 1 ];
7821               i3d = ind1[ 2 ];
7822               i0t = ind2[ 1 ];
7823               i2t = ind2[ 3 ];
7824             }
7825             else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7826                      curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7827               // stuck with 1-3 diagonal
7828               i0  = ind1[ 0 ];
7829               i1d = ind1[ 1 ];
7830               i2  = ind1[ 2 ];
7831               i3d = ind1[ 3 ];
7832               i0t = ind2[ 0 ];
7833               i2t = ind2[ 1 ];
7834             }
7835             else {
7836               ASSERT(0);
7837             }
7838             // tetrahedron 1
7839             uniqueNodes[ 0 ] = curNodes [ i0 ];
7840             uniqueNodes[ 1 ] = curNodes [ i1d ];
7841             uniqueNodes[ 2 ] = curNodes [ i3d ];
7842             uniqueNodes[ 3 ] = curNodes [ i0t ];
7843             nbUniqueNodes = 4;
7844             // tetrahedron 2
7845             SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7846                                                          curNodes[ i2 ],
7847                                                          curNodes[ i3d ],
7848                                                          curNodes[ i2t ]);
7849             myLastCreatedElems.Append(newElem);
7850             if ( aShapeId )
7851               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7852             isOk = true;
7853           }
7854           else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7855                    ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7856             // --------------------------------------------> prism
7857             // find 2 opposite triangles
7858             nbUniqueNodes = 6;
7859             for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7860               if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7861                 // find indices of kept and replaced nodes
7862                 // and fill unique nodes of 2 opposite triangles
7863                 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7864                 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7865                 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7866                 // fill unique nodes
7867                 iUnique = 0;
7868                 isOk = true;
7869                 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7870                   const SMDS_MeshNode* n     = curNodes[ind1[ iCur ]];
7871                   const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7872                   if ( n == nInit ) {
7873                     // iCur of a linked node of the opposite face (make normals co-directed):
7874                     int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7875                     // check that correspondent corners of triangles are linked
7876                     if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7877                       isOk = false;
7878                     else {
7879                       uniqueNodes[ iUnique ] = n;
7880                       uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7881                       iUnique++;
7882                     }
7883                   }
7884                 }
7885                 break;
7886               }
7887             }
7888           }
7889         } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7890         else
7891         {
7892           MESSAGE("MergeNodes() removes hexahedron "<< elem);
7893         }
7894         break;
7895       } // HEXAHEDRON
7896
7897       default:
7898         isOk = false;
7899       } // switch ( nbNodes )
7900
7901     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7902
7903     if ( isOk ) // the non-poly elem remains valid after sticking nodes
7904     {
7905       if ( nbNodes != nbUniqueNodes ||
7906            !aMesh->ChangeElementNodes( elem, & curNodes[0], nbNodes ))
7907       {
7908         elemType.Init( elem ).SetID( elem->GetID() );
7909
7910         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7911         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7912
7913         uniqueNodes.resize(nbUniqueNodes);
7914         SMDS_MeshElement* newElem = this->AddElement( uniqueNodes, elemType );
7915         if ( sm && newElem )
7916           sm->AddElement( newElem );
7917         if ( elem != newElem )
7918           ReplaceElemInGroups( elem, newElem, aMesh );
7919       }
7920     }
7921     else {
7922       // Remove invalid regular element or invalid polygon
7923       rmElemIds.push_back( elem->GetID() );
7924     }
7925
7926   } // loop on elements
7927
7928   // Remove bad elements, then equal nodes (order important)
7929
7930   Remove( rmElemIds, false );
7931   Remove( rmNodeIds, true );
7932
7933   return;
7934 }
7935
7936
7937 // ========================================================
7938 // class   : SortableElement
7939 // purpose : allow sorting elements basing on their nodes
7940 // ========================================================
7941 class SortableElement : public set <const SMDS_MeshElement*>
7942 {
7943 public:
7944
7945   SortableElement( const SMDS_MeshElement* theElem )
7946   {
7947     myElem = theElem;
7948     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7949     while ( nodeIt->more() )
7950       this->insert( nodeIt->next() );
7951   }
7952
7953   const SMDS_MeshElement* Get() const
7954   { return myElem; }
7955
7956 private:
7957   mutable const SMDS_MeshElement* myElem;
7958 };
7959
7960 //=======================================================================
7961 //function : FindEqualElements
7962 //purpose  : Return list of group of elements built on the same nodes.
7963 //           Search among theElements or in the whole mesh if theElements is empty
7964 //=======================================================================
7965
7966 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
7967                                          TListOfListOfElementsID & theGroupsOfElementsID)
7968 {
7969   myLastCreatedElems.Clear();
7970   myLastCreatedNodes.Clear();
7971
7972   typedef map< SortableElement, int > TMapOfNodeSet;
7973   typedef list<int> TGroupOfElems;
7974
7975   if ( theElements.empty() )
7976   { // get all elements in the mesh
7977     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7978     while ( eIt->more() )
7979       theElements.insert( theElements.end(), eIt->next() );
7980   }
7981
7982   vector< TGroupOfElems > arrayOfGroups;
7983   TGroupOfElems groupOfElems;
7984   TMapOfNodeSet mapOfNodeSet;
7985
7986   TIDSortedElemSet::iterator elemIt = theElements.begin();
7987   for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7988   {
7989     const SMDS_MeshElement* curElem = *elemIt;
7990     SortableElement SE(curElem);
7991     // check uniqueness
7992     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7993     if ( !pp.second ) { // one more coincident elem
7994       TMapOfNodeSet::iterator& itSE = pp.first;
7995       int ind = (*itSE).second;
7996       arrayOfGroups[ind].push_back( curElem->GetID() );
7997     }
7998     else {
7999       arrayOfGroups.push_back( groupOfElems );
8000       arrayOfGroups.back().push_back( curElem->GetID() );
8001       i++;
8002     }
8003   }
8004
8005   groupOfElems.clear();
8006   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8007   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8008   {
8009     if ( groupIt->size() > 1 ) {
8010       //groupOfElems.sort(); -- theElements is sorted already
8011       theGroupsOfElementsID.push_back( groupOfElems );
8012       theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
8013     }
8014   }
8015 }
8016
8017 //=======================================================================
8018 //function : MergeElements
8019 //purpose  : In each given group, substitute all elements by the first one.
8020 //=======================================================================
8021
8022 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8023 {
8024   myLastCreatedElems.Clear();
8025   myLastCreatedNodes.Clear();
8026
8027   typedef list<int> TListOfIDs;
8028   TListOfIDs rmElemIds; // IDs of elems to remove
8029
8030   SMESHDS_Mesh* aMesh = GetMeshDS();
8031
8032   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8033   while ( groupsIt != theGroupsOfElementsID.end() ) {
8034     TListOfIDs& aGroupOfElemID = *groupsIt;
8035     aGroupOfElemID.sort();
8036     int elemIDToKeep = aGroupOfElemID.front();
8037     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8038     aGroupOfElemID.pop_front();
8039     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8040     while ( idIt != aGroupOfElemID.end() ) {
8041       int elemIDToRemove = *idIt;
8042       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8043       // add the kept element in groups of removed one (PAL15188)
8044       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8045       rmElemIds.push_back( elemIDToRemove );
8046       ++idIt;
8047     }
8048     ++groupsIt;
8049   }
8050
8051   Remove( rmElemIds, false );
8052 }
8053
8054 //=======================================================================
8055 //function : MergeEqualElements
8056 //purpose  : Remove all but one of elements built on the same nodes.
8057 //=======================================================================
8058
8059 void SMESH_MeshEditor::MergeEqualElements()
8060 {
8061   TIDSortedElemSet aMeshElements; /* empty input ==
8062                                      to merge equal elements in the whole mesh */
8063   TListOfListOfElementsID aGroupsOfElementsID;
8064   FindEqualElements(aMeshElements, aGroupsOfElementsID);
8065   MergeElements(aGroupsOfElementsID);
8066 }
8067
8068 //=======================================================================
8069 //function : findAdjacentFace
8070 //purpose  :
8071 //=======================================================================
8072
8073 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8074                                                 const SMDS_MeshNode* n2,
8075                                                 const SMDS_MeshElement* elem)
8076 {
8077   TIDSortedElemSet elemSet, avoidSet;
8078   if ( elem )
8079     avoidSet.insert ( elem );
8080   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8081 }
8082
8083 //=======================================================================
8084 //function : findSegment
8085 //purpose  : Return a mesh segment by two nodes one of which can be medium
8086 //=======================================================================
8087
8088 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8089                                            const SMDS_MeshNode* n2)
8090 {
8091   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8092   while ( it->more() )
8093   {
8094     const SMDS_MeshElement* seg = it->next();
8095     if ( seg->GetNodeIndex( n2 ) >= 0 )
8096       return seg;
8097   }
8098   return 0;
8099 }
8100
8101 //=======================================================================
8102 //function : FindFreeBorder
8103 //purpose  :
8104 //=======================================================================
8105
8106 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8107
8108 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
8109                                        const SMDS_MeshNode*             theSecondNode,
8110                                        const SMDS_MeshNode*             theLastNode,
8111                                        list< const SMDS_MeshNode* > &   theNodes,
8112                                        list< const SMDS_MeshElement* >& theFaces)
8113 {
8114   if ( !theFirstNode || !theSecondNode )
8115     return false;
8116   // find border face between theFirstNode and theSecondNode
8117   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8118   if ( !curElem )
8119     return false;
8120
8121   theFaces.push_back( curElem );
8122   theNodes.push_back( theFirstNode );
8123   theNodes.push_back( theSecondNode );
8124
8125   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8126   TIDSortedElemSet foundElems;
8127   bool needTheLast = ( theLastNode != 0 );
8128
8129   while ( nStart != theLastNode ) {
8130     if ( nStart == theFirstNode )
8131       return !needTheLast;
8132
8133     // find all free border faces sharing form nStart
8134
8135     list< const SMDS_MeshElement* > curElemList;
8136     list< const SMDS_MeshNode* >    nStartList;
8137     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8138     while ( invElemIt->more() ) {
8139       const SMDS_MeshElement* e = invElemIt->next();
8140       if ( e == curElem || foundElems.insert( e ).second ) {
8141         // get nodes
8142         int iNode = 0, nbNodes = e->NbNodes();
8143         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8144
8145         if ( e->IsQuadratic() ) {
8146           const SMDS_VtkFace* F =
8147             dynamic_cast<const SMDS_VtkFace*>(e);
8148           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8149           // use special nodes iterator
8150           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8151           while( anIter->more() ) {
8152             nodes[ iNode++ ] = cast2Node(anIter->next());
8153           }
8154         }
8155         else {
8156           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8157           while ( nIt->more() )
8158             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8159         }
8160         nodes[ iNode ] = nodes[ 0 ];
8161         // check 2 links
8162         for ( iNode = 0; iNode < nbNodes; iNode++ )
8163           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8164                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8165               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8166           {
8167             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8168             curElemList.push_back( e );
8169           }
8170       }
8171     }
8172     // analyse the found
8173
8174     int nbNewBorders = curElemList.size();
8175     if ( nbNewBorders == 0 ) {
8176       // no free border furthermore
8177       return !needTheLast;
8178     }
8179     else if ( nbNewBorders == 1 ) {
8180       // one more element found
8181       nIgnore = nStart;
8182       nStart = nStartList.front();
8183       curElem = curElemList.front();
8184       theFaces.push_back( curElem );
8185       theNodes.push_back( nStart );
8186     }
8187     else {
8188       // several continuations found
8189       list< const SMDS_MeshElement* >::iterator curElemIt;
8190       list< const SMDS_MeshNode* >::iterator nStartIt;
8191       // check if one of them reached the last node
8192       if ( needTheLast ) {
8193         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8194              curElemIt!= curElemList.end();
8195              curElemIt++, nStartIt++ )
8196           if ( *nStartIt == theLastNode ) {
8197             theFaces.push_back( *curElemIt );
8198             theNodes.push_back( *nStartIt );
8199             return true;
8200           }
8201       }
8202       // find the best free border by the continuations
8203       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8204       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8205       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8206            curElemIt!= curElemList.end();
8207            curElemIt++, nStartIt++ )
8208       {
8209         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8210         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8211         // find one more free border
8212         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8213           cNL->clear();
8214           cFL->clear();
8215         }
8216         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8217           // choice: clear a worse one
8218           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8219           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8220           contNodes[ iWorse ].clear();
8221           contFaces[ iWorse ].clear();
8222         }
8223       }
8224       if ( contNodes[0].empty() && contNodes[1].empty() )
8225         return false;
8226
8227       // append the best free border
8228       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8229       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8230       theNodes.pop_back(); // remove nIgnore
8231       theNodes.pop_back(); // remove nStart
8232       theFaces.pop_back(); // remove curElem
8233       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8234       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8235       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8236       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8237       return true;
8238
8239     } // several continuations found
8240   } // while ( nStart != theLastNode )
8241
8242   return true;
8243 }
8244
8245 //=======================================================================
8246 //function : CheckFreeBorderNodes
8247 //purpose  : Return true if the tree nodes are on a free border
8248 //=======================================================================
8249
8250 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8251                                             const SMDS_MeshNode* theNode2,
8252                                             const SMDS_MeshNode* theNode3)
8253 {
8254   list< const SMDS_MeshNode* > nodes;
8255   list< const SMDS_MeshElement* > faces;
8256   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8257 }
8258
8259 //=======================================================================
8260 //function : SewFreeBorder
8261 //purpose  :
8262 //warning  : for border-to-side sewing theSideSecondNode is considered as
8263 //           the last side node and theSideThirdNode is not used
8264 //=======================================================================
8265
8266 SMESH_MeshEditor::Sew_Error
8267 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8268                                  const SMDS_MeshNode* theBordSecondNode,
8269                                  const SMDS_MeshNode* theBordLastNode,
8270                                  const SMDS_MeshNode* theSideFirstNode,
8271                                  const SMDS_MeshNode* theSideSecondNode,
8272                                  const SMDS_MeshNode* theSideThirdNode,
8273                                  const bool           theSideIsFreeBorder,
8274                                  const bool           toCreatePolygons,
8275                                  const bool           toCreatePolyedrs)
8276 {
8277   myLastCreatedElems.Clear();
8278   myLastCreatedNodes.Clear();
8279
8280   MESSAGE("::SewFreeBorder()");
8281   Sew_Error aResult = SEW_OK;
8282
8283   // ====================================
8284   //    find side nodes and elements
8285   // ====================================
8286
8287   list< const SMDS_MeshNode* >    nSide[ 2 ];
8288   list< const SMDS_MeshElement* > eSide[ 2 ];
8289   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
8290   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8291
8292   // Free border 1
8293   // --------------
8294   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8295                       nSide[0], eSide[0])) {
8296     MESSAGE(" Free Border 1 not found " );
8297     aResult = SEW_BORDER1_NOT_FOUND;
8298   }
8299   if (theSideIsFreeBorder) {
8300     // Free border 2
8301     // --------------
8302     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8303                         nSide[1], eSide[1])) {
8304       MESSAGE(" Free Border 2 not found " );
8305       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8306     }
8307   }
8308   if ( aResult != SEW_OK )
8309     return aResult;
8310
8311   if (!theSideIsFreeBorder) {
8312     // Side 2
8313     // --------------
8314
8315     // -------------------------------------------------------------------------
8316     // Algo:
8317     // 1. If nodes to merge are not coincident, move nodes of the free border
8318     //    from the coord sys defined by the direction from the first to last
8319     //    nodes of the border to the correspondent sys of the side 2
8320     // 2. On the side 2, find the links most co-directed with the correspondent
8321     //    links of the free border
8322     // -------------------------------------------------------------------------
8323
8324     // 1. Since sewing may break if there are volumes to split on the side 2,
8325     //    we wont move nodes but just compute new coordinates for them
8326     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8327     TNodeXYZMap nBordXYZ;
8328     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8329     list< const SMDS_MeshNode* >::iterator nBordIt;
8330
8331     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8332     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8333     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8334     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8335     double tol2 = 1.e-8;
8336     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8337     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8338       // Need node movement.
8339
8340       // find X and Z axes to create trsf
8341       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8342       gp_Vec X = Zs ^ Zb;
8343       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8344         // Zb || Zs
8345         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8346
8347       // coord systems
8348       gp_Ax3 toBordAx( Pb1, Zb, X );
8349       gp_Ax3 fromSideAx( Ps1, Zs, X );
8350       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8351       // set trsf
8352       gp_Trsf toBordSys, fromSide2Sys;
8353       toBordSys.SetTransformation( toBordAx );
8354       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8355       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8356
8357       // move
8358       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8359         const SMDS_MeshNode* n = *nBordIt;
8360         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8361         toBordSys.Transforms( xyz );
8362         fromSide2Sys.Transforms( xyz );
8363         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8364       }
8365     }
8366     else {
8367       // just insert nodes XYZ in the nBordXYZ map
8368       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8369         const SMDS_MeshNode* n = *nBordIt;
8370         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8371       }
8372     }
8373
8374     // 2. On the side 2, find the links most co-directed with the correspondent
8375     //    links of the free border
8376
8377     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8378     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8379     sideNodes.push_back( theSideFirstNode );
8380
8381     bool hasVolumes = false;
8382     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8383     set<long> foundSideLinkIDs, checkedLinkIDs;
8384     SMDS_VolumeTool volume;
8385     //const SMDS_MeshNode* faceNodes[ 4 ];
8386
8387     const SMDS_MeshNode*    sideNode;
8388     const SMDS_MeshElement* sideElem;
8389     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8390     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8391     nBordIt = bordNodes.begin();
8392     nBordIt++;
8393     // border node position and border link direction to compare with
8394     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8395     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8396     // choose next side node by link direction or by closeness to
8397     // the current border node:
8398     bool searchByDir = ( *nBordIt != theBordLastNode );
8399     do {
8400       // find the next node on the Side 2
8401       sideNode = 0;
8402       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8403       long linkID;
8404       checkedLinkIDs.clear();
8405       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8406
8407       // loop on inverse elements of current node (prevSideNode) on the Side 2
8408       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8409       while ( invElemIt->more() )
8410       {
8411         const SMDS_MeshElement* elem = invElemIt->next();
8412         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8413         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8414         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8415         bool isVolume = volume.Set( elem );
8416         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8417         if ( isVolume ) // --volume
8418           hasVolumes = true;
8419         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8420           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8421           if(elem->IsQuadratic()) {
8422             const SMDS_VtkFace* F =
8423               dynamic_cast<const SMDS_VtkFace*>(elem);
8424             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8425             // use special nodes iterator
8426             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8427             while( anIter->more() ) {
8428               nodes[ iNode ] = cast2Node(anIter->next());
8429               if ( nodes[ iNode++ ] == prevSideNode )
8430                 iPrevNode = iNode - 1;
8431             }
8432           }
8433           else {
8434             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8435             while ( nIt->more() ) {
8436               nodes[ iNode ] = cast2Node( nIt->next() );
8437               if ( nodes[ iNode++ ] == prevSideNode )
8438                 iPrevNode = iNode - 1;
8439             }
8440           }
8441           // there are 2 links to check
8442           nbNodes = 2;
8443         }
8444         else // --edge
8445           continue;
8446         // loop on links, to be precise, on the second node of links
8447         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8448           const SMDS_MeshNode* n = nodes[ iNode ];
8449           if ( isVolume ) {
8450             if ( !volume.IsLinked( n, prevSideNode ))
8451               continue;
8452           }
8453           else {
8454             if ( iNode ) // a node before prevSideNode
8455               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8456             else         // a node after prevSideNode
8457               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8458           }
8459           // check if this link was already used
8460           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8461           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8462           if (!isJustChecked &&
8463               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8464           {
8465             // test a link geometrically
8466             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8467             bool linkIsBetter = false;
8468             double dot = 0.0, dist = 0.0;
8469             if ( searchByDir ) { // choose most co-directed link
8470               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8471               linkIsBetter = ( dot > maxDot );
8472             }
8473             else { // choose link with the node closest to bordPos
8474               dist = ( nextXYZ - bordPos ).SquareModulus();
8475               linkIsBetter = ( dist < minDist );
8476             }
8477             if ( linkIsBetter ) {
8478               maxDot = dot;
8479               minDist = dist;
8480               linkID = iLink;
8481               sideNode = n;
8482               sideElem = elem;
8483             }
8484           }
8485         }
8486       } // loop on inverse elements of prevSideNode
8487
8488       if ( !sideNode ) {
8489         MESSAGE(" Cant find path by links of the Side 2 ");
8490         return SEW_BAD_SIDE_NODES;
8491       }
8492       sideNodes.push_back( sideNode );
8493       sideElems.push_back( sideElem );
8494       foundSideLinkIDs.insert ( linkID );
8495       prevSideNode = sideNode;
8496
8497       if ( *nBordIt == theBordLastNode )
8498         searchByDir = false;
8499       else {
8500         // find the next border link to compare with
8501         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8502         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8503         // move to next border node if sideNode is before forward border node (bordPos)
8504         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8505           prevBordNode = *nBordIt;
8506           nBordIt++;
8507           bordPos = nBordXYZ[ *nBordIt ];
8508           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8509           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8510         }
8511       }
8512     }
8513     while ( sideNode != theSideSecondNode );
8514
8515     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8516       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8517       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8518     }
8519   } // end nodes search on the side 2
8520
8521   // ============================
8522   // sew the border to the side 2
8523   // ============================
8524
8525   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8526   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8527
8528   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8529   if ( toMergeConformal && toCreatePolygons )
8530   {
8531     // do not merge quadrangles if polygons are OK (IPAL0052824)
8532     eIt[0] = eSide[0].begin();
8533     eIt[1] = eSide[1].begin();
8534     bool allQuads[2] = { true, true };
8535     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8536       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8537         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8538     }
8539     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8540   }
8541
8542   TListOfListOfNodes nodeGroupsToMerge;
8543   if (( toMergeConformal ) ||
8544       ( theSideIsFreeBorder && !theSideThirdNode )) {
8545
8546     // all nodes are to be merged
8547
8548     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8549          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8550          nIt[0]++, nIt[1]++ )
8551     {
8552       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8553       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8554       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8555     }
8556   }
8557   else {
8558
8559     // insert new nodes into the border and the side to get equal nb of segments
8560
8561     // get normalized parameters of nodes on the borders
8562     vector< double > param[ 2 ];
8563     param[0].resize( maxNbNodes );
8564     param[1].resize( maxNbNodes );
8565     int iNode, iBord;
8566     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8567       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8568       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8569       const SMDS_MeshNode* nPrev = *nIt;
8570       double bordLength = 0;
8571       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8572         const SMDS_MeshNode* nCur = *nIt;
8573         gp_XYZ segment (nCur->X() - nPrev->X(),
8574                         nCur->Y() - nPrev->Y(),
8575                         nCur->Z() - nPrev->Z());
8576         double segmentLen = segment.Modulus();
8577         bordLength += segmentLen;
8578         param[ iBord ][ iNode ] = bordLength;
8579         nPrev = nCur;
8580       }
8581       // normalize within [0,1]
8582       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8583         param[ iBord ][ iNode ] /= bordLength;
8584       }
8585     }
8586
8587     // loop on border segments
8588     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8589     int i[ 2 ] = { 0, 0 };
8590     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8591     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8592
8593     TElemOfNodeListMap insertMap;
8594     TElemOfNodeListMap::iterator insertMapIt;
8595     // insertMap is
8596     // key:   elem to insert nodes into
8597     // value: 2 nodes to insert between + nodes to be inserted
8598     do {
8599       bool next[ 2 ] = { false, false };
8600
8601       // find min adjacent segment length after sewing
8602       double nextParam = 10., prevParam = 0;
8603       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8604         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8605           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8606         if ( i[ iBord ] > 0 )
8607           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8608       }
8609       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8610       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8611       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8612
8613       // choose to insert or to merge nodes
8614       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8615       if ( Abs( du ) <= minSegLen * 0.2 ) {
8616         // merge
8617         // ------
8618         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8619         const SMDS_MeshNode* n0 = *nIt[0];
8620         const SMDS_MeshNode* n1 = *nIt[1];
8621         nodeGroupsToMerge.back().push_back( n1 );
8622         nodeGroupsToMerge.back().push_back( n0 );
8623         // position of node of the border changes due to merge
8624         param[ 0 ][ i[0] ] += du;
8625         // move n1 for the sake of elem shape evaluation during insertion.
8626         // n1 will be removed by MergeNodes() anyway
8627         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8628         next[0] = next[1] = true;
8629       }
8630       else {
8631         // insert
8632         // ------
8633         int intoBord = ( du < 0 ) ? 0 : 1;
8634         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8635         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8636         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8637         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8638         if ( intoBord == 1 ) {
8639           // move node of the border to be on a link of elem of the side
8640           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8641           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8642           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8643           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8644           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8645         }
8646         insertMapIt = insertMap.find( elem );
8647         bool  notFound = ( insertMapIt == insertMap.end() );
8648         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8649         if ( otherLink ) {
8650           // insert into another link of the same element:
8651           // 1. perform insertion into the other link of the elem
8652           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8653           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8654           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8655           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8656           // 2. perform insertion into the link of adjacent faces
8657           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8658             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8659           }
8660           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8661             InsertNodesIntoLink( seg, n12, n22, nodeList );
8662           }
8663           if (toCreatePolyedrs) {
8664             // perform insertion into the links of adjacent volumes
8665             UpdateVolumes(n12, n22, nodeList);
8666           }
8667           // 3. find an element appeared on n1 and n2 after the insertion
8668           insertMap.erase( elem );
8669           elem = findAdjacentFace( n1, n2, 0 );
8670         }
8671         if ( notFound || otherLink ) {
8672           // add element and nodes of the side into the insertMap
8673           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8674           (*insertMapIt).second.push_back( n1 );
8675           (*insertMapIt).second.push_back( n2 );
8676         }
8677         // add node to be inserted into elem
8678         (*insertMapIt).second.push_back( nIns );
8679         next[ 1 - intoBord ] = true;
8680       }
8681
8682       // go to the next segment
8683       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8684         if ( next[ iBord ] ) {
8685           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8686             eIt[ iBord ]++;
8687           nPrev[ iBord ] = *nIt[ iBord ];
8688           nIt[ iBord ]++; i[ iBord ]++;
8689         }
8690       }
8691     }
8692     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8693
8694     // perform insertion of nodes into elements
8695
8696     for (insertMapIt = insertMap.begin();
8697          insertMapIt != insertMap.end();
8698          insertMapIt++ )
8699     {
8700       const SMDS_MeshElement* elem = (*insertMapIt).first;
8701       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8702       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8703       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8704
8705       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8706
8707       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8708         InsertNodesIntoLink( seg, n1, n2, nodeList );
8709       }
8710
8711       if ( !theSideIsFreeBorder ) {
8712         // look for and insert nodes into the faces adjacent to elem
8713         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8714           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8715         }
8716       }
8717       if (toCreatePolyedrs) {
8718         // perform insertion into the links of adjacent volumes
8719         UpdateVolumes(n1, n2, nodeList);
8720       }
8721     }
8722   } // end: insert new nodes
8723
8724   MergeNodes ( nodeGroupsToMerge );
8725
8726
8727   // Remove coincident segments
8728
8729   // get new segments
8730   TIDSortedElemSet segments;
8731   SMESH_SequenceOfElemPtr newFaces;
8732   for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8733   {
8734     if ( !myLastCreatedElems(i) ) continue;
8735     if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8736       segments.insert( segments.end(), myLastCreatedElems(i) );
8737     else
8738       newFaces.Append( myLastCreatedElems(i) );
8739   }
8740   // get segments adjacent to merged nodes
8741   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8742   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8743   {
8744     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8745     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8746     while ( segIt->more() )
8747       segments.insert( segIt->next() );
8748   }
8749
8750   // find coincident
8751   TListOfListOfElementsID equalGroups;
8752   if ( !segments.empty() )
8753     FindEqualElements( segments, equalGroups );
8754   if ( !equalGroups.empty() )
8755   {
8756     // remove from segments those that will be removed
8757     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8758     for ( ; itGroups != equalGroups.end(); ++itGroups )
8759     {
8760       list< int >& group = *itGroups;
8761       list< int >::iterator id = group.begin();
8762       for ( ++id; id != group.end(); ++id )
8763         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8764           segments.erase( seg );
8765     }
8766     // remove equal segments
8767     MergeElements( equalGroups );
8768
8769     // restore myLastCreatedElems
8770     myLastCreatedElems = newFaces;
8771     TIDSortedElemSet::iterator seg = segments.begin();
8772     for ( ; seg != segments.end(); ++seg )
8773       myLastCreatedElems.Append( *seg );
8774   }
8775
8776   return aResult;
8777 }
8778
8779 //=======================================================================
8780 //function : InsertNodesIntoLink
8781 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8782 //           and theBetweenNode2 and split theElement
8783 //=======================================================================
8784
8785 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8786                                            const SMDS_MeshNode*        theBetweenNode1,
8787                                            const SMDS_MeshNode*        theBetweenNode2,
8788                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8789                                            const bool                  toCreatePoly)
8790 {
8791   if ( !theElement ) return;
8792
8793   SMESHDS_Mesh *aMesh = GetMeshDS();
8794   vector<const SMDS_MeshElement*> newElems;
8795
8796   if ( theElement->GetType() == SMDSAbs_Edge )
8797   {
8798     theNodesToInsert.push_front( theBetweenNode1 );
8799     theNodesToInsert.push_back ( theBetweenNode2 );
8800     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8801     const SMDS_MeshNode* n1 = *n;
8802     for ( ++n; n != theNodesToInsert.end(); ++n )
8803     {
8804       const SMDS_MeshNode* n2 = *n;
8805       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8806         AddToSameGroups( seg, theElement, aMesh );
8807       else
8808         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8809       n1 = n2;
8810     }
8811     theNodesToInsert.pop_front();
8812     theNodesToInsert.pop_back();
8813
8814     if ( theElement->IsQuadratic() ) // add a not split part
8815     {
8816       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8817                                           theElement->end_nodes() );
8818       int iOther = 0, nbN = nodes.size();
8819       for ( ; iOther < nbN; ++iOther )
8820         if ( nodes[iOther] != theBetweenNode1 &&
8821              nodes[iOther] != theBetweenNode2 )
8822           break;
8823       if      ( iOther == 0 )
8824       {
8825         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8826           AddToSameGroups( seg, theElement, aMesh );
8827         else
8828           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8829       }
8830       else if ( iOther == 2 )
8831       {
8832         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8833           AddToSameGroups( seg, theElement, aMesh );
8834         else
8835           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8836       }
8837     }
8838     // treat new elements
8839     for ( size_t i = 0; i < newElems.size(); ++i )
8840       if ( newElems[i] )
8841       {
8842         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8843         myLastCreatedElems.Append( newElems[i] );
8844       }
8845     ReplaceElemInGroups( theElement, newElems, aMesh );
8846     aMesh->RemoveElement( theElement );
8847     return;
8848
8849   } // if ( theElement->GetType() == SMDSAbs_Edge )
8850
8851   const SMDS_MeshElement* theFace = theElement;
8852   if ( theFace->GetType() != SMDSAbs_Face ) return;
8853
8854   // find indices of 2 link nodes and of the rest nodes
8855   int iNode = 0, il1, il2, i3, i4;
8856   il1 = il2 = i3 = i4 = -1;
8857   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8858
8859   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8860   while ( nodeIt->more() ) {
8861     const SMDS_MeshNode* n = nodeIt->next();
8862     if ( n == theBetweenNode1 )
8863       il1 = iNode;
8864     else if ( n == theBetweenNode2 )
8865       il2 = iNode;
8866     else if ( i3 < 0 )
8867       i3 = iNode;
8868     else
8869       i4 = iNode;
8870     nodes[ iNode++ ] = n;
8871   }
8872   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8873     return ;
8874
8875   // arrange link nodes to go one after another regarding the face orientation
8876   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8877   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8878   if ( reverse ) {
8879     iNode = il1;
8880     il1 = il2;
8881     il2 = iNode;
8882     aNodesToInsert.reverse();
8883   }
8884   // check that not link nodes of a quadrangles are in good order
8885   int nbFaceNodes = theFace->NbNodes();
8886   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8887     iNode = i3;
8888     i3 = i4;
8889     i4 = iNode;
8890   }
8891
8892   if (toCreatePoly || theFace->IsPoly()) {
8893
8894     iNode = 0;
8895     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8896
8897     // add nodes of face up to first node of link
8898     bool isFLN = false;
8899
8900     if ( theFace->IsQuadratic() ) {
8901       const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8902       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8903       // use special nodes iterator
8904       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8905       while( anIter->more()  && !isFLN ) {
8906         const SMDS_MeshNode* n = cast2Node(anIter->next());
8907         poly_nodes[iNode++] = n;
8908         if (n == nodes[il1]) {
8909           isFLN = true;
8910         }
8911       }
8912       // add nodes to insert
8913       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8914       for (; nIt != aNodesToInsert.end(); nIt++) {
8915         poly_nodes[iNode++] = *nIt;
8916       }
8917       // add nodes of face starting from last node of link
8918       while ( anIter->more() ) {
8919         poly_nodes[iNode++] = cast2Node(anIter->next());
8920       }
8921     }
8922     else {
8923       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8924       while ( nodeIt->more() && !isFLN ) {
8925         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8926         poly_nodes[iNode++] = n;
8927         if (n == nodes[il1]) {
8928           isFLN = true;
8929         }
8930       }
8931       // add nodes to insert
8932       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8933       for (; nIt != aNodesToInsert.end(); nIt++) {
8934         poly_nodes[iNode++] = *nIt;
8935       }
8936       // add nodes of face starting from last node of link
8937       while ( nodeIt->more() ) {
8938         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8939         poly_nodes[iNode++] = n;
8940       }
8941     }
8942
8943     // make a new face
8944     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8945   }
8946
8947   else if ( !theFace->IsQuadratic() )
8948   {
8949     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8950     int nbLinkNodes = 2 + aNodesToInsert.size();
8951     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8952     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8953     linkNodes[ 0 ] = nodes[ il1 ];
8954     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8955     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8956     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8957       linkNodes[ iNode++ ] = *nIt;
8958     }
8959     // decide how to split a quadrangle: compare possible variants
8960     // and choose which of splits to be a quadrangle
8961     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8962     if ( nbFaceNodes == 3 ) {
8963       iBestQuad = nbSplits;
8964       i4 = i3;
8965     }
8966     else if ( nbFaceNodes == 4 ) {
8967       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8968       double aBestRate = DBL_MAX;
8969       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8970         i1 = 0; i2 = 1;
8971         double aBadRate = 0;
8972         // evaluate elements quality
8973         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8974           if ( iSplit == iQuad ) {
8975             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8976                                    linkNodes[ i2++ ],
8977                                    nodes[ i3 ],
8978                                    nodes[ i4 ]);
8979             aBadRate += getBadRate( &quad, aCrit );
8980           }
8981           else {
8982             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8983                                    linkNodes[ i2++ ],
8984                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8985             aBadRate += getBadRate( &tria, aCrit );
8986           }
8987         }
8988         // choice
8989         if ( aBadRate < aBestRate ) {
8990           iBestQuad = iQuad;
8991           aBestRate = aBadRate;
8992         }
8993       }
8994     }
8995
8996     // create new elements
8997     i1 = 0; i2 = 1;
8998     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8999     {
9000       if ( iSplit == iBestQuad )
9001         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9002                                             linkNodes[ i2++ ],
9003                                             nodes[ i3 ],
9004                                             nodes[ i4 ]));
9005       else
9006         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9007                                             linkNodes[ i2++ ],
9008                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
9009     }
9010
9011     const SMDS_MeshNode* newNodes[ 4 ];
9012     newNodes[ 0 ] = linkNodes[ i1 ];
9013     newNodes[ 1 ] = linkNodes[ i2 ];
9014     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9015     newNodes[ 3 ] = nodes[ i4 ];
9016     if (iSplit == iBestQuad)
9017       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
9018     else
9019       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
9020
9021   } // end if(!theFace->IsQuadratic())
9022
9023   else { // theFace is quadratic
9024     // we have to split theFace on simple triangles and one simple quadrangle
9025     int tmp = il1/2;
9026     int nbshift = tmp*2;
9027     // shift nodes in nodes[] by nbshift
9028     int i,j;
9029     for(i=0; i<nbshift; i++) {
9030       const SMDS_MeshNode* n = nodes[0];
9031       for(j=0; j<nbFaceNodes-1; j++) {
9032         nodes[j] = nodes[j+1];
9033       }
9034       nodes[nbFaceNodes-1] = n;
9035     }
9036     il1 = il1 - nbshift;
9037     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9038     //   n0      n1     n2    n0      n1     n2
9039     //     +-----+-----+        +-----+-----+
9040     //      \         /         |           |
9041     //       \       /          |           |
9042     //      n5+     +n3       n7+           +n3
9043     //         \   /            |           |
9044     //          \ /             |           |
9045     //           +              +-----+-----+
9046     //           n4           n6      n5     n4
9047
9048     // create new elements
9049     int n1,n2,n3;
9050     if ( nbFaceNodes == 6 ) { // quadratic triangle
9051       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9052       if ( theFace->IsMediumNode(nodes[il1]) ) {
9053         // create quadrangle
9054         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
9055         n1 = 1;
9056         n2 = 2;
9057         n3 = 3;
9058       }
9059       else {
9060         // create quadrangle
9061         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
9062         n1 = 0;
9063         n2 = 1;
9064         n3 = 5;
9065       }
9066     }
9067     else { // nbFaceNodes==8 - quadratic quadrangle
9068       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9069       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9070       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9071       if ( theFace->IsMediumNode( nodes[ il1 ])) {
9072         // create quadrangle
9073         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9074         n1 = 1;
9075         n2 = 2;
9076         n3 = 3;
9077       }
9078       else {
9079         // create quadrangle
9080         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9081         n1 = 0;
9082         n2 = 1;
9083         n3 = 7;
9084       }
9085     }
9086     // create needed triangles using n1,n2,n3 and inserted nodes
9087     int nbn = 2 + aNodesToInsert.size();
9088     vector<const SMDS_MeshNode*> aNodes(nbn);
9089     aNodes[0    ] = nodes[n1];
9090     aNodes[nbn-1] = nodes[n2];
9091     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9092     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9093       aNodes[iNode++] = *nIt;
9094     }
9095     for ( i = 1; i < nbn; i++ )
9096       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9097   }
9098
9099   // remove the old face
9100   for ( size_t i = 0; i < newElems.size(); ++i )
9101     if ( newElems[i] )
9102     {
9103       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9104       myLastCreatedElems.Append( newElems[i] );
9105     }
9106   ReplaceElemInGroups( theFace, newElems, aMesh );
9107   aMesh->RemoveElement(theFace);
9108
9109 } // InsertNodesIntoLink()
9110
9111 //=======================================================================
9112 //function : UpdateVolumes
9113 //purpose  :
9114 //=======================================================================
9115
9116 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
9117                                       const SMDS_MeshNode*        theBetweenNode2,
9118                                       list<const SMDS_MeshNode*>& theNodesToInsert)
9119 {
9120   myLastCreatedElems.Clear();
9121   myLastCreatedNodes.Clear();
9122
9123   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9124   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9125     const SMDS_MeshElement* elem = invElemIt->next();
9126
9127     // check, if current volume has link theBetweenNode1 - theBetweenNode2
9128     SMDS_VolumeTool aVolume (elem);
9129     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9130       continue;
9131
9132     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9133     int iface, nbFaces = aVolume.NbFaces();
9134     vector<const SMDS_MeshNode *> poly_nodes;
9135     vector<int> quantities (nbFaces);
9136
9137     for (iface = 0; iface < nbFaces; iface++) {
9138       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9139       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9140       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9141
9142       for (int inode = 0; inode < nbFaceNodes; inode++) {
9143         poly_nodes.push_back(faceNodes[inode]);
9144
9145         if (nbInserted == 0) {
9146           if (faceNodes[inode] == theBetweenNode1) {
9147             if (faceNodes[inode + 1] == theBetweenNode2) {
9148               nbInserted = theNodesToInsert.size();
9149
9150               // add nodes to insert
9151               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9152               for (; nIt != theNodesToInsert.end(); nIt++) {
9153                 poly_nodes.push_back(*nIt);
9154               }
9155             }
9156           }
9157           else if (faceNodes[inode] == theBetweenNode2) {
9158             if (faceNodes[inode + 1] == theBetweenNode1) {
9159               nbInserted = theNodesToInsert.size();
9160
9161               // add nodes to insert in reversed order
9162               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9163               nIt--;
9164               for (; nIt != theNodesToInsert.begin(); nIt--) {
9165                 poly_nodes.push_back(*nIt);
9166               }
9167               poly_nodes.push_back(*nIt);
9168             }
9169           }
9170           else {
9171           }
9172         }
9173       }
9174       quantities[iface] = nbFaceNodes + nbInserted;
9175     }
9176
9177     // Replace the volume
9178     SMESHDS_Mesh *aMesh = GetMeshDS();
9179
9180     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9181     {
9182       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9183       myLastCreatedElems.Append( newElem );
9184       ReplaceElemInGroups( elem, newElem, aMesh );
9185     }
9186     aMesh->RemoveElement( elem );
9187   }
9188 }
9189
9190 namespace
9191 {
9192   //================================================================================
9193   /*!
9194    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9195    */
9196   //================================================================================
9197
9198   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9199                            vector<const SMDS_MeshNode *> & nodes,
9200                            vector<int> &                   nbNodeInFaces )
9201   {
9202     nodes.clear();
9203     nbNodeInFaces.clear();
9204     SMDS_VolumeTool vTool ( elem );
9205     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9206     {
9207       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9208       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9209       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9210     }
9211   }
9212 }
9213
9214 //=======================================================================
9215 /*!
9216  * \brief Convert elements contained in a sub-mesh to quadratic
9217  * \return int - nb of checked elements
9218  */
9219 //=======================================================================
9220
9221 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9222                                              SMESH_MesherHelper& theHelper,
9223                                              const bool          theForce3d)
9224 {
9225   int nbElem = 0;
9226   if( !theSm ) return nbElem;
9227
9228   vector<int> nbNodeInFaces;
9229   vector<const SMDS_MeshNode *> nodes;
9230   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9231   while(ElemItr->more())
9232   {
9233     nbElem++;
9234     const SMDS_MeshElement* elem = ElemItr->next();
9235     if( !elem ) continue;
9236
9237     // analyse a necessity of conversion
9238     const SMDSAbs_ElementType aType = elem->GetType();
9239     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9240       continue;
9241     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9242     bool hasCentralNodes = false;
9243     if ( elem->IsQuadratic() )
9244     {
9245       bool alreadyOK;
9246       switch ( aGeomType ) {
9247       case SMDSEntity_Quad_Triangle:
9248       case SMDSEntity_Quad_Quadrangle:
9249       case SMDSEntity_Quad_Hexa:
9250         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9251
9252       case SMDSEntity_BiQuad_Triangle:
9253       case SMDSEntity_BiQuad_Quadrangle:
9254       case SMDSEntity_TriQuad_Hexa:
9255         alreadyOK = theHelper.GetIsBiQuadratic();
9256         hasCentralNodes = true;
9257         break;
9258       default:
9259         alreadyOK = true;
9260       }
9261       // take into account already present modium nodes
9262       switch ( aType ) {
9263       case SMDSAbs_Volume:
9264         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9265       case SMDSAbs_Face:
9266         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9267       case SMDSAbs_Edge:
9268         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9269       default:;
9270       }
9271       if ( alreadyOK )
9272         continue;
9273     }
9274     // get elem data needed to re-create it
9275     //
9276     const int id      = elem->GetID();
9277     const int nbNodes = elem->NbCornerNodes();
9278     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9279     if ( aGeomType == SMDSEntity_Polyhedra )
9280       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9281     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9282       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9283
9284     // remove a linear element
9285     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9286
9287     // remove central nodes of biquadratic elements (biquad->quad convertion)
9288     if ( hasCentralNodes )
9289       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9290         if ( nodes[i]->NbInverseElements() == 0 )
9291           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9292
9293     const SMDS_MeshElement* NewElem = 0;
9294
9295     switch( aType )
9296     {
9297     case SMDSAbs_Edge :
9298       {
9299         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9300         break;
9301       }
9302     case SMDSAbs_Face :
9303       {
9304         switch(nbNodes)
9305         {
9306         case 3:
9307           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9308           break;
9309         case 4:
9310           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9311           break;
9312         default:
9313           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9314         }
9315         break;
9316       }
9317     case SMDSAbs_Volume :
9318       {
9319         switch( aGeomType )
9320         {
9321         case SMDSEntity_Tetra:
9322           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9323           break;
9324         case SMDSEntity_Pyramid:
9325           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9326           break;
9327         case SMDSEntity_Penta:
9328           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9329           break;
9330         case SMDSEntity_Hexa:
9331         case SMDSEntity_Quad_Hexa:
9332         case SMDSEntity_TriQuad_Hexa:
9333           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9334                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9335           break;
9336         case SMDSEntity_Hexagonal_Prism:
9337         default:
9338           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9339         }
9340         break;
9341       }
9342     default :
9343       continue;
9344     }
9345     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9346     if( NewElem && NewElem->getshapeId() < 1 )
9347       theSm->AddElement( NewElem );
9348   }
9349   return nbElem;
9350 }
9351 //=======================================================================
9352 //function : ConvertToQuadratic
9353 //purpose  :
9354 //=======================================================================
9355
9356 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9357 {
9358   SMESHDS_Mesh* meshDS = GetMeshDS();
9359
9360   SMESH_MesherHelper aHelper(*myMesh);
9361
9362   aHelper.SetIsQuadratic( true );
9363   aHelper.SetIsBiQuadratic( theToBiQuad );
9364   aHelper.SetElementsOnShape(true);
9365   aHelper.ToFixNodeParameters( true );
9366
9367   // convert elements assigned to sub-meshes
9368   int nbCheckedElems = 0;
9369   if ( myMesh->HasShapeToMesh() )
9370   {
9371     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9372     {
9373       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9374       while ( smIt->more() ) {
9375         SMESH_subMesh* sm = smIt->next();
9376         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9377           aHelper.SetSubShape( sm->GetSubShape() );
9378           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9379         }
9380       }
9381     }
9382   }
9383
9384   // convert elements NOT assigned to sub-meshes
9385   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9386   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9387   {
9388     aHelper.SetElementsOnShape(false);
9389     SMESHDS_SubMesh *smDS = 0;
9390
9391     // convert edges
9392     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9393     while( aEdgeItr->more() )
9394     {
9395       const SMDS_MeshEdge* edge = aEdgeItr->next();
9396       if ( !edge->IsQuadratic() )
9397       {
9398         int                  id = edge->GetID();
9399         const SMDS_MeshNode* n1 = edge->GetNode(0);
9400         const SMDS_MeshNode* n2 = edge->GetNode(1);
9401
9402         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9403
9404         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9405         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9406       }
9407       else
9408       {
9409         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9410       }
9411     }
9412
9413     // convert faces
9414     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9415     while( aFaceItr->more() )
9416     {
9417       const SMDS_MeshFace* face = aFaceItr->next();
9418       if ( !face ) continue;
9419       
9420       const SMDSAbs_EntityType type = face->GetEntityType();
9421       bool alreadyOK;
9422       switch( type )
9423       {
9424       case SMDSEntity_Quad_Triangle:
9425       case SMDSEntity_Quad_Quadrangle:
9426         alreadyOK = !theToBiQuad;
9427         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9428         break;
9429       case SMDSEntity_BiQuad_Triangle:
9430       case SMDSEntity_BiQuad_Quadrangle:
9431         alreadyOK = theToBiQuad;
9432         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9433         break;
9434       default: alreadyOK = false;
9435       }
9436       if ( alreadyOK )
9437         continue;
9438
9439       const int id = face->GetID();
9440       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9441
9442       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9443
9444       SMDS_MeshFace * NewFace = 0;
9445       switch( type )
9446       {
9447       case SMDSEntity_Triangle:
9448       case SMDSEntity_Quad_Triangle:
9449       case SMDSEntity_BiQuad_Triangle:
9450         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9451         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9452           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9453         break;
9454
9455       case SMDSEntity_Quadrangle:
9456       case SMDSEntity_Quad_Quadrangle:
9457       case SMDSEntity_BiQuad_Quadrangle:
9458         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9459         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9460           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9461         break;
9462
9463       default:;
9464         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9465       }
9466       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9467     }
9468
9469     // convert volumes
9470     vector<int> nbNodeInFaces;
9471     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9472     while(aVolumeItr->more())
9473     {
9474       const SMDS_MeshVolume* volume = aVolumeItr->next();
9475       if ( !volume ) continue;
9476
9477       const SMDSAbs_EntityType type = volume->GetEntityType();
9478       if ( volume->IsQuadratic() )
9479       {
9480         bool alreadyOK;
9481         switch ( type )
9482         {
9483         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9484         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9485         default:                      alreadyOK = true;
9486         }
9487         if ( alreadyOK )
9488         {
9489           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9490           continue;
9491         }
9492       }
9493       const int id = volume->GetID();
9494       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9495       if ( type == SMDSEntity_Polyhedra )
9496         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9497       else if ( type == SMDSEntity_Hexagonal_Prism )
9498         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9499
9500       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9501
9502       SMDS_MeshVolume * NewVolume = 0;
9503       switch ( type )
9504       {
9505       case SMDSEntity_Tetra:
9506         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9507         break;
9508       case SMDSEntity_Hexa:
9509       case SMDSEntity_Quad_Hexa:
9510       case SMDSEntity_TriQuad_Hexa:
9511         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9512                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9513         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9514           if ( nodes[i]->NbInverseElements() == 0 )
9515             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9516         break;
9517       case SMDSEntity_Pyramid:
9518         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9519                                       nodes[3], nodes[4], id, theForce3d);
9520         break;
9521       case SMDSEntity_Penta:
9522         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9523                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9524         break;
9525       case SMDSEntity_Hexagonal_Prism:
9526       default:
9527         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9528       }
9529       ReplaceElemInGroups(volume, NewVolume, meshDS);
9530     }
9531   }
9532
9533   if ( !theForce3d )
9534   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9535     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9536     // aHelper.FixQuadraticElements(myError);
9537     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9538   }
9539 }
9540
9541 //================================================================================
9542 /*!
9543  * \brief Makes given elements quadratic
9544  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9545  *  \param theElements - elements to make quadratic
9546  */
9547 //================================================================================
9548
9549 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9550                                           TIDSortedElemSet& theElements,
9551                                           const bool        theToBiQuad)
9552 {
9553   if ( theElements.empty() ) return;
9554
9555   // we believe that all theElements are of the same type
9556   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9557
9558   // get all nodes shared by theElements
9559   TIDSortedNodeSet allNodes;
9560   TIDSortedElemSet::iterator eIt = theElements.begin();
9561   for ( ; eIt != theElements.end(); ++eIt )
9562     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9563
9564   // complete theElements with elements of lower dim whose all nodes are in allNodes
9565
9566   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9567   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9568   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9569   for ( ; nIt != allNodes.end(); ++nIt )
9570   {
9571     const SMDS_MeshNode* n = *nIt;
9572     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9573     while ( invIt->more() )
9574     {
9575       const SMDS_MeshElement*      e = invIt->next();
9576       const SMDSAbs_ElementType type = e->GetType();
9577       if ( e->IsQuadratic() )
9578       {
9579         quadAdjacentElems[ type ].insert( e );
9580
9581         bool alreadyOK;
9582         switch ( e->GetEntityType() ) {
9583         case SMDSEntity_Quad_Triangle:
9584         case SMDSEntity_Quad_Quadrangle:
9585         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9586         case SMDSEntity_BiQuad_Triangle:
9587         case SMDSEntity_BiQuad_Quadrangle:
9588         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9589         default:                           alreadyOK = true;
9590         }
9591         if ( alreadyOK )
9592           continue;
9593       }
9594       if ( type >= elemType )
9595         continue; // same type or more complex linear element
9596
9597       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9598         continue; // e is already checked
9599
9600       // check nodes
9601       bool allIn = true;
9602       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9603       while ( nodeIt->more() && allIn )
9604         allIn = allNodes.count( nodeIt->next() );
9605       if ( allIn )
9606         theElements.insert(e );
9607     }
9608   }
9609
9610   SMESH_MesherHelper helper(*myMesh);
9611   helper.SetIsQuadratic( true );
9612   helper.SetIsBiQuadratic( theToBiQuad );
9613
9614   // add links of quadratic adjacent elements to the helper
9615
9616   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9617     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9618           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9619     {
9620       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9621     }
9622   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9623     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9624           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9625     {
9626       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9627     }
9628   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9629     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9630           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9631     {
9632       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9633     }
9634
9635   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9636
9637   SMESHDS_Mesh*  meshDS = GetMeshDS();
9638   SMESHDS_SubMesh* smDS = 0;
9639   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9640   {
9641     const SMDS_MeshElement* elem = *eIt;
9642
9643     bool alreadyOK;
9644     int nbCentralNodes = 0;
9645     switch ( elem->GetEntityType() ) {
9646       // linear convertible
9647     case SMDSEntity_Edge:
9648     case SMDSEntity_Triangle:
9649     case SMDSEntity_Quadrangle:
9650     case SMDSEntity_Tetra:
9651     case SMDSEntity_Pyramid:
9652     case SMDSEntity_Hexa:
9653     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9654       // quadratic that can become bi-quadratic
9655     case SMDSEntity_Quad_Triangle:
9656     case SMDSEntity_Quad_Quadrangle:
9657     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9658       // bi-quadratic
9659     case SMDSEntity_BiQuad_Triangle:
9660     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9661     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9662       // the rest
9663     default:                           alreadyOK = true;
9664     }
9665     if ( alreadyOK ) continue;
9666
9667     const SMDSAbs_ElementType type = elem->GetType();
9668     const int                   id = elem->GetID();
9669     const int              nbNodes = elem->NbCornerNodes();
9670     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9671
9672     helper.SetSubShape( elem->getshapeId() );
9673
9674     if ( !smDS || !smDS->Contains( elem ))
9675       smDS = meshDS->MeshElements( elem->getshapeId() );
9676     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9677
9678     SMDS_MeshElement * newElem = 0;
9679     switch( nbNodes )
9680     {
9681     case 4: // cases for most frequently used element types go first (for optimization)
9682       if ( type == SMDSAbs_Volume )
9683         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9684       else
9685         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9686       break;
9687     case 8:
9688       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9689                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9690       break;
9691     case 3:
9692       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9693       break;
9694     case 2:
9695       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9696       break;
9697     case 5:
9698       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9699                                  nodes[4], id, theForce3d);
9700       break;
9701     case 6:
9702       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9703                                  nodes[4], nodes[5], id, theForce3d);
9704       break;
9705     default:;
9706     }
9707     ReplaceElemInGroups( elem, newElem, meshDS);
9708     if( newElem && smDS )
9709       smDS->AddElement( newElem );
9710
9711      // remove central nodes
9712     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9713       if ( nodes[i]->NbInverseElements() == 0 )
9714         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9715
9716   } // loop on theElements
9717
9718   if ( !theForce3d )
9719   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9720     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9721     // helper.FixQuadraticElements( myError );
9722     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9723   }
9724 }
9725
9726 //=======================================================================
9727 /*!
9728  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9729  * \return int - nb of checked elements
9730  */
9731 //=======================================================================
9732
9733 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9734                                      SMDS_ElemIteratorPtr theItr,
9735                                      const int            theShapeID)
9736 {
9737   int nbElem = 0;
9738   SMESHDS_Mesh* meshDS = GetMeshDS();
9739   ElemFeatures elemType;
9740   vector<const SMDS_MeshNode *> nodes;
9741
9742   while( theItr->more() )
9743   {
9744     const SMDS_MeshElement* elem = theItr->next();
9745     nbElem++;
9746     if( elem && elem->IsQuadratic())
9747     {
9748       // get elem data
9749       int nbCornerNodes = elem->NbCornerNodes();
9750       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9751
9752       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9753
9754       //remove a quadratic element
9755       if ( !theSm || !theSm->Contains( elem ))
9756         theSm = meshDS->MeshElements( elem->getshapeId() );
9757       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9758
9759       // remove medium nodes
9760       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9761         if ( nodes[i]->NbInverseElements() == 0 )
9762           meshDS->RemoveFreeNode( nodes[i], theSm );
9763
9764       // add a linear element
9765       nodes.resize( nbCornerNodes );
9766       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9767       ReplaceElemInGroups(elem, newElem, meshDS);
9768       if( theSm && newElem )
9769         theSm->AddElement( newElem );
9770     }
9771   }
9772   return nbElem;
9773 }
9774
9775 //=======================================================================
9776 //function : ConvertFromQuadratic
9777 //purpose  :
9778 //=======================================================================
9779
9780 bool SMESH_MeshEditor::ConvertFromQuadratic()
9781 {
9782   int nbCheckedElems = 0;
9783   if ( myMesh->HasShapeToMesh() )
9784   {
9785     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9786     {
9787       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9788       while ( smIt->more() ) {
9789         SMESH_subMesh* sm = smIt->next();
9790         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9791           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9792       }
9793     }
9794   }
9795
9796   int totalNbElems =
9797     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9798   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9799   {
9800     SMESHDS_SubMesh *aSM = 0;
9801     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9802   }
9803
9804   return true;
9805 }
9806
9807 namespace
9808 {
9809   //================================================================================
9810   /*!
9811    * \brief Return true if all medium nodes of the element are in the node set
9812    */
9813   //================================================================================
9814
9815   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9816   {
9817     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9818       if ( !nodeSet.count( elem->GetNode(i) ))
9819         return false;
9820     return true;
9821   }
9822 }
9823
9824 //================================================================================
9825 /*!
9826  * \brief Makes given elements linear
9827  */
9828 //================================================================================
9829
9830 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9831 {
9832   if ( theElements.empty() ) return;
9833
9834   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9835   set<int> mediumNodeIDs;
9836   TIDSortedElemSet::iterator eIt = theElements.begin();
9837   for ( ; eIt != theElements.end(); ++eIt )
9838   {
9839     const SMDS_MeshElement* e = *eIt;
9840     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9841       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9842   }
9843
9844   // replace given elements by linear ones
9845   SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9846   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9847
9848   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9849   // except those elements sharing medium nodes of quadratic element whose medium nodes
9850   // are not all in mediumNodeIDs
9851
9852   // get remaining medium nodes
9853   TIDSortedNodeSet mediumNodes;
9854   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9855   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9856     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9857       mediumNodes.insert( mediumNodes.end(), n );
9858
9859   // find more quadratic elements to convert
9860   TIDSortedElemSet moreElemsToConvert;
9861   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9862   for ( ; nIt != mediumNodes.end(); ++nIt )
9863   {
9864     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9865     while ( invIt->more() )
9866     {
9867       const SMDS_MeshElement* e = invIt->next();
9868       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9869       {
9870         // find a more complex element including e and
9871         // whose medium nodes are not in mediumNodes
9872         bool complexFound = false;
9873         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9874         {
9875           SMDS_ElemIteratorPtr invIt2 =
9876             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9877           while ( invIt2->more() )
9878           {
9879             const SMDS_MeshElement* eComplex = invIt2->next();
9880             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9881             {
9882               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9883               if ( nbCommonNodes == e->NbNodes())
9884               {
9885                 complexFound = true;
9886                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9887                 break;
9888               }
9889             }
9890           }
9891         }
9892         if ( !complexFound )
9893           moreElemsToConvert.insert( e );
9894       }
9895     }
9896   }
9897   elemIt = elemSetIterator( moreElemsToConvert );
9898   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9899 }
9900
9901 //=======================================================================
9902 //function : SewSideElements
9903 //purpose  :
9904 //=======================================================================
9905
9906 SMESH_MeshEditor::Sew_Error
9907 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9908                                    TIDSortedElemSet&    theSide2,
9909                                    const SMDS_MeshNode* theFirstNode1,
9910                                    const SMDS_MeshNode* theFirstNode2,
9911                                    const SMDS_MeshNode* theSecondNode1,
9912                                    const SMDS_MeshNode* theSecondNode2)
9913 {
9914   myLastCreatedElems.Clear();
9915   myLastCreatedNodes.Clear();
9916
9917   MESSAGE ("::::SewSideElements()");
9918   if ( theSide1.size() != theSide2.size() )
9919     return SEW_DIFF_NB_OF_ELEMENTS;
9920
9921   Sew_Error aResult = SEW_OK;
9922   // Algo:
9923   // 1. Build set of faces representing each side
9924   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9925   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9926
9927   // =======================================================================
9928   // 1. Build set of faces representing each side:
9929   // =======================================================================
9930   // a. build set of nodes belonging to faces
9931   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9932   // c. create temporary faces representing side of volumes if correspondent
9933   //    face does not exist
9934
9935   SMESHDS_Mesh* aMesh = GetMeshDS();
9936   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9937   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9938   TIDSortedElemSet             faceSet1, faceSet2;
9939   set<const SMDS_MeshElement*> volSet1,  volSet2;
9940   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9941   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9942   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9943   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9944   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9945   int iSide, iFace, iNode;
9946
9947   list<const SMDS_MeshElement* > tempFaceList;
9948   for ( iSide = 0; iSide < 2; iSide++ ) {
9949     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9950     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9951     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9952     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9953     set<const SMDS_MeshElement*>::iterator vIt;
9954     TIDSortedElemSet::iterator eIt;
9955     set<const SMDS_MeshNode*>::iterator    nIt;
9956
9957     // check that given nodes belong to given elements
9958     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9959     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9960     int firstIndex = -1, secondIndex = -1;
9961     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9962       const SMDS_MeshElement* elem = *eIt;
9963       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9964       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9965       if ( firstIndex > -1 && secondIndex > -1 ) break;
9966     }
9967     if ( firstIndex < 0 || secondIndex < 0 ) {
9968       // we can simply return until temporary faces created
9969       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9970     }
9971
9972     // -----------------------------------------------------------
9973     // 1a. Collect nodes of existing faces
9974     //     and build set of face nodes in order to detect missing
9975     //     faces corresponding to sides of volumes
9976     // -----------------------------------------------------------
9977
9978     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9979
9980     // loop on the given element of a side
9981     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9982       //const SMDS_MeshElement* elem = *eIt;
9983       const SMDS_MeshElement* elem = *eIt;
9984       if ( elem->GetType() == SMDSAbs_Face ) {
9985         faceSet->insert( elem );
9986         set <const SMDS_MeshNode*> faceNodeSet;
9987         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9988         while ( nodeIt->more() ) {
9989           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9990           nodeSet->insert( n );
9991           faceNodeSet.insert( n );
9992         }
9993         setOfFaceNodeSet.insert( faceNodeSet );
9994       }
9995       else if ( elem->GetType() == SMDSAbs_Volume )
9996         volSet->insert( elem );
9997     }
9998     // ------------------------------------------------------------------------------
9999     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10000     // ------------------------------------------------------------------------------
10001
10002     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10003       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10004       while ( fIt->more() ) { // loop on faces sharing a node
10005         const SMDS_MeshElement* f = fIt->next();
10006         if ( faceSet->find( f ) == faceSet->end() ) {
10007           // check if all nodes are in nodeSet and
10008           // complete setOfFaceNodeSet if they are
10009           set <const SMDS_MeshNode*> faceNodeSet;
10010           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10011           bool allInSet = true;
10012           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10013             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10014             if ( nodeSet->find( n ) == nodeSet->end() )
10015               allInSet = false;
10016             else
10017               faceNodeSet.insert( n );
10018           }
10019           if ( allInSet ) {
10020             faceSet->insert( f );
10021             setOfFaceNodeSet.insert( faceNodeSet );
10022           }
10023         }
10024       }
10025     }
10026
10027     // -------------------------------------------------------------------------
10028     // 1c. Create temporary faces representing sides of volumes if correspondent
10029     //     face does not exist
10030     // -------------------------------------------------------------------------
10031
10032     if ( !volSet->empty() ) {
10033       //int nodeSetSize = nodeSet->size();
10034
10035       // loop on given volumes
10036       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10037         SMDS_VolumeTool vol (*vIt);
10038         // loop on volume faces: find free faces
10039         // --------------------------------------
10040         list<const SMDS_MeshElement* > freeFaceList;
10041         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10042           if ( !vol.IsFreeFace( iFace ))
10043             continue;
10044           // check if there is already a face with same nodes in a face set
10045           const SMDS_MeshElement* aFreeFace = 0;
10046           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10047           int nbNodes = vol.NbFaceNodes( iFace );
10048           set <const SMDS_MeshNode*> faceNodeSet;
10049           vol.GetFaceNodes( iFace, faceNodeSet );
10050           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10051           if ( isNewFace ) {
10052             // no such a face is given but it still can exist, check it
10053             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10054             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10055           }
10056           if ( !aFreeFace ) {
10057             // create a temporary face
10058             if ( nbNodes == 3 ) {
10059               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10060               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10061             }
10062             else if ( nbNodes == 4 ) {
10063               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10064               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10065             }
10066             else {
10067               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10068               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10069               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10070             }
10071             if ( aFreeFace )
10072               tempFaceList.push_back( aFreeFace );
10073           }
10074
10075           if ( aFreeFace )
10076             freeFaceList.push_back( aFreeFace );
10077
10078         } // loop on faces of a volume
10079
10080         // choose one of several free faces of a volume
10081         // --------------------------------------------
10082         if ( freeFaceList.size() > 1 ) {
10083           // choose a face having max nb of nodes shared by other elems of a side
10084           int maxNbNodes = -1;
10085           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10086           while ( fIt != freeFaceList.end() ) { // loop on free faces
10087             int nbSharedNodes = 0;
10088             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10089             while ( nodeIt->more() ) { // loop on free face nodes
10090               const SMDS_MeshNode* n =
10091                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10092               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10093               while ( invElemIt->more() ) {
10094                 const SMDS_MeshElement* e = invElemIt->next();
10095                 nbSharedNodes += faceSet->count( e );
10096                 nbSharedNodes += elemSet->count( e );
10097               }
10098             }
10099             if ( nbSharedNodes > maxNbNodes ) {
10100               maxNbNodes = nbSharedNodes;
10101               freeFaceList.erase( freeFaceList.begin(), fIt++ );
10102             }
10103             else if ( nbSharedNodes == maxNbNodes ) {
10104               fIt++;
10105             }
10106             else {
10107               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10108             }
10109           }
10110           if ( freeFaceList.size() > 1 )
10111           {
10112             // could not choose one face, use another way
10113             // choose a face most close to the bary center of the opposite side
10114             gp_XYZ aBC( 0., 0., 0. );
10115             set <const SMDS_MeshNode*> addedNodes;
10116             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10117             eIt = elemSet2->begin();
10118             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10119               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10120               while ( nodeIt->more() ) { // loop on free face nodes
10121                 const SMDS_MeshNode* n =
10122                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10123                 if ( addedNodes.insert( n ).second )
10124                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10125               }
10126             }
10127             aBC /= addedNodes.size();
10128             double minDist = DBL_MAX;
10129             fIt = freeFaceList.begin();
10130             while ( fIt != freeFaceList.end() ) { // loop on free faces
10131               double dist = 0;
10132               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10133               while ( nodeIt->more() ) { // loop on free face nodes
10134                 const SMDS_MeshNode* n =
10135                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10136                 gp_XYZ p( n->X(),n->Y(),n->Z() );
10137                 dist += ( aBC - p ).SquareModulus();
10138               }
10139               if ( dist < minDist ) {
10140                 minDist = dist;
10141                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10142               }
10143               else
10144                 fIt = freeFaceList.erase( fIt++ );
10145             }
10146           }
10147         } // choose one of several free faces of a volume
10148
10149         if ( freeFaceList.size() == 1 ) {
10150           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10151           faceSet->insert( aFreeFace );
10152           // complete a node set with nodes of a found free face
10153           //           for ( iNode = 0; iNode < ; iNode++ )
10154           //             nodeSet->insert( fNodes[ iNode ] );
10155         }
10156
10157       } // loop on volumes of a side
10158
10159       //       // complete a set of faces if new nodes in a nodeSet appeared
10160       //       // ----------------------------------------------------------
10161       //       if ( nodeSetSize != nodeSet->size() ) {
10162       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10163       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10164       //           while ( fIt->more() ) { // loop on faces sharing a node
10165       //             const SMDS_MeshElement* f = fIt->next();
10166       //             if ( faceSet->find( f ) == faceSet->end() ) {
10167       //               // check if all nodes are in nodeSet and
10168       //               // complete setOfFaceNodeSet if they are
10169       //               set <const SMDS_MeshNode*> faceNodeSet;
10170       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10171       //               bool allInSet = true;
10172       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10173       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10174       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10175       //                   allInSet = false;
10176       //                 else
10177       //                   faceNodeSet.insert( n );
10178       //               }
10179       //               if ( allInSet ) {
10180       //                 faceSet->insert( f );
10181       //                 setOfFaceNodeSet.insert( faceNodeSet );
10182       //               }
10183       //             }
10184       //           }
10185       //         }
10186       //       }
10187     } // Create temporary faces, if there are volumes given
10188   } // loop on sides
10189
10190   if ( faceSet1.size() != faceSet2.size() ) {
10191     // delete temporary faces: they are in reverseElements of actual nodes
10192 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10193 //    while ( tmpFaceIt->more() )
10194 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10195 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10196 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10197 //      aMesh->RemoveElement(*tmpFaceIt);
10198     MESSAGE("Diff nb of faces");
10199     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10200   }
10201
10202   // ============================================================
10203   // 2. Find nodes to merge:
10204   //              bind a node to remove to a node to put instead
10205   // ============================================================
10206
10207   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10208   if ( theFirstNode1 != theFirstNode2 )
10209     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10210   if ( theSecondNode1 != theSecondNode2 )
10211     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10212
10213   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10214   set< long > linkIdSet; // links to process
10215   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10216
10217   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10218   list< NLink > linkList[2];
10219   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10220   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10221   // loop on links in linkList; find faces by links and append links
10222   // of the found faces to linkList
10223   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10224   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10225   {
10226     NLink link[] = { *linkIt[0], *linkIt[1] };
10227     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10228     if ( !linkIdSet.count( linkID ) )
10229       continue;
10230
10231     // by links, find faces in the face sets,
10232     // and find indices of link nodes in the found faces;
10233     // in a face set, there is only one or no face sharing a link
10234     // ---------------------------------------------------------------
10235
10236     const SMDS_MeshElement* face[] = { 0, 0 };
10237     vector<const SMDS_MeshNode*> fnodes[2];
10238     int iLinkNode[2][2];
10239     TIDSortedElemSet avoidSet;
10240     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10241       const SMDS_MeshNode* n1 = link[iSide].first;
10242       const SMDS_MeshNode* n2 = link[iSide].second;
10243       //cout << "Side " << iSide << " ";
10244       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10245       // find a face by two link nodes
10246       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10247                                                       *faceSetPtr[ iSide ], avoidSet,
10248                                                       &iLinkNode[iSide][0],
10249                                                       &iLinkNode[iSide][1] );
10250       if ( face[ iSide ])
10251       {
10252         //cout << " F " << face[ iSide]->GetID() <<endl;
10253         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10254         // put face nodes to fnodes
10255         if ( face[ iSide ]->IsQuadratic() )
10256         {
10257           // use interlaced nodes iterator
10258           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10259           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10260           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10261           while ( nIter->more() )
10262             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10263         }
10264         else
10265         {
10266           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10267                                   face[ iSide ]->end_nodes() );
10268         }
10269         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10270       }
10271     }
10272
10273     // check similarity of elements of the sides
10274     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10275       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10276       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10277         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10278       }
10279       else {
10280         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10281       }
10282       break; // do not return because it's necessary to remove tmp faces
10283     }
10284
10285     // set nodes to merge
10286     // -------------------
10287
10288     if ( face[0] && face[1] )  {
10289       const int nbNodes = face[0]->NbNodes();
10290       if ( nbNodes != face[1]->NbNodes() ) {
10291         MESSAGE("Diff nb of face nodes");
10292         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10293         break; // do not return because it s necessary to remove tmp faces
10294       }
10295       bool reverse[] = { false, false }; // order of nodes in the link
10296       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10297         // analyse link orientation in faces
10298         int i1 = iLinkNode[ iSide ][ 0 ];
10299         int i2 = iLinkNode[ iSide ][ 1 ];
10300         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10301       }
10302       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10303       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10304       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10305       {
10306         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10307                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10308       }
10309
10310       // add other links of the faces to linkList
10311       // -----------------------------------------
10312
10313       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10314         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10315         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10316         if ( !iter_isnew.second ) { // already in a set: no need to process
10317           linkIdSet.erase( iter_isnew.first );
10318         }
10319         else // new in set == encountered for the first time: add
10320         {
10321           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10322           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10323           linkList[0].push_back ( NLink( n1, n2 ));
10324           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10325         }
10326       }
10327     } // 2 faces found
10328
10329     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10330       break;
10331
10332   } // loop on link lists
10333
10334   if ( aResult == SEW_OK &&
10335        ( //linkIt[0] != linkList[0].end() ||
10336          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10337     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10338              " " << (faceSetPtr[1]->empty()));
10339     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10340   }
10341
10342   // ====================================================================
10343   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10344   // ====================================================================
10345
10346   // delete temporary faces
10347 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10348 //  while ( tmpFaceIt->more() )
10349 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10350   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10351   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10352     aMesh->RemoveElement(*tmpFaceIt);
10353
10354   if ( aResult != SEW_OK)
10355     return aResult;
10356
10357   list< int > nodeIDsToRemove;
10358   vector< const SMDS_MeshNode*> nodes;
10359   ElemFeatures elemType;
10360
10361   // loop on nodes replacement map
10362   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10363   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10364     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10365     {
10366       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10367       nodeIDsToRemove.push_back( nToRemove->GetID() );
10368       // loop on elements sharing nToRemove
10369       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10370       while ( invElemIt->more() ) {
10371         const SMDS_MeshElement* e = invElemIt->next();
10372         // get a new suite of nodes: make replacement
10373         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10374         nodes.resize( nbNodes );
10375         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10376         while ( nIt->more() ) {
10377           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10378           nnIt = nReplaceMap.find( n );
10379           if ( nnIt != nReplaceMap.end() ) {
10380             nbReplaced++;
10381             n = (*nnIt).second;
10382           }
10383           nodes[ i++ ] = n;
10384         }
10385         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10386         //         elemIDsToRemove.push_back( e->GetID() );
10387         //       else
10388         if ( nbReplaced )
10389         {
10390           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10391           aMesh->RemoveElement( e );
10392
10393           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10394           {
10395             AddToSameGroups( newElem, e, aMesh );
10396             if ( int aShapeId = e->getshapeId() )
10397               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10398           }
10399         }
10400       }
10401     }
10402
10403   Remove( nodeIDsToRemove, true );
10404
10405   return aResult;
10406 }
10407
10408 //================================================================================
10409 /*!
10410  * \brief Find corresponding nodes in two sets of faces
10411  * \param theSide1 - first face set
10412  * \param theSide2 - second first face
10413  * \param theFirstNode1 - a boundary node of set 1
10414  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10415  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10416  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10417  * \param nReplaceMap - output map of corresponding nodes
10418  * \return bool  - is a success or not
10419  */
10420 //================================================================================
10421
10422 #ifdef _DEBUG_
10423 //#define DEBUG_MATCHING_NODES
10424 #endif
10425
10426 SMESH_MeshEditor::Sew_Error
10427 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10428                                     set<const SMDS_MeshElement*>& theSide2,
10429                                     const SMDS_MeshNode*          theFirstNode1,
10430                                     const SMDS_MeshNode*          theFirstNode2,
10431                                     const SMDS_MeshNode*          theSecondNode1,
10432                                     const SMDS_MeshNode*          theSecondNode2,
10433                                     TNodeNodeMap &                nReplaceMap)
10434 {
10435   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10436
10437   nReplaceMap.clear();
10438   if ( theFirstNode1 != theFirstNode2 )
10439     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10440   if ( theSecondNode1 != theSecondNode2 )
10441     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10442
10443   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10444   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10445
10446   list< NLink > linkList[2];
10447   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10448   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10449
10450   // loop on links in linkList; find faces by links and append links
10451   // of the found faces to linkList
10452   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10453   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10454     NLink link[] = { *linkIt[0], *linkIt[1] };
10455     if ( linkSet.find( link[0] ) == linkSet.end() )
10456       continue;
10457
10458     // by links, find faces in the face sets,
10459     // and find indices of link nodes in the found faces;
10460     // in a face set, there is only one or no face sharing a link
10461     // ---------------------------------------------------------------
10462
10463     const SMDS_MeshElement* face[] = { 0, 0 };
10464     list<const SMDS_MeshNode*> notLinkNodes[2];
10465     //bool reverse[] = { false, false }; // order of notLinkNodes
10466     int nbNodes[2];
10467     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10468     {
10469       const SMDS_MeshNode* n1 = link[iSide].first;
10470       const SMDS_MeshNode* n2 = link[iSide].second;
10471       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10472       set< const SMDS_MeshElement* > facesOfNode1;
10473       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10474       {
10475         // during a loop of the first node, we find all faces around n1,
10476         // during a loop of the second node, we find one face sharing both n1 and n2
10477         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10478         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10479         while ( fIt->more() ) { // loop on faces sharing a node
10480           const SMDS_MeshElement* f = fIt->next();
10481           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10482               ! facesOfNode1.insert( f ).second ) // f encounters twice
10483           {
10484             if ( face[ iSide ] ) {
10485               MESSAGE( "2 faces per link " );
10486               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10487             }
10488             face[ iSide ] = f;
10489             faceSet->erase( f );
10490
10491             // get not link nodes
10492             int nbN = f->NbNodes();
10493             if ( f->IsQuadratic() )
10494               nbN /= 2;
10495             nbNodes[ iSide ] = nbN;
10496             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10497             int i1 = f->GetNodeIndex( n1 );
10498             int i2 = f->GetNodeIndex( n2 );
10499             int iEnd = nbN, iBeg = -1, iDelta = 1;
10500             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10501             if ( reverse ) {
10502               std::swap( iEnd, iBeg ); iDelta = -1;
10503             }
10504             int i = i2;
10505             while ( true ) {
10506               i += iDelta;
10507               if ( i == iEnd ) i = iBeg + iDelta;
10508               if ( i == i1 ) break;
10509               nodes.push_back ( f->GetNode( i ) );
10510             }
10511           }
10512         }
10513       }
10514     }
10515     // check similarity of elements of the sides
10516     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10517       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10518       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10519         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10520       }
10521       else {
10522         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10523       }
10524     }
10525
10526     // set nodes to merge
10527     // -------------------
10528
10529     if ( face[0] && face[1] )  {
10530       if ( nbNodes[0] != nbNodes[1] ) {
10531         MESSAGE("Diff nb of face nodes");
10532         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10533       }
10534 #ifdef DEBUG_MATCHING_NODES
10535       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10536                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10537                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10538 #endif
10539       int nbN = nbNodes[0];
10540       {
10541         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10542         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10543         for ( int i = 0 ; i < nbN - 2; ++i ) {
10544 #ifdef DEBUG_MATCHING_NODES
10545           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10546 #endif
10547           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10548         }
10549       }
10550
10551       // add other links of the face 1 to linkList
10552       // -----------------------------------------
10553
10554       const SMDS_MeshElement* f0 = face[0];
10555       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10556       for ( int i = 0; i < nbN; i++ )
10557       {
10558         const SMDS_MeshNode* n2 = f0->GetNode( i );
10559         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10560           linkSet.insert( SMESH_TLink( n1, n2 ));
10561         if ( !iter_isnew.second ) { // already in a set: no need to process
10562           linkSet.erase( iter_isnew.first );
10563         }
10564         else // new in set == encountered for the first time: add
10565         {
10566 #ifdef DEBUG_MATCHING_NODES
10567           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10568                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10569 #endif
10570           linkList[0].push_back ( NLink( n1, n2 ));
10571           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10572         }
10573         n1 = n2;
10574       }
10575     } // 2 faces found
10576   } // loop on link lists
10577
10578   return SEW_OK;
10579 }
10580
10581 //================================================================================
10582 /*!
10583  * \brief Create elements equal (on same nodes) to given ones
10584  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10585  *              elements of the uppest dimension are duplicated.
10586  */
10587 //================================================================================
10588
10589 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10590 {
10591   ClearLastCreated();
10592   SMESHDS_Mesh* mesh = GetMeshDS();
10593
10594   // get an element type and an iterator over elements
10595
10596   SMDSAbs_ElementType type;
10597   SMDS_ElemIteratorPtr elemIt;
10598   vector< const SMDS_MeshElement* > allElems;
10599   if ( theElements.empty() )
10600   {
10601     if ( mesh->NbNodes() == 0 )
10602       return;
10603     // get most complex type
10604     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10605       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10606       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10607     };
10608     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10609       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10610       {
10611         type = types[i];
10612         break;
10613       }
10614     // put all elements in the vector <allElems>
10615     allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10616     elemIt = mesh->elementsIterator( type );
10617     while ( elemIt->more() )
10618       allElems.push_back( elemIt->next());
10619     elemIt = elemSetIterator( allElems );
10620   }
10621   else
10622   {
10623     type = (*theElements.begin())->GetType();
10624     elemIt = elemSetIterator( theElements );
10625   }
10626
10627   // duplicate elements
10628
10629   ElemFeatures elemType;
10630
10631   vector< const SMDS_MeshNode* > nodes;
10632   while ( elemIt->more() )
10633   {
10634     const SMDS_MeshElement* elem = elemIt->next();
10635     if ( elem->GetType() != type )
10636       continue;
10637
10638     elemType.Init( elem, /*basicOnly=*/false );
10639     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10640
10641     AddElement( nodes, elemType );
10642   }
10643 }
10644
10645 //================================================================================
10646 /*!
10647   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10648   \param theElems - the list of elements (edges or faces) to be replicated
10649   The nodes for duplication could be found from these elements
10650   \param theNodesNot - list of nodes to NOT replicate
10651   \param theAffectedElems - the list of elements (cells and edges) to which the
10652   replicated nodes should be associated to.
10653   \return TRUE if operation has been completed successfully, FALSE otherwise
10654 */
10655 //================================================================================
10656
10657 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10658                                     const TIDSortedElemSet& theNodesNot,
10659                                     const TIDSortedElemSet& theAffectedElems )
10660 {
10661   myLastCreatedElems.Clear();
10662   myLastCreatedNodes.Clear();
10663
10664   if ( theElems.size() == 0 )
10665     return false;
10666
10667   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10668   if ( !aMeshDS )
10669     return false;
10670
10671   bool res = false;
10672   TNodeNodeMap anOldNodeToNewNode;
10673   // duplicate elements and nodes
10674   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10675   // replce nodes by duplications
10676   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10677   return res;
10678 }
10679
10680 //================================================================================
10681 /*!
10682   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10683   \param theMeshDS - mesh instance
10684   \param theElems - the elements replicated or modified (nodes should be changed)
10685   \param theNodesNot - nodes to NOT replicate
10686   \param theNodeNodeMap - relation of old node to new created node
10687   \param theIsDoubleElem - flag os to replicate element or modify
10688   \return TRUE if operation has been completed successfully, FALSE otherwise
10689 */
10690 //================================================================================
10691
10692 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10693                                    const TIDSortedElemSet& theElems,
10694                                    const TIDSortedElemSet& theNodesNot,
10695                                    TNodeNodeMap&           theNodeNodeMap,
10696                                    const bool              theIsDoubleElem )
10697 {
10698   MESSAGE("doubleNodes");
10699   // iterate through element and duplicate them (by nodes duplication)
10700   bool res = false;
10701   std::vector<const SMDS_MeshNode*> newNodes;
10702   ElemFeatures elemType;
10703
10704   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10705   for ( ;  elemItr != theElems.end(); ++elemItr )
10706   {
10707     const SMDS_MeshElement* anElem = *elemItr;
10708     if (!anElem)
10709       continue;
10710
10711     // duplicate nodes to duplicate element
10712     bool isDuplicate = false;
10713     newNodes.resize( anElem->NbNodes() );
10714     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10715     int ind = 0;
10716     while ( anIter->more() )
10717     {
10718       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10719       const SMDS_MeshNode*  aNewNode = aCurrNode;
10720       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10721       if ( n2n != theNodeNodeMap.end() )
10722       {
10723         aNewNode = n2n->second;
10724       }
10725       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10726       {
10727         // duplicate node
10728         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10729         copyPosition( aCurrNode, aNewNode );
10730         theNodeNodeMap[ aCurrNode ] = aNewNode;
10731         myLastCreatedNodes.Append( aNewNode );
10732       }
10733       isDuplicate |= (aCurrNode != aNewNode);
10734       newNodes[ ind++ ] = aNewNode;
10735     }
10736     if ( !isDuplicate )
10737       continue;
10738
10739     if ( theIsDoubleElem )
10740       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10741     else
10742       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10743
10744     res = true;
10745   }
10746   return res;
10747 }
10748
10749 //================================================================================
10750 /*!
10751   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10752   \param theNodes - identifiers of nodes to be doubled
10753   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10754   nodes. If list of element identifiers is empty then nodes are doubled but
10755   they not assigned to elements
10756   \return TRUE if operation has been completed successfully, FALSE otherwise
10757 */
10758 //================================================================================
10759
10760 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10761                                     const std::list< int >& theListOfModifiedElems )
10762 {
10763   MESSAGE("DoubleNodes");
10764   myLastCreatedElems.Clear();
10765   myLastCreatedNodes.Clear();
10766
10767   if ( theListOfNodes.size() == 0 )
10768     return false;
10769
10770   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10771   if ( !aMeshDS )
10772     return false;
10773
10774   // iterate through nodes and duplicate them
10775
10776   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10777
10778   std::list< int >::const_iterator aNodeIter;
10779   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10780   {
10781     int aCurr = *aNodeIter;
10782     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10783     if ( !aNode )
10784       continue;
10785
10786     // duplicate node
10787
10788     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10789     if ( aNewNode )
10790     {
10791       copyPosition( aNode, aNewNode );
10792       anOldNodeToNewNode[ aNode ] = aNewNode;
10793       myLastCreatedNodes.Append( aNewNode );
10794     }
10795   }
10796
10797   // Create map of new nodes for modified elements
10798
10799   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10800
10801   std::list< int >::const_iterator anElemIter;
10802   for ( anElemIter = theListOfModifiedElems.begin();
10803         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10804   {
10805     int aCurr = *anElemIter;
10806     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10807     if ( !anElem )
10808       continue;
10809
10810     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10811
10812     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10813     int ind = 0;
10814     while ( anIter->more() )
10815     {
10816       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10817       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10818       {
10819         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10820         aNodeArr[ ind++ ] = aNewNode;
10821       }
10822       else
10823         aNodeArr[ ind++ ] = aCurrNode;
10824     }
10825     anElemToNodes[ anElem ] = aNodeArr;
10826   }
10827
10828   // Change nodes of elements
10829
10830   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10831     anElemToNodesIter = anElemToNodes.begin();
10832   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10833   {
10834     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10835     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10836     if ( anElem )
10837       {
10838       MESSAGE("ChangeElementNodes");
10839       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10840       }
10841   }
10842
10843   return true;
10844 }
10845
10846 namespace {
10847
10848   //================================================================================
10849   /*!
10850   \brief Check if element located inside shape
10851   \return TRUE if IN or ON shape, FALSE otherwise
10852   */
10853   //================================================================================
10854
10855   template<class Classifier>
10856   bool isInside(const SMDS_MeshElement* theElem,
10857                 Classifier&             theClassifier,
10858                 const double            theTol)
10859   {
10860     gp_XYZ centerXYZ (0, 0, 0);
10861     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10862     while (aNodeItr->more())
10863       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10864
10865     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10866     theClassifier.Perform(aPnt, theTol);
10867     TopAbs_State aState = theClassifier.State();
10868     return (aState == TopAbs_IN || aState == TopAbs_ON );
10869   }
10870
10871   //================================================================================
10872   /*!
10873    * \brief Classifier of the 3D point on the TopoDS_Face
10874    *        with interaface suitable for isInside()
10875    */
10876   //================================================================================
10877
10878   struct _FaceClassifier
10879   {
10880     Extrema_ExtPS       _extremum;
10881     BRepAdaptor_Surface _surface;
10882     TopAbs_State        _state;
10883
10884     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10885     {
10886       _extremum.Initialize( _surface,
10887                             _surface.FirstUParameter(), _surface.LastUParameter(),
10888                             _surface.FirstVParameter(), _surface.LastVParameter(),
10889                             _surface.Tolerance(), _surface.Tolerance() );
10890     }
10891     void Perform(const gp_Pnt& aPnt, double theTol)
10892     {
10893       theTol *= theTol;
10894       _state = TopAbs_OUT;
10895       _extremum.Perform(aPnt);
10896       if ( _extremum.IsDone() )
10897         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10898           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10899     }
10900     TopAbs_State State() const
10901     {
10902       return _state;
10903     }
10904   };
10905 }
10906
10907 //================================================================================
10908 /*!
10909   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10910   This method is the first step of DoubleNodeElemGroupsInRegion.
10911   \param theElems - list of groups of elements (edges or faces) to be replicated
10912   \param theNodesNot - list of groups of nodes not to replicated
10913   \param theShape - shape to detect affected elements (element which geometric center
10914          located on or inside shape). If the shape is null, detection is done on faces orientations
10915          (select elements with a gravity center on the side given by faces normals).
10916          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10917          The replicated nodes should be associated to affected elements.
10918   \return groups of affected elements
10919   \sa DoubleNodeElemGroupsInRegion()
10920  */
10921 //================================================================================
10922
10923 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10924                                                    const TIDSortedElemSet& theNodesNot,
10925                                                    const TopoDS_Shape&     theShape,
10926                                                    TIDSortedElemSet&       theAffectedElems)
10927 {
10928   if ( theShape.IsNull() )
10929   {
10930     std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10931     std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10932     std::set<const SMDS_MeshElement*> edgesToCheck;
10933     alreadyCheckedNodes.clear();
10934     alreadyCheckedElems.clear();
10935     edgesToCheck.clear();
10936
10937     // --- iterates on elements to be replicated and get elements by back references from their nodes
10938
10939     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10940     int ielem;
10941     for ( ielem=1;  elemItr != theElems.end(); ++elemItr )
10942     {
10943       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10944       if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10945         continue;
10946       gp_XYZ normal;
10947       SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10948       MESSAGE("element " << ielem++ <<  " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10949       std::set<const SMDS_MeshNode*> nodesElem;
10950       nodesElem.clear();
10951       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10952       while ( nodeItr->more() )
10953       {
10954         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10955         nodesElem.insert(aNode);
10956       }
10957       std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10958       for (; nodit != nodesElem.end(); nodit++)
10959       {
10960         MESSAGE("  noeud ");
10961         const SMDS_MeshNode* aNode = *nodit;
10962         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10963           continue;
10964         if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10965           continue;
10966         alreadyCheckedNodes.insert(aNode);
10967         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10968         while ( backElemItr->more() )
10969         {
10970           MESSAGE("    backelem ");
10971           const SMDS_MeshElement* curElem = backElemItr->next();
10972           if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10973             continue;
10974           if (theElems.find(curElem) != theElems.end())
10975             continue;
10976           alreadyCheckedElems.insert(curElem);
10977           double x=0, y=0, z=0;
10978           int nb = 0;
10979           SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10980           while ( nodeItr2->more() )
10981           {
10982             const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10983             x += anotherNode->X();
10984             y += anotherNode->Y();
10985             z += anotherNode->Z();
10986             nb++;
10987           }
10988           gp_XYZ p;
10989           p.SetCoord( x/nb -aNode->X(),
10990                       y/nb -aNode->Y(),
10991                       z/nb -aNode->Z() );
10992           MESSAGE("      check " << p.X() << " " << p.Y() << " " << p.Z());
10993           if (normal*p > 0)
10994           {
10995             MESSAGE("    --- inserted")
10996             theAffectedElems.insert( curElem );
10997           }
10998           else if (curElem->GetType() == SMDSAbs_Edge)
10999             edgesToCheck.insert(curElem);
11000         }
11001       }
11002     }
11003     // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
11004     std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
11005     for( ; eit != edgesToCheck.end(); eit++)
11006     {
11007       bool onside = true;
11008       const SMDS_MeshElement* anEdge = *eit;
11009       SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
11010       while ( nodeItr->more() )
11011       {
11012         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11013         if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
11014         {
11015           onside = false;
11016           break;
11017         }
11018       }
11019       if (onside)
11020       {
11021         MESSAGE("    --- edge onside inserted")
11022         theAffectedElems.insert(anEdge);
11023       }
11024     }
11025   }
11026   else
11027   {
11028     const double aTol = Precision::Confusion();
11029     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11030     auto_ptr<_FaceClassifier>              aFaceClassifier;
11031     if ( theShape.ShapeType() == TopAbs_SOLID )
11032     {
11033       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11034       bsc3d->PerformInfinitePoint(aTol);
11035     }
11036     else if (theShape.ShapeType() == TopAbs_FACE )
11037     {
11038       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11039     }
11040
11041     // iterates on indicated elements and get elements by back references from their nodes
11042     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11043     int ielem;
11044     for ( ielem = 1;  elemItr != theElems.end(); ++elemItr )
11045     {
11046       MESSAGE("element " << ielem++);
11047       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11048       if (!anElem)
11049         continue;
11050       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11051       while ( nodeItr->more() )
11052       {
11053         MESSAGE("  noeud ");
11054         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11055         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11056           continue;
11057         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11058         while ( backElemItr->more() )
11059         {
11060           MESSAGE("    backelem ");
11061           const SMDS_MeshElement* curElem = backElemItr->next();
11062           if ( curElem && theElems.find(curElem) == theElems.end() &&
11063               ( bsc3d.get() ?
11064                 isInside( curElem, *bsc3d, aTol ) :
11065                 isInside( curElem, *aFaceClassifier, aTol )))
11066             theAffectedElems.insert( curElem );
11067         }
11068       }
11069     }
11070   }
11071   return true;
11072 }
11073
11074 //================================================================================
11075 /*!
11076   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11077   \param theElems - group of of elements (edges or faces) to be replicated
11078   \param theNodesNot - group of nodes not to replicate
11079   \param theShape - shape to detect affected elements (element which geometric center
11080   located on or inside shape).
11081   The replicated nodes should be associated to affected elements.
11082   \return TRUE if operation has been completed successfully, FALSE otherwise
11083 */
11084 //================================================================================
11085
11086 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11087                                             const TIDSortedElemSet& theNodesNot,
11088                                             const TopoDS_Shape&     theShape )
11089 {
11090   if ( theShape.IsNull() )
11091     return false;
11092
11093   const double aTol = Precision::Confusion();
11094   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11095   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
11096   if ( theShape.ShapeType() == TopAbs_SOLID )
11097   {
11098     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11099     bsc3d->PerformInfinitePoint(aTol);
11100   }
11101   else if (theShape.ShapeType() == TopAbs_FACE )
11102   {
11103     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11104   }
11105
11106   // iterates on indicated elements and get elements by back references from their nodes
11107   TIDSortedElemSet anAffected;
11108   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11109   for ( ;  elemItr != theElems.end(); ++elemItr )
11110   {
11111     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11112     if (!anElem)
11113       continue;
11114
11115     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11116     while ( nodeItr->more() )
11117     {
11118       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11119       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11120         continue;
11121       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11122       while ( backElemItr->more() )
11123       {
11124         const SMDS_MeshElement* curElem = backElemItr->next();
11125         if ( curElem && theElems.find(curElem) == theElems.end() &&
11126              ( bsc3d ?
11127                isInside( curElem, *bsc3d, aTol ) :
11128                isInside( curElem, *aFaceClassifier, aTol )))
11129           anAffected.insert( curElem );
11130       }
11131     }
11132   }
11133   return DoubleNodes( theElems, theNodesNot, anAffected );
11134 }
11135
11136 /*!
11137  *  \brief compute an oriented angle between two planes defined by four points.
11138  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11139  *  @param p0 base of the rotation axe
11140  *  @param p1 extremity of the rotation axe
11141  *  @param g1 belongs to the first plane
11142  *  @param g2 belongs to the second plane
11143  */
11144 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11145 {
11146 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11147 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11148 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11149 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11150   gp_Vec vref(p0, p1);
11151   gp_Vec v1(p0, g1);
11152   gp_Vec v2(p0, g2);
11153   gp_Vec n1 = vref.Crossed(v1);
11154   gp_Vec n2 = vref.Crossed(v2);
11155   try {
11156     return n2.AngleWithRef(n1, vref);
11157   }
11158   catch ( Standard_Failure ) {
11159   }
11160   return Max( v1.Magnitude(), v2.Magnitude() );
11161 }
11162
11163 /*!
11164  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11165  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11166  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11167  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11168  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11169  * 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.
11170  * 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.
11171  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11172  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11173  * \param theElems - list of groups of volumes, where a group of volume is a set of
11174  *        SMDS_MeshElements sorted by Id.
11175  * \param createJointElems - if TRUE, create the elements
11176  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11177  *        the boundary between \a theDomains and the rest mesh
11178  * \return TRUE if operation has been completed successfully, FALSE otherwise
11179  */
11180 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11181                                                      bool                                 createJointElems,
11182                                                      bool                                 onAllBoundaries)
11183 {
11184   MESSAGE("----------------------------------------------");
11185   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11186   MESSAGE("----------------------------------------------");
11187
11188   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11189   meshDS->BuildDownWardConnectivity(true);
11190   CHRONO(50);
11191   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11192
11193   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11194   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11195   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11196
11197   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11198   std::map<int,int>celldom; // cell vtkId --> domain
11199   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11200   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11201   faceDomains.clear();
11202   celldom.clear();
11203   cellDomains.clear();
11204   nodeDomains.clear();
11205   std::map<int,int> emptyMap;
11206   std::set<int> emptySet;
11207   emptyMap.clear();
11208
11209   MESSAGE(".. Number of domains :"<<theElems.size());
11210
11211   TIDSortedElemSet theRestDomElems;
11212   const int iRestDom  = -1;
11213   const int idom0     = onAllBoundaries ? iRestDom : 0;
11214   const int nbDomains = theElems.size();
11215
11216   // Check if the domains do not share an element
11217   for (int idom = 0; idom < nbDomains-1; idom++)
11218   {
11219     //       MESSAGE("... Check of domain #" << idom);
11220     const TIDSortedElemSet& domain = theElems[idom];
11221     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11222     for (; elemItr != domain.end(); ++elemItr)
11223     {
11224       const SMDS_MeshElement* anElem = *elemItr;
11225       int idombisdeb = idom + 1 ;
11226       // check if the element belongs to a domain further in the list
11227       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11228       {
11229         const TIDSortedElemSet& domainbis = theElems[idombis];
11230         if ( domainbis.count( anElem ))
11231         {
11232           MESSAGE(".... Domain #" << idom);
11233           MESSAGE(".... Domain #" << idombis);
11234           throw SALOME_Exception("The domains are not disjoint.");
11235           return false ;
11236         }
11237       }
11238     }
11239   }
11240
11241   for (int idom = 0; idom < nbDomains; idom++)
11242   {
11243
11244     // --- build a map (face to duplicate --> volume to modify)
11245     //     with all the faces shared by 2 domains (group of elements)
11246     //     and corresponding volume of this domain, for each shared face.
11247     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11248
11249     MESSAGE("... Neighbors of domain #" << idom);
11250     const TIDSortedElemSet& domain = theElems[idom];
11251     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11252     for (; elemItr != domain.end(); ++elemItr)
11253     {
11254       const SMDS_MeshElement* anElem = *elemItr;
11255       if (!anElem)
11256         continue;
11257       int vtkId = anElem->getVtkId();
11258       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11259       int neighborsVtkIds[NBMAXNEIGHBORS];
11260       int downIds[NBMAXNEIGHBORS];
11261       unsigned char downTypes[NBMAXNEIGHBORS];
11262       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11263       for (int n = 0; n < nbNeighbors; n++)
11264       {
11265         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11266         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11267         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11268         {
11269           bool ok = false;
11270           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11271           {
11272             // MESSAGE("Domain " << idombis);
11273             const TIDSortedElemSet& domainbis = theElems[idombis];
11274             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11275           }
11276           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11277           {
11278             DownIdType face(downIds[n], downTypes[n]);
11279             if (!faceDomains[face].count(idom))
11280             {
11281               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11282               celldom[vtkId] = idom;
11283               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11284             }
11285             if ( !ok )
11286             {
11287               theRestDomElems.insert( elem );
11288               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11289               celldom[neighborsVtkIds[n]] = iRestDom;
11290             }
11291           }
11292         }
11293       }
11294     }
11295   }
11296
11297   //MESSAGE("Number of shared faces " << faceDomains.size());
11298   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11299
11300   // --- explore the shared faces domain by domain,
11301   //     explore the nodes of the face and see if they belong to a cell in the domain,
11302   //     which has only a node or an edge on the border (not a shared face)
11303
11304   for (int idomain = idom0; idomain < nbDomains; idomain++)
11305   {
11306     //MESSAGE("Domain " << idomain);
11307     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11308     itface = faceDomains.begin();
11309     for (; itface != faceDomains.end(); ++itface)
11310     {
11311       const std::map<int, int>& domvol = itface->second;
11312       if (!domvol.count(idomain))
11313         continue;
11314       DownIdType face = itface->first;
11315       //MESSAGE(" --- face " << face.cellId);
11316       std::set<int> oldNodes;
11317       oldNodes.clear();
11318       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11319       std::set<int>::iterator itn = oldNodes.begin();
11320       for (; itn != oldNodes.end(); ++itn)
11321       {
11322         int oldId = *itn;
11323         //MESSAGE("     node " << oldId);
11324         vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11325         for (int i=0; i<l.ncells; i++)
11326         {
11327           int vtkId = l.cells[i];
11328           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11329           if (!domain.count(anElem))
11330             continue;
11331           int vtkType = grid->GetCellType(vtkId);
11332           int downId = grid->CellIdToDownId(vtkId);
11333           if (downId < 0)
11334           {
11335             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11336             continue; // not OK at this stage of the algorithm:
11337             //no cells created after BuildDownWardConnectivity
11338           }
11339           DownIdType aCell(downId, vtkType);
11340           cellDomains[aCell][idomain] = vtkId;
11341           celldom[vtkId] = idomain;
11342           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11343         }
11344       }
11345     }
11346   }
11347
11348   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11349   //     for each shared face, get the nodes
11350   //     for each node, for each domain of the face, create a clone of the node
11351
11352   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11353   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11354   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11355
11356   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11357   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11358   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11359
11360   MESSAGE(".. Duplication of the nodes");
11361   for (int idomain = idom0; idomain < nbDomains; idomain++)
11362   {
11363     itface = faceDomains.begin();
11364     for (; itface != faceDomains.end(); ++itface)
11365     {
11366       const std::map<int, int>& domvol = itface->second;
11367       if (!domvol.count(idomain))
11368         continue;
11369       DownIdType face = itface->first;
11370       //MESSAGE(" --- face " << face.cellId);
11371       std::set<int> oldNodes;
11372       oldNodes.clear();
11373       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11374       std::set<int>::iterator itn = oldNodes.begin();
11375       for (; itn != oldNodes.end(); ++itn)
11376       {
11377         int oldId = *itn;
11378         if (nodeDomains[oldId].empty())
11379         {
11380           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11381           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11382         }
11383         std::map<int, int>::const_iterator itdom = domvol.begin();
11384         for (; itdom != domvol.end(); ++itdom)
11385         {
11386           int idom = itdom->first;
11387           //MESSAGE("         domain " << idom);
11388           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11389           {
11390             if (nodeDomains[oldId].size() >= 2) // a multiple node
11391             {
11392               vector<int> orderedDoms;
11393               //MESSAGE("multiple node " << oldId);
11394               if (mutipleNodes.count(oldId))
11395                 orderedDoms = mutipleNodes[oldId];
11396               else
11397               {
11398                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11399                 for (; it != nodeDomains[oldId].end(); ++it)
11400                   orderedDoms.push_back(it->first);
11401               }
11402               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11403               //stringstream txt;
11404               //for (int i=0; i<orderedDoms.size(); i++)
11405               //  txt << orderedDoms[i] << " ";
11406               //MESSAGE("orderedDoms " << txt.str());
11407               mutipleNodes[oldId] = orderedDoms;
11408             }
11409             double *coords = grid->GetPoint(oldId);
11410             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11411             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11412             int newId = newNode->getVtkId();
11413             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11414             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11415           }
11416         }
11417       }
11418     }
11419   }
11420
11421   MESSAGE(".. Creation of elements");
11422   for (int idomain = idom0; idomain < nbDomains; idomain++)
11423   {
11424     itface = faceDomains.begin();
11425     for (; itface != faceDomains.end(); ++itface)
11426     {
11427       std::map<int, int> domvol = itface->second;
11428       if (!domvol.count(idomain))
11429         continue;
11430       DownIdType face = itface->first;
11431       //MESSAGE(" --- face " << face.cellId);
11432       std::set<int> oldNodes;
11433       oldNodes.clear();
11434       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11435       int nbMultipleNodes = 0;
11436       std::set<int>::iterator itn = oldNodes.begin();
11437       for (; itn != oldNodes.end(); ++itn)
11438       {
11439         int oldId = *itn;
11440         if (mutipleNodes.count(oldId))
11441           nbMultipleNodes++;
11442       }
11443       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11444       {
11445         //MESSAGE("multiple Nodes detected on a shared face");
11446         int downId = itface->first.cellId;
11447         unsigned char cellType = itface->first.cellType;
11448         // --- shared edge or shared face ?
11449         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11450         {
11451           int nodes[3];
11452           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11453           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11454             if (mutipleNodes.count(nodes[i]))
11455               if (!mutipleNodesToFace.count(nodes[i]))
11456                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11457         }
11458         else // shared face (between two volumes)
11459         {
11460           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11461           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11462           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11463           for (int ie =0; ie < nbEdges; ie++)
11464           {
11465             int nodes[3];
11466             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11467             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11468             {
11469               vector<int> vn0 = mutipleNodes[nodes[0]];
11470               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11471               vector<int> doms;
11472               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11473                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11474                   if ( vn0[i0] == vn1[i1] )
11475                     doms.push_back( vn0[ i0 ]);
11476               if ( doms.size() > 2 )
11477               {
11478                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11479                 double *coords = grid->GetPoint(nodes[0]);
11480                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11481                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11482                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11483                 gp_Pnt gref;
11484                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11485                 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11486                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11487                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11488                 for ( size_t id = 0; id < doms.size(); id++ )
11489                 {
11490                   int idom = doms[id];
11491                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11492                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11493                   {
11494                     int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11495                     SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11496                     if (domain.count(elem))
11497                     {
11498                       SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11499                       domvol[idom] = svol;
11500                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11501                       double values[3];
11502                       vtkIdType npts = 0;
11503                       vtkIdType* pts = 0;
11504                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11505                       SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11506                       if (id ==0)
11507                       {
11508                         gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11509                         angleDom[idom] = 0;
11510                       }
11511                       else
11512                       {
11513                         gp_Pnt g(values[0], values[1], values[2]);
11514                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11515                         //MESSAGE("  angle=" << angleDom[idom]);
11516                       }
11517                       break;
11518                     }
11519                   }
11520                 }
11521                 map<double, int> sortedDom; // sort domains by angle
11522                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11523                   sortedDom[ia->second] = ia->first;
11524                 vector<int> vnodes;
11525                 vector<int> vdom;
11526                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11527                 {
11528                   vdom.push_back(ib->second);
11529                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11530                 }
11531                 for (int ino = 0; ino < nbNodes; ino++)
11532                   vnodes.push_back(nodes[ino]);
11533                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11534               }
11535             }
11536           }
11537         }
11538       }
11539     }
11540   }
11541
11542   // --- iterate on shared faces (volumes to modify, face to extrude)
11543   //     get node id's of the face (id SMDS = id VTK)
11544   //     create flat element with old and new nodes if requested
11545
11546   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11547   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11548
11549   std::map<int, std::map<long,int> > nodeQuadDomains;
11550   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11551
11552   MESSAGE(".. Creation of elements: simple junction");
11553   if (createJointElems)
11554   {
11555     int idg;
11556     string joints2DName = "joints2D";
11557     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11558     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11559     string joints3DName = "joints3D";
11560     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11561     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11562
11563     itface = faceDomains.begin();
11564     for (; itface != faceDomains.end(); ++itface)
11565     {
11566       DownIdType face = itface->first;
11567       std::set<int> oldNodes;
11568       std::set<int>::iterator itn;
11569       oldNodes.clear();
11570       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11571
11572       std::map<int, int> domvol = itface->second;
11573       std::map<int, int>::iterator itdom = domvol.begin();
11574       int dom1 = itdom->first;
11575       int vtkVolId = itdom->second;
11576       itdom++;
11577       int dom2 = itdom->first;
11578       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11579                                                        nodeQuadDomains);
11580       stringstream grpname;
11581       grpname << "j_";
11582       if (dom1 < dom2)
11583         grpname << dom1 << "_" << dom2;
11584       else
11585         grpname << dom2 << "_" << dom1;
11586       string namegrp = grpname.str();
11587       if (!mapOfJunctionGroups.count(namegrp))
11588         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11589       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11590       if (sgrp)
11591         sgrp->Add(vol->GetID());
11592       if (vol->GetType() == SMDSAbs_Volume)
11593         joints3DGrp->Add(vol->GetID());
11594       else if (vol->GetType() == SMDSAbs_Face)
11595         joints2DGrp->Add(vol->GetID());
11596     }
11597   }
11598
11599   // --- create volumes on multiple domain intersection if requested
11600   //     iterate on mutipleNodesToFace
11601   //     iterate on edgesMultiDomains
11602
11603   MESSAGE(".. Creation of elements: multiple junction");
11604   if (createJointElems)
11605   {
11606     // --- iterate on mutipleNodesToFace
11607
11608     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11609     for (; itn != mutipleNodesToFace.end(); ++itn)
11610     {
11611       int node = itn->first;
11612       vector<int> orderDom = itn->second;
11613       vector<vtkIdType> orderedNodes;
11614       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11615         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11616       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11617
11618       stringstream grpname;
11619       grpname << "m2j_";
11620       grpname << 0 << "_" << 0;
11621       int idg;
11622       string namegrp = grpname.str();
11623       if (!mapOfJunctionGroups.count(namegrp))
11624         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11625       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11626       if (sgrp)
11627         sgrp->Add(face->GetID());
11628     }
11629
11630     // --- iterate on edgesMultiDomains
11631
11632     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11633     for (; ite != edgesMultiDomains.end(); ++ite)
11634     {
11635       vector<int> nodes = ite->first;
11636       vector<int> orderDom = ite->second;
11637       vector<vtkIdType> orderedNodes;
11638       if (nodes.size() == 2)
11639       {
11640         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11641         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11642           if ( orderDom.size() == 3 )
11643             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11644               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11645           else
11646             for (int idom = orderDom.size()-1; idom >=0; idom--)
11647               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11648         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11649
11650         int idg;
11651         string namegrp = "jointsMultiples";
11652         if (!mapOfJunctionGroups.count(namegrp))
11653           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11654         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11655         if (sgrp)
11656           sgrp->Add(vol->GetID());
11657       }
11658       else
11659       {
11660         //INFOS("Quadratic multiple joints not implemented");
11661         // TODO quadratic nodes
11662       }
11663     }
11664   }
11665
11666   // --- list the explicit faces and edges of the mesh that need to be modified,
11667   //     i.e. faces and edges built with one or more duplicated nodes.
11668   //     associate these faces or edges to their corresponding domain.
11669   //     only the first domain found is kept when a face or edge is shared
11670
11671   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11672   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11673   faceOrEdgeDom.clear();
11674   feDom.clear();
11675
11676   MESSAGE(".. Modification of elements");
11677   for (int idomain = idom0; idomain < nbDomains; idomain++)
11678   {
11679     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11680     for (; itnod != nodeDomains.end(); ++itnod)
11681     {
11682       int oldId = itnod->first;
11683       //MESSAGE("     node " << oldId);
11684       vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11685       for (int i = 0; i < l.ncells; i++)
11686       {
11687         int vtkId = l.cells[i];
11688         int vtkType = grid->GetCellType(vtkId);
11689         int downId = grid->CellIdToDownId(vtkId);
11690         if (downId < 0)
11691           continue; // new cells: not to be modified
11692         DownIdType aCell(downId, vtkType);
11693         int volParents[1000];
11694         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11695         for (int j = 0; j < nbvol; j++)
11696           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11697             if (!feDom.count(vtkId))
11698             {
11699               feDom[vtkId] = idomain;
11700               faceOrEdgeDom[aCell] = emptyMap;
11701               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11702               //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11703               //        << " type " << vtkType << " downId " << downId);
11704             }
11705       }
11706     }
11707   }
11708
11709   // --- iterate on shared faces (volumes to modify, face to extrude)
11710   //     get node id's of the face
11711   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11712
11713   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11714   for (int m=0; m<3; m++)
11715   {
11716     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11717     itface = (*amap).begin();
11718     for (; itface != (*amap).end(); ++itface)
11719     {
11720       DownIdType face = itface->first;
11721       std::set<int> oldNodes;
11722       std::set<int>::iterator itn;
11723       oldNodes.clear();
11724       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11725       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11726       std::map<int, int> localClonedNodeIds;
11727
11728       std::map<int, int> domvol = itface->second;
11729       std::map<int, int>::iterator itdom = domvol.begin();
11730       for (; itdom != domvol.end(); ++itdom)
11731       {
11732         int idom = itdom->first;
11733         int vtkVolId = itdom->second;
11734         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11735         localClonedNodeIds.clear();
11736         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11737         {
11738           int oldId = *itn;
11739           if (nodeDomains[oldId].count(idom))
11740           {
11741             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11742             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11743           }
11744         }
11745         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11746       }
11747     }
11748   }
11749
11750   // Remove empty groups (issue 0022812)
11751   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11752   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11753   {
11754     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11755       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11756   }
11757
11758   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11759   grid->BuildLinks();
11760
11761   CHRONOSTOP(50);
11762   counters::stats();
11763   return true;
11764 }
11765
11766 /*!
11767  * \brief Double nodes on some external faces and create flat elements.
11768  * Flat elements are mainly used by some types of mechanic calculations.
11769  *
11770  * Each group of the list must be constituted of faces.
11771  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11772  * @param theElems - list of groups of faces, where a group of faces is a set of
11773  * SMDS_MeshElements sorted by Id.
11774  * @return TRUE if operation has been completed successfully, FALSE otherwise
11775  */
11776 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11777 {
11778   MESSAGE("-------------------------------------------------");
11779   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11780   MESSAGE("-------------------------------------------------");
11781
11782   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11783
11784   // --- For each group of faces
11785   //     duplicate the nodes, create a flat element based on the face
11786   //     replace the nodes of the faces by their clones
11787
11788   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11789   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11790   clonedNodes.clear();
11791   intermediateNodes.clear();
11792   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11793   mapOfJunctionGroups.clear();
11794
11795   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11796   {
11797     const TIDSortedElemSet&           domain = theElems[idom];
11798     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11799     for ( ; elemItr != domain.end(); ++elemItr )
11800     {
11801       SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11802       SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11803       if (!aFace)
11804         continue;
11805       // MESSAGE("aFace=" << aFace->GetID());
11806       bool isQuad = aFace->IsQuadratic();
11807       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11808
11809       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11810
11811       SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11812       while (nodeIt->more())
11813       {
11814         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11815         bool isMedium = isQuad && (aFace->IsMediumNode(node));
11816         if (isMedium)
11817           ln2.push_back(node);
11818         else
11819           ln0.push_back(node);
11820
11821         const SMDS_MeshNode* clone = 0;
11822         if (!clonedNodes.count(node))
11823         {
11824           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11825           copyPosition( node, clone );
11826           clonedNodes[node] = clone;
11827         }
11828         else
11829           clone = clonedNodes[node];
11830
11831         if (isMedium)
11832           ln3.push_back(clone);
11833         else
11834           ln1.push_back(clone);
11835
11836         const SMDS_MeshNode* inter = 0;
11837         if (isQuad && (!isMedium))
11838         {
11839           if (!intermediateNodes.count(node))
11840           {
11841             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11842             copyPosition( node, inter );
11843             intermediateNodes[node] = inter;
11844           }
11845           else
11846             inter = intermediateNodes[node];
11847           ln4.push_back(inter);
11848         }
11849       }
11850
11851       // --- extrude the face
11852
11853       vector<const SMDS_MeshNode*> ln;
11854       SMDS_MeshVolume* vol = 0;
11855       vtkIdType aType = aFace->GetVtkType();
11856       switch (aType)
11857       {
11858       case VTK_TRIANGLE:
11859         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11860         // MESSAGE("vol prism " << vol->GetID());
11861         ln.push_back(ln1[0]);
11862         ln.push_back(ln1[1]);
11863         ln.push_back(ln1[2]);
11864         break;
11865       case VTK_QUAD:
11866         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11867         // MESSAGE("vol hexa " << vol->GetID());
11868         ln.push_back(ln1[0]);
11869         ln.push_back(ln1[1]);
11870         ln.push_back(ln1[2]);
11871         ln.push_back(ln1[3]);
11872         break;
11873       case VTK_QUADRATIC_TRIANGLE:
11874         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11875                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11876         // MESSAGE("vol quad prism " << vol->GetID());
11877         ln.push_back(ln1[0]);
11878         ln.push_back(ln1[1]);
11879         ln.push_back(ln1[2]);
11880         ln.push_back(ln3[0]);
11881         ln.push_back(ln3[1]);
11882         ln.push_back(ln3[2]);
11883         break;
11884       case VTK_QUADRATIC_QUAD:
11885         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11886         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11887         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11888         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11889                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11890                                 ln4[0], ln4[1], ln4[2], ln4[3]);
11891         // MESSAGE("vol quad hexa " << vol->GetID());
11892         ln.push_back(ln1[0]);
11893         ln.push_back(ln1[1]);
11894         ln.push_back(ln1[2]);
11895         ln.push_back(ln1[3]);
11896         ln.push_back(ln3[0]);
11897         ln.push_back(ln3[1]);
11898         ln.push_back(ln3[2]);
11899         ln.push_back(ln3[3]);
11900         break;
11901       case VTK_POLYGON:
11902         break;
11903       default:
11904         break;
11905       }
11906
11907       if (vol)
11908       {
11909         stringstream grpname;
11910         grpname << "jf_";
11911         grpname << idom;
11912         int idg;
11913         string namegrp = grpname.str();
11914         if (!mapOfJunctionGroups.count(namegrp))
11915           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11916         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11917         if (sgrp)
11918           sgrp->Add(vol->GetID());
11919       }
11920
11921       // --- modify the face
11922
11923       aFace->ChangeNodes(&ln[0], ln.size());
11924     }
11925   }
11926   return true;
11927 }
11928
11929 /*!
11930  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11931  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11932  *  groups of faces to remove inside the object, (idem edges).
11933  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11934  */
11935 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
11936                                       const TopoDS_Shape&             theShape,
11937                                       SMESH_NodeSearcher*             theNodeSearcher,
11938                                       const char*                     groupName,
11939                                       std::vector<double>&            nodesCoords,
11940                                       std::vector<std::vector<int> >& listOfListOfNodes)
11941 {
11942   MESSAGE("--------------------------------");
11943   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11944   MESSAGE("--------------------------------");
11945
11946   // --- zone of volumes to remove is given :
11947   //     1 either by a geom shape (one or more vertices) and a radius,
11948   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11949   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11950   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11951   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11952   //     defined by it's name.
11953
11954   SMESHDS_GroupBase* groupDS = 0;
11955   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11956   while ( groupIt->more() )
11957   {
11958     groupDS = 0;
11959     SMESH_Group * group = groupIt->next();
11960     if ( !group ) continue;
11961     groupDS = group->GetGroupDS();
11962     if ( !groupDS || groupDS->IsEmpty() ) continue;
11963     std::string grpName = group->GetName();
11964     //MESSAGE("grpName=" << grpName);
11965     if (grpName == groupName)
11966       break;
11967     else
11968       groupDS = 0;
11969   }
11970
11971   bool isNodeGroup = false;
11972   bool isNodeCoords = false;
11973   if (groupDS)
11974   {
11975     if (groupDS->GetType() != SMDSAbs_Node)
11976       return;
11977     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11978   }
11979
11980   if (nodesCoords.size() > 0)
11981     isNodeCoords = true; // a list o nodes given by their coordinates
11982   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11983
11984   // --- define groups to build
11985
11986   int idg; // --- group of SMDS volumes
11987   string grpvName = groupName;
11988   grpvName += "_vol";
11989   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11990   if (!grp)
11991   {
11992     MESSAGE("group not created " << grpvName);
11993     return;
11994   }
11995   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11996
11997   int idgs; // --- group of SMDS faces on the skin
11998   string grpsName = groupName;
11999   grpsName += "_skin";
12000   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12001   if (!grps)
12002   {
12003     MESSAGE("group not created " << grpsName);
12004     return;
12005   }
12006   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12007
12008   int idgi; // --- group of SMDS faces internal (several shapes)
12009   string grpiName = groupName;
12010   grpiName += "_internalFaces";
12011   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12012   if (!grpi)
12013   {
12014     MESSAGE("group not created " << grpiName);
12015     return;
12016   }
12017   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12018
12019   int idgei; // --- group of SMDS faces internal (several shapes)
12020   string grpeiName = groupName;
12021   grpeiName += "_internalEdges";
12022   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12023   if (!grpei)
12024   {
12025     MESSAGE("group not created " << grpeiName);
12026     return;
12027   }
12028   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12029
12030   // --- build downward connectivity
12031
12032   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12033   meshDS->BuildDownWardConnectivity(true);
12034   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12035
12036   // --- set of volumes detected inside
12037
12038   std::set<int> setOfInsideVol;
12039   std::set<int> setOfVolToCheck;
12040
12041   std::vector<gp_Pnt> gpnts;
12042   gpnts.clear();
12043
12044   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12045   {
12046     MESSAGE("group of nodes provided");
12047     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12048     while ( elemIt->more() )
12049     {
12050       const SMDS_MeshElement* elem = elemIt->next();
12051       if (!elem)
12052         continue;
12053       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12054       if (!node)
12055         continue;
12056       SMDS_MeshElement* vol = 0;
12057       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12058       while (volItr->more())
12059       {
12060         vol = (SMDS_MeshElement*)volItr->next();
12061         setOfInsideVol.insert(vol->getVtkId());
12062         sgrp->Add(vol->GetID());
12063       }
12064     }
12065   }
12066   else if (isNodeCoords)
12067   {
12068     MESSAGE("list of nodes coordinates provided");
12069     size_t i = 0;
12070     int k = 0;
12071     while ( i < nodesCoords.size()-2 )
12072     {
12073       double x = nodesCoords[i++];
12074       double y = nodesCoords[i++];
12075       double z = nodesCoords[i++];
12076       gp_Pnt p = gp_Pnt(x, y ,z);
12077       gpnts.push_back(p);
12078       MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12079       k++;
12080     }
12081   }
12082   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12083   {
12084     MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12085     TopTools_IndexedMapOfShape vertexMap;
12086     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12087     gp_Pnt p = gp_Pnt(0,0,0);
12088     if (vertexMap.Extent() < 1)
12089       return;
12090
12091     for ( int i = 1; i <= vertexMap.Extent(); ++i )
12092     {
12093       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12094       p = BRep_Tool::Pnt(vertex);
12095       gpnts.push_back(p);
12096       MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12097     }
12098   }
12099
12100   if (gpnts.size() > 0)
12101   {
12102     int nodeId = 0;
12103     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12104     if (startNode)
12105       nodeId = startNode->GetID();
12106     MESSAGE("nodeId " << nodeId);
12107
12108     double radius2 = radius*radius;
12109     MESSAGE("radius2 " << radius2);
12110
12111     // --- volumes on start node
12112
12113     setOfVolToCheck.clear();
12114     SMDS_MeshElement* startVol = 0;
12115     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12116     while (volItr->more())
12117     {
12118       startVol = (SMDS_MeshElement*)volItr->next();
12119       setOfVolToCheck.insert(startVol->getVtkId());
12120     }
12121     if (setOfVolToCheck.empty())
12122     {
12123       MESSAGE("No volumes found");
12124       return;
12125     }
12126
12127     // --- starting with central volumes then their neighbors, check if they are inside
12128     //     or outside the domain, until no more new neighbor volume is inside.
12129     //     Fill the group of inside volumes
12130
12131     std::map<int, double> mapOfNodeDistance2;
12132     mapOfNodeDistance2.clear();
12133     std::set<int> setOfOutsideVol;
12134     while (!setOfVolToCheck.empty())
12135     {
12136       std::set<int>::iterator it = setOfVolToCheck.begin();
12137       int vtkId = *it;
12138       MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12139       bool volInside = false;
12140       vtkIdType npts = 0;
12141       vtkIdType* pts = 0;
12142       grid->GetCellPoints(vtkId, npts, pts);
12143       for (int i=0; i<npts; i++)
12144       {
12145         double distance2 = 0;
12146         if (mapOfNodeDistance2.count(pts[i]))
12147         {
12148           distance2 = mapOfNodeDistance2[pts[i]];
12149           MESSAGE("point " << pts[i] << " distance2 " << distance2);
12150         }
12151         else
12152         {
12153           double *coords = grid->GetPoint(pts[i]);
12154           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12155           distance2 = 1.E40;
12156           for ( size_t j = 0; j < gpnts.size(); j++ )
12157           {
12158             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12159             if (d2 < distance2)
12160             {
12161               distance2 = d2;
12162               if (distance2 < radius2)
12163                 break;
12164             }
12165           }
12166           mapOfNodeDistance2[pts[i]] = distance2;
12167           MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12168         }
12169         if (distance2 < radius2)
12170         {
12171           volInside = true; // one or more nodes inside the domain
12172           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12173           break;
12174         }
12175       }
12176       if (volInside)
12177       {
12178         setOfInsideVol.insert(vtkId);
12179         MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12180         int neighborsVtkIds[NBMAXNEIGHBORS];
12181         int downIds[NBMAXNEIGHBORS];
12182         unsigned char downTypes[NBMAXNEIGHBORS];
12183         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12184         for (int n = 0; n < nbNeighbors; n++)
12185           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12186             setOfVolToCheck.insert(neighborsVtkIds[n]);
12187       }
12188       else
12189       {
12190         setOfOutsideVol.insert(vtkId);
12191         MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12192       }
12193       setOfVolToCheck.erase(vtkId);
12194     }
12195   }
12196
12197   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12198   //     If yes, add the volume to the inside set
12199
12200   bool addedInside = true;
12201   std::set<int> setOfVolToReCheck;
12202   while (addedInside)
12203   {
12204     MESSAGE(" --------------------------- re check");
12205     addedInside = false;
12206     std::set<int>::iterator itv = setOfInsideVol.begin();
12207     for (; itv != setOfInsideVol.end(); ++itv)
12208     {
12209       int vtkId = *itv;
12210       int neighborsVtkIds[NBMAXNEIGHBORS];
12211       int downIds[NBMAXNEIGHBORS];
12212       unsigned char downTypes[NBMAXNEIGHBORS];
12213       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12214       for (int n = 0; n < nbNeighbors; n++)
12215         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12216           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12217     }
12218     setOfVolToCheck = setOfVolToReCheck;
12219     setOfVolToReCheck.clear();
12220     while  (!setOfVolToCheck.empty())
12221     {
12222       std::set<int>::iterator it = setOfVolToCheck.begin();
12223       int vtkId = *it;
12224       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12225       {
12226         MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12227         int countInside = 0;
12228         int neighborsVtkIds[NBMAXNEIGHBORS];
12229         int downIds[NBMAXNEIGHBORS];
12230         unsigned char downTypes[NBMAXNEIGHBORS];
12231         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12232         for (int n = 0; n < nbNeighbors; n++)
12233           if (setOfInsideVol.count(neighborsVtkIds[n]))
12234             countInside++;
12235         MESSAGE("countInside " << countInside);
12236         if (countInside > 1)
12237         {
12238           MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12239           setOfInsideVol.insert(vtkId);
12240           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12241           addedInside = true;
12242         }
12243         else
12244           setOfVolToReCheck.insert(vtkId);
12245       }
12246       setOfVolToCheck.erase(vtkId);
12247     }
12248   }
12249
12250   // --- map of Downward faces at the boundary, inside the global volume
12251   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12252   //     fill group of SMDS faces inside the volume (when several volume shapes)
12253   //     fill group of SMDS faces on the skin of the global volume (if skin)
12254
12255   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12256   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12257   std::set<int>::iterator it = setOfInsideVol.begin();
12258   for (; it != setOfInsideVol.end(); ++it)
12259   {
12260     int vtkId = *it;
12261     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12262     int neighborsVtkIds[NBMAXNEIGHBORS];
12263     int downIds[NBMAXNEIGHBORS];
12264     unsigned char downTypes[NBMAXNEIGHBORS];
12265     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12266     for (int n = 0; n < nbNeighbors; n++)
12267     {
12268       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12269       if (neighborDim == 3)
12270       {
12271         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12272         {
12273           DownIdType face(downIds[n], downTypes[n]);
12274           boundaryFaces[face] = vtkId;
12275         }
12276         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12277         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12278         if (vtkFaceId >= 0)
12279         {
12280           sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12281           // find also the smds edges on this face
12282           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12283           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12284           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12285           for (int i = 0; i < nbEdges; i++)
12286           {
12287             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12288             if (vtkEdgeId >= 0)
12289               sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12290           }
12291         }
12292       }
12293       else if (neighborDim == 2) // skin of the volume
12294       {
12295         DownIdType face(downIds[n], downTypes[n]);
12296         skinFaces[face] = vtkId;
12297         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12298         if (vtkFaceId >= 0)
12299           sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12300       }
12301     }
12302   }
12303
12304   // --- identify the edges constituting the wire of each subshape on the skin
12305   //     define polylines with the nodes of edges, equivalent to wires
12306   //     project polylines on subshapes, and partition, to get geom faces
12307
12308   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12309   std::set<int> emptySet;
12310   emptySet.clear();
12311   std::set<int> shapeIds;
12312
12313   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12314   while (itelem->more())
12315   {
12316     const SMDS_MeshElement *elem = itelem->next();
12317     int shapeId = elem->getshapeId();
12318     int vtkId = elem->getVtkId();
12319     if (!shapeIdToVtkIdSet.count(shapeId))
12320     {
12321       shapeIdToVtkIdSet[shapeId] = emptySet;
12322       shapeIds.insert(shapeId);
12323     }
12324     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12325   }
12326
12327   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12328   std::set<DownIdType, DownIdCompare> emptyEdges;
12329   emptyEdges.clear();
12330
12331   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12332   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12333   {
12334     int shapeId = itShape->first;
12335     MESSAGE(" --- Shape ID --- "<< shapeId);
12336     shapeIdToEdges[shapeId] = emptyEdges;
12337
12338     std::vector<int> nodesEdges;
12339
12340     std::set<int>::iterator its = itShape->second.begin();
12341     for (; its != itShape->second.end(); ++its)
12342     {
12343       int vtkId = *its;
12344       MESSAGE("     " << vtkId);
12345       int neighborsVtkIds[NBMAXNEIGHBORS];
12346       int downIds[NBMAXNEIGHBORS];
12347       unsigned char downTypes[NBMAXNEIGHBORS];
12348       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12349       for (int n = 0; n < nbNeighbors; n++)
12350       {
12351         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12352           continue;
12353         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12354         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12355         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12356         {
12357           DownIdType edge(downIds[n], downTypes[n]);
12358           if (!shapeIdToEdges[shapeId].count(edge))
12359           {
12360             shapeIdToEdges[shapeId].insert(edge);
12361             int vtkNodeId[3];
12362             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12363             nodesEdges.push_back(vtkNodeId[0]);
12364             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12365             MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12366           }
12367         }
12368       }
12369     }
12370
12371     std::list<int> order;
12372     order.clear();
12373     if (nodesEdges.size() > 0)
12374     {
12375       order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12376       nodesEdges[0] = -1;
12377       order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
12378       nodesEdges[1] = -1; // do not reuse this edge
12379       bool found = true;
12380       while (found)
12381       {
12382         int nodeTofind = order.back(); // try first to push back
12383         int i = 0;
12384         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12385           if (nodesEdges[i] == nodeTofind)
12386             break;
12387         if ( i == (int) nodesEdges.size() )
12388           found = false; // no follower found on back
12389         else
12390         {
12391           if (i%2) // odd ==> use the previous one
12392             if (nodesEdges[i-1] < 0)
12393               found = false;
12394             else
12395             {
12396               order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
12397               nodesEdges[i-1] = -1;
12398             }
12399           else // even ==> use the next one
12400             if (nodesEdges[i+1] < 0)
12401               found = false;
12402             else
12403             {
12404               order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
12405               nodesEdges[i+1] = -1;
12406             }
12407         }
12408         if (found)
12409           continue;
12410         // try to push front
12411         found = true;
12412         nodeTofind = order.front(); // try to push front
12413         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12414           if ( nodesEdges[i] == nodeTofind )
12415             break;
12416         if ( i == (int)nodesEdges.size() )
12417         {
12418           found = false; // no predecessor found on front
12419           continue;
12420         }
12421         if (i%2) // odd ==> use the previous one
12422           if (nodesEdges[i-1] < 0)
12423             found = false;
12424           else
12425           {
12426             order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
12427             nodesEdges[i-1] = -1;
12428           }
12429         else // even ==> use the next one
12430           if (nodesEdges[i+1] < 0)
12431             found = false;
12432           else
12433           {
12434             order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
12435             nodesEdges[i+1] = -1;
12436           }
12437       }
12438     }
12439
12440
12441     std::vector<int> nodes;
12442     nodes.push_back(shapeId);
12443     std::list<int>::iterator itl = order.begin();
12444     for (; itl != order.end(); itl++)
12445     {
12446       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12447       MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12448     }
12449     listOfListOfNodes.push_back(nodes);
12450   }
12451
12452   //     partition geom faces with blocFissure
12453   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12454   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12455
12456   return;
12457 }
12458
12459
12460 //================================================================================
12461 /*!
12462  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12463  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12464  * \return TRUE if operation has been completed successfully, FALSE otherwise
12465  */
12466 //================================================================================
12467
12468 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12469 {
12470   // iterates on volume elements and detect all free faces on them
12471   SMESHDS_Mesh* aMesh = GetMeshDS();
12472   if (!aMesh)
12473     return false;
12474
12475   ElemFeatures faceType( SMDSAbs_Face );
12476   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12477   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12478   while(vIt->more())
12479   {
12480     const SMDS_MeshVolume* volume = vIt->next();
12481     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12482     vTool.SetExternalNormal();
12483     const int iQuad = volume->IsQuadratic();
12484     faceType.SetQuad( iQuad );
12485     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12486     {
12487       if (!vTool.IsFreeFace(iface))
12488         continue;
12489       nbFree++;
12490       vector<const SMDS_MeshNode *> nodes;
12491       int nbFaceNodes = vTool.NbFaceNodes(iface);
12492       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12493       int inode = 0;
12494       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12495         nodes.push_back(faceNodes[inode]);
12496
12497       if (iQuad) // add medium nodes
12498       {
12499         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12500           nodes.push_back(faceNodes[inode]);
12501         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12502           nodes.push_back(faceNodes[8]);
12503       }
12504       // add new face based on volume nodes
12505       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12506       {
12507         nbExisted++; // face already exsist
12508       }
12509       else
12510       {
12511         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12512         nbCreated++;
12513       }
12514     }
12515   }
12516   return ( nbFree == ( nbExisted + nbCreated ));
12517 }
12518
12519 namespace
12520 {
12521   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12522   {
12523     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12524       return n;
12525     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12526   }
12527 }
12528 //================================================================================
12529 /*!
12530  * \brief Creates missing boundary elements
12531  *  \param elements - elements whose boundary is to be checked
12532  *  \param dimension - defines type of boundary elements to create
12533  *  \param group - a group to store created boundary elements in
12534  *  \param targetMesh - a mesh to store created boundary elements in
12535  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12536  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12537  *                                boundary elements will be copied into the targetMesh
12538  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12539  *                                boundary elements will be added into the new group
12540  *  \param aroundElements - if true, elements will be created on boundary of given
12541  *                          elements else, on boundary of the whole mesh.
12542  * \return nb of added boundary elements
12543  */
12544 //================================================================================
12545
12546 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12547                                        Bnd_Dimension           dimension,
12548                                        SMESH_Group*            group/*=0*/,
12549                                        SMESH_Mesh*             targetMesh/*=0*/,
12550                                        bool                    toCopyElements/*=false*/,
12551                                        bool                    toCopyExistingBoundary/*=false*/,
12552                                        bool                    toAddExistingBondary/*= false*/,
12553                                        bool                    aroundElements/*= false*/)
12554 {
12555   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12556   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12557   // hope that all elements are of the same type, do not check them all
12558   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12559     throw SALOME_Exception(LOCALIZED("wrong element type"));
12560
12561   if ( !targetMesh )
12562     toCopyElements = toCopyExistingBoundary = false;
12563
12564   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12565   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12566   int nbAddedBnd = 0;
12567
12568   // editor adding present bnd elements and optionally holding elements to add to the group
12569   SMESH_MeshEditor* presentEditor;
12570   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12571   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12572
12573   SMESH_MesherHelper helper( *myMesh );
12574   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12575   SMDS_VolumeTool vTool;
12576   TIDSortedElemSet avoidSet;
12577   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12578   size_t inode;
12579
12580   typedef vector<const SMDS_MeshNode*> TConnectivity;
12581   TConnectivity tgtNodes;
12582   ElemFeatures elemKind( missType ), elemToCopy;
12583
12584   vector<const SMDS_MeshElement*> presentBndElems;
12585   vector<TConnectivity>           missingBndElems;
12586   vector<int>                     freeFacets;
12587   TConnectivity nodes, elemNodes;
12588
12589   SMDS_ElemIteratorPtr eIt;
12590   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12591   else                  eIt = elemSetIterator( elements );
12592
12593   while (eIt->more())
12594   {
12595     const SMDS_MeshElement* elem = eIt->next();
12596     const int              iQuad = elem->IsQuadratic();
12597     elemKind.SetQuad( iQuad );
12598
12599     // ------------------------------------------------------------------------------------
12600     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12601     // ------------------------------------------------------------------------------------
12602     presentBndElems.clear();
12603     missingBndElems.clear();
12604     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12605     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12606     {
12607       const SMDS_MeshElement* otherVol = 0;
12608       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12609       {
12610         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12611              ( !aroundElements || elements.count( otherVol )))
12612           continue;
12613         freeFacets.push_back( iface );
12614       }
12615       if ( missType == SMDSAbs_Face )
12616         vTool.SetExternalNormal();
12617       for ( size_t i = 0; i < freeFacets.size(); ++i )
12618       {
12619         int                iface = freeFacets[i];
12620         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12621         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12622         if ( missType == SMDSAbs_Edge ) // boundary edges
12623         {
12624           nodes.resize( 2+iQuad );
12625           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12626           {
12627             for ( size_t j = 0; j < nodes.size(); ++j )
12628               nodes[ j ] = nn[ i+j ];
12629             if ( const SMDS_MeshElement* edge =
12630                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12631               presentBndElems.push_back( edge );
12632             else
12633               missingBndElems.push_back( nodes );
12634           }
12635         }
12636         else // boundary face
12637         {
12638           nodes.clear();
12639           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12640             nodes.push_back( nn[inode] ); // add corner nodes
12641           if (iQuad)
12642             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12643               nodes.push_back( nn[inode] ); // add medium nodes
12644           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12645           if ( iCenter > 0 )
12646             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12647
12648           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12649                                                                SMDSAbs_Face, /*noMedium=*/false ))
12650             presentBndElems.push_back( f );
12651           else
12652             missingBndElems.push_back( nodes );
12653
12654           if ( targetMesh != myMesh )
12655           {
12656             // add 1D elements on face boundary to be added to a new mesh
12657             const SMDS_MeshElement* edge;
12658             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12659             {
12660               if ( iQuad )
12661                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12662               else
12663                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12664               if ( edge && avoidSet.insert( edge ).second )
12665                 presentBndElems.push_back( edge );
12666             }
12667           }
12668         }
12669       }
12670     }
12671     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12672     {
12673       avoidSet.clear(), avoidSet.insert( elem );
12674       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12675                         SMDS_MeshElement::iterator() );
12676       elemNodes.push_back( elemNodes[0] );
12677       nodes.resize( 2 + iQuad );
12678       const int nbLinks = elem->NbCornerNodes();
12679       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12680       {
12681         nodes[0] = elemNodes[iN];
12682         nodes[1] = elemNodes[iN+1+iQuad];
12683         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12684           continue; // not free link
12685
12686         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12687         if ( const SMDS_MeshElement* edge =
12688              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12689           presentBndElems.push_back( edge );
12690         else
12691           missingBndElems.push_back( nodes );
12692       }
12693     }
12694
12695     // ---------------------------------
12696     // 2. Add missing boundary elements
12697     // ---------------------------------
12698     if ( targetMesh != myMesh )
12699       // instead of making a map of nodes in this mesh and targetMesh,
12700       // we create nodes with same IDs.
12701       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12702       {
12703         TConnectivity& srcNodes = missingBndElems[i];
12704         tgtNodes.resize( srcNodes.size() );
12705         for ( inode = 0; inode < srcNodes.size(); ++inode )
12706           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12707         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12708                                                                    missType,
12709                                                                    /*noMedium=*/false))
12710           continue;
12711         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12712         ++nbAddedBnd;
12713       }
12714     else
12715       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12716       {
12717         TConnectivity& nodes = missingBndElems[ i ];
12718         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12719                                                                    missType,
12720                                                                    /*noMedium=*/false))
12721           continue;
12722         SMDS_MeshElement* newElem =
12723           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12724         nbAddedBnd += bool( newElem );
12725
12726         // try to set a new element to a shape
12727         if ( myMesh->HasShapeToMesh() )
12728         {
12729           bool ok = true;
12730           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12731           const size_t nbN = nodes.size() / (iQuad+1 );
12732           for ( inode = 0; inode < nbN && ok; ++inode )
12733           {
12734             pair<int, TopAbs_ShapeEnum> i_stype =
12735               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12736             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12737               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12738           }
12739           if ( ok && mediumShapes.size() > 1 )
12740           {
12741             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12742             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12743             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12744             {
12745               if (( ok = ( stype_i->first != stype_i_0.first )))
12746                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12747                                         aMesh->IndexToShape( stype_i_0.second ));
12748             }
12749           }
12750           if ( ok && mediumShapes.begin()->first == missShapeType )
12751             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12752         }
12753       }
12754
12755     // ----------------------------------
12756     // 3. Copy present boundary elements
12757     // ----------------------------------
12758     if ( toCopyExistingBoundary )
12759       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12760       {
12761         const SMDS_MeshElement* e = presentBndElems[i];
12762         tgtNodes.resize( e->NbNodes() );
12763         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12764           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12765         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12766       }
12767     else // store present elements to add them to a group
12768       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12769       {
12770         presentEditor->myLastCreatedElems.Append( presentBndElems[ i ]);
12771       }
12772
12773   } // loop on given elements
12774
12775   // ---------------------------------------------
12776   // 4. Fill group with boundary elements
12777   // ---------------------------------------------
12778   if ( group )
12779   {
12780     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12781       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12782         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12783   }
12784   tgtEditor.myLastCreatedElems.Clear();
12785   tgtEditor2.myLastCreatedElems.Clear();
12786
12787   // -----------------------
12788   // 5. Copy given elements
12789   // -----------------------
12790   if ( toCopyElements && targetMesh != myMesh )
12791   {
12792     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12793     else                  eIt = elemSetIterator( elements );
12794     while (eIt->more())
12795     {
12796       const SMDS_MeshElement* elem = eIt->next();
12797       tgtNodes.resize( elem->NbNodes() );
12798       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12799         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12800       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12801
12802       tgtEditor.myLastCreatedElems.Clear();
12803     }
12804   }
12805   return nbAddedBnd;
12806 }
12807
12808 //================================================================================
12809 /*!
12810  * \brief Copy node position and set \a to node on the same geometry
12811  */
12812 //================================================================================
12813
12814 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12815                                      const SMDS_MeshNode* to )
12816 {
12817   if ( !from || !to ) return;
12818
12819   SMDS_PositionPtr pos = from->GetPosition();
12820   if ( !pos || from->getshapeId() < 1 ) return;
12821
12822   switch ( pos->GetTypeOfPosition() )
12823   {
12824   case SMDS_TOP_3DSPACE: break;
12825
12826   case SMDS_TOP_FACE:
12827   {
12828     const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12829     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12830                                 fPos->GetUParameter(), fPos->GetVParameter() );
12831     break;
12832   }
12833   case SMDS_TOP_EDGE:
12834   {
12835     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12836     const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12837     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12838     break;
12839   }
12840   case SMDS_TOP_VERTEX:
12841   {
12842     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12843     break;
12844   }
12845   case SMDS_TOP_UNSPEC:
12846   default:;
12847   }
12848 }