Salome HOME
Fix crashes caused by mesh compacting
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // File      : SMESH_MeshEditor.cxx
24 // Created   : Mon Apr 12 16:10:22 2004
25 // Author    : Edward AGAPOV (eap)
26
27 #include "SMESH_MeshEditor.hxx"
28
29 #include "SMDS_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
48
49 #include <Basics_OCCTVersion.hxx>
50
51 #include "utilities.h"
52 #include "chrono.hxx"
53
54 #include <BRepAdaptor_Surface.hxx>
55 #include <BRepBuilderAPI_MakeEdge.hxx>
56 #include <BRepClass3d_SolidClassifier.hxx>
57 #include <BRep_Tool.hxx>
58 #include <ElCLib.hxx>
59 #include <Extrema_GenExtPS.hxx>
60 #include <Extrema_POnCurv.hxx>
61 #include <Extrema_POnSurf.hxx>
62 #include <Geom2d_Curve.hxx>
63 #include <GeomAdaptor_Surface.hxx>
64 #include <Geom_Curve.hxx>
65 #include <Geom_Surface.hxx>
66 #include <Precision.hxx>
67 #include <TColStd_ListOfInteger.hxx>
68 #include <TopAbs_State.hxx>
69 #include <TopExp.hxx>
70 #include <TopExp_Explorer.hxx>
71 #include <TopTools_ListIteratorOfListOfShape.hxx>
72 #include <TopTools_ListOfShape.hxx>
73 #include <TopTools_SequenceOfShape.hxx>
74 #include <TopoDS.hxx>
75 #include <TopoDS_Edge.hxx>
76 #include <TopoDS_Face.hxx>
77 #include <TopoDS_Solid.hxx>
78 #include <gp.hxx>
79 #include <gp_Ax1.hxx>
80 #include <gp_Dir.hxx>
81 #include <gp_Lin.hxx>
82 #include <gp_Pln.hxx>
83 #include <gp_Trsf.hxx>
84 #include <gp_Vec.hxx>
85 #include <gp_XY.hxx>
86 #include <gp_XYZ.hxx>
87
88 #include <cmath>
89
90 #include <map>
91 #include <set>
92 #include <numeric>
93 #include <limits>
94 #include <algorithm>
95 #include <sstream>
96
97 #include <boost/tuple/tuple.hpp>
98
99 #include <Standard_Failure.hxx>
100 #include <Standard_ErrorHandler.hxx>
101 #include <OSD_Parallel.hxx>
102
103 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
104
105 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
106
107 using namespace std;
108 using namespace SMESH::Controls;
109
110 namespace
111 {
112   template < class ELEM_SET >
113   SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
114   {
115     typedef SMDS_SetIterator
116       < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
117     return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
118   }
119 }
120
121 //=======================================================================
122 //function : SMESH_MeshEditor
123 //purpose  :
124 //=======================================================================
125
126 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
127   :myMesh( theMesh ) // theMesh may be NULL
128 {
129 }
130
131 //================================================================================
132 /*!
133  * \brief Return mesh DS
134  */
135 //================================================================================
136
137 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
138 {
139   return myMesh->GetMeshDS();
140 }
141
142
143 //================================================================================
144 /*!
145  * \brief Clears myLastCreatedNodes and myLastCreatedElems
146  */
147 //================================================================================
148
149 void SMESH_MeshEditor::ClearLastCreated()
150 {
151   myLastCreatedNodes.Clear();
152   myLastCreatedElems.Clear();
153 }
154
155 //================================================================================
156 /*!
157  * \brief Initializes members by an existing element
158  *  \param [in] elem - the source element
159  *  \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
160  */
161 //================================================================================
162
163 SMESH_MeshEditor::ElemFeatures&
164 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
165 {
166   if ( elem )
167   {
168     myType = elem->GetType();
169     if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
170     {
171       myIsPoly = elem->IsPoly();
172       if ( myIsPoly )
173       {
174         myIsQuad = elem->IsQuadratic();
175         if ( myType == SMDSAbs_Volume && !basicOnly )
176         {
177           vector<int > quant = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
178           myPolyhedQuantities.swap( quant );
179         }
180       }
181     }
182     else if ( myType == SMDSAbs_Ball && !basicOnly )
183     {
184       myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
185     }
186   }
187   return *this;
188 }
189
190 //=======================================================================
191 /*!
192  * \brief Add element
193  */
194 //=======================================================================
195
196 SMDS_MeshElement*
197 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
198                              const ElemFeatures&                  features)
199 {
200   SMDS_MeshElement* e = 0;
201   int nbnode = node.size();
202   SMESHDS_Mesh* mesh = GetMeshDS();
203   const int ID = features.myID;
204
205   switch ( features.myType ) {
206   case SMDSAbs_Face:
207     if ( !features.myIsPoly ) {
208       if      (nbnode == 3) {
209         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
210         else           e = mesh->AddFace      (node[0], node[1], node[2] );
211       }
212       else if (nbnode == 4) {
213         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
214         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
215       }
216       else if (nbnode == 6) {
217         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
218                                                node[4], node[5], ID);
219         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
220                                                node[4], node[5] );
221       }
222       else if (nbnode == 7) {
223         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
224                                                node[4], node[5], node[6], ID);
225         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
226                                                node[4], node[5], node[6] );
227       }
228       else if (nbnode == 8) {
229         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
230                                                node[4], node[5], node[6], node[7], ID);
231         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
232                                                node[4], node[5], node[6], node[7] );
233       }
234       else if (nbnode == 9) {
235         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
236                                                node[4], node[5], node[6], node[7], node[8], ID);
237         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
238                                                node[4], node[5], node[6], node[7], node[8] );
239       }
240     }
241     else if ( !features.myIsQuad )
242     {
243       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
244       else           e = mesh->AddPolygonalFace      (node    );
245     }
246     else if ( nbnode % 2 == 0 ) // just a protection
247     {
248       if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
249       else           e = mesh->AddQuadPolygonalFace      (node    );
250     }
251     break;
252
253   case SMDSAbs_Volume:
254     if ( !features.myIsPoly ) {
255       if      (nbnode == 4) {
256         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
257         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
258       }
259       else if (nbnode == 5) {
260         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
261                                                  node[4], ID);
262         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
263                                                  node[4] );
264       }
265       else if (nbnode == 6) {
266         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
267                                                  node[4], node[5], ID);
268         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
269                                                  node[4], node[5] );
270       }
271       else if (nbnode == 8) {
272         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
273                                                  node[4], node[5], node[6], node[7], ID);
274         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
275                                                  node[4], node[5], node[6], node[7] );
276       }
277       else if (nbnode == 10) {
278         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
279                                                  node[4], node[5], node[6], node[7],
280                                                  node[8], node[9], ID);
281         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
282                                                  node[4], node[5], node[6], node[7],
283                                                  node[8], node[9] );
284       }
285       else if (nbnode == 12) {
286         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
287                                                  node[4], node[5], node[6], node[7],
288                                                  node[8], node[9], node[10], node[11], ID);
289         else           e = mesh->AddVolume      (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       }
293       else if (nbnode == 13) {
294         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
295                                                  node[4], node[5], node[6], node[7],
296                                                  node[8], node[9], node[10],node[11],
297                                                  node[12],ID);
298         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
299                                                  node[4], node[5], node[6], node[7],
300                                                  node[8], node[9], node[10],node[11],
301                                                  node[12] );
302       }
303       else if (nbnode == 15) {
304         if ( ID >= 1 ) e = mesh->AddVolumeWithID(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],ID);
308         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
309                                                  node[4], node[5], node[6], node[7],
310                                                  node[8], node[9], node[10],node[11],
311                                                  node[12],node[13],node[14] );
312       }
313       else if (nbnode == 20) {
314         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
315                                                  node[4], node[5], node[6], node[7],
316                                                  node[8], node[9], node[10],node[11],
317                                                  node[12],node[13],node[14],node[15],
318                                                  node[16],node[17],node[18],node[19],ID);
319         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
320                                                  node[4], node[5], node[6], node[7],
321                                                  node[8], node[9], node[10],node[11],
322                                                  node[12],node[13],node[14],node[15],
323                                                  node[16],node[17],node[18],node[19] );
324       }
325       else if (nbnode == 27) {
326         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
327                                                  node[4], node[5], node[6], node[7],
328                                                  node[8], node[9], node[10],node[11],
329                                                  node[12],node[13],node[14],node[15],
330                                                  node[16],node[17],node[18],node[19],
331                                                  node[20],node[21],node[22],node[23],
332                                                  node[24],node[25],node[26], ID);
333         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
334                                                  node[4], node[5], node[6], node[7],
335                                                  node[8], node[9], node[10],node[11],
336                                                  node[12],node[13],node[14],node[15],
337                                                  node[16],node[17],node[18],node[19],
338                                                  node[20],node[21],node[22],node[23],
339                                                  node[24],node[25],node[26] );
340       }
341     }
342     else if ( !features.myIsQuad )
343     {
344       if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
345       else           e = mesh->AddPolyhedralVolume      (node, features.myPolyhedQuantities    );
346     }
347     else
348     {
349       // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
350       // else           e = mesh->AddQuadPolyhedralVolume      (node, features.myPolyhedQuantities   );
351     }
352     break;
353
354   case SMDSAbs_Edge:
355     if ( nbnode == 2 ) {
356       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
357       else           e = mesh->AddEdge      (node[0], node[1] );
358     }
359     else if ( nbnode == 3 ) {
360       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
361       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
362     }
363     break;
364
365   case SMDSAbs_0DElement:
366     if ( nbnode == 1 ) {
367       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
368       else           e = mesh->Add0DElement      (node[0] );
369     }
370     break;
371
372   case SMDSAbs_Node:
373     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
374     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z()    );
375     break;
376
377   case SMDSAbs_Ball:
378     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
379     else           e = mesh->AddBall      (node[0], features.myBallDiameter    );
380     break;
381
382   default:;
383   }
384   if ( e ) myLastCreatedElems.Append( e );
385   return e;
386 }
387
388 //=======================================================================
389 /*!
390  * \brief Add element
391  */
392 //=======================================================================
393
394 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
395                                                const ElemFeatures& features)
396 {
397   vector<const SMDS_MeshNode*> nodes;
398   nodes.reserve( nodeIDs.size() );
399   vector<int>::const_iterator id = nodeIDs.begin();
400   while ( id != nodeIDs.end() ) {
401     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
402       nodes.push_back( node );
403     else
404       return 0;
405   }
406   return AddElement( nodes, features );
407 }
408
409 //=======================================================================
410 //function : Remove
411 //purpose  : Remove a node or an element.
412 //           Modify a compute state of sub-meshes which become empty
413 //=======================================================================
414
415 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
416                               const bool         isNodes )
417 {
418   myLastCreatedElems.Clear();
419   myLastCreatedNodes.Clear();
420
421   SMESHDS_Mesh* aMesh = GetMeshDS();
422   set< SMESH_subMesh *> smmap;
423
424   int removed = 0;
425   list<int>::const_iterator it = theIDs.begin();
426   for ( ; it != theIDs.end(); it++ ) {
427     const SMDS_MeshElement * elem;
428     if ( isNodes )
429       elem = aMesh->FindNode( *it );
430     else
431       elem = aMesh->FindElement( *it );
432     if ( !elem )
433       continue;
434
435     // Notify VERTEX sub-meshes about modification
436     if ( isNodes ) {
437       const SMDS_MeshNode* node = cast2Node( elem );
438       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
439         if ( int aShapeID = node->getshapeId() )
440           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
441             smmap.insert( sm );
442     }
443     // Find sub-meshes to notify about modification
444     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
445     //     while ( nodeIt->more() ) {
446     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
447     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
448     //       if ( aPosition.get() ) {
449     //         if ( int aShapeID = aPosition->GetShapeId() ) {
450     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
451     //             smmap.insert( sm );
452     //         }
453     //       }
454     //     }
455
456     // Do remove
457     if ( isNodes )
458       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
459     else
460       aMesh->RemoveElement( elem );
461     removed++;
462   }
463
464   // Notify sub-meshes about modification
465   if ( !smmap.empty() ) {
466     set< SMESH_subMesh *>::iterator smIt;
467     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
468       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
469   }
470
471   //   // Check if the whole mesh becomes empty
472   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
473   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
474
475   return removed;
476 }
477
478 //================================================================================
479 /*!
480  * \brief Create 0D elements on all nodes of the given object.
481  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
482  *                    the all mesh is treated
483  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
484  *  \param duplicateElements - to add one more 0D element to a node or not
485  */
486 //================================================================================
487
488 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
489                                                    TIDSortedElemSet&       all0DElems,
490                                                    const bool              duplicateElements )
491 {
492   SMDS_ElemIteratorPtr elemIt;
493   if ( elements.empty() )
494   {
495     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
496   }
497   else
498   {
499     elemIt = elemSetIterator( elements );
500   }
501
502   while ( elemIt->more() )
503   {
504     const SMDS_MeshElement* e = elemIt->next();
505     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
506     while ( nodeIt->more() )
507     {
508       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
509       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
510       if ( duplicateElements || !it0D->more() )
511       {
512         myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
513         all0DElems.insert( myLastCreatedElems.Last() );
514       }
515       while ( it0D->more() )
516         all0DElems.insert( it0D->next() );
517     }
518   }
519 }
520
521 //=======================================================================
522 //function : FindShape
523 //purpose  : Return an index of the shape theElem is on
524 //           or zero if a shape not found
525 //=======================================================================
526
527 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
528 {
529   myLastCreatedElems.Clear();
530   myLastCreatedNodes.Clear();
531
532   SMESHDS_Mesh * aMesh = GetMeshDS();
533   if ( aMesh->ShapeToMesh().IsNull() )
534     return 0;
535
536   int aShapeID = theElem->getshapeId();
537   if ( aShapeID < 1 )
538     return 0;
539
540   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
541     if ( sm->Contains( theElem ))
542       return aShapeID;
543
544   if ( theElem->GetType() == SMDSAbs_Node ) {
545     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
546   }
547   else {
548     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
549   }
550
551   TopoDS_Shape aShape; // the shape a node of theElem is on
552   if ( theElem->GetType() != SMDSAbs_Node )
553   {
554     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
555     while ( nodeIt->more() ) {
556       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
557       if ((aShapeID = node->getshapeId()) > 0) {
558         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
559           if ( sm->Contains( theElem ))
560             return aShapeID;
561           if ( aShape.IsNull() )
562             aShape = aMesh->IndexToShape( aShapeID );
563         }
564       }
565     }
566   }
567
568   // None of nodes is on a proper shape,
569   // find the shape among ancestors of aShape on which a node is
570   if ( !aShape.IsNull() ) {
571     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
572     for ( ; ancIt.More(); ancIt.Next() ) {
573       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
574       if ( sm && sm->Contains( theElem ))
575         return aMesh->ShapeToIndex( ancIt.Value() );
576     }
577   }
578   else
579   {
580     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
581     while ( const SMESHDS_SubMesh* sm = smIt->next() )
582       if ( sm->Contains( theElem ))
583         return sm->GetID();
584   }
585
586   return 0;
587 }
588
589 //=======================================================================
590 //function : IsMedium
591 //purpose  :
592 //=======================================================================
593
594 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
595                                 const SMDSAbs_ElementType typeToCheck)
596 {
597   bool isMedium = false;
598   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
599   while (it->more() && !isMedium ) {
600     const SMDS_MeshElement* elem = it->next();
601     isMedium = elem->IsMediumNode(node);
602   }
603   return isMedium;
604 }
605
606 //=======================================================================
607 //function : shiftNodesQuadTria
608 //purpose  : Shift nodes in the array corresponded to quadratic triangle
609 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
610 //=======================================================================
611
612 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
613 {
614   const SMDS_MeshNode* nd1 = aNodes[0];
615   aNodes[0] = aNodes[1];
616   aNodes[1] = aNodes[2];
617   aNodes[2] = nd1;
618   const SMDS_MeshNode* nd2 = aNodes[3];
619   aNodes[3] = aNodes[4];
620   aNodes[4] = aNodes[5];
621   aNodes[5] = nd2;
622 }
623
624 //=======================================================================
625 //function : nbEdgeConnectivity
626 //purpose  : return number of the edges connected with the theNode.
627 //           if theEdges has connections with the other type of the
628 //           elements, return -1
629 //=======================================================================
630
631 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
632 {
633   // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
634   // int nb=0;
635   // while(elemIt->more()) {
636   //   elemIt->next();
637   //   nb++;
638   // }
639   // return nb;
640   return theNode->NbInverseElements();
641 }
642
643 //=======================================================================
644 //function : getNodesFromTwoTria
645 //purpose  : 
646 //=======================================================================
647
648 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
649                                 const SMDS_MeshElement * theTria2,
650                                 vector< const SMDS_MeshNode*>& N1,
651                                 vector< const SMDS_MeshNode*>& N2)
652 {
653   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
654   if ( N1.size() < 6 ) return false;
655   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
656   if ( N2.size() < 6 ) return false;
657
658   int sames[3] = {-1,-1,-1};
659   int nbsames = 0;
660   int i, j;
661   for(i=0; i<3; i++) {
662     for(j=0; j<3; j++) {
663       if(N1[i]==N2[j]) {
664         sames[i] = j;
665         nbsames++;
666         break;
667       }
668     }
669   }
670   if(nbsames!=2) return false;
671   if(sames[0]>-1) {
672     shiftNodesQuadTria(N1);
673     if(sames[1]>-1) {
674       shiftNodesQuadTria(N1);
675     }
676   }
677   i = sames[0] + sames[1] + sames[2];
678   for(; i<2; i++) {
679     shiftNodesQuadTria(N2);
680   }
681   // now we receive following N1 and N2 (using numeration as in the image below)
682   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
683   // i.e. first nodes from both arrays form a new diagonal
684   return true;
685 }
686
687 //=======================================================================
688 //function : InverseDiag
689 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
690 //           but having other common link.
691 //           Return False if args are improper
692 //=======================================================================
693
694 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
695                                     const SMDS_MeshElement * theTria2 )
696 {
697   myLastCreatedElems.Clear();
698   myLastCreatedNodes.Clear();
699
700   if (!theTria1 || !theTria2)
701     return false;
702
703   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
704   if (!F1) return false;
705   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
706   if (!F2) return false;
707   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
708       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
709
710     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
711     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
712     //    |/ |                                         | \|
713     //  B +--+ 2                                     B +--+ 2
714
715     // put nodes in array and find out indices of the same ones
716     const SMDS_MeshNode* aNodes [6];
717     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
718     int i = 0;
719     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
720     while ( it->more() ) {
721       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
722
723       if ( i > 2 ) // theTria2
724         // find same node of theTria1
725         for ( int j = 0; j < 3; j++ )
726           if ( aNodes[ i ] == aNodes[ j ]) {
727             sameInd[ j ] = i;
728             sameInd[ i ] = j;
729             break;
730           }
731       // next
732       i++;
733       if ( i == 3 ) {
734         if ( it->more() )
735           return false; // theTria1 is not a triangle
736         it = theTria2->nodesIterator();
737       }
738       if ( i == 6 && it->more() )
739         return false; // theTria2 is not a triangle
740     }
741
742     // find indices of 1,2 and of A,B in theTria1
743     int iA = -1, iB = 0, i1 = 0, i2 = 0;
744     for ( i = 0; i < 6; i++ ) {
745       if ( sameInd [ i ] == -1 ) {
746         if ( i < 3 ) i1 = i;
747         else         i2 = i;
748       }
749       else if (i < 3) {
750         if ( iA >= 0) iB = i;
751         else          iA = i;
752       }
753     }
754     // nodes 1 and 2 should not be the same
755     if ( aNodes[ i1 ] == aNodes[ i2 ] )
756       return false;
757
758     // theTria1: A->2
759     aNodes[ iA ] = aNodes[ i2 ];
760     // theTria2: B->1
761     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
762
763     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
764     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
765
766     return true;
767
768   } // end if(F1 && F2)
769
770   // check case of quadratic faces
771   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
772       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
773     return false;
774   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
775       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
776     return false;
777
778   //       5
779   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
780   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
781   //    |   / |
782   //  7 +  +  + 6
783   //    | /9  |
784   //    |/    |
785   //  4 +--+--+ 3
786   //       8
787
788   vector< const SMDS_MeshNode* > N1;
789   vector< const SMDS_MeshNode* > N2;
790   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
791     return false;
792   // now we receive following N1 and N2 (using numeration as above image)
793   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
794   // i.e. first nodes from both arrays determ new diagonal
795
796   vector< const SMDS_MeshNode*> N1new( N1.size() );
797   vector< const SMDS_MeshNode*> N2new( N2.size() );
798   N1new.back() = N1.back(); // central node of biquadratic
799   N2new.back() = N2.back();
800   N1new[0] = N1[0];  N2new[0] = N1[0];
801   N1new[1] = N2[0];  N2new[1] = N1[1];
802   N1new[2] = N2[1];  N2new[2] = N2[0];
803   N1new[3] = N1[4];  N2new[3] = N1[3];
804   N1new[4] = N2[3];  N2new[4] = N2[5];
805   N1new[5] = N1[5];  N2new[5] = N1[4];
806   // change nodes in faces
807   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
808   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
809
810   // move the central node of biquadratic triangle
811   SMESH_MesherHelper helper( *GetMesh() );
812   for ( int is2nd = 0; is2nd < 2; ++is2nd )
813   {
814     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
815     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
816     if ( nodes.size() < 7 )
817       continue;
818     helper.SetSubShape( tria->getshapeId() );
819     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
820     gp_Pnt xyz;
821     if ( F.IsNull() )
822     {
823       xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
824               SMESH_TNodeXYZ( nodes[4] ) +
825               SMESH_TNodeXYZ( nodes[5] )) / 3.;
826     }
827     else
828     {
829       bool checkUV;
830       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
831                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
832                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
833       TopLoc_Location loc;
834       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
835       xyz = S->Value( uv.X(), uv.Y() );
836       xyz.Transform( loc );
837       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
838            nodes[6]->getshapeId() > 0 )
839         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
840     }
841     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
842   }
843   return true;
844 }
845
846 //=======================================================================
847 //function : findTriangles
848 //purpose  : find triangles sharing theNode1-theNode2 link
849 //=======================================================================
850
851 static bool findTriangles(const SMDS_MeshNode *    theNode1,
852                           const SMDS_MeshNode *    theNode2,
853                           const SMDS_MeshElement*& theTria1,
854                           const SMDS_MeshElement*& theTria2)
855 {
856   if ( !theNode1 || !theNode2 ) return false;
857
858   theTria1 = theTria2 = 0;
859
860   set< const SMDS_MeshElement* > emap;
861   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
862   while (it->more()) {
863     const SMDS_MeshElement* elem = it->next();
864     if ( elem->NbCornerNodes() == 3 )
865       emap.insert( elem );
866   }
867   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
868   while (it->more()) {
869     const SMDS_MeshElement* elem = it->next();
870     if ( emap.count( elem )) {
871       if ( !theTria1 )
872       {
873         theTria1 = elem;
874       }
875       else  
876       {
877         theTria2 = elem;
878         // theTria1 must be element with minimum ID
879         if ( theTria2->GetID() < theTria1->GetID() )
880           std::swap( theTria2, theTria1 );
881         return true;
882       }
883     }
884   }
885   return false;
886 }
887
888 //=======================================================================
889 //function : InverseDiag
890 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
891 //           with ones built on the same 4 nodes but having other common link.
892 //           Return false if proper faces not found
893 //=======================================================================
894
895 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
896                                     const SMDS_MeshNode * theNode2)
897 {
898   myLastCreatedElems.Clear();
899   myLastCreatedNodes.Clear();
900
901   const SMDS_MeshElement *tr1, *tr2;
902   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
903     return false;
904
905   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
906   if (!F1) return false;
907   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
908   if (!F2) return false;
909   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
910       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
911
912     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
913     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
914     //    |/ |                                    | \|
915     //  B +--+ 2                                B +--+ 2
916
917     // put nodes in array
918     // and find indices of 1,2 and of A in tr1 and of B in tr2
919     int i, iA1 = 0, i1 = 0;
920     const SMDS_MeshNode* aNodes1 [3];
921     SMDS_ElemIteratorPtr it;
922     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
923       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
924       if ( aNodes1[ i ] == theNode1 )
925         iA1 = i; // node A in tr1
926       else if ( aNodes1[ i ] != theNode2 )
927         i1 = i;  // node 1
928     }
929     int iB2 = 0, i2 = 0;
930     const SMDS_MeshNode* aNodes2 [3];
931     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
932       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
933       if ( aNodes2[ i ] == theNode2 )
934         iB2 = i; // node B in tr2
935       else if ( aNodes2[ i ] != theNode1 )
936         i2 = i;  // node 2
937     }
938
939     // nodes 1 and 2 should not be the same
940     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
941       return false;
942
943     // tr1: A->2
944     aNodes1[ iA1 ] = aNodes2[ i2 ];
945     // tr2: B->1
946     aNodes2[ iB2 ] = aNodes1[ i1 ];
947
948     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
949     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
950
951     return true;
952   }
953
954   // check case of quadratic faces
955   return InverseDiag(tr1,tr2);
956 }
957
958 //=======================================================================
959 //function : getQuadrangleNodes
960 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
961 //           fusion of triangles tr1 and tr2 having shared link on
962 //           theNode1 and theNode2
963 //=======================================================================
964
965 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
966                         const SMDS_MeshNode *    theNode1,
967                         const SMDS_MeshNode *    theNode2,
968                         const SMDS_MeshElement * tr1,
969                         const SMDS_MeshElement * tr2 )
970 {
971   if( tr1->NbNodes() != tr2->NbNodes() )
972     return false;
973   // find the 4-th node to insert into tr1
974   const SMDS_MeshNode* n4 = 0;
975   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
976   int i=0;
977   while ( !n4 && i<3 ) {
978     const SMDS_MeshNode * n = cast2Node( it->next() );
979     i++;
980     bool isDiag = ( n == theNode1 || n == theNode2 );
981     if ( !isDiag )
982       n4 = n;
983   }
984   // Make an array of nodes to be in a quadrangle
985   int iNode = 0, iFirstDiag = -1;
986   it = tr1->nodesIterator();
987   i=0;
988   while ( i<3 ) {
989     const SMDS_MeshNode * n = cast2Node( it->next() );
990     i++;
991     bool isDiag = ( n == theNode1 || n == theNode2 );
992     if ( isDiag ) {
993       if ( iFirstDiag < 0 )
994         iFirstDiag = iNode;
995       else if ( iNode - iFirstDiag == 1 )
996         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
997     }
998     else if ( n == n4 ) {
999       return false; // tr1 and tr2 should not have all the same nodes
1000     }
1001     theQuadNodes[ iNode++ ] = n;
1002   }
1003   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
1004     theQuadNodes[ iNode ] = n4;
1005
1006   return true;
1007 }
1008
1009 //=======================================================================
1010 //function : DeleteDiag
1011 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
1012 //           with a quadrangle built on the same 4 nodes.
1013 //           Return false if proper faces not found
1014 //=======================================================================
1015
1016 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1017                                    const SMDS_MeshNode * theNode2)
1018 {
1019   myLastCreatedElems.Clear();
1020   myLastCreatedNodes.Clear();
1021
1022   const SMDS_MeshElement *tr1, *tr2;
1023   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1024     return false;
1025
1026   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1027   if (!F1) return false;
1028   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1029   if (!F2) return false;
1030   SMESHDS_Mesh * aMesh = GetMeshDS();
1031
1032   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1033       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1034
1035     const SMDS_MeshNode* aNodes [ 4 ];
1036     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1037       return false;
1038
1039     const SMDS_MeshElement* newElem = 0;
1040     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1041     myLastCreatedElems.Append(newElem);
1042     AddToSameGroups( newElem, tr1, aMesh );
1043     int aShapeId = tr1->getshapeId();
1044     if ( aShapeId )
1045       {
1046         aMesh->SetMeshElementOnShape( newElem, aShapeId );
1047       }
1048     aMesh->RemoveElement( tr1 );
1049     aMesh->RemoveElement( tr2 );
1050
1051     return true;
1052   }
1053
1054   // check case of quadratic faces
1055   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1056     return false;
1057   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1058     return false;
1059
1060   //       5
1061   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1062   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1063   //    |   / |
1064   //  7 +  +  + 6
1065   //    | /9  |
1066   //    |/    |
1067   //  4 +--+--+ 3
1068   //       8
1069
1070   vector< const SMDS_MeshNode* > N1;
1071   vector< const SMDS_MeshNode* > N2;
1072   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1073     return false;
1074   // now we receive following N1 and N2 (using numeration as above image)
1075   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1076   // i.e. first nodes from both arrays determ new diagonal
1077
1078   const SMDS_MeshNode* aNodes[8];
1079   aNodes[0] = N1[0];
1080   aNodes[1] = N1[1];
1081   aNodes[2] = N2[0];
1082   aNodes[3] = N2[1];
1083   aNodes[4] = N1[3];
1084   aNodes[5] = N2[5];
1085   aNodes[6] = N2[3];
1086   aNodes[7] = N1[5];
1087
1088   const SMDS_MeshElement* newElem = 0;
1089   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1090                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1091   myLastCreatedElems.Append(newElem);
1092   AddToSameGroups( newElem, tr1, aMesh );
1093   int aShapeId = tr1->getshapeId();
1094   if ( aShapeId )
1095     {
1096       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1097     }
1098   aMesh->RemoveElement( tr1 );
1099   aMesh->RemoveElement( tr2 );
1100
1101   // remove middle node (9)
1102   GetMeshDS()->RemoveNode( N1[4] );
1103
1104   return true;
1105 }
1106
1107 //=======================================================================
1108 //function : Reorient
1109 //purpose  : Reverse theElement orientation
1110 //=======================================================================
1111
1112 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1113 {
1114   myLastCreatedElems.Clear();
1115   myLastCreatedNodes.Clear();
1116
1117   if (!theElem)
1118     return false;
1119   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1120   if ( !it || !it->more() )
1121     return false;
1122
1123   const SMDSAbs_ElementType type = theElem->GetType();
1124   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1125     return false;
1126
1127   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1128   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1129   {
1130     const SMDS_VtkVolume* aPolyedre =
1131       dynamic_cast<const SMDS_VtkVolume*>( theElem );
1132     if (!aPolyedre) {
1133       MESSAGE("Warning: bad volumic element");
1134       return false;
1135     }
1136     const int nbFaces = aPolyedre->NbFaces();
1137     vector<const SMDS_MeshNode *> poly_nodes;
1138     vector<int> quantities (nbFaces);
1139
1140     // reverse each face of the polyedre
1141     for (int iface = 1; iface <= nbFaces; iface++) {
1142       int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1143       quantities[iface - 1] = nbFaceNodes;
1144
1145       for (inode = nbFaceNodes; inode >= 1; inode--) {
1146         const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1147         poly_nodes.push_back(curNode);
1148       }
1149     }
1150     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1151   }
1152   else // other elements
1153   {
1154     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1155     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1156     if ( interlace.empty() )
1157     {
1158       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1159     }
1160     else
1161     {
1162       SMDS_MeshCell::applyInterlace( interlace, nodes );
1163     }
1164     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1165   }
1166   return false;
1167 }
1168
1169 //================================================================================
1170 /*!
1171  * \brief Reorient faces.
1172  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1173  * \param theDirection - desired direction of normal of \a theFace
1174  * \param theFace - one of \a theFaces that should be oriented according to
1175  *        \a theDirection and whose orientation defines orientation of other faces
1176  * \return number of reoriented faces.
1177  */
1178 //================================================================================
1179
1180 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1181                                   const gp_Dir&            theDirection,
1182                                   const SMDS_MeshElement * theFace)
1183 {
1184   int nbReori = 0;
1185   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1186
1187   if ( theFaces.empty() )
1188   {
1189     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1190     while ( fIt->more() )
1191       theFaces.insert( theFaces.end(), fIt->next() );
1192   }
1193
1194   // orient theFace according to theDirection
1195   gp_XYZ normal;
1196   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1197   if ( normal * theDirection.XYZ() < 0 )
1198     nbReori += Reorient( theFace );
1199
1200   // Orient other faces
1201
1202   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1203   TIDSortedElemSet avoidSet;
1204   set< SMESH_TLink > checkedLinks;
1205   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1206
1207   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1208     theFaces.erase( theFace );
1209   startFaces.insert( theFace );
1210
1211   int nodeInd1, nodeInd2;
1212   const SMDS_MeshElement*           otherFace;
1213   vector< const SMDS_MeshElement* > facesNearLink;
1214   vector< std::pair< int, int > >   nodeIndsOfFace;
1215
1216   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1217   while ( !startFaces.empty() )
1218   {
1219     startFace = startFaces.begin();
1220     theFace = *startFace;
1221     startFaces.erase( startFace );
1222     if ( !visitedFaces.insert( theFace ).second )
1223       continue;
1224
1225     avoidSet.clear();
1226     avoidSet.insert(theFace);
1227
1228     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1229
1230     const int nbNodes = theFace->NbCornerNodes();
1231     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1232     {
1233       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1234       linkIt_isNew = checkedLinks.insert( link );
1235       if ( !linkIt_isNew.second )
1236       {
1237         // link has already been checked and won't be encountered more
1238         // if the group (theFaces) is manifold
1239         //checkedLinks.erase( linkIt_isNew.first );
1240       }
1241       else
1242       {
1243         facesNearLink.clear();
1244         nodeIndsOfFace.clear();
1245         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1246                                                              theFaces, avoidSet,
1247                                                              &nodeInd1, &nodeInd2 )))
1248           if ( otherFace != theFace)
1249           {
1250             facesNearLink.push_back( otherFace );
1251             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1252             avoidSet.insert( otherFace );
1253           }
1254         if ( facesNearLink.size() > 1 )
1255         {
1256           // NON-MANIFOLD mesh shell !
1257           // select a face most co-directed with theFace,
1258           // other faces won't be visited this time
1259           gp_XYZ NF, NOF;
1260           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1261           double proj, maxProj = -1;
1262           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1263             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1264             if (( proj = Abs( NF * NOF )) > maxProj ) {
1265               maxProj = proj;
1266               otherFace = facesNearLink[i];
1267               nodeInd1  = nodeIndsOfFace[i].first;
1268               nodeInd2  = nodeIndsOfFace[i].second;
1269             }
1270           }
1271           // not to visit rejected faces
1272           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1273             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1274               visitedFaces.insert( facesNearLink[i] );
1275         }
1276         else if ( facesNearLink.size() == 1 )
1277         {
1278           otherFace = facesNearLink[0];
1279           nodeInd1  = nodeIndsOfFace.back().first;
1280           nodeInd2  = nodeIndsOfFace.back().second;
1281         }
1282         if ( otherFace && otherFace != theFace)
1283         {
1284           // link must be reverse in otherFace if orientation ot otherFace
1285           // is same as that of theFace
1286           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1287           {
1288             nbReori += Reorient( otherFace );
1289           }
1290           startFaces.insert( otherFace );
1291         }
1292       }
1293       std::swap( link.first, link.second ); // reverse the link
1294     }
1295   }
1296   return nbReori;
1297 }
1298
1299 //================================================================================
1300 /*!
1301  * \brief Reorient faces basing on orientation of adjacent volumes.
1302  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1303  * \param theVolumes - reference volumes.
1304  * \param theOutsideNormal - to orient faces to have their normal
1305  *        pointing either \a outside or \a inside the adjacent volumes.
1306  * \return number of reoriented faces.
1307  */
1308 //================================================================================
1309
1310 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1311                                       TIDSortedElemSet & theVolumes,
1312                                       const bool         theOutsideNormal)
1313 {
1314   int nbReori = 0;
1315
1316   SMDS_ElemIteratorPtr faceIt;
1317   if ( theFaces.empty() )
1318     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1319   else
1320     faceIt = elemSetIterator( theFaces );
1321
1322   vector< const SMDS_MeshNode* > faceNodes;
1323   TIDSortedElemSet checkedVolumes;
1324   set< const SMDS_MeshNode* > faceNodesSet;
1325   SMDS_VolumeTool volumeTool;
1326
1327   while ( faceIt->more() ) // loop on given faces
1328   {
1329     const SMDS_MeshElement* face = faceIt->next();
1330     if ( face->GetType() != SMDSAbs_Face )
1331       continue;
1332
1333     const size_t nbCornersNodes = face->NbCornerNodes();
1334     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1335
1336     checkedVolumes.clear();
1337     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1338     while ( vIt->more() )
1339     {
1340       const SMDS_MeshElement* volume = vIt->next();
1341
1342       if ( !checkedVolumes.insert( volume ).second )
1343         continue;
1344       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1345         continue;
1346
1347       // is volume adjacent?
1348       bool allNodesCommon = true;
1349       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1350         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1351       if ( !allNodesCommon )
1352         continue;
1353
1354       // get nodes of a corresponding volume facet
1355       faceNodesSet.clear();
1356       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1357       volumeTool.Set( volume );
1358       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1359       if ( facetID < 0 ) continue;
1360       volumeTool.SetExternalNormal();
1361       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1362
1363       // compare order of faceNodes and facetNodes
1364       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1365       int iNN[2];
1366       for ( int i = 0; i < 2; ++i )
1367       {
1368         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1369         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1370           if ( faceNodes[ iN ] == n )
1371           {
1372             iNN[ i ] = iN;
1373             break;
1374           }
1375       }
1376       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1377       if ( isOutside != theOutsideNormal )
1378         nbReori += Reorient( face );
1379     }
1380   }  // loop on given faces
1381
1382   return nbReori;
1383 }
1384
1385 //=======================================================================
1386 //function : getBadRate
1387 //purpose  :
1388 //=======================================================================
1389
1390 static double getBadRate (const SMDS_MeshElement*               theElem,
1391                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1392 {
1393   SMESH::Controls::TSequenceOfXYZ P;
1394   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1395     return 1e100;
1396   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1397   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1398 }
1399
1400 //=======================================================================
1401 //function : QuadToTri
1402 //purpose  : Cut quadrangles into triangles.
1403 //           theCrit is used to select a diagonal to cut
1404 //=======================================================================
1405
1406 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1407                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1408 {
1409   myLastCreatedElems.Clear();
1410   myLastCreatedNodes.Clear();
1411
1412   if ( !theCrit.get() )
1413     return false;
1414
1415   SMESHDS_Mesh * aMesh = GetMeshDS();
1416
1417   Handle(Geom_Surface) surface;
1418   SMESH_MesherHelper   helper( *GetMesh() );
1419
1420   TIDSortedElemSet::iterator itElem;
1421   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1422   {
1423     const SMDS_MeshElement* elem = *itElem;
1424     if ( !elem || elem->GetType() != SMDSAbs_Face )
1425       continue;
1426     if ( elem->NbCornerNodes() != 4 )
1427       continue;
1428
1429     // retrieve element nodes
1430     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1431
1432     // compare two sets of possible triangles
1433     double aBadRate1, aBadRate2; // to what extent a set is bad
1434     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1435     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1436     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1437
1438     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1439     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1440     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1441
1442     const int aShapeId = FindShape( elem );
1443     const SMDS_MeshElement* newElem1 = 0;
1444     const SMDS_MeshElement* newElem2 = 0;
1445
1446     if ( !elem->IsQuadratic() ) // split liner quadrangle
1447     {
1448       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1449       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1450       if ( aBadRate1 <= aBadRate2 ) {
1451         // tr1 + tr2 is better
1452         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1453         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1454       }
1455       else {
1456         // tr3 + tr4 is better
1457         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1458         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1459       }
1460     }
1461     else // split quadratic quadrangle
1462     {
1463       helper.SetIsQuadratic( true );
1464       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1465
1466       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1467       if ( aNodes.size() == 9 )
1468       {
1469         helper.SetIsBiQuadratic( true );
1470         if ( aBadRate1 <= aBadRate2 )
1471           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1472         else
1473           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1474       }
1475       // create a new element
1476       if ( aBadRate1 <= aBadRate2 ) {
1477         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1478         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1479       }
1480       else {
1481         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1482         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1483       }
1484     } // quadratic case
1485
1486     // care of a new element
1487
1488     myLastCreatedElems.Append(newElem1);
1489     myLastCreatedElems.Append(newElem2);
1490     AddToSameGroups( newElem1, elem, aMesh );
1491     AddToSameGroups( newElem2, elem, aMesh );
1492
1493     // put a new triangle on the same shape
1494     if ( aShapeId )
1495       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1496     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1497
1498     aMesh->RemoveElement( elem );
1499   }
1500   return true;
1501 }
1502
1503 //=======================================================================
1504 /*!
1505  * \brief Split each of given quadrangles into 4 triangles.
1506  * \param theElems - The faces to be splitted. If empty all faces are split.
1507  */
1508 //=======================================================================
1509
1510 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1511 {
1512   myLastCreatedElems.Clear();
1513   myLastCreatedNodes.Clear();
1514
1515   SMESH_MesherHelper helper( *GetMesh() );
1516   helper.SetElementsOnShape( true );
1517
1518   SMDS_ElemIteratorPtr faceIt;
1519   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1520   else                    faceIt = elemSetIterator( theElems );
1521
1522   bool   checkUV;
1523   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1524   gp_XYZ xyz[9];
1525   vector< const SMDS_MeshNode* > nodes;
1526   SMESHDS_SubMesh*               subMeshDS = 0;
1527   TopoDS_Face                    F;
1528   Handle(Geom_Surface)           surface;
1529   TopLoc_Location                loc;
1530
1531   while ( faceIt->more() )
1532   {
1533     const SMDS_MeshElement* quad = faceIt->next();
1534     if ( !quad || quad->NbCornerNodes() != 4 )
1535       continue;
1536
1537     // get a surface the quad is on
1538
1539     if ( quad->getshapeId() < 1 )
1540     {
1541       F.Nullify();
1542       helper.SetSubShape( 0 );
1543       subMeshDS = 0;
1544     }
1545     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1546     {
1547       helper.SetSubShape( quad->getshapeId() );
1548       if ( !helper.GetSubShape().IsNull() &&
1549            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1550       {
1551         F = TopoDS::Face( helper.GetSubShape() );
1552         surface = BRep_Tool::Surface( F, loc );
1553         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1554       }
1555       else
1556       {
1557         helper.SetSubShape( 0 );
1558         subMeshDS = 0;
1559       }
1560     }
1561
1562     // create a central node
1563
1564     const SMDS_MeshNode* nCentral;
1565     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1566
1567     if ( nodes.size() == 9 )
1568     {
1569       nCentral = nodes.back();
1570     }
1571     else
1572     {
1573       size_t iN = 0;
1574       if ( F.IsNull() )
1575       {
1576         for ( ; iN < nodes.size(); ++iN )
1577           xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1578
1579         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1580           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1581
1582         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1583                                    xyz[0], xyz[1], xyz[2], xyz[3],
1584                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1585       }
1586       else
1587       {
1588         for ( ; iN < nodes.size(); ++iN )
1589           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1590
1591         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1592           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1593
1594         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1595                                   uv[0], uv[1], uv[2], uv[3],
1596                                   uv[4], uv[5], uv[6], uv[7] );
1597
1598         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1599         xyz[ 8 ] = p.XYZ();
1600       }
1601
1602       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1603                                  uv[8].X(), uv[8].Y() );
1604       myLastCreatedNodes.Append( nCentral );
1605     }
1606
1607     // create 4 triangles
1608
1609     helper.SetIsQuadratic  ( nodes.size() > 4 );
1610     helper.SetIsBiQuadratic( nodes.size() == 9 );
1611     if ( helper.GetIsQuadratic() )
1612       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1613
1614     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1615
1616     for ( int i = 0; i < 4; ++i )
1617     {
1618       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1619                                                nodes[(i+1)%4],
1620                                                nCentral );
1621       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1622       myLastCreatedElems.Append( tria );
1623     }
1624   }
1625 }
1626
1627 //=======================================================================
1628 //function : BestSplit
1629 //purpose  : Find better diagonal for cutting.
1630 //=======================================================================
1631
1632 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1633                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1634 {
1635   myLastCreatedElems.Clear();
1636   myLastCreatedNodes.Clear();
1637
1638   if (!theCrit.get())
1639     return -1;
1640
1641   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1642     return -1;
1643
1644   if( theQuad->NbNodes()==4 ||
1645       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1646
1647     // retrieve element nodes
1648     const SMDS_MeshNode* aNodes [4];
1649     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1650     int i = 0;
1651     //while (itN->more())
1652     while (i<4) {
1653       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1654     }
1655     // compare two sets of possible triangles
1656     double aBadRate1, aBadRate2; // to what extent a set is bad
1657     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1658     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1659     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1660
1661     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1662     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1663     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1664     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1665     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1666     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1667       return 1; // diagonal 1-3
1668
1669     return 2; // diagonal 2-4
1670   }
1671   return -1;
1672 }
1673
1674 namespace
1675 {
1676   // Methods of splitting volumes into tetra
1677
1678   const int theHexTo5_1[5*4+1] =
1679     {
1680       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1681     };
1682   const int theHexTo5_2[5*4+1] =
1683     {
1684       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1685     };
1686   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1687
1688   const int theHexTo6_1[6*4+1] =
1689     {
1690       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
1691     };
1692   const int theHexTo6_2[6*4+1] =
1693     {
1694       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
1695     };
1696   const int theHexTo6_3[6*4+1] =
1697     {
1698       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
1699     };
1700   const int theHexTo6_4[6*4+1] =
1701     {
1702       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
1703     };
1704   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1705
1706   const int thePyraTo2_1[2*4+1] =
1707     {
1708       0, 1, 2, 4,    0, 2, 3, 4,   -1
1709     };
1710   const int thePyraTo2_2[2*4+1] =
1711     {
1712       1, 2, 3, 4,    1, 3, 0, 4,   -1
1713     };
1714   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1715
1716   const int thePentaTo3_1[3*4+1] =
1717     {
1718       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1719     };
1720   const int thePentaTo3_2[3*4+1] =
1721     {
1722       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1723     };
1724   const int thePentaTo3_3[3*4+1] =
1725     {
1726       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1727     };
1728   const int thePentaTo3_4[3*4+1] =
1729     {
1730       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1731     };
1732   const int thePentaTo3_5[3*4+1] =
1733     {
1734       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1735     };
1736   const int thePentaTo3_6[3*4+1] =
1737     {
1738       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1739     };
1740   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1741                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1742
1743   // Methods of splitting hexahedron into prisms
1744
1745   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1746     {
1747       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
1748     };
1749   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1750     {
1751       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
1752     };
1753   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1754     {
1755       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
1756     };
1757
1758   const int theHexTo2Prisms_BT_1[6*2+1] =
1759     {
1760       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1761     };
1762   const int theHexTo2Prisms_BT_2[6*2+1] =
1763     {
1764       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1765     };
1766   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1767
1768   const int theHexTo2Prisms_LR_1[6*2+1] =
1769     {
1770       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1771     };
1772   const int theHexTo2Prisms_LR_2[6*2+1] =
1773     {
1774       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1775     };
1776   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1777
1778   const int theHexTo2Prisms_FB_1[6*2+1] =
1779     {
1780       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1781     };
1782   const int theHexTo2Prisms_FB_2[6*2+1] =
1783     {
1784       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1785     };
1786   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1787
1788
1789   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1790   {
1791     int _n1, _n2, _n3;
1792     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1793     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1794     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1795                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1796   };
1797   struct TSplitMethod
1798   {
1799     int        _nbSplits;
1800     int        _nbCorners;
1801     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1802     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1803     bool       _ownConn;      //!< to delete _connectivity in destructor
1804     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1805
1806     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1807       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1808     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1809     bool hasFacet( const TTriangleFacet& facet ) const
1810     {
1811       if ( _nbCorners == 4 )
1812       {
1813         const int* tetConn = _connectivity;
1814         for ( ; tetConn[0] >= 0; tetConn += 4 )
1815           if (( facet.contains( tetConn[0] ) +
1816                 facet.contains( tetConn[1] ) +
1817                 facet.contains( tetConn[2] ) +
1818                 facet.contains( tetConn[3] )) == 3 )
1819             return true;
1820       }
1821       else // prism, _nbCorners == 6
1822       {
1823         const int* prismConn = _connectivity;
1824         for ( ; prismConn[0] >= 0; prismConn += 6 )
1825         {
1826           if (( facet.contains( prismConn[0] ) &&
1827                 facet.contains( prismConn[1] ) &&
1828                 facet.contains( prismConn[2] ))
1829               ||
1830               ( facet.contains( prismConn[3] ) &&
1831                 facet.contains( prismConn[4] ) &&
1832                 facet.contains( prismConn[5] )))
1833             return true;
1834         }
1835       }
1836       return false;
1837     }
1838   };
1839
1840   //=======================================================================
1841   /*!
1842    * \brief return TSplitMethod for the given element to split into tetrahedra
1843    */
1844   //=======================================================================
1845
1846   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1847   {
1848     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1849
1850     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1851     // an edge and a face barycenter; tertaherdons are based on triangles and
1852     // a volume barycenter
1853     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1854
1855     // Find out how adjacent volumes are split
1856
1857     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1858     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1859     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1860     {
1861       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1862       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1863       if ( nbNodes < 4 ) continue;
1864
1865       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1866       const int* nInd = vol.GetFaceNodesIndices( iF );
1867       if ( nbNodes == 4 )
1868       {
1869         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1870         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1871         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1872         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1873       }
1874       else
1875       {
1876         int iCom = 0; // common node of triangle faces to split into
1877         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1878         {
1879           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1880                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1881                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1882           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1883                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1884                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1885           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1886           {
1887             triaSplits.push_back( t012 );
1888             triaSplits.push_back( t023 );
1889             break;
1890           }
1891         }
1892       }
1893       if ( !triaSplits.empty() )
1894         hasAdjacentSplits = true;
1895     }
1896
1897     // Among variants of split method select one compliant with adjacent volumes
1898
1899     TSplitMethod method;
1900     if ( !vol.Element()->IsPoly() && !is24TetMode )
1901     {
1902       int nbVariants = 2, nbTet = 0;
1903       const int** connVariants = 0;
1904       switch ( vol.Element()->GetEntityType() )
1905       {
1906       case SMDSEntity_Hexa:
1907       case SMDSEntity_Quad_Hexa:
1908       case SMDSEntity_TriQuad_Hexa:
1909         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1910           connVariants = theHexTo5, nbTet = 5;
1911         else
1912           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1913         break;
1914       case SMDSEntity_Pyramid:
1915       case SMDSEntity_Quad_Pyramid:
1916         connVariants = thePyraTo2;  nbTet = 2;
1917         break;
1918       case SMDSEntity_Penta:
1919       case SMDSEntity_Quad_Penta:
1920         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1921         break;
1922       default:
1923         nbVariants = 0;
1924       }
1925       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1926       {
1927         // check method compliancy with adjacent tetras,
1928         // all found splits must be among facets of tetras described by this method
1929         method = TSplitMethod( nbTet, connVariants[variant] );
1930         if ( hasAdjacentSplits && method._nbSplits > 0 )
1931         {
1932           bool facetCreated = true;
1933           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1934           {
1935             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1936             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1937               facetCreated = method.hasFacet( *facet );
1938           }
1939           if ( !facetCreated )
1940             method = TSplitMethod(0); // incompatible method
1941         }
1942       }
1943     }
1944     if ( method._nbSplits < 1 )
1945     {
1946       // No standard method is applicable, use a generic solution:
1947       // each facet of a volume is split into triangles and
1948       // each of triangles and a volume barycenter form a tetrahedron.
1949
1950       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1951
1952       int* connectivity = new int[ maxTetConnSize + 1 ];
1953       method._connectivity = connectivity;
1954       method._ownConn = true;
1955       method._baryNode = !isHex27; // to create central node or not
1956
1957       int connSize = 0;
1958       int baryCenInd = vol.NbNodes() - int( isHex27 );
1959       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1960       {
1961         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1962         const int*   nInd = vol.GetFaceNodesIndices( iF );
1963         // find common node of triangle facets of tetra to create
1964         int iCommon = 0; // index in linear numeration
1965         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1966         if ( !triaSplits.empty() )
1967         {
1968           // by found facets
1969           const TTriangleFacet* facet = &triaSplits.front();
1970           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1971             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1972                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1973               break;
1974         }
1975         else if ( nbNodes > 3 && !is24TetMode )
1976         {
1977           // find the best method of splitting into triangles by aspect ratio
1978           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1979           map< double, int > badness2iCommon;
1980           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1981           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1982           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1983           {
1984             double badness = 0;
1985             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1986             {
1987               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1988                                       nodes[ iQ*((iLast-1)%nbNodes)],
1989                                       nodes[ iQ*((iLast  )%nbNodes)]);
1990               badness += getBadRate( &tria, aspectRatio );
1991             }
1992             badness2iCommon.insert( make_pair( badness, iCommon ));
1993           }
1994           // use iCommon with lowest badness
1995           iCommon = badness2iCommon.begin()->second;
1996         }
1997         if ( iCommon >= nbNodes )
1998           iCommon = 0; // something wrong
1999
2000         // fill connectivity of tetrahedra based on a current face
2001         int nbTet = nbNodes - 2;
2002         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2003         {
2004           int faceBaryCenInd;
2005           if ( isHex27 )
2006           {
2007             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2008             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2009           }
2010           else
2011           {
2012             method._faceBaryNode[ iF ] = 0;
2013             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2014           }
2015           nbTet = nbNodes;
2016           for ( int i = 0; i < nbTet; ++i )
2017           {
2018             int i1 = i, i2 = (i+1) % nbNodes;
2019             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2020             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2021             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2022             connectivity[ connSize++ ] = faceBaryCenInd;
2023             connectivity[ connSize++ ] = baryCenInd;
2024           }
2025         }
2026         else
2027         {
2028           for ( int i = 0; i < nbTet; ++i )
2029           {
2030             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2031             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2032             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2033             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2034             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2035             connectivity[ connSize++ ] = baryCenInd;
2036           }
2037         }
2038         method._nbSplits += nbTet;
2039
2040       } // loop on volume faces
2041
2042       connectivity[ connSize++ ] = -1;
2043
2044     } // end of generic solution
2045
2046     return method;
2047   }
2048   //=======================================================================
2049   /*!
2050    * \brief return TSplitMethod to split haxhedron into prisms
2051    */
2052   //=======================================================================
2053
2054   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2055                                     const int        methodFlags,
2056                                     const int        facetToSplit)
2057   {
2058     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2059     // B, T, L, B, R, F
2060     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2061
2062     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2063     {
2064       static TSplitMethod to4methods[4]; // order BT, LR, FB
2065       if ( to4methods[iF]._nbSplits == 0 )
2066       {
2067         switch ( iF ) {
2068         case 0:
2069           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2070           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2071           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2072           break;
2073         case 1:
2074           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2075           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2076           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2077           break;
2078         case 2:
2079           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2080           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2081           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2082           break;
2083         default: return to4methods[3];
2084         }
2085         to4methods[iF]._nbSplits  = 4;
2086         to4methods[iF]._nbCorners = 6;
2087       }
2088       return to4methods[iF];
2089     }
2090     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2091
2092     TSplitMethod method;
2093
2094     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2095
2096     const int nbVariants = 2, nbSplits = 2;
2097     const int** connVariants = 0;
2098     switch ( iF ) {
2099     case 0: connVariants = theHexTo2Prisms_BT; break;
2100     case 1: connVariants = theHexTo2Prisms_LR; break;
2101     case 2: connVariants = theHexTo2Prisms_FB; break;
2102     default: return method;
2103     }
2104
2105     // look for prisms adjacent via facetToSplit and an opposite one
2106     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2107     {
2108       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2109       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2110       if ( nbNodes != 4 ) return method;
2111
2112       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2113       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2114       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2115       TTriangleFacet* t;
2116       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2117         t = &t012;
2118       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2119         t = &t123;
2120       else
2121         continue;
2122
2123       // there are adjacent prism
2124       for ( int variant = 0; variant < nbVariants; ++variant )
2125       {
2126         // check method compliancy with adjacent prisms,
2127         // the found prism facets must be among facets of prisms described by current method
2128         method._nbSplits     = nbSplits;
2129         method._nbCorners    = 6;
2130         method._connectivity = connVariants[ variant ];
2131         if ( method.hasFacet( *t ))
2132           return method;
2133       }
2134     }
2135
2136     // No adjacent prisms. Select a variant with a best aspect ratio.
2137
2138     double badness[2] = { 0., 0. };
2139     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2140     const SMDS_MeshNode** nodes = vol.GetNodes();
2141     for ( int variant = 0; variant < nbVariants; ++variant )
2142       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2143       {
2144         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2145         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2146
2147         method._connectivity = connVariants[ variant ];
2148         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2149         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2150         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2151
2152         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2153                                 nodes[ t->_n2 ],
2154                                 nodes[ t->_n3 ] );
2155         badness[ variant ] += getBadRate( &tria, aspectRatio );
2156       }
2157     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2158
2159     method._nbSplits     = nbSplits;
2160     method._nbCorners    = 6;
2161     method._connectivity = connVariants[ iBetter ];
2162
2163     return method;
2164   }
2165
2166   //================================================================================
2167   /*!
2168    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2169    */
2170   //================================================================================
2171
2172   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2173                                        const SMDSAbs_GeometryType geom ) const
2174   {
2175     // find the tetrahedron including the three nodes of facet
2176     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2177     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2178     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2179     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2180     while ( volIt1->more() )
2181     {
2182       const SMDS_MeshElement* v = volIt1->next();
2183       if ( v->GetGeomType() != geom )
2184         continue;
2185       const int lastCornerInd = v->NbCornerNodes() - 1;
2186       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2187         continue; // medium node not allowed
2188       const int ind2 = v->GetNodeIndex( n2 );
2189       if ( ind2 < 0 || lastCornerInd < ind2 )
2190         continue;
2191       const int ind3 = v->GetNodeIndex( n3 );
2192       if ( ind3 < 0 || lastCornerInd < ind3 )
2193         continue;
2194       return true;
2195     }
2196     return false;
2197   }
2198
2199   //=======================================================================
2200   /*!
2201    * \brief A key of a face of volume
2202    */
2203   //=======================================================================
2204
2205   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2206   {
2207     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2208     {
2209       TIDSortedNodeSet sortedNodes;
2210       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2211       int nbNodes = vol.NbFaceNodes( iF );
2212       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2213       for ( int i = 0; i < nbNodes; i += iQ )
2214         sortedNodes.insert( fNodes[i] );
2215       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2216       first.first   = (*(n++))->GetID();
2217       first.second  = (*(n++))->GetID();
2218       second.first  = (*(n++))->GetID();
2219       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2220     }
2221   };
2222 } // namespace
2223
2224 //=======================================================================
2225 //function : SplitVolumes
2226 //purpose  : Split volume elements into tetrahedra or prisms.
2227 //           If facet ID < 0, element is split into tetrahedra,
2228 //           else a hexahedron is split into prisms so that the given facet is
2229 //           split into triangles
2230 //=======================================================================
2231
2232 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2233                                      const int            theMethodFlags)
2234 {
2235   SMDS_VolumeTool    volTool;
2236   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2237   fHelper.ToFixNodeParameters( true );
2238
2239   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2240   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2241
2242   SMESH_SequenceOfElemPtr newNodes, newElems;
2243
2244   // map face of volume to it's baricenrtic node
2245   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2246   double bc[3];
2247   vector<const SMDS_MeshElement* > splitVols;
2248
2249   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2250   for ( ; elem2facet != theElems.end(); ++elem2facet )
2251   {
2252     const SMDS_MeshElement* elem = elem2facet->first;
2253     const int       facetToSplit = elem2facet->second;
2254     if ( elem->GetType() != SMDSAbs_Volume )
2255       continue;
2256     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2257     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2258       continue;
2259
2260     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2261
2262     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2263                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2264                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2265     if ( splitMethod._nbSplits < 1 ) continue;
2266
2267     // find submesh to add new tetras to
2268     if ( !subMesh || !subMesh->Contains( elem ))
2269     {
2270       int shapeID = FindShape( elem );
2271       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2272       subMesh = GetMeshDS()->MeshElements( shapeID );
2273     }
2274     int iQ;
2275     if ( elem->IsQuadratic() )
2276     {
2277       iQ = 2;
2278       // add quadratic links to the helper
2279       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2280       {
2281         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2282         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2283         for ( int iN = 0; iN < nbN; iN += iQ )
2284           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2285       }
2286       helper.SetIsQuadratic( true );
2287     }
2288     else
2289     {
2290       iQ = 1;
2291       helper.SetIsQuadratic( false );
2292     }
2293     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2294                                         volTool.GetNodes() + elem->NbNodes() );
2295     helper.SetElementsOnShape( true );
2296     if ( splitMethod._baryNode )
2297     {
2298       // make a node at barycenter
2299       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2300       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2301       nodes.push_back( gcNode );
2302       newNodes.Append( gcNode );
2303     }
2304     if ( !splitMethod._faceBaryNode.empty() )
2305     {
2306       // make or find baricentric nodes of faces
2307       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2308       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2309       {
2310         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2311           volFace2BaryNode.insert
2312           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2313         if ( !f_n->second )
2314         {
2315           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2316           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2317         }
2318         nodes.push_back( iF_n->second = f_n->second );
2319       }
2320     }
2321
2322     // make new volumes
2323     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2324     const int* volConn = splitMethod._connectivity;
2325     if ( splitMethod._nbCorners == 4 ) // tetra
2326       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2327         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2328                                                             nodes[ volConn[1] ],
2329                                                             nodes[ volConn[2] ],
2330                                                             nodes[ volConn[3] ]));
2331     else // prisms
2332       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2333         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2334                                                             nodes[ volConn[1] ],
2335                                                             nodes[ volConn[2] ],
2336                                                             nodes[ volConn[3] ],
2337                                                             nodes[ volConn[4] ],
2338                                                             nodes[ volConn[5] ]));
2339
2340     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2341
2342     // Split faces on sides of the split volume
2343
2344     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2345     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2346     {
2347       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2348       if ( nbNodes < 4 ) continue;
2349
2350       // find an existing face
2351       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2352                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2353       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2354                                                                        /*noMedium=*/false))
2355       {
2356         // make triangles
2357         helper.SetElementsOnShape( false );
2358         vector< const SMDS_MeshElement* > triangles;
2359
2360         // find submesh to add new triangles in
2361         if ( !fSubMesh || !fSubMesh->Contains( face ))
2362         {
2363           int shapeID = FindShape( face );
2364           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2365         }
2366         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2367         if ( iF_n != splitMethod._faceBaryNode.end() )
2368         {
2369           const SMDS_MeshNode *baryNode = iF_n->second;
2370           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2371           {
2372             const SMDS_MeshNode* n1 = fNodes[iN];
2373             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2374             const SMDS_MeshNode *n3 = baryNode;
2375             if ( !volTool.IsFaceExternal( iF ))
2376               swap( n2, n3 );
2377             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2378           }
2379           if ( fSubMesh ) // update position of the bary node on geometry
2380           {
2381             if ( subMesh )
2382               subMesh->RemoveNode( baryNode, false );
2383             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2384             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2385             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2386             {
2387               fHelper.SetSubShape( s );
2388               gp_XY uv( 1e100, 1e100 );
2389               double distXYZ[4];
2390               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2391                                         uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2392                    uv.X() < 1e100 )
2393               {
2394                 // node is too far from the surface
2395                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2396                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2397                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2398               }
2399             }
2400           }
2401         }
2402         else
2403         {
2404           // among possible triangles create ones described by split method
2405           const int* nInd = volTool.GetFaceNodesIndices( iF );
2406           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2407           int iCom = 0; // common node of triangle faces to split into
2408           list< TTriangleFacet > facets;
2409           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2410           {
2411             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2412                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2413                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2414             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2415                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2416                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2417             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2418             {
2419               facets.push_back( t012 );
2420               facets.push_back( t023 );
2421               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2422                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2423                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2424                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2425               break;
2426             }
2427           }
2428           list< TTriangleFacet >::iterator facet = facets.begin();
2429           if ( facet == facets.end() )
2430             break;
2431           for ( ; facet != facets.end(); ++facet )
2432           {
2433             if ( !volTool.IsFaceExternal( iF ))
2434               swap( facet->_n2, facet->_n3 );
2435             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2436                                                  volNodes[ facet->_n2 ],
2437                                                  volNodes[ facet->_n3 ]));
2438           }
2439         }
2440         for ( size_t i = 0; i < triangles.size(); ++i )
2441         {
2442           if ( !triangles[ i ]) continue;
2443           if ( fSubMesh )
2444             fSubMesh->AddElement( triangles[ i ]);
2445           newElems.Append( triangles[ i ]);
2446         }
2447         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2448         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2449
2450       } // while a face based on facet nodes exists
2451     } // loop on volume faces to split them into triangles
2452
2453     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2454
2455     if ( geomType == SMDSEntity_TriQuad_Hexa )
2456     {
2457       // remove medium nodes that could become free
2458       for ( int i = 20; i < volTool.NbNodes(); ++i )
2459         if ( volNodes[i]->NbInverseElements() == 0 )
2460           GetMeshDS()->RemoveNode( volNodes[i] );
2461     }
2462   } // loop on volumes to split
2463
2464   myLastCreatedNodes = newNodes;
2465   myLastCreatedElems = newElems;
2466 }
2467
2468 //=======================================================================
2469 //function : GetHexaFacetsToSplit
2470 //purpose  : For hexahedra that will be split into prisms, finds facets to
2471 //           split into triangles. Only hexahedra adjacent to the one closest
2472 //           to theFacetNormal.Location() are returned.
2473 //param [in,out] theHexas - the hexahedra
2474 //param [in]     theFacetNormal - facet normal
2475 //param [out]    theFacets - the hexahedra and found facet IDs
2476 //=======================================================================
2477
2478 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2479                                              const gp_Ax1&     theFacetNormal,
2480                                              TFacetOfElem &    theFacets)
2481 {
2482   #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2483
2484   // Find a hexa closest to the location of theFacetNormal
2485
2486   const SMDS_MeshElement* startHex;
2487   {
2488     // get SMDS_ElemIteratorPtr on theHexas
2489     typedef const SMDS_MeshElement*                                      TValue;
2490     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2491     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2492     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2493     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2494     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2495       ( new TElemSetIter( theHexas.begin(),
2496                           theHexas.end(),
2497                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2498
2499     SMESH_ElementSearcher* searcher =
2500       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2501
2502     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2503
2504     delete searcher;
2505
2506     if ( !startHex )
2507       throw SALOME_Exception( THIS_METHOD "startHex not found");
2508   }
2509
2510   // Select a facet of startHex by theFacetNormal
2511
2512   SMDS_VolumeTool vTool( startHex );
2513   double norm[3], dot, maxDot = 0;
2514   int facetID = -1;
2515   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2516     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2517     {
2518       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2519       if ( dot > maxDot )
2520       {
2521         facetID = iF;
2522         maxDot = dot;
2523       }
2524     }
2525   if ( facetID < 0 )
2526     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2527
2528   // Fill theFacets starting from facetID of startHex
2529
2530   // facets used for searching of volumes adjacent to already treated ones
2531   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2532   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2533   TFacetMap facetsToCheck;
2534
2535   set<const SMDS_MeshNode*> facetNodes;
2536   const SMDS_MeshElement*   curHex;
2537
2538   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2539
2540   while ( startHex )
2541   {
2542     // move in two directions from startHex via facetID
2543     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2544     {
2545       curHex       = startHex;
2546       int curFacet = facetID;
2547       if ( is2nd ) // do not treat startHex twice
2548       {
2549         vTool.Set( curHex );
2550         if ( vTool.IsFreeFace( curFacet, &curHex ))
2551         {
2552           curHex = 0;
2553         }
2554         else
2555         {
2556           vTool.GetFaceNodes( curFacet, facetNodes );
2557           vTool.Set( curHex );
2558           curFacet = vTool.GetFaceIndex( facetNodes );
2559         }
2560       }
2561       while ( curHex )
2562       {
2563         // store a facet to split
2564         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2565         {
2566           theFacets.insert( make_pair( curHex, -1 ));
2567           break;
2568         }
2569         if ( !allHex && !theHexas.count( curHex ))
2570           break;
2571
2572         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2573           theFacets.insert( make_pair( curHex, curFacet ));
2574         if ( !facetIt2isNew.second )
2575           break;
2576
2577         // remember not-to-split facets in facetsToCheck
2578         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2579         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2580         {
2581           if ( iF == curFacet && iF == oppFacet )
2582             continue;
2583           TVolumeFaceKey facetKey ( vTool, iF );
2584           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2585           pair< TFacetMap::iterator, bool > it2isnew =
2586             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2587           if ( !it2isnew.second )
2588             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2589         }
2590         // pass to a volume adjacent via oppFacet
2591         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2592         {
2593           curHex = 0;
2594         }
2595         else
2596         {
2597           // get a new curFacet
2598           vTool.GetFaceNodes( oppFacet, facetNodes );
2599           vTool.Set( curHex );
2600           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2601         }
2602       }
2603     } // move in two directions from startHex via facetID
2604
2605     // Find a new startHex by facetsToCheck
2606
2607     startHex = 0;
2608     facetID  = -1;
2609     TFacetMap::iterator fIt = facetsToCheck.begin();
2610     while ( !startHex && fIt != facetsToCheck.end() )
2611     {
2612       const TElemFacets&  elemFacets = fIt->second;
2613       const SMDS_MeshElement*    hex = elemFacets.first->first;
2614       int                 splitFacet = elemFacets.first->second;
2615       int               lateralFacet = elemFacets.second;
2616       facetsToCheck.erase( fIt );
2617       fIt = facetsToCheck.begin();
2618
2619       vTool.Set( hex );
2620       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2621            curHex->GetGeomType() != SMDSGeom_HEXA )
2622         continue;
2623       if ( !allHex && !theHexas.count( curHex ))
2624         continue;
2625
2626       startHex = curHex;
2627
2628       // find a facet of startHex to split
2629
2630       set<const SMDS_MeshNode*> lateralNodes;
2631       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2632       vTool.GetFaceNodes( splitFacet,   facetNodes );
2633       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2634       vTool.Set( startHex );
2635       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2636
2637       // look for a facet of startHex having common nodes with facetNodes
2638       // but not lateralFacet
2639       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2640       {
2641         if ( iF == lateralFacet )
2642           continue;
2643         int nbCommonNodes = 0;
2644         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2645         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2646           nbCommonNodes += facetNodes.count( nn[ iN ]);
2647
2648         if ( nbCommonNodes >= 2 )
2649         {
2650           facetID = iF;
2651           break;
2652         }
2653       }
2654       if ( facetID < 0 )
2655         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2656     }
2657   } //   while ( startHex )
2658
2659   return;
2660 }
2661
2662 namespace
2663 {
2664   //================================================================================
2665   /*!
2666    * \brief Selects nodes of several elements according to a given interlace
2667    *  \param [in] srcNodes - nodes to select from
2668    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2669    *  \param [in] interlace - indices of nodes for all elements
2670    *  \param [in] nbElems - nb of elements
2671    *  \param [in] nbNodes - nb of nodes in each element
2672    *  \param [in] mesh - the mesh
2673    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2674    *  \param [in] type - type of elements to look for
2675    */
2676   //================================================================================
2677
2678   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2679                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2680                     const int*                            interlace,
2681                     const int                             nbElems,
2682                     const int                             nbNodes,
2683                     SMESHDS_Mesh*                         mesh = 0,
2684                     list< const SMDS_MeshElement* >*      elemQueue=0,
2685                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2686   {
2687     for ( int iE = 0; iE < nbElems; ++iE )
2688     {
2689       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2690       const int*                         select = & interlace[iE*nbNodes];
2691       elemNodes.resize( nbNodes );
2692       for ( int iN = 0; iN < nbNodes; ++iN )
2693         elemNodes[iN] = srcNodes[ select[ iN ]];
2694     }
2695     const SMDS_MeshElement* e;
2696     if ( elemQueue )
2697       for ( int iE = 0; iE < nbElems; ++iE )
2698         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2699           elemQueue->push_back( e );
2700   }
2701 }
2702
2703 //=======================================================================
2704 /*
2705  * Split bi-quadratic elements into linear ones without creation of additional nodes
2706  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2707  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2708  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2709  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2710  *   will be split in order to keep the mesh conformal.
2711  *  \param elems - elements to split
2712  */
2713 //=======================================================================
2714
2715 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2716 {
2717   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2718   vector<const SMDS_MeshElement* > splitElems;
2719   list< const SMDS_MeshElement* > elemQueue;
2720   list< const SMDS_MeshElement* >::iterator elemIt;
2721
2722   SMESHDS_Mesh * mesh = GetMeshDS();
2723   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2724   int nbElems, nbNodes;
2725
2726   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2727   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2728   {
2729     elemQueue.clear();
2730     elemQueue.push_back( *elemSetIt );
2731     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2732     {
2733       const SMDS_MeshElement* elem = *elemIt;
2734       switch( elem->GetEntityType() )
2735       {
2736       case SMDSEntity_TriQuad_Hexa: // HEX27
2737       {
2738         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2739         nbElems  = nbNodes = 8;
2740         elemType = & hexaType;
2741
2742         // get nodes for new elements
2743         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2744                                  { 1,9,20,8,    17,22,26,21 },
2745                                  { 2,10,20,9,   18,23,26,22 },
2746                                  { 3,11,20,10,  19,24,26,23 },
2747                                  { 16,21,26,24, 4,12,25,15  },
2748                                  { 17,22,26,21, 5,13,25,12  },
2749                                  { 18,23,26,22, 6,14,25,13  },
2750                                  { 19,24,26,23, 7,15,25,14  }};
2751         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2752
2753         // add boundary faces to elemQueue
2754         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2755                                  { 4,5,6,7, 12,13,14,15, 25 },
2756                                  { 0,1,5,4, 8,17,12,16,  21 },
2757                                  { 1,2,6,5, 9,18,13,17,  22 },
2758                                  { 2,3,7,6, 10,19,14,18, 23 },
2759                                  { 3,0,4,7, 11,16,15,19, 24 }};
2760         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2761
2762         // add boundary segments to elemQueue
2763         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2764                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2765                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2766         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2767         break;
2768       }
2769       case SMDSEntity_BiQuad_Triangle: // TRIA7
2770       {
2771         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2772         nbElems = 3;
2773         nbNodes = 4;
2774         elemType = & quadType;
2775
2776         // get nodes for new elements
2777         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2778         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2779
2780         // add boundary segments to elemQueue
2781         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2782         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2783         break;
2784       }
2785       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2786       {
2787         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2788         nbElems = 4;
2789         nbNodes = 4;
2790         elemType = & quadType;
2791
2792         // get nodes for new elements
2793         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2794         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2795
2796         // add boundary segments to elemQueue
2797         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2798         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2799         break;
2800       }
2801       case SMDSEntity_Quad_Edge:
2802       {
2803         if ( elemIt == elemQueue.begin() )
2804           continue; // an elem is in theElems
2805         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2806         nbElems = 2;
2807         nbNodes = 2;
2808         elemType = & segType;
2809
2810         // get nodes for new elements
2811         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2812         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2813         break;
2814       }
2815       default: continue;
2816       } // switch( elem->GetEntityType() )
2817
2818       // Create new elements
2819
2820       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2821
2822       splitElems.clear();
2823
2824       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2825       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2826       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2827       //elemType->SetID( -1 );
2828
2829       for ( int iE = 0; iE < nbElems; ++iE )
2830         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2831
2832
2833       ReplaceElemInGroups( elem, splitElems, mesh );
2834
2835       if ( subMesh )
2836         for ( size_t i = 0; i < splitElems.size(); ++i )
2837           subMesh->AddElement( splitElems[i] );
2838     }
2839   }
2840 }
2841
2842 //=======================================================================
2843 //function : AddToSameGroups
2844 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2845 //=======================================================================
2846
2847 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2848                                         const SMDS_MeshElement* elemInGroups,
2849                                         SMESHDS_Mesh *          aMesh)
2850 {
2851   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2852   if (!groups.empty()) {
2853     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2854     for ( ; grIt != groups.end(); grIt++ ) {
2855       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2856       if ( group && group->Contains( elemInGroups ))
2857         group->SMDSGroup().Add( elemToAdd );
2858     }
2859   }
2860 }
2861
2862
2863 //=======================================================================
2864 //function : RemoveElemFromGroups
2865 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2866 //=======================================================================
2867 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2868                                              SMESHDS_Mesh *          aMesh)
2869 {
2870   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2871   if (!groups.empty())
2872   {
2873     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2874     for (; GrIt != groups.end(); GrIt++)
2875     {
2876       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2877       if (!grp || grp->IsEmpty()) continue;
2878       grp->SMDSGroup().Remove(removeelem);
2879     }
2880   }
2881 }
2882
2883 //================================================================================
2884 /*!
2885  * \brief Replace elemToRm by elemToAdd in the all groups
2886  */
2887 //================================================================================
2888
2889 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2890                                             const SMDS_MeshElement* elemToAdd,
2891                                             SMESHDS_Mesh *          aMesh)
2892 {
2893   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2894   if (!groups.empty()) {
2895     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2896     for ( ; grIt != groups.end(); grIt++ ) {
2897       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2898       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2899         group->SMDSGroup().Add( elemToAdd );
2900     }
2901   }
2902 }
2903
2904 //================================================================================
2905 /*!
2906  * \brief Replace elemToRm by elemToAdd in the all groups
2907  */
2908 //================================================================================
2909
2910 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2911                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2912                                             SMESHDS_Mesh *                         aMesh)
2913 {
2914   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2915   if (!groups.empty())
2916   {
2917     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2918     for ( ; grIt != groups.end(); grIt++ ) {
2919       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2920       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2921         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2922           group->SMDSGroup().Add( elemToAdd[ i ] );
2923     }
2924   }
2925 }
2926
2927 //=======================================================================
2928 //function : QuadToTri
2929 //purpose  : Cut quadrangles into triangles.
2930 //           theCrit is used to select a diagonal to cut
2931 //=======================================================================
2932
2933 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2934                                   const bool         the13Diag)
2935 {
2936   myLastCreatedElems.Clear();
2937   myLastCreatedNodes.Clear();
2938
2939   SMESHDS_Mesh * aMesh = GetMeshDS();
2940
2941   Handle(Geom_Surface) surface;
2942   SMESH_MesherHelper   helper( *GetMesh() );
2943
2944   TIDSortedElemSet::iterator itElem;
2945   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2946   {
2947     const SMDS_MeshElement* elem = *itElem;
2948     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2949       continue;
2950
2951     if ( elem->NbNodes() == 4 ) {
2952       // retrieve element nodes
2953       const SMDS_MeshNode* aNodes [4];
2954       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2955       int i = 0;
2956       while ( itN->more() )
2957         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2958
2959       int aShapeId = FindShape( elem );
2960       const SMDS_MeshElement* newElem1 = 0;
2961       const SMDS_MeshElement* newElem2 = 0;
2962       if ( the13Diag ) {
2963         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2964         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2965       }
2966       else {
2967         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2968         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2969       }
2970       myLastCreatedElems.Append(newElem1);
2971       myLastCreatedElems.Append(newElem2);
2972       // put a new triangle on the same shape and add to the same groups
2973       if ( aShapeId )
2974       {
2975         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2976         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2977       }
2978       AddToSameGroups( newElem1, elem, aMesh );
2979       AddToSameGroups( newElem2, elem, aMesh );
2980       aMesh->RemoveElement( elem );
2981     }
2982
2983     // Quadratic quadrangle
2984
2985     else if ( elem->NbNodes() >= 8 )
2986     {
2987       // get surface elem is on
2988       int aShapeId = FindShape( elem );
2989       if ( aShapeId != helper.GetSubShapeID() ) {
2990         surface.Nullify();
2991         TopoDS_Shape shape;
2992         if ( aShapeId > 0 )
2993           shape = aMesh->IndexToShape( aShapeId );
2994         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2995           TopoDS_Face face = TopoDS::Face( shape );
2996           surface = BRep_Tool::Surface( face );
2997           if ( !surface.IsNull() )
2998             helper.SetSubShape( shape );
2999         }
3000       }
3001
3002       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3003       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3004       for ( int i = 0; itN->more(); ++i )
3005         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3006
3007       const SMDS_MeshNode* centrNode = aNodes[8];
3008       if ( centrNode == 0 )
3009       {
3010         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3011                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3012                                            surface.IsNull() );
3013         myLastCreatedNodes.Append(centrNode);
3014       }
3015
3016       // create a new element
3017       const SMDS_MeshElement* newElem1 = 0;
3018       const SMDS_MeshElement* newElem2 = 0;
3019       if ( the13Diag ) {
3020         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3021                                   aNodes[6], aNodes[7], centrNode );
3022         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3023                                   centrNode, aNodes[4], aNodes[5] );
3024       }
3025       else {
3026         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3027                                   aNodes[7], aNodes[4], centrNode );
3028         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3029                                   centrNode, aNodes[5], aNodes[6] );
3030       }
3031       myLastCreatedElems.Append(newElem1);
3032       myLastCreatedElems.Append(newElem2);
3033       // put a new triangle on the same shape and add to the same groups
3034       if ( aShapeId )
3035       {
3036         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3037         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3038       }
3039       AddToSameGroups( newElem1, elem, aMesh );
3040       AddToSameGroups( newElem2, elem, aMesh );
3041       aMesh->RemoveElement( elem );
3042     }
3043   }
3044
3045   return true;
3046 }
3047
3048 //=======================================================================
3049 //function : getAngle
3050 //purpose  :
3051 //=======================================================================
3052
3053 double getAngle(const SMDS_MeshElement * tr1,
3054                 const SMDS_MeshElement * tr2,
3055                 const SMDS_MeshNode *    n1,
3056                 const SMDS_MeshNode *    n2)
3057 {
3058   double angle = 2. * M_PI; // bad angle
3059
3060   // get normals
3061   SMESH::Controls::TSequenceOfXYZ P1, P2;
3062   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3063        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3064     return angle;
3065   gp_Vec N1,N2;
3066   if(!tr1->IsQuadratic())
3067     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3068   else
3069     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3070   if ( N1.SquareMagnitude() <= gp::Resolution() )
3071     return angle;
3072   if(!tr2->IsQuadratic())
3073     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3074   else
3075     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3076   if ( N2.SquareMagnitude() <= gp::Resolution() )
3077     return angle;
3078
3079   // find the first diagonal node n1 in the triangles:
3080   // take in account a diagonal link orientation
3081   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3082   for ( int t = 0; t < 2; t++ ) {
3083     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3084     int i = 0, iDiag = -1;
3085     while ( it->more()) {
3086       const SMDS_MeshElement *n = it->next();
3087       if ( n == n1 || n == n2 ) {
3088         if ( iDiag < 0)
3089           iDiag = i;
3090         else {
3091           if ( i - iDiag == 1 )
3092             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3093           else
3094             nFirst[ t ] = n;
3095           break;
3096         }
3097       }
3098       i++;
3099     }
3100   }
3101   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3102     N2.Reverse();
3103
3104   angle = N1.Angle( N2 );
3105   //SCRUTE( angle );
3106   return angle;
3107 }
3108
3109 // =================================================
3110 // class generating a unique ID for a pair of nodes
3111 // and able to return nodes by that ID
3112 // =================================================
3113 class LinkID_Gen {
3114 public:
3115
3116   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3117     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3118   {}
3119
3120   long GetLinkID (const SMDS_MeshNode * n1,
3121                   const SMDS_MeshNode * n2) const
3122   {
3123     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3124   }
3125
3126   bool GetNodes (const long             theLinkID,
3127                  const SMDS_MeshNode* & theNode1,
3128                  const SMDS_MeshNode* & theNode2) const
3129   {
3130     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3131     if ( !theNode1 ) return false;
3132     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3133     if ( !theNode2 ) return false;
3134     return true;
3135   }
3136
3137 private:
3138   LinkID_Gen();
3139   const SMESHDS_Mesh* myMesh;
3140   long                myMaxID;
3141 };
3142
3143
3144 //=======================================================================
3145 //function : TriToQuad
3146 //purpose  : Fuse neighbour triangles into quadrangles.
3147 //           theCrit is used to select a neighbour to fuse with.
3148 //           theMaxAngle is a max angle between element normals at which
3149 //           fusion is still performed.
3150 //=======================================================================
3151
3152 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3153                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3154                                   const double                         theMaxAngle)
3155 {
3156   myLastCreatedElems.Clear();
3157   myLastCreatedNodes.Clear();
3158
3159   if ( !theCrit.get() )
3160     return false;
3161
3162   SMESHDS_Mesh * aMesh = GetMeshDS();
3163
3164   // Prepare data for algo: build
3165   // 1. map of elements with their linkIDs
3166   // 2. map of linkIDs with their elements
3167
3168   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3169   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3170   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3171   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3172
3173   TIDSortedElemSet::iterator itElem;
3174   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3175   {
3176     const SMDS_MeshElement* elem = *itElem;
3177     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3178     bool IsTria = ( elem->NbCornerNodes()==3 );
3179     if (!IsTria) continue;
3180
3181     // retrieve element nodes
3182     const SMDS_MeshNode* aNodes [4];
3183     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3184     int i = 0;
3185     while ( i < 3 )
3186       aNodes[ i++ ] = itN->next();
3187     aNodes[ 3 ] = aNodes[ 0 ];
3188
3189     // fill maps
3190     for ( i = 0; i < 3; i++ ) {
3191       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3192       // check if elements sharing a link can be fused
3193       itLE = mapLi_listEl.find( link );
3194       if ( itLE != mapLi_listEl.end() ) {
3195         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3196           continue;
3197         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3198         //if ( FindShape( elem ) != FindShape( elem2 ))
3199         //  continue; // do not fuse triangles laying on different shapes
3200         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3201           continue; // avoid making badly shaped quads
3202         (*itLE).second.push_back( elem );
3203       }
3204       else {
3205         mapLi_listEl[ link ].push_back( elem );
3206       }
3207       mapEl_setLi [ elem ].insert( link );
3208     }
3209   }
3210   // Clean the maps from the links shared by a sole element, ie
3211   // links to which only one element is bound in mapLi_listEl
3212
3213   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3214     int nbElems = (*itLE).second.size();
3215     if ( nbElems < 2  ) {
3216       const SMDS_MeshElement* elem = (*itLE).second.front();
3217       SMESH_TLink link = (*itLE).first;
3218       mapEl_setLi[ elem ].erase( link );
3219       if ( mapEl_setLi[ elem ].empty() )
3220         mapEl_setLi.erase( elem );
3221     }
3222   }
3223
3224   // Algo: fuse triangles into quadrangles
3225
3226   while ( ! mapEl_setLi.empty() ) {
3227     // Look for the start element:
3228     // the element having the least nb of shared links
3229     const SMDS_MeshElement* startElem = 0;
3230     int minNbLinks = 4;
3231     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3232       int nbLinks = (*itEL).second.size();
3233       if ( nbLinks < minNbLinks ) {
3234         startElem = (*itEL).first;
3235         minNbLinks = nbLinks;
3236         if ( minNbLinks == 1 )
3237           break;
3238       }
3239     }
3240
3241     // search elements to fuse starting from startElem or links of elements
3242     // fused earlyer - startLinks
3243     list< SMESH_TLink > startLinks;
3244     while ( startElem || !startLinks.empty() ) {
3245       while ( !startElem && !startLinks.empty() ) {
3246         // Get an element to start, by a link
3247         SMESH_TLink linkId = startLinks.front();
3248         startLinks.pop_front();
3249         itLE = mapLi_listEl.find( linkId );
3250         if ( itLE != mapLi_listEl.end() ) {
3251           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3252           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3253           for ( ; itE != listElem.end() ; itE++ )
3254             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3255               startElem = (*itE);
3256           mapLi_listEl.erase( itLE );
3257         }
3258       }
3259
3260       if ( startElem ) {
3261         // Get candidates to be fused
3262         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3263         const SMESH_TLink *link12 = 0, *link13 = 0;
3264         startElem = 0;
3265         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3266         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3267         ASSERT( !setLi.empty() );
3268         set< SMESH_TLink >::iterator itLi;
3269         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3270         {
3271           const SMESH_TLink & link = (*itLi);
3272           itLE = mapLi_listEl.find( link );
3273           if ( itLE == mapLi_listEl.end() )
3274             continue;
3275
3276           const SMDS_MeshElement* elem = (*itLE).second.front();
3277           if ( elem == tr1 )
3278             elem = (*itLE).second.back();
3279           mapLi_listEl.erase( itLE );
3280           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3281             continue;
3282           if ( tr2 ) {
3283             tr3 = elem;
3284             link13 = &link;
3285           }
3286           else {
3287             tr2 = elem;
3288             link12 = &link;
3289           }
3290
3291           // add other links of elem to list of links to re-start from
3292           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3293           set< SMESH_TLink >::iterator it;
3294           for ( it = links.begin(); it != links.end(); it++ ) {
3295             const SMESH_TLink& link2 = (*it);
3296             if ( link2 != link )
3297               startLinks.push_back( link2 );
3298           }
3299         }
3300
3301         // Get nodes of possible quadrangles
3302         const SMDS_MeshNode *n12 [4], *n13 [4];
3303         bool Ok12 = false, Ok13 = false;
3304         const SMDS_MeshNode *linkNode1, *linkNode2;
3305         if(tr2) {
3306           linkNode1 = link12->first;
3307           linkNode2 = link12->second;
3308           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3309             Ok12 = true;
3310         }
3311         if(tr3) {
3312           linkNode1 = link13->first;
3313           linkNode2 = link13->second;
3314           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3315             Ok13 = true;
3316         }
3317
3318         // Choose a pair to fuse
3319         if ( Ok12 && Ok13 ) {
3320           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3321           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3322           double aBadRate12 = getBadRate( &quad12, theCrit );
3323           double aBadRate13 = getBadRate( &quad13, theCrit );
3324           if (  aBadRate13 < aBadRate12 )
3325             Ok12 = false;
3326           else
3327             Ok13 = false;
3328         }
3329
3330         // Make quadrangles
3331         // and remove fused elems and remove links from the maps
3332         mapEl_setLi.erase( tr1 );
3333         if ( Ok12 )
3334         {
3335           mapEl_setLi.erase( tr2 );
3336           mapLi_listEl.erase( *link12 );
3337           if ( tr1->NbNodes() == 3 )
3338           {
3339             const SMDS_MeshElement* newElem = 0;
3340             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3341             myLastCreatedElems.Append(newElem);
3342             AddToSameGroups( newElem, tr1, aMesh );
3343             int aShapeId = tr1->getshapeId();
3344             if ( aShapeId )
3345               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3346             aMesh->RemoveElement( tr1 );
3347             aMesh->RemoveElement( tr2 );
3348           }
3349           else {
3350             vector< const SMDS_MeshNode* > N1;
3351             vector< const SMDS_MeshNode* > N2;
3352             getNodesFromTwoTria(tr1,tr2,N1,N2);
3353             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3354             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3355             // i.e. first nodes from both arrays form a new diagonal
3356             const SMDS_MeshNode* aNodes[8];
3357             aNodes[0] = N1[0];
3358             aNodes[1] = N1[1];
3359             aNodes[2] = N2[0];
3360             aNodes[3] = N2[1];
3361             aNodes[4] = N1[3];
3362             aNodes[5] = N2[5];
3363             aNodes[6] = N2[3];
3364             aNodes[7] = N1[5];
3365             const SMDS_MeshElement* newElem = 0;
3366             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3367               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3368                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3369             else
3370               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3371                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3372             myLastCreatedElems.Append(newElem);
3373             AddToSameGroups( newElem, tr1, aMesh );
3374             int aShapeId = tr1->getshapeId();
3375             if ( aShapeId )
3376               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3377             aMesh->RemoveElement( tr1 );
3378             aMesh->RemoveElement( tr2 );
3379             // remove middle node (9)
3380             if ( N1[4]->NbInverseElements() == 0 )
3381               aMesh->RemoveNode( N1[4] );
3382             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3383               aMesh->RemoveNode( N1[6] );
3384             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3385               aMesh->RemoveNode( N2[6] );
3386           }
3387         }
3388         else if ( Ok13 )
3389         {
3390           mapEl_setLi.erase( tr3 );
3391           mapLi_listEl.erase( *link13 );
3392           if ( tr1->NbNodes() == 3 ) {
3393             const SMDS_MeshElement* newElem = 0;
3394             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3395             myLastCreatedElems.Append(newElem);
3396             AddToSameGroups( newElem, tr1, aMesh );
3397             int aShapeId = tr1->getshapeId();
3398             if ( aShapeId )
3399               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3400             aMesh->RemoveElement( tr1 );
3401             aMesh->RemoveElement( tr3 );
3402           }
3403           else {
3404             vector< const SMDS_MeshNode* > N1;
3405             vector< const SMDS_MeshNode* > N2;
3406             getNodesFromTwoTria(tr1,tr3,N1,N2);
3407             // now we receive following N1 and N2 (using numeration as above image)
3408             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3409             // i.e. first nodes from both arrays form a new diagonal
3410             const SMDS_MeshNode* aNodes[8];
3411             aNodes[0] = N1[0];
3412             aNodes[1] = N1[1];
3413             aNodes[2] = N2[0];
3414             aNodes[3] = N2[1];
3415             aNodes[4] = N1[3];
3416             aNodes[5] = N2[5];
3417             aNodes[6] = N2[3];
3418             aNodes[7] = N1[5];
3419             const SMDS_MeshElement* newElem = 0;
3420             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3421               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3422                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3423             else
3424               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3425                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3426             myLastCreatedElems.Append(newElem);
3427             AddToSameGroups( newElem, tr1, aMesh );
3428             int aShapeId = tr1->getshapeId();
3429             if ( aShapeId )
3430               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3431             aMesh->RemoveElement( tr1 );
3432             aMesh->RemoveElement( tr3 );
3433             // remove middle node (9)
3434             if ( N1[4]->NbInverseElements() == 0 )
3435               aMesh->RemoveNode( N1[4] );
3436             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3437               aMesh->RemoveNode( N1[6] );
3438             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3439               aMesh->RemoveNode( N2[6] );
3440           }
3441         }
3442
3443         // Next element to fuse: the rejected one
3444         if ( tr3 )
3445           startElem = Ok12 ? tr3 : tr2;
3446
3447       } // if ( startElem )
3448     } // while ( startElem || !startLinks.empty() )
3449   } // while ( ! mapEl_setLi.empty() )
3450
3451   return true;
3452 }
3453
3454
3455 /*#define DUMPSO(txt) \
3456 //  cout << txt << endl;
3457 //=============================================================================
3458 //
3459 //
3460 //
3461 //=============================================================================
3462 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3463 {
3464 if ( i1 == i2 )
3465 return;
3466 int tmp = idNodes[ i1 ];
3467 idNodes[ i1 ] = idNodes[ i2 ];
3468 idNodes[ i2 ] = tmp;
3469 gp_Pnt Ptmp = P[ i1 ];
3470 P[ i1 ] = P[ i2 ];
3471 P[ i2 ] = Ptmp;
3472 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3473 }
3474
3475 //=======================================================================
3476 //function : SortQuadNodes
3477 //purpose  : Set 4 nodes of a quadrangle face in a good order.
3478 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
3479 //           1 or 2 else 0.
3480 //=======================================================================
3481
3482 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3483 int               idNodes[] )
3484 {
3485   gp_Pnt P[4];
3486   int i;
3487   for ( i = 0; i < 4; i++ ) {
3488     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3489     if ( !n ) return 0;
3490     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3491   }
3492
3493   gp_Vec V1(P[0], P[1]);
3494   gp_Vec V2(P[0], P[2]);
3495   gp_Vec V3(P[0], P[3]);
3496
3497   gp_Vec Cross1 = V1 ^ V2;
3498   gp_Vec Cross2 = V2 ^ V3;
3499
3500   i = 0;
3501   if (Cross1.Dot(Cross2) < 0)
3502   {
3503     Cross1 = V2 ^ V1;
3504     Cross2 = V1 ^ V3;
3505
3506     if (Cross1.Dot(Cross2) < 0)
3507       i = 2;
3508     else
3509       i = 1;
3510     swap ( i, i + 1, idNodes, P );
3511
3512     //     for ( int ii = 0; ii < 4; ii++ ) {
3513     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3514     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3515     //     }
3516   }
3517   return i;
3518 }
3519
3520 //=======================================================================
3521 //function : SortHexaNodes
3522 //purpose  : Set 8 nodes of a hexahedron in a good order.
3523 //           Return success status
3524 //=======================================================================
3525
3526 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3527                                       int               idNodes[] )
3528 {
3529   gp_Pnt P[8];
3530   int i;
3531   DUMPSO( "INPUT: ========================================");
3532   for ( i = 0; i < 8; i++ ) {
3533     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3534     if ( !n ) return false;
3535     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3536     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3537   }
3538   DUMPSO( "========================================");
3539
3540
3541   set<int> faceNodes;  // ids of bottom face nodes, to be found
3542   set<int> checkedId1; // ids of tried 2-nd nodes
3543   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3544   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
3545   int iMin, iLoop1 = 0;
3546
3547   // Loop to try the 2-nd nodes
3548
3549   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3550   {
3551     // Find not checked 2-nd node
3552     for ( i = 1; i < 8; i++ )
3553       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3554         int id1 = idNodes[i];
3555         swap ( 1, i, idNodes, P );
3556         checkedId1.insert ( id1 );
3557         break;
3558       }
3559
3560     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3561     // ie that all but meybe one (id3 which is on the same face) nodes
3562     // lay on the same side from the triangle plane.
3563
3564     bool manyInPlane = false; // more than 4 nodes lay in plane
3565     int iLoop2 = 0;
3566     while ( ++iLoop2 < 6 ) {
3567
3568       // get 1-2-3 plane coeffs
3569       Standard_Real A, B, C, D;
3570       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3571       if ( N.SquareMagnitude() > gp::Resolution() )
3572       {
3573         gp_Pln pln ( P[0], N );
3574         pln.Coefficients( A, B, C, D );
3575
3576         // find the node (iMin) closest to pln
3577         Standard_Real dist[ 8 ], minDist = DBL_MAX;
3578         set<int> idInPln;
3579         for ( i = 3; i < 8; i++ ) {
3580           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3581           if ( fabs( dist[i] ) < minDist ) {
3582             minDist = fabs( dist[i] );
3583             iMin = i;
3584           }
3585           if ( fabs( dist[i] ) <= tol )
3586             idInPln.insert( idNodes[i] );
3587         }
3588
3589         // there should not be more than 4 nodes in bottom plane
3590         if ( idInPln.size() > 1 )
3591         {
3592           DUMPSO( "### idInPln.size() = " << idInPln.size());
3593           // idInPlane does not contain the first 3 nodes
3594           if ( manyInPlane || idInPln.size() == 5)
3595             return false; // all nodes in one plane
3596           manyInPlane = true;
3597
3598           // set the 1-st node to be not in plane
3599           for ( i = 3; i < 8; i++ ) {
3600             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3601               DUMPSO( "### Reset 0-th node");
3602               swap( 0, i, idNodes, P );
3603               break;
3604             }
3605           }
3606
3607           // reset to re-check second nodes
3608           leastDist = DBL_MAX;
3609           faceNodes.clear();
3610           checkedId1.clear();
3611           iLoop1 = 0;
3612           break; // from iLoop2;
3613         }
3614
3615         // check that the other 4 nodes are on the same side
3616         bool sameSide = true;
3617         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3618         for ( i = 3; sameSide && i < 8; i++ ) {
3619           if ( i != iMin )
3620             sameSide = ( isNeg == dist[i] <= 0.);
3621         }
3622
3623         // keep best solution
3624         if ( sameSide && minDist < leastDist ) {
3625           leastDist = minDist;
3626           faceNodes.clear();
3627           faceNodes.insert( idNodes[ 1 ] );
3628           faceNodes.insert( idNodes[ 2 ] );
3629           faceNodes.insert( idNodes[ iMin ] );
3630           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3631                   << " leastDist = " << leastDist);
3632           if ( leastDist <= DBL_MIN )
3633             break;
3634         }
3635       }
3636
3637       // set next 3-d node to check
3638       int iNext = 2 + iLoop2;
3639       if ( iNext < 8 ) {
3640         DUMPSO( "Try 2-nd");
3641         swap ( 2, iNext, idNodes, P );
3642       }
3643     } // while ( iLoop2 < 6 )
3644   } // iLoop1
3645
3646   if ( faceNodes.empty() ) return false;
3647
3648   // Put the faceNodes in proper places
3649   for ( i = 4; i < 8; i++ ) {
3650     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3651       // find a place to put
3652       int iTo = 1;
3653       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3654         iTo++;
3655       DUMPSO( "Set faceNodes");
3656       swap ( iTo, i, idNodes, P );
3657     }
3658   }
3659
3660
3661   // Set nodes of the found bottom face in good order
3662   DUMPSO( " Found bottom face: ");
3663   i = SortQuadNodes( theMesh, idNodes );
3664   if ( i ) {
3665     gp_Pnt Ptmp = P[ i ];
3666     P[ i ] = P[ i+1 ];
3667     P[ i+1 ] = Ptmp;
3668   }
3669   //   else
3670   //     for ( int ii = 0; ii < 4; ii++ ) {
3671   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3672   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3673   //    }
3674
3675   // Gravity center of the top and bottom faces
3676   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3677   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3678
3679   // Get direction from the bottom to the top face
3680   gp_Vec upDir ( aGCb, aGCt );
3681   Standard_Real upDirSize = upDir.Magnitude();
3682   if ( upDirSize <= gp::Resolution() ) return false;
3683   upDir / upDirSize;
3684
3685   // Assure that the bottom face normal points up
3686   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3687   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3688   if ( Nb.Dot( upDir ) < 0 ) {
3689     DUMPSO( "Reverse bottom face");
3690     swap( 1, 3, idNodes, P );
3691   }
3692
3693   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3694   Standard_Real minDist = DBL_MAX;
3695   for ( i = 4; i < 8; i++ ) {
3696     // projection of P[i] to the plane defined by P[0] and upDir
3697     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3698     Standard_Real sqDist = P[0].SquareDistance( Pp );
3699     if ( sqDist < minDist ) {
3700       minDist = sqDist;
3701       iMin = i;
3702     }
3703   }
3704   DUMPSO( "Set 4-th");
3705   swap ( 4, iMin, idNodes, P );
3706
3707   // Set nodes of the top face in good order
3708   DUMPSO( "Sort top face");
3709   i = SortQuadNodes( theMesh, &idNodes[4] );
3710   if ( i ) {
3711     i += 4;
3712     gp_Pnt Ptmp = P[ i ];
3713     P[ i ] = P[ i+1 ];
3714     P[ i+1 ] = Ptmp;
3715   }
3716
3717   // Assure that direction of the top face normal is from the bottom face
3718   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3719   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3720   if ( Nt.Dot( upDir ) < 0 ) {
3721     DUMPSO( "Reverse top face");
3722     swap( 5, 7, idNodes, P );
3723   }
3724
3725   //   DUMPSO( "OUTPUT: ========================================");
3726   //   for ( i = 0; i < 8; i++ ) {
3727   //     float *p = ugrid->GetPoint(idNodes[i]);
3728   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3729   //   }
3730
3731   return true;
3732 }*/
3733
3734 //================================================================================
3735 /*!
3736  * \brief Return nodes linked to the given one
3737  * \param theNode - the node
3738  * \param linkedNodes - the found nodes
3739  * \param type - the type of elements to check
3740  *
3741  * Medium nodes are ignored
3742  */
3743 //================================================================================
3744
3745 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3746                                        TIDSortedElemSet &   linkedNodes,
3747                                        SMDSAbs_ElementType  type )
3748 {
3749   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3750   while ( elemIt->more() )
3751   {
3752     const SMDS_MeshElement* elem = elemIt->next();
3753     if(elem->GetType() == SMDSAbs_0DElement)
3754       continue;
3755
3756     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3757     if ( elem->GetType() == SMDSAbs_Volume )
3758     {
3759       SMDS_VolumeTool vol( elem );
3760       while ( nodeIt->more() ) {
3761         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3762         if ( theNode != n && vol.IsLinked( theNode, n ))
3763           linkedNodes.insert( n );
3764       }
3765     }
3766     else
3767     {
3768       for ( int i = 0; nodeIt->more(); ++i ) {
3769         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3770         if ( n == theNode ) {
3771           int iBefore = i - 1;
3772           int iAfter  = i + 1;
3773           if ( elem->IsQuadratic() ) {
3774             int nb = elem->NbNodes() / 2;
3775             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3776             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3777           }
3778           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3779           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3780         }
3781       }
3782     }
3783   }
3784 }
3785
3786 //=======================================================================
3787 //function : laplacianSmooth
3788 //purpose  : pulls theNode toward the center of surrounding nodes directly
3789 //           connected to that node along an element edge
3790 //=======================================================================
3791
3792 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3793                      const Handle(Geom_Surface)&          theSurface,
3794                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3795 {
3796   // find surrounding nodes
3797
3798   TIDSortedElemSet nodeSet;
3799   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3800
3801   // compute new coodrs
3802
3803   double coord[] = { 0., 0., 0. };
3804   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3805   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3806     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3807     if ( theSurface.IsNull() ) { // smooth in 3D
3808       coord[0] += node->X();
3809       coord[1] += node->Y();
3810       coord[2] += node->Z();
3811     }
3812     else { // smooth in 2D
3813       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3814       gp_XY* uv = theUVMap[ node ];
3815       coord[0] += uv->X();
3816       coord[1] += uv->Y();
3817     }
3818   }
3819   int nbNodes = nodeSet.size();
3820   if ( !nbNodes )
3821     return;
3822   coord[0] /= nbNodes;
3823   coord[1] /= nbNodes;
3824
3825   if ( !theSurface.IsNull() ) {
3826     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3827     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3828     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3829     coord[0] = p3d.X();
3830     coord[1] = p3d.Y();
3831     coord[2] = p3d.Z();
3832   }
3833   else
3834     coord[2] /= nbNodes;
3835
3836   // move node
3837
3838   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3839 }
3840
3841 //=======================================================================
3842 //function : centroidalSmooth
3843 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3844 //           surrounding elements
3845 //=======================================================================
3846
3847 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3848                       const Handle(Geom_Surface)&          theSurface,
3849                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3850 {
3851   gp_XYZ aNewXYZ(0.,0.,0.);
3852   SMESH::Controls::Area anAreaFunc;
3853   double totalArea = 0.;
3854   int nbElems = 0;
3855
3856   // compute new XYZ
3857
3858   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3859   while ( elemIt->more() )
3860   {
3861     const SMDS_MeshElement* elem = elemIt->next();
3862     nbElems++;
3863
3864     gp_XYZ elemCenter(0.,0.,0.);
3865     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3866     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3867     int nn = elem->NbNodes();
3868     if(elem->IsQuadratic()) nn = nn/2;
3869     int i=0;
3870     //while ( itN->more() ) {
3871     while ( i<nn ) {
3872       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3873       i++;
3874       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3875       aNodePoints.push_back( aP );
3876       if ( !theSurface.IsNull() ) { // smooth in 2D
3877         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3878         gp_XY* uv = theUVMap[ aNode ];
3879         aP.SetCoord( uv->X(), uv->Y(), 0. );
3880       }
3881       elemCenter += aP;
3882     }
3883     double elemArea = anAreaFunc.GetValue( aNodePoints );
3884     totalArea += elemArea;
3885     elemCenter /= nn;
3886     aNewXYZ += elemCenter * elemArea;
3887   }
3888   aNewXYZ /= totalArea;
3889   if ( !theSurface.IsNull() ) {
3890     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3891     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3892   }
3893
3894   // move node
3895
3896   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3897 }
3898
3899 //=======================================================================
3900 //function : getClosestUV
3901 //purpose  : return UV of closest projection
3902 //=======================================================================
3903
3904 static bool getClosestUV (Extrema_GenExtPS& projector,
3905                           const gp_Pnt&     point,
3906                           gp_XY &           result)
3907 {
3908   projector.Perform( point );
3909   if ( projector.IsDone() ) {
3910     double u, v, minVal = DBL_MAX;
3911     for ( int i = projector.NbExt(); i > 0; i-- )
3912       if ( projector.SquareDistance( i ) < minVal ) {
3913         minVal = projector.SquareDistance( i );
3914         projector.Point( i ).Parameter( u, v );
3915       }
3916     result.SetCoord( u, v );
3917     return true;
3918   }
3919   return false;
3920 }
3921
3922 //=======================================================================
3923 //function : Smooth
3924 //purpose  : Smooth theElements during theNbIterations or until a worst
3925 //           element has aspect ratio <= theTgtAspectRatio.
3926 //           Aspect Ratio varies in range [1.0, inf].
3927 //           If theElements is empty, the whole mesh is smoothed.
3928 //           theFixedNodes contains additionally fixed nodes. Nodes built
3929 //           on edges and boundary nodes are always fixed.
3930 //=======================================================================
3931
3932 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3933                                set<const SMDS_MeshNode*> & theFixedNodes,
3934                                const SmoothMethod          theSmoothMethod,
3935                                const int                   theNbIterations,
3936                                double                      theTgtAspectRatio,
3937                                const bool                  the2D)
3938 {
3939   myLastCreatedElems.Clear();
3940   myLastCreatedNodes.Clear();
3941
3942   if ( theTgtAspectRatio < 1.0 )
3943     theTgtAspectRatio = 1.0;
3944
3945   const double disttol = 1.e-16;
3946
3947   SMESH::Controls::AspectRatio aQualityFunc;
3948
3949   SMESHDS_Mesh* aMesh = GetMeshDS();
3950
3951   if ( theElems.empty() ) {
3952     // add all faces to theElems
3953     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3954     while ( fIt->more() ) {
3955       const SMDS_MeshElement* face = fIt->next();
3956       theElems.insert( theElems.end(), face );
3957     }
3958   }
3959   // get all face ids theElems are on
3960   set< int > faceIdSet;
3961   TIDSortedElemSet::iterator itElem;
3962   if ( the2D )
3963     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3964       int fId = FindShape( *itElem );
3965       // check that corresponding submesh exists and a shape is face
3966       if (fId &&
3967           faceIdSet.find( fId ) == faceIdSet.end() &&
3968           aMesh->MeshElements( fId )) {
3969         TopoDS_Shape F = aMesh->IndexToShape( fId );
3970         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3971           faceIdSet.insert( fId );
3972       }
3973     }
3974   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3975
3976   // ===============================================
3977   // smooth elements on each TopoDS_Face separately
3978   // ===============================================
3979
3980   SMESH_MesherHelper helper( *GetMesh() );
3981
3982   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3983   for ( ; fId != faceIdSet.rend(); ++fId )
3984   {
3985     // get face surface and submesh
3986     Handle(Geom_Surface) surface;
3987     SMESHDS_SubMesh* faceSubMesh = 0;
3988     TopoDS_Face face;
3989     double fToler2 = 0;
3990     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3991     bool isUPeriodic = false, isVPeriodic = false;
3992     if ( *fId )
3993     {
3994       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3995       surface = BRep_Tool::Surface( face );
3996       faceSubMesh = aMesh->MeshElements( *fId );
3997       fToler2 = BRep_Tool::Tolerance( face );
3998       fToler2 *= fToler2 * 10.;
3999       isUPeriodic = surface->IsUPeriodic();
4000       // if ( isUPeriodic )
4001       //   surface->UPeriod();
4002       isVPeriodic = surface->IsVPeriodic();
4003       // if ( isVPeriodic )
4004       //   surface->VPeriod();
4005       surface->Bounds( u1, u2, v1, v2 );
4006       helper.SetSubShape( face );
4007     }
4008     // ---------------------------------------------------------
4009     // for elements on a face, find movable and fixed nodes and
4010     // compute UV for them
4011     // ---------------------------------------------------------
4012     bool checkBoundaryNodes = false;
4013     bool isQuadratic = false;
4014     set<const SMDS_MeshNode*> setMovableNodes;
4015     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4016     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4017     list< const SMDS_MeshElement* > elemsOnFace;
4018
4019     Extrema_GenExtPS projector;
4020     GeomAdaptor_Surface surfAdaptor;
4021     if ( !surface.IsNull() ) {
4022       surfAdaptor.Load( surface );
4023       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4024     }
4025     int nbElemOnFace = 0;
4026     itElem = theElems.begin();
4027     // loop on not yet smoothed elements: look for elems on a face
4028     while ( itElem != theElems.end() )
4029     {
4030       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4031         break; // all elements found
4032
4033       const SMDS_MeshElement* elem = *itElem;
4034       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4035            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4036         ++itElem;
4037         continue;
4038       }
4039       elemsOnFace.push_back( elem );
4040       theElems.erase( itElem++ );
4041       nbElemOnFace++;
4042
4043       if ( !isQuadratic )
4044         isQuadratic = elem->IsQuadratic();
4045
4046       // get movable nodes of elem
4047       const SMDS_MeshNode* node;
4048       SMDS_TypeOfPosition posType;
4049       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4050       int nn = 0, nbn =  elem->NbNodes();
4051       if(elem->IsQuadratic())
4052         nbn = nbn/2;
4053       while ( nn++ < nbn ) {
4054         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4055         const SMDS_PositionPtr& pos = node->GetPosition();
4056         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4057         if (posType != SMDS_TOP_EDGE &&
4058             posType != SMDS_TOP_VERTEX &&
4059             theFixedNodes.find( node ) == theFixedNodes.end())
4060         {
4061           // check if all faces around the node are on faceSubMesh
4062           // because a node on edge may be bound to face
4063           bool all = true;
4064           if ( faceSubMesh ) {
4065             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4066             while ( eIt->more() && all ) {
4067               const SMDS_MeshElement* e = eIt->next();
4068               all = faceSubMesh->Contains( e );
4069             }
4070           }
4071           if ( all )
4072             setMovableNodes.insert( node );
4073           else
4074             checkBoundaryNodes = true;
4075         }
4076         if ( posType == SMDS_TOP_3DSPACE )
4077           checkBoundaryNodes = true;
4078       }
4079
4080       if ( surface.IsNull() )
4081         continue;
4082
4083       // get nodes to check UV
4084       list< const SMDS_MeshNode* > uvCheckNodes;
4085       const SMDS_MeshNode* nodeInFace = 0;
4086       itN = elem->nodesIterator();
4087       nn = 0; nbn =  elem->NbNodes();
4088       if(elem->IsQuadratic())
4089         nbn = nbn/2;
4090       while ( nn++ < nbn ) {
4091         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4092         if ( node->GetPosition()->GetDim() == 2 )
4093           nodeInFace = node;
4094         if ( uvMap.find( node ) == uvMap.end() )
4095           uvCheckNodes.push_back( node );
4096         // add nodes of elems sharing node
4097         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4098         //         while ( eIt->more() ) {
4099         //           const SMDS_MeshElement* e = eIt->next();
4100         //           if ( e != elem ) {
4101         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4102         //             while ( nIt->more() ) {
4103         //               const SMDS_MeshNode* n =
4104         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4105         //               if ( uvMap.find( n ) == uvMap.end() )
4106         //                 uvCheckNodes.push_back( n );
4107         //             }
4108         //           }
4109         //         }
4110       }
4111       // check UV on face
4112       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4113       for ( ; n != uvCheckNodes.end(); ++n ) {
4114         node = *n;
4115         gp_XY uv( 0, 0 );
4116         const SMDS_PositionPtr& pos = node->GetPosition();
4117         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4118         // get existing UV
4119         if ( pos )
4120         {
4121           bool toCheck = true;
4122           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4123         }
4124         // compute not existing UV
4125         bool project = ( posType == SMDS_TOP_3DSPACE );
4126         // double dist1 = DBL_MAX, dist2 = 0;
4127         // if ( posType != SMDS_TOP_3DSPACE ) {
4128         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4129         //   project = dist1 > fToler2;
4130         // }
4131         if ( project ) { // compute new UV
4132           gp_XY newUV;
4133           gp_Pnt pNode = SMESH_TNodeXYZ( node );
4134           if ( !getClosestUV( projector, pNode, newUV )) {
4135             MESSAGE("Node Projection Failed " << node);
4136           }
4137           else {
4138             if ( isUPeriodic )
4139               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4140             if ( isVPeriodic )
4141               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4142             // check new UV
4143             // if ( posType != SMDS_TOP_3DSPACE )
4144             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4145             // if ( dist2 < dist1 )
4146               uv = newUV;
4147           }
4148         }
4149         // store UV in the map
4150         listUV.push_back( uv );
4151         uvMap.insert( make_pair( node, &listUV.back() ));
4152       }
4153     } // loop on not yet smoothed elements
4154
4155     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4156       checkBoundaryNodes = true;
4157
4158     // fix nodes on mesh boundary
4159
4160     if ( checkBoundaryNodes ) {
4161       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4162       map< SMESH_TLink, int >::iterator link_nb;
4163       // put all elements links to linkNbMap
4164       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4165       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4166         const SMDS_MeshElement* elem = (*elemIt);
4167         int nbn =  elem->NbCornerNodes();
4168         // loop on elem links: insert them in linkNbMap
4169         for ( int iN = 0; iN < nbn; ++iN ) {
4170           const SMDS_MeshNode* n1 = elem->GetNode( iN );
4171           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4172           SMESH_TLink link( n1, n2 );
4173           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4174           link_nb->second++;
4175         }
4176       }
4177       // remove nodes that are in links encountered only once from setMovableNodes
4178       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4179         if ( link_nb->second == 1 ) {
4180           setMovableNodes.erase( link_nb->first.node1() );
4181           setMovableNodes.erase( link_nb->first.node2() );
4182         }
4183       }
4184     }
4185
4186     // -----------------------------------------------------
4187     // for nodes on seam edge, compute one more UV ( uvMap2 );
4188     // find movable nodes linked to nodes on seam and which
4189     // are to be smoothed using the second UV ( uvMap2 )
4190     // -----------------------------------------------------
4191
4192     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4193     if ( !surface.IsNull() ) {
4194       TopExp_Explorer eExp( face, TopAbs_EDGE );
4195       for ( ; eExp.More(); eExp.Next() ) {
4196         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4197         if ( !BRep_Tool::IsClosed( edge, face ))
4198           continue;
4199         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4200         if ( !sm ) continue;
4201         // find out which parameter varies for a node on seam
4202         double f,l;
4203         gp_Pnt2d uv1, uv2;
4204         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4205         if ( pcurve.IsNull() ) continue;
4206         uv1 = pcurve->Value( f );
4207         edge.Reverse();
4208         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4209         if ( pcurve.IsNull() ) continue;
4210         uv2 = pcurve->Value( f );
4211         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4212         // assure uv1 < uv2
4213         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4214           std::swap( uv1, uv2 );
4215         // get nodes on seam and its vertices
4216         list< const SMDS_MeshNode* > seamNodes;
4217         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4218         while ( nSeamIt->more() ) {
4219           const SMDS_MeshNode* node = nSeamIt->next();
4220           if ( !isQuadratic || !IsMedium( node ))
4221             seamNodes.push_back( node );
4222         }
4223         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4224         for ( ; vExp.More(); vExp.Next() ) {
4225           sm = aMesh->MeshElements( vExp.Current() );
4226           if ( sm ) {
4227             nSeamIt = sm->GetNodes();
4228             while ( nSeamIt->more() )
4229               seamNodes.push_back( nSeamIt->next() );
4230           }
4231         }
4232         // loop on nodes on seam
4233         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4234         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4235           const SMDS_MeshNode* nSeam = *noSeIt;
4236           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4237           if ( n_uv == uvMap.end() )
4238             continue;
4239           // set the first UV
4240           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4241           // set the second UV
4242           listUV.push_back( *n_uv->second );
4243           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4244           if ( uvMap2.empty() )
4245             uvMap2 = uvMap; // copy the uvMap contents
4246           uvMap2[ nSeam ] = &listUV.back();
4247
4248           // collect movable nodes linked to ones on seam in nodesNearSeam
4249           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4250           while ( eIt->more() ) {
4251             const SMDS_MeshElement* e = eIt->next();
4252             int nbUseMap1 = 0, nbUseMap2 = 0;
4253             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4254             int nn = 0, nbn =  e->NbNodes();
4255             if(e->IsQuadratic()) nbn = nbn/2;
4256             while ( nn++ < nbn )
4257             {
4258               const SMDS_MeshNode* n =
4259                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4260               if (n == nSeam ||
4261                   setMovableNodes.find( n ) == setMovableNodes.end() )
4262                 continue;
4263               // add only nodes being closer to uv2 than to uv1
4264               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4265               //              0.5 * ( n->Y() + nSeam->Y() ),
4266               //              0.5 * ( n->Z() + nSeam->Z() ));
4267               // gp_XY uv;
4268               // getClosestUV( projector, pMid, uv );
4269               double x = uvMap[ n ]->Coord( iPar );
4270               if ( Abs( uv1.Coord( iPar ) - x ) >
4271                    Abs( uv2.Coord( iPar ) - x )) {
4272                 nodesNearSeam.insert( n );
4273                 nbUseMap2++;
4274               }
4275               else
4276                 nbUseMap1++;
4277             }
4278             // for centroidalSmooth all element nodes must
4279             // be on one side of a seam
4280             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4281               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4282               nn = 0;
4283               while ( nn++ < nbn ) {
4284                 const SMDS_MeshNode* n =
4285                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4286                 setMovableNodes.erase( n );
4287               }
4288             }
4289           }
4290         } // loop on nodes on seam
4291       } // loop on edge of a face
4292     } // if ( !face.IsNull() )
4293
4294     if ( setMovableNodes.empty() ) {
4295       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4296       continue; // goto next face
4297     }
4298
4299     // -------------
4300     // SMOOTHING //
4301     // -------------
4302
4303     int it = -1;
4304     double maxRatio = -1., maxDisplacement = -1.;
4305     set<const SMDS_MeshNode*>::iterator nodeToMove;
4306     for ( it = 0; it < theNbIterations; it++ ) {
4307       maxDisplacement = 0.;
4308       nodeToMove = setMovableNodes.begin();
4309       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4310         const SMDS_MeshNode* node = (*nodeToMove);
4311         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4312
4313         // smooth
4314         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4315         if ( theSmoothMethod == LAPLACIAN )
4316           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4317         else
4318           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4319
4320         // node displacement
4321         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4322         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4323         if ( aDispl > maxDisplacement )
4324           maxDisplacement = aDispl;
4325       }
4326       // no node movement => exit
4327       //if ( maxDisplacement < 1.e-16 ) {
4328       if ( maxDisplacement < disttol ) {
4329         MESSAGE("-- no node movement --");
4330         break;
4331       }
4332
4333       // check elements quality
4334       maxRatio  = 0;
4335       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4336       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4337         const SMDS_MeshElement* elem = (*elemIt);
4338         if ( !elem || elem->GetType() != SMDSAbs_Face )
4339           continue;
4340         SMESH::Controls::TSequenceOfXYZ aPoints;
4341         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4342           double aValue = aQualityFunc.GetValue( aPoints );
4343           if ( aValue > maxRatio )
4344             maxRatio = aValue;
4345         }
4346       }
4347       if ( maxRatio <= theTgtAspectRatio ) {
4348         //MESSAGE("-- quality achieved --");
4349         break;
4350       }
4351       if (it+1 == theNbIterations) {
4352         //MESSAGE("-- Iteration limit exceeded --");
4353       }
4354     } // smoothing iterations
4355
4356     // MESSAGE(" Face id: " << *fId <<
4357     //         " Nb iterstions: " << it <<
4358     //         " Displacement: " << maxDisplacement <<
4359     //         " Aspect Ratio " << maxRatio);
4360
4361     // ---------------------------------------
4362     // new nodes positions are computed,
4363     // record movement in DS and set new UV
4364     // ---------------------------------------
4365     nodeToMove = setMovableNodes.begin();
4366     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4367       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4368       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4369       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4370       if ( node_uv != uvMap.end() ) {
4371         gp_XY* uv = node_uv->second;
4372         node->SetPosition
4373           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4374       }
4375     }
4376
4377     // move medium nodes of quadratic elements
4378     if ( isQuadratic )
4379     {
4380       vector<const SMDS_MeshNode*> nodes;
4381       bool checkUV;
4382       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4383       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4384       {
4385         const SMDS_MeshElement* QF = *elemIt;
4386         if ( QF->IsQuadratic() )
4387         {
4388           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4389                         SMDS_MeshElement::iterator() );
4390           nodes.push_back( nodes[0] );
4391           gp_Pnt xyz;
4392           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4393           {
4394             if ( !surface.IsNull() )
4395             {
4396               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4397               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4398               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4399               xyz = surface->Value( uv.X(), uv.Y() );
4400             }
4401             else {
4402               xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4403             }
4404             if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4405               // we have to move a medium node
4406               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4407           }
4408         }
4409       }
4410     }
4411
4412   } // loop on face ids
4413
4414 }
4415
4416 namespace
4417 {
4418   //=======================================================================
4419   //function : isReverse
4420   //purpose  : Return true if normal of prevNodes is not co-directied with
4421   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4422   //           iNotSame is where prevNodes and nextNodes are different.
4423   //           If result is true then future volume orientation is OK
4424   //=======================================================================
4425
4426   bool isReverse(const SMDS_MeshElement*             face,
4427                  const vector<const SMDS_MeshNode*>& prevNodes,
4428                  const vector<const SMDS_MeshNode*>& nextNodes,
4429                  const int                           iNotSame)
4430   {
4431
4432     SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4433     SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4434     gp_XYZ extrDir( pN - pP ), faceNorm;
4435     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4436
4437     return faceNorm * extrDir < 0.0;
4438   }
4439
4440   //================================================================================
4441   /*!
4442    * \brief Assure that theElemSets[0] holds elements, not nodes
4443    */
4444   //================================================================================
4445
4446   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4447   {
4448     if ( !theElemSets[0].empty() &&
4449          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4450     {
4451       std::swap( theElemSets[0], theElemSets[1] );
4452     }
4453     else if ( !theElemSets[1].empty() &&
4454               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4455     {
4456       std::swap( theElemSets[0], theElemSets[1] );
4457     }
4458   }
4459 }
4460
4461 //=======================================================================
4462 /*!
4463  * \brief Create elements by sweeping an element
4464  * \param elem - element to sweep
4465  * \param newNodesItVec - nodes generated from each node of the element
4466  * \param newElems - generated elements
4467  * \param nbSteps - number of sweeping steps
4468  * \param srcElements - to append elem for each generated element
4469  */
4470 //=======================================================================
4471
4472 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4473                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4474                                     list<const SMDS_MeshElement*>&        newElems,
4475                                     const size_t                          nbSteps,
4476                                     SMESH_SequenceOfElemPtr&              srcElements)
4477 {
4478   SMESHDS_Mesh* aMesh = GetMeshDS();
4479
4480   const int           nbNodes = elem->NbNodes();
4481   const int         nbCorners = elem->NbCornerNodes();
4482   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4483                                                           polyhedron creation !!! */
4484   // Loop on elem nodes:
4485   // find new nodes and detect same nodes indices
4486   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4487   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4488   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4489   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4490
4491   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4492   vector<int> sames(nbNodes);
4493   vector<bool> isSingleNode(nbNodes);
4494
4495   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4496     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4497     const SMDS_MeshNode*                         node = nnIt->first;
4498     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4499     if ( listNewNodes.empty() )
4500       return;
4501
4502     itNN   [ iNode ] = listNewNodes.begin();
4503     prevNod[ iNode ] = node;
4504     nextNod[ iNode ] = listNewNodes.front();
4505
4506     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4507                                                              corner node of linear */
4508     if ( prevNod[ iNode ] != nextNod [ iNode ])
4509       nbDouble += !isSingleNode[iNode];
4510
4511     if( iNode < nbCorners ) { // check corners only
4512       if ( prevNod[ iNode ] == nextNod [ iNode ])
4513         sames[nbSame++] = iNode;
4514       else
4515         iNotSameNode = iNode;
4516     }
4517   }
4518
4519   if ( nbSame == nbNodes || nbSame > 2) {
4520     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4521     return;
4522   }
4523
4524   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4525   {
4526     // fix nodes order to have bottom normal external
4527     if ( baseType == SMDSEntity_Polygon )
4528     {
4529       std::reverse( itNN.begin(), itNN.end() );
4530       std::reverse( prevNod.begin(), prevNod.end() );
4531       std::reverse( midlNod.begin(), midlNod.end() );
4532       std::reverse( nextNod.begin(), nextNod.end() );
4533       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4534     }
4535     else
4536     {
4537       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4538       SMDS_MeshCell::applyInterlace( ind, itNN );
4539       SMDS_MeshCell::applyInterlace( ind, prevNod );
4540       SMDS_MeshCell::applyInterlace( ind, nextNod );
4541       SMDS_MeshCell::applyInterlace( ind, midlNod );
4542       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4543       if ( nbSame > 0 )
4544       {
4545         sames[nbSame] = iNotSameNode;
4546         for ( int j = 0; j <= nbSame; ++j )
4547           for ( size_t i = 0; i < ind.size(); ++i )
4548             if ( ind[i] == sames[j] )
4549             {
4550               sames[j] = i;
4551               break;
4552             }
4553         iNotSameNode = sames[nbSame];
4554       }
4555     }
4556   }
4557   else if ( elem->GetType() == SMDSAbs_Edge )
4558   {
4559     // orient a new face same as adjacent one
4560     int i1, i2;
4561     const SMDS_MeshElement* e;
4562     TIDSortedElemSet dummy;
4563     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4564         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4565         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4566     {
4567       // there is an adjacent face, check order of nodes in it
4568       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4569       if ( sameOrder )
4570       {
4571         std::swap( itNN[0],    itNN[1] );
4572         std::swap( prevNod[0], prevNod[1] );
4573         std::swap( nextNod[0], nextNod[1] );
4574 #if defined(__APPLE__)
4575         std::swap( isSingleNode[0], isSingleNode[1] );
4576 #else
4577         isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4578 #endif
4579         if ( nbSame > 0 )
4580           sames[0] = 1 - sames[0];
4581         iNotSameNode = 1 - iNotSameNode;
4582       }
4583     }
4584   }
4585
4586   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4587   if ( nbSame > 0 ) {
4588     iSameNode    = sames[ nbSame-1 ];
4589     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4590     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4591     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4592   }
4593
4594   if ( baseType == SMDSEntity_Polygon )
4595   {
4596     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4597     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4598   }
4599   else if ( baseType == SMDSEntity_Quad_Polygon )
4600   {
4601     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4602     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4603   }
4604
4605   // make new elements
4606   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4607   {
4608     // get next nodes
4609     for ( iNode = 0; iNode < nbNodes; iNode++ )
4610     {
4611       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4612       nextNod[ iNode ] = *itNN[ iNode ]++;
4613     }
4614
4615     SMDS_MeshElement* aNewElem = 0;
4616     /*if(!elem->IsPoly())*/ {
4617       switch ( baseType ) {
4618       case SMDSEntity_0D:
4619       case SMDSEntity_Node: { // sweep NODE
4620         if ( nbSame == 0 ) {
4621           if ( isSingleNode[0] )
4622             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4623           else
4624             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4625         }
4626         else
4627           return;
4628         break;
4629       }
4630       case SMDSEntity_Edge: { // sweep EDGE
4631         if ( nbDouble == 0 )
4632         {
4633           if ( nbSame == 0 ) // ---> quadrangle
4634             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4635                                       nextNod[ 1 ], nextNod[ 0 ] );
4636           else               // ---> triangle
4637             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4638                                       nextNod[ iNotSameNode ] );
4639         }
4640         else                 // ---> polygon
4641         {
4642           vector<const SMDS_MeshNode*> poly_nodes;
4643           poly_nodes.push_back( prevNod[0] );
4644           poly_nodes.push_back( prevNod[1] );
4645           if ( prevNod[1] != nextNod[1] )
4646           {
4647             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4648             poly_nodes.push_back( nextNod[1] );
4649           }
4650           if ( prevNod[0] != nextNod[0] )
4651           {
4652             poly_nodes.push_back( nextNod[0] );
4653             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4654           }
4655           switch ( poly_nodes.size() ) {
4656           case 3:
4657             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4658             break;
4659           case 4:
4660             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4661                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4662             break;
4663           default:
4664             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4665           }
4666         }
4667         break;
4668       }
4669       case SMDSEntity_Triangle: // TRIANGLE --->
4670         {
4671           if ( nbDouble > 0 ) break;
4672           if ( nbSame == 0 )       // ---> pentahedron
4673             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4674                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4675
4676           else if ( nbSame == 1 )  // ---> pyramid
4677             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4678                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4679                                          nextNod[ iSameNode ]);
4680
4681           else // 2 same nodes:       ---> tetrahedron
4682             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4683                                          nextNod[ iNotSameNode ]);
4684           break;
4685         }
4686       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4687         {
4688           if ( nbSame == 2 )
4689             return;
4690           if ( nbDouble+nbSame == 2 )
4691           {
4692             if(nbSame==0) {      // ---> quadratic quadrangle
4693               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4694                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4695             }
4696             else { //(nbSame==1) // ---> quadratic triangle
4697               if(sames[0]==2) {
4698                 return; // medium node on axis
4699               }
4700               else if(sames[0]==0)
4701                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4702                                           prevNod[2], midlNod[1], nextNod[2] );
4703               else // sames[0]==1
4704                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4705                                           prevNod[2], nextNod[2], midlNod[0]);
4706             }
4707           }
4708           else if ( nbDouble == 3 )
4709           {
4710             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4711               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4712                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4713             }
4714           }
4715           else
4716             return;
4717           break;
4718         }
4719       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4720         if ( nbDouble > 0 ) break;
4721
4722         if ( nbSame == 0 )       // ---> hexahedron
4723           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4724                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4725
4726         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4727           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4728                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4729                                        nextNod[ iSameNode ]);
4730           newElems.push_back( aNewElem );
4731           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4732                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4733                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4734         }
4735         else if ( nbSame == 2 ) { // ---> pentahedron
4736           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4737             // iBeforeSame is same too
4738             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4739                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4740                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4741           else
4742             // iAfterSame is same too
4743             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4744                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4745                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4746         }
4747         break;
4748       }
4749       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4750       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4751         if ( nbDouble+nbSame != 3 ) break;
4752         if(nbSame==0) {
4753           // --->  pentahedron with 15 nodes
4754           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4755                                        nextNod[0], nextNod[1], nextNod[2],
4756                                        prevNod[3], prevNod[4], prevNod[5],
4757                                        nextNod[3], nextNod[4], nextNod[5],
4758                                        midlNod[0], midlNod[1], midlNod[2]);
4759         }
4760         else if(nbSame==1) {
4761           // --->  2d order pyramid of 13 nodes
4762           int apex = iSameNode;
4763           int i0 = ( apex + 1 ) % nbCorners;
4764           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4765           int i0a = apex + 3;
4766           int i1a = i1 + 3;
4767           int i01 = i0 + 3;
4768           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4769                                       nextNod[i0], nextNod[i1], prevNod[apex],
4770                                       prevNod[i01], midlNod[i0],
4771                                       nextNod[i01], midlNod[i1],
4772                                       prevNod[i1a], prevNod[i0a],
4773                                       nextNod[i0a], nextNod[i1a]);
4774         }
4775         else if(nbSame==2) {
4776           // --->  2d order tetrahedron of 10 nodes
4777           int n1 = iNotSameNode;
4778           int n2 = ( n1 + 1             ) % nbCorners;
4779           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4780           int n12 = n1 + 3;
4781           int n23 = n2 + 3;
4782           int n31 = n3 + 3;
4783           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4784                                        prevNod[n12], prevNod[n23], prevNod[n31],
4785                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4786         }
4787         break;
4788       }
4789       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4790         if( nbSame == 0 ) {
4791           if ( nbDouble != 4 ) break;
4792           // --->  hexahedron with 20 nodes
4793           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4794                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4795                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4796                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4797                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4798         }
4799         else if(nbSame==1) {
4800           // ---> pyramid + pentahedron - can not be created since it is needed
4801           // additional middle node at the center of face
4802           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4803           return;
4804         }
4805         else if( nbSame == 2 ) {
4806           if ( nbDouble != 2 ) break;
4807           // --->  2d order Pentahedron with 15 nodes
4808           int n1,n2,n4,n5;
4809           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4810             // iBeforeSame is same too
4811             n1 = iBeforeSame;
4812             n2 = iOpposSame;
4813             n4 = iSameNode;
4814             n5 = iAfterSame;
4815           }
4816           else {
4817             // iAfterSame is same too
4818             n1 = iSameNode;
4819             n2 = iBeforeSame;
4820             n4 = iAfterSame;
4821             n5 = iOpposSame;
4822           }
4823           int n12 = n2 + 4;
4824           int n45 = n4 + 4;
4825           int n14 = n1 + 4;
4826           int n25 = n5 + 4;
4827           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4828                                        prevNod[n4], prevNod[n5], nextNod[n5],
4829                                        prevNod[n12], midlNod[n2], nextNod[n12],
4830                                        prevNod[n45], midlNod[n5], nextNod[n45],
4831                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4832         }
4833         break;
4834       }
4835       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4836
4837         if( nbSame == 0 && nbDouble == 9 ) {
4838           // --->  tri-quadratic hexahedron with 27 nodes
4839           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4840                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4841                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4842                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4843                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4844                                        prevNod[8], // bottom center
4845                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4846                                        nextNod[8], // top center
4847                                        midlNod[8]);// elem center
4848         }
4849         else
4850         {
4851           return;
4852         }
4853         break;
4854       }
4855       case SMDSEntity_Polygon: { // sweep POLYGON
4856
4857         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4858           // --->  hexagonal prism
4859           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4860                                        prevNod[3], prevNod[4], prevNod[5],
4861                                        nextNod[0], nextNod[1], nextNod[2],
4862                                        nextNod[3], nextNod[4], nextNod[5]);
4863         }
4864         break;
4865       }
4866       case SMDSEntity_Ball:
4867         return;
4868
4869       default:
4870         break;
4871       } // switch ( baseType )
4872     } // scope
4873
4874     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4875     {
4876       if ( baseType != SMDSEntity_Polygon )
4877       {
4878         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4879         SMDS_MeshCell::applyInterlace( ind, prevNod );
4880         SMDS_MeshCell::applyInterlace( ind, nextNod );
4881         SMDS_MeshCell::applyInterlace( ind, midlNod );
4882         SMDS_MeshCell::applyInterlace( ind, itNN );
4883         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4884         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4885       }
4886       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4887       vector<int> quantities (nbNodes + 2);
4888       polyedre_nodes.clear();
4889       quantities.clear();
4890
4891       // bottom of prism
4892       for (int inode = 0; inode < nbNodes; inode++)
4893         polyedre_nodes.push_back( prevNod[inode] );
4894       quantities.push_back( nbNodes );
4895
4896       // top of prism
4897       polyedre_nodes.push_back( nextNod[0] );
4898       for (int inode = nbNodes; inode-1; --inode )
4899         polyedre_nodes.push_back( nextNod[inode-1] );
4900       quantities.push_back( nbNodes );
4901
4902       // side faces
4903       // 3--6--2
4904       // |     |
4905       // 7     5
4906       // |     |
4907       // 0--4--1
4908       const int iQuad = elem->IsQuadratic();
4909       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4910       {
4911         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4912         int inextface = (iface+1+iQuad) % nbNodes;
4913         int imid      = (iface+1) % nbNodes;
4914         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4915         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4916         polyedre_nodes.push_back( prevNod[iface] );             // 1
4917         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4918         {
4919           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4920           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4921         }
4922         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4923         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4924         {
4925           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4926           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4927         }
4928         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4929         if ( nbFaceNodes > 2 )
4930           quantities.push_back( nbFaceNodes );
4931         else // degenerated face
4932           polyedre_nodes.resize( prevNbNodes );
4933       }
4934       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4935
4936     } // try to create a polyherdal prism
4937
4938     if ( aNewElem ) {
4939       newElems.push_back( aNewElem );
4940       myLastCreatedElems.Append(aNewElem);
4941       srcElements.Append( elem );
4942     }
4943
4944     // set new prev nodes
4945     for ( iNode = 0; iNode < nbNodes; iNode++ )
4946       prevNod[ iNode ] = nextNod[ iNode ];
4947
4948   } // loop on steps
4949 }
4950
4951 //=======================================================================
4952 /*!
4953  * \brief Create 1D and 2D elements around swept elements
4954  * \param mapNewNodes - source nodes and ones generated from them
4955  * \param newElemsMap - source elements and ones generated from them
4956  * \param elemNewNodesMap - nodes generated from each node of each element
4957  * \param elemSet - all swept elements
4958  * \param nbSteps - number of sweeping steps
4959  * \param srcElements - to append elem for each generated element
4960  */
4961 //=======================================================================
4962
4963 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4964                                   TTElemOfElemListMap &    newElemsMap,
4965                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4966                                   TIDSortedElemSet&        elemSet,
4967                                   const int                nbSteps,
4968                                   SMESH_SequenceOfElemPtr& srcElements)
4969 {
4970   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4971   SMESHDS_Mesh* aMesh = GetMeshDS();
4972
4973   // Find nodes belonging to only one initial element - sweep them into edges.
4974
4975   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4976   for ( ; nList != mapNewNodes.end(); nList++ )
4977   {
4978     const SMDS_MeshNode* node =
4979       static_cast<const SMDS_MeshNode*>( nList->first );
4980     if ( newElemsMap.count( node ))
4981       continue; // node was extruded into edge
4982     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4983     int nbInitElems = 0;
4984     const SMDS_MeshElement* el = 0;
4985     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4986     while ( eIt->more() && nbInitElems < 2 ) {
4987       const SMDS_MeshElement* e = eIt->next();
4988       SMDSAbs_ElementType  type = e->GetType();
4989       if ( type == SMDSAbs_Volume ||
4990            type < highType ||
4991            !elemSet.count(e))
4992         continue;
4993       if ( type > highType ) {
4994         nbInitElems = 0;
4995         highType    = type;
4996       }
4997       el = e;
4998       ++nbInitElems;
4999     }
5000     if ( nbInitElems == 1 ) {
5001       bool NotCreateEdge = el && el->IsMediumNode(node);
5002       if(!NotCreateEdge) {
5003         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5004         list<const SMDS_MeshElement*> newEdges;
5005         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5006       }
5007     }
5008   }
5009
5010   // Make a ceiling for each element ie an equal element of last new nodes.
5011   // Find free links of faces - make edges and sweep them into faces.
5012
5013   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5014
5015   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
5016   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5017   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5018   {
5019     const SMDS_MeshElement* elem = itElem->first;
5020     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5021
5022     if(itElem->second.size()==0) continue;
5023
5024     const bool isQuadratic = elem->IsQuadratic();
5025
5026     if ( elem->GetType() == SMDSAbs_Edge ) {
5027       // create a ceiling edge
5028       if ( !isQuadratic ) {
5029         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5030                                vecNewNodes[ 1 ]->second.back())) {
5031           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5032                                                    vecNewNodes[ 1 ]->second.back()));
5033           srcElements.Append( elem );
5034         }
5035       }
5036       else {
5037         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5038                                vecNewNodes[ 1 ]->second.back(),
5039                                vecNewNodes[ 2 ]->second.back())) {
5040           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5041                                                    vecNewNodes[ 1 ]->second.back(),
5042                                                    vecNewNodes[ 2 ]->second.back()));
5043           srcElements.Append( elem );
5044         }
5045       }
5046     }
5047     if ( elem->GetType() != SMDSAbs_Face )
5048       continue;
5049
5050     bool hasFreeLinks = false;
5051
5052     TIDSortedElemSet avoidSet;
5053     avoidSet.insert( elem );
5054
5055     set<const SMDS_MeshNode*> aFaceLastNodes;
5056     int iNode, nbNodes = vecNewNodes.size();
5057     if ( !isQuadratic ) {
5058       // loop on the face nodes
5059       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5060         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5061         // look for free links of the face
5062         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5063         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5064         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5065         // check if a link n1-n2 is free
5066         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5067           hasFreeLinks = true;
5068           // make a new edge and a ceiling for a new edge
5069           const SMDS_MeshElement* edge;
5070           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5071             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5072             srcElements.Append( myLastCreatedElems.Last() );
5073           }
5074           n1 = vecNewNodes[ iNode ]->second.back();
5075           n2 = vecNewNodes[ iNext ]->second.back();
5076           if ( !aMesh->FindEdge( n1, n2 )) {
5077             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5078             srcElements.Append( edge );
5079           }
5080         }
5081       }
5082     }
5083     else { // elem is quadratic face
5084       int nbn = nbNodes/2;
5085       for ( iNode = 0; iNode < nbn; iNode++ ) {
5086         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5087         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5088         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5089         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5090         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5091         // check if a link is free
5092         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5093              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5094              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5095           hasFreeLinks = true;
5096           // make an edge and a ceiling for a new edge
5097           // find medium node
5098           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5099             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5100             srcElements.Append( elem );
5101           }
5102           n1 = vecNewNodes[ iNode ]->second.back();
5103           n2 = vecNewNodes[ iNext ]->second.back();
5104           n3 = vecNewNodes[ iNode+nbn ]->second.back();
5105           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5106             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5107             srcElements.Append( elem );
5108           }
5109         }
5110       }
5111       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5112         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5113       }
5114     }
5115
5116     // sweep free links into faces
5117
5118     if ( hasFreeLinks ) {
5119       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5120       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5121
5122       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5123       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5124       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5125         initNodeSet.insert( vecNewNodes[ iNode ]->first );
5126         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5127       }
5128       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
5129         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5130         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5131       }
5132       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5133         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5134         std::advance( v, volNb );
5135         // find indices of free faces of a volume and their source edges
5136         list< int > freeInd;
5137         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5138         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5139         int iF, nbF = vTool.NbFaces();
5140         for ( iF = 0; iF < nbF; iF ++ ) {
5141           if (vTool.IsFreeFace( iF ) &&
5142               vTool.GetFaceNodes( iF, faceNodeSet ) &&
5143               initNodeSet != faceNodeSet) // except an initial face
5144           {
5145             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5146               continue;
5147             if ( faceNodeSet == initNodeSetNoCenter )
5148               continue;
5149             freeInd.push_back( iF );
5150             // find source edge of a free face iF
5151             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5152             vector<const SMDS_MeshNode*>::iterator lastCommom;
5153             commonNodes.resize( nbNodes, 0 );
5154             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5155                                                 initNodeSet.begin(), initNodeSet.end(),
5156                                                 commonNodes.begin());
5157             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5158               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5159             else
5160               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5161 #ifdef _DEBUG_
5162             if ( !srcEdges.back() )
5163             {
5164               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5165                    << iF << " of volume #" << vTool.ID() << endl;
5166             }
5167 #endif
5168           }
5169         }
5170         if ( freeInd.empty() )
5171           continue;
5172
5173         // create wall faces for all steps;
5174         // if such a face has been already created by sweep of edge,
5175         // assure that its orientation is OK
5176         for ( int iStep = 0; iStep < nbSteps; iStep++ )
5177         {
5178           vTool.Set( *v, /*ignoreCentralNodes=*/false );
5179           vTool.SetExternalNormal();
5180           const int nextShift = vTool.IsForward() ? +1 : -1;
5181           list< int >::iterator ind = freeInd.begin();
5182           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5183           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5184           {
5185             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5186             int nbn = vTool.NbFaceNodes( *ind );
5187             const SMDS_MeshElement * f = 0;
5188             if ( nbn == 3 )              ///// triangle
5189             {
5190               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5191               if ( !f ||
5192                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5193               {
5194                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5195                                                      nodes[ 1 ],
5196                                                      nodes[ 1 + nextShift ] };
5197                 if ( f )
5198                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5199                 else
5200                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5201                                                             newOrder[ 2 ] ));
5202               }
5203             }
5204             else if ( nbn == 4 )       ///// quadrangle
5205             {
5206               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5207               if ( !f ||
5208                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5209               {
5210                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5211                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
5212                 if ( f )
5213                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5214                 else
5215                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5216                                                             newOrder[ 2 ], newOrder[ 3 ]));
5217               }
5218             }
5219             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5220             {
5221               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5222               if ( !f ||
5223                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5224               {
5225                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5226                                                      nodes[2],
5227                                                      nodes[2 + 2*nextShift],
5228                                                      nodes[3 - 2*nextShift],
5229                                                      nodes[3],
5230                                                      nodes[3 + 2*nextShift]};
5231                 if ( f )
5232                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5233                 else
5234                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5235                                                             newOrder[ 1 ],
5236                                                             newOrder[ 2 ],
5237                                                             newOrder[ 3 ],
5238                                                             newOrder[ 4 ],
5239                                                             newOrder[ 5 ] ));
5240               }
5241             }
5242             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5243             {
5244               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5245                                    nodes[1], nodes[3], nodes[5], nodes[7] );
5246               if ( !f ||
5247                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5248               {
5249                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5250                                                      nodes[4 - 2*nextShift],
5251                                                      nodes[4],
5252                                                      nodes[4 + 2*nextShift],
5253                                                      nodes[1],
5254                                                      nodes[5 - 2*nextShift],
5255                                                      nodes[5],
5256                                                      nodes[5 + 2*nextShift] };
5257                 if ( f )
5258                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5259                 else
5260                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5261                                                            newOrder[ 2 ], newOrder[ 3 ],
5262                                                            newOrder[ 4 ], newOrder[ 5 ],
5263                                                            newOrder[ 6 ], newOrder[ 7 ]));
5264               }
5265             }
5266             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5267             {
5268               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5269                                       SMDSAbs_Face, /*noMedium=*/false);
5270               if ( !f ||
5271                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5272               {
5273                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5274                                                      nodes[4 - 2*nextShift],
5275                                                      nodes[4],
5276                                                      nodes[4 + 2*nextShift],
5277                                                      nodes[1],
5278                                                      nodes[5 - 2*nextShift],
5279                                                      nodes[5],
5280                                                      nodes[5 + 2*nextShift],
5281                                                      nodes[8] };
5282                 if ( f )
5283                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5284                 else
5285                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5286                                                            newOrder[ 2 ], newOrder[ 3 ],
5287                                                            newOrder[ 4 ], newOrder[ 5 ],
5288                                                            newOrder[ 6 ], newOrder[ 7 ],
5289                                                            newOrder[ 8 ]));
5290               }
5291             }
5292             else  //////// polygon
5293             {
5294               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5295               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5296               if ( !f ||
5297                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5298               {
5299                 if ( !vTool.IsForward() )
5300                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5301                 if ( f )
5302                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5303                 else
5304                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5305               }
5306             }
5307
5308             while ( srcElements.Length() < myLastCreatedElems.Length() )
5309               srcElements.Append( *srcEdge );
5310
5311           }  // loop on free faces
5312
5313           // go to the next volume
5314           iVol = 0;
5315           while ( iVol++ < nbVolumesByStep ) v++;
5316
5317         } // loop on steps
5318       } // loop on volumes of one step
5319     } // sweep free links into faces
5320
5321     // Make a ceiling face with a normal external to a volume
5322
5323     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5324     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5325     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5326
5327     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5328       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5329       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5330     }
5331     if ( iF >= 0 )
5332     {
5333       lastVol.SetExternalNormal();
5334       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5335       const               int nbn = lastVol.NbFaceNodes( iF );
5336       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5337       if ( !hasFreeLinks ||
5338            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5339       {
5340         const vector<int>& interlace =
5341           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5342         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5343
5344         AddElement( nodeVec, anyFace.Init( elem ));
5345
5346         while ( srcElements.Length() < myLastCreatedElems.Length() )
5347           srcElements.Append( elem );
5348       }
5349     }
5350   } // loop on swept elements
5351 }
5352
5353 //=======================================================================
5354 //function : RotationSweep
5355 //purpose  :
5356 //=======================================================================
5357
5358 SMESH_MeshEditor::PGroupIDs
5359 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5360                                 const gp_Ax1&      theAxis,
5361                                 const double       theAngle,
5362                                 const int          theNbSteps,
5363                                 const double       theTol,
5364                                 const bool         theMakeGroups,
5365                                 const bool         theMakeWalls)
5366 {
5367   myLastCreatedElems.Clear();
5368   myLastCreatedNodes.Clear();
5369
5370   // source elements for each generated one
5371   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5372
5373   gp_Trsf aTrsf;
5374   aTrsf.SetRotation( theAxis, theAngle );
5375   gp_Trsf aTrsf2;
5376   aTrsf2.SetRotation( theAxis, theAngle/2. );
5377
5378   gp_Lin aLine( theAxis );
5379   double aSqTol = theTol * theTol;
5380
5381   SMESHDS_Mesh* aMesh = GetMeshDS();
5382
5383   TNodeOfNodeListMap mapNewNodes;
5384   TElemOfVecOfNnlmiMap mapElemNewNodes;
5385   TTElemOfElemListMap newElemsMap;
5386
5387   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5388                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5389                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5390   // loop on theElemSets
5391   setElemsFirst( theElemSets );
5392   TIDSortedElemSet::iterator itElem;
5393   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5394   {
5395     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5396     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5397       const SMDS_MeshElement* elem = *itElem;
5398       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5399         continue;
5400       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5401       newNodesItVec.reserve( elem->NbNodes() );
5402
5403       // loop on elem nodes
5404       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5405       while ( itN->more() )
5406       {
5407         const SMDS_MeshNode* node = cast2Node( itN->next() );
5408
5409         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5410         double coord[3];
5411         aXYZ.Coord( coord[0], coord[1], coord[2] );
5412         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5413
5414         // check if a node has been already sweeped
5415         TNodeOfNodeListMapItr nIt =
5416           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5417         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5418         if ( listNewNodes.empty() )
5419         {
5420           // check if we are to create medium nodes between corner ones
5421           bool needMediumNodes = false;
5422           if ( isQuadraticMesh )
5423           {
5424             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5425             while (it->more() && !needMediumNodes )
5426             {
5427               const SMDS_MeshElement* invElem = it->next();
5428               if ( invElem != elem && !theElems.count( invElem )) continue;
5429               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5430               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5431                 needMediumNodes = true;
5432             }
5433           }
5434
5435           // make new nodes
5436           const SMDS_MeshNode * newNode = node;
5437           for ( int i = 0; i < theNbSteps; i++ ) {
5438             if ( !isOnAxis ) {
5439               if ( needMediumNodes )  // create a medium node
5440               {
5441                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5442                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5443                 myLastCreatedNodes.Append(newNode);
5444                 srcNodes.Append( node );
5445                 listNewNodes.push_back( newNode );
5446                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5447               }
5448               else {
5449                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5450               }
5451               // create a corner node
5452               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5453               myLastCreatedNodes.Append(newNode);
5454               srcNodes.Append( node );
5455               listNewNodes.push_back( newNode );
5456             }
5457             else {
5458               listNewNodes.push_back( newNode );
5459               // if ( needMediumNodes )
5460               //   listNewNodes.push_back( newNode );
5461             }
5462           }
5463         }
5464         newNodesItVec.push_back( nIt );
5465       }
5466       // make new elements
5467       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5468     }
5469   }
5470
5471   if ( theMakeWalls )
5472     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5473
5474   PGroupIDs newGroupIDs;
5475   if ( theMakeGroups )
5476     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5477
5478   return newGroupIDs;
5479 }
5480
5481 //=======================================================================
5482 //function : ExtrusParam
5483 //purpose  : standard construction
5484 //=======================================================================
5485
5486 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5487                                             const int                theNbSteps,
5488                                             const std::list<double>& theScales,
5489                                             const gp_XYZ*            theBasePoint,
5490                                             const int                theFlags,
5491                                             const double             theTolerance):
5492   myDir( theStep ),
5493   myBaseP( Precision::Infinite(), 0, 0 ),
5494   myFlags( theFlags ),
5495   myTolerance( theTolerance ),
5496   myElemsToUse( NULL )
5497 {
5498   mySteps = new TColStd_HSequenceOfReal;
5499   const double stepSize = theStep.Magnitude();
5500   for (int i=1; i<=theNbSteps; i++ )
5501     mySteps->Append( stepSize );
5502
5503   int nbScales = theScales.size();
5504   if ( nbScales > 0 )
5505   {
5506     if ( IsLinearVariation() && nbScales < theNbSteps )
5507     {
5508       myScales.reserve( theNbSteps );
5509       std::list<double>::const_iterator scale = theScales.begin();
5510       double prevScale = 1.0;
5511       for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
5512       {
5513         int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
5514         int    stDelta = Max( 1, iStep - myScales.size());
5515         double scDelta = ( *scale - prevScale ) / stDelta;
5516         for ( int iStep = 0; iStep < stDelta; ++iStep )
5517         {
5518           myScales.push_back( prevScale + scDelta );
5519           prevScale = myScales.back();
5520         }
5521         prevScale = *scale;
5522       }
5523     }
5524     else
5525     {
5526       myScales.assign( theScales.begin(), theScales.end() );
5527     }
5528   }
5529   if ( theBasePoint )
5530   {
5531     myBaseP = *theBasePoint;
5532   }
5533
5534   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5535       ( theTolerance > 0 ))
5536   {
5537     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5538   }
5539   else
5540   {
5541     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5542   }
5543 }
5544
5545 //=======================================================================
5546 //function : ExtrusParam
5547 //purpose  : steps are given explicitly
5548 //=======================================================================
5549
5550 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5551                                             Handle(TColStd_HSequenceOfReal) theSteps,
5552                                             const int                       theFlags,
5553                                             const double                    theTolerance):
5554   myDir( theDir ),
5555   mySteps( theSteps ),
5556   myFlags( theFlags ),
5557   myTolerance( theTolerance ),
5558   myElemsToUse( NULL )
5559 {
5560   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5561       ( theTolerance > 0 ))
5562   {
5563     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5564   }
5565   else
5566   {
5567     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5568   }
5569 }
5570
5571 //=======================================================================
5572 //function : ExtrusParam
5573 //purpose  : for extrusion by normal
5574 //=======================================================================
5575
5576 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5577                                             const int    theNbSteps,
5578                                             const int    theFlags,
5579                                             const int    theDim ):
5580   myDir( 1,0,0 ),
5581   mySteps( new TColStd_HSequenceOfReal ),
5582   myFlags( theFlags ),
5583   myTolerance( 0 ),
5584   myElemsToUse( NULL )
5585 {
5586   for (int i = 0; i < theNbSteps; i++ )
5587     mySteps->Append( theStepSize );
5588
5589   if ( theDim == 1 )
5590   {
5591     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5592   }
5593   else
5594   {
5595     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5596   }
5597 }
5598
5599 //=======================================================================
5600 //function : ExtrusParam::SetElementsToUse
5601 //purpose  : stores elements to use for extrusion by normal, depending on
5602 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5603 //           define myBaseP for scaling
5604 //=======================================================================
5605
5606 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5607                                                       const TIDSortedElemSet& nodes )
5608 {
5609   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5610
5611   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5612   {
5613     myBaseP.SetCoord( 0.,0.,0. );
5614     TIDSortedElemSet newNodes;
5615
5616     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5617     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5618     {
5619       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5620       TIDSortedElemSet::const_iterator itElem = elements.begin();
5621       for ( ; itElem != elements.end(); itElem++ )
5622       {
5623         const SMDS_MeshElement* elem = *itElem;
5624         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5625         while ( itN->more() ) {
5626           const SMDS_MeshElement* node = itN->next();
5627           if ( newNodes.insert( node ).second )
5628             myBaseP += SMESH_TNodeXYZ( node );
5629         }
5630       }
5631     }
5632     myBaseP /= newNodes.size();
5633   }
5634 }
5635
5636 //=======================================================================
5637 //function : ExtrusParam::beginStepIter
5638 //purpose  : prepare iteration on steps
5639 //=======================================================================
5640
5641 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5642 {
5643   myWithMediumNodes = withMediumNodes;
5644   myNextStep = 1;
5645   myCurSteps.clear();
5646 }
5647 //=======================================================================
5648 //function : ExtrusParam::moreSteps
5649 //purpose  : are there more steps?
5650 //=======================================================================
5651
5652 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5653 {
5654   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5655 }
5656 //=======================================================================
5657 //function : ExtrusParam::nextStep
5658 //purpose  : returns the next step
5659 //=======================================================================
5660
5661 double SMESH_MeshEditor::ExtrusParam::nextStep()
5662 {
5663   double res = 0;
5664   if ( !myCurSteps.empty() )
5665   {
5666     res = myCurSteps.back();
5667     myCurSteps.pop_back();
5668   }
5669   else if ( myNextStep <= mySteps->Length() )
5670   {
5671     myCurSteps.push_back( mySteps->Value( myNextStep ));
5672     ++myNextStep;
5673     if ( myWithMediumNodes )
5674     {
5675       myCurSteps.back() /= 2.;
5676       myCurSteps.push_back( myCurSteps.back() );
5677     }
5678     res = nextStep();
5679   }
5680   return res;
5681 }
5682
5683 //=======================================================================
5684 //function : ExtrusParam::makeNodesByDir
5685 //purpose  : create nodes for standard extrusion
5686 //=======================================================================
5687
5688 int SMESH_MeshEditor::ExtrusParam::
5689 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5690                 const SMDS_MeshNode*              srcNode,
5691                 std::list<const SMDS_MeshNode*> & newNodes,
5692                 const bool                        makeMediumNodes)
5693 {
5694   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5695
5696   int nbNodes = 0;
5697   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5698   {
5699     p += myDir.XYZ() * nextStep();
5700     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5701     newNodes.push_back( newNode );
5702   }
5703
5704   if ( !myScales.empty() )
5705   {
5706     if ( makeMediumNodes && myMediumScales.empty() )
5707     {
5708       myMediumScales.resize( myScales.size() );
5709       double prevFactor = 1.;
5710       for ( size_t i = 0; i < myScales.size(); ++i )
5711       {
5712         myMediumScales[i] = 0.5 * ( prevFactor + myScales[i] );
5713         prevFactor = myScales[i];
5714       }
5715     }
5716     typedef std::vector<double>::iterator ScaleIt;
5717     ScaleIt scales[] = { myScales.begin(), myMediumScales.begin() };
5718
5719     size_t iSc = 0, nbScales = myScales.size() + myMediumScales.size();
5720
5721     gp_XYZ center = myBaseP;
5722     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5723     size_t iN  = 0;
5724     for ( beginStepIter( makeMediumNodes ); moreSteps() && ( iN < nbScales ); ++nIt, ++iN )
5725     {
5726       center += myDir.XYZ() * nextStep();
5727
5728       iSc += int( makeMediumNodes );
5729       ScaleIt& scale = scales[ iSc % 2 ];
5730       
5731       gp_XYZ xyz = SMESH_TNodeXYZ( *nIt );
5732       xyz = ( *scale * ( xyz - center )) + center;
5733       mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5734
5735       ++scale;
5736     }
5737   }
5738   return nbNodes;
5739 }
5740
5741 //=======================================================================
5742 //function : ExtrusParam::makeNodesByDirAndSew
5743 //purpose  : create nodes for standard extrusion with sewing
5744 //=======================================================================
5745
5746 int SMESH_MeshEditor::ExtrusParam::
5747 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5748                       const SMDS_MeshNode*              srcNode,
5749                       std::list<const SMDS_MeshNode*> & newNodes,
5750                       const bool                        makeMediumNodes)
5751 {
5752   gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5753
5754   int nbNodes = 0;
5755   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5756   {
5757     P1 += myDir.XYZ() * nextStep();
5758
5759     // try to search in sequence of existing nodes
5760     // if myNodes.Length()>0 we 'nave to use given sequence
5761     // else - use all nodes of mesh
5762     const SMDS_MeshNode * node = 0;
5763     if ( myNodes.Length() > 0 ) {
5764       int i;
5765       for(i=1; i<=myNodes.Length(); i++) {
5766         gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5767         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5768         {
5769           node = myNodes.Value(i);
5770           break;
5771         }
5772       }
5773     }
5774     else {
5775       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5776       while(itn->more()) {
5777         SMESH_TNodeXYZ P2( itn->next() );
5778         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5779         {
5780           node = P2._node;
5781           break;
5782         }
5783       }
5784     }
5785
5786     if ( !node )
5787       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5788
5789     newNodes.push_back( node );
5790
5791   } // loop on steps
5792
5793   return nbNodes;
5794 }
5795
5796 //=======================================================================
5797 //function : ExtrusParam::makeNodesByNormal2D
5798 //purpose  : create nodes for extrusion using normals of faces
5799 //=======================================================================
5800
5801 int SMESH_MeshEditor::ExtrusParam::
5802 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5803                      const SMDS_MeshNode*              srcNode,
5804                      std::list<const SMDS_MeshNode*> & newNodes,
5805                      const bool                        makeMediumNodes)
5806 {
5807   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5808
5809   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5810
5811   // get normals to faces sharing srcNode
5812   vector< gp_XYZ > norms, baryCenters;
5813   gp_XYZ norm, avgNorm( 0,0,0 );
5814   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5815   while ( faceIt->more() )
5816   {
5817     const SMDS_MeshElement* face = faceIt->next();
5818     if ( myElemsToUse && !myElemsToUse->count( face ))
5819       continue;
5820     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5821     {
5822       norms.push_back( norm );
5823       avgNorm += norm;
5824       if ( !alongAvgNorm )
5825       {
5826         gp_XYZ bc(0,0,0);
5827         int nbN = 0;
5828         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5829           bc += SMESH_TNodeXYZ( nIt->next() );
5830         baryCenters.push_back( bc / nbN );
5831       }
5832     }
5833   }
5834
5835   if ( norms.empty() ) return 0;
5836
5837   double normSize = avgNorm.Modulus();
5838   if ( normSize < std::numeric_limits<double>::min() )
5839     return 0;
5840
5841   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5842   {
5843     myDir = avgNorm;
5844     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5845   }
5846
5847   avgNorm /= normSize;
5848
5849   int nbNodes = 0;
5850   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5851   {
5852     gp_XYZ pNew = p;
5853     double stepSize = nextStep();
5854
5855     if ( norms.size() > 1 )
5856     {
5857       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5858       {
5859         // translate plane of a face
5860         baryCenters[ iF ] += norms[ iF ] * stepSize;
5861
5862         // find point of intersection of the face plane located at baryCenters[ iF ]
5863         // and avgNorm located at pNew
5864         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5865         double dot  = ( norms[ iF ] * avgNorm );
5866         if ( dot < std::numeric_limits<double>::min() )
5867           dot = stepSize * 1e-3;
5868         double step = -( norms[ iF ] * pNew + d ) / dot;
5869         pNew += step * avgNorm;
5870       }
5871     }
5872     else
5873     {
5874       pNew += stepSize * avgNorm;
5875     }
5876     p = pNew;
5877
5878     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5879     newNodes.push_back( newNode );
5880   }
5881   return nbNodes;
5882 }
5883
5884 //=======================================================================
5885 //function : ExtrusParam::makeNodesByNormal1D
5886 //purpose  : create nodes for extrusion using normals of edges
5887 //=======================================================================
5888
5889 int SMESH_MeshEditor::ExtrusParam::
5890 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5891                      const SMDS_MeshNode*              srcNode,
5892                      std::list<const SMDS_MeshNode*> & newNodes,
5893                      const bool                        makeMediumNodes)
5894 {
5895   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5896   return 0;
5897 }
5898
5899 //=======================================================================
5900 //function : ExtrusionSweep
5901 //purpose  :
5902 //=======================================================================
5903
5904 SMESH_MeshEditor::PGroupIDs
5905 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5906                                   const gp_Vec&        theStep,
5907                                   const int            theNbSteps,
5908                                   TTElemOfElemListMap& newElemsMap,
5909                                   const int            theFlags,
5910                                   const double         theTolerance)
5911 {
5912   ExtrusParam aParams( theStep, theNbSteps, std::list<double>(), 0, theFlags, theTolerance );
5913   return ExtrusionSweep( theElems, aParams, newElemsMap );
5914 }
5915
5916
5917 //=======================================================================
5918 //function : ExtrusionSweep
5919 //purpose  :
5920 //=======================================================================
5921
5922 SMESH_MeshEditor::PGroupIDs
5923 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5924                                   ExtrusParam&         theParams,
5925                                   TTElemOfElemListMap& newElemsMap)
5926 {
5927   myLastCreatedElems.Clear();
5928   myLastCreatedNodes.Clear();
5929
5930   // source elements for each generated one
5931   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5932
5933   setElemsFirst( theElemSets );
5934   const int nbSteps = theParams.NbSteps();
5935   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5936
5937   TNodeOfNodeListMap   mapNewNodes;
5938   TElemOfVecOfNnlmiMap mapElemNewNodes;
5939
5940   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5941                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5942                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5943   // loop on theElems
5944   TIDSortedElemSet::iterator itElem;
5945   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5946   {
5947     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5948     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5949     {
5950       // check element type
5951       const SMDS_MeshElement* elem = *itElem;
5952       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5953         continue;
5954
5955       const size_t nbNodes = elem->NbNodes();
5956       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5957       newNodesItVec.reserve( nbNodes );
5958
5959       // loop on elem nodes
5960       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5961       while ( itN->more() )
5962       {
5963         // check if a node has been already sweeped
5964         const SMDS_MeshNode* node = cast2Node( itN->next() );
5965         TNodeOfNodeListMap::iterator nIt =
5966           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5967         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5968         if ( listNewNodes.empty() )
5969         {
5970           // make new nodes
5971
5972           // check if we are to create medium nodes between corner ones
5973           bool needMediumNodes = false;
5974           if ( isQuadraticMesh )
5975           {
5976             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5977             while (it->more() && !needMediumNodes )
5978             {
5979               const SMDS_MeshElement* invElem = it->next();
5980               if ( invElem != elem && !theElems.count( invElem )) continue;
5981               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5982               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5983                 needMediumNodes = true;
5984             }
5985           }
5986           // create nodes for all steps
5987           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5988           {
5989             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5990             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5991             {
5992               myLastCreatedNodes.Append( *newNodesIt );
5993               srcNodes.Append( node );
5994             }
5995           }
5996           else
5997           {
5998             break; // newNodesItVec will be shorter than nbNodes
5999           }
6000         }
6001         newNodesItVec.push_back( nIt );
6002       }
6003       // make new elements
6004       if ( newNodesItVec.size() == nbNodes )
6005         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
6006     }
6007   }
6008
6009   if ( theParams.ToMakeBoundary() ) {
6010     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
6011   }
6012   PGroupIDs newGroupIDs;
6013   if ( theParams.ToMakeGroups() )
6014     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
6015
6016   return newGroupIDs;
6017 }
6018
6019 //=======================================================================
6020 //function : ExtrusionAlongTrack
6021 //purpose  :
6022 //=======================================================================
6023 SMESH_MeshEditor::Extrusion_Error
6024 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
6025                                        SMESH_subMesh*       theTrack,
6026                                        const SMDS_MeshNode* theN1,
6027                                        const bool           theHasAngles,
6028                                        list<double>&        theAngles,
6029                                        const bool           theLinearVariation,
6030                                        const bool           theHasRefPoint,
6031                                        const gp_Pnt&        theRefPoint,
6032                                        const bool           theMakeGroups)
6033 {
6034   myLastCreatedElems.Clear();
6035   myLastCreatedNodes.Clear();
6036
6037   int aNbE;
6038   std::list<double> aPrms;
6039   TIDSortedElemSet::iterator itElem;
6040
6041   gp_XYZ aGC;
6042   TopoDS_Edge aTrackEdge;
6043   TopoDS_Vertex aV1, aV2;
6044
6045   SMDS_ElemIteratorPtr aItE;
6046   SMDS_NodeIteratorPtr aItN;
6047   SMDSAbs_ElementType aTypeE;
6048
6049   TNodeOfNodeListMap mapNewNodes;
6050
6051   // 1. Check data
6052   aNbE = theElements[0].size() + theElements[1].size();
6053   // nothing to do
6054   if ( !aNbE )
6055     return EXTR_NO_ELEMENTS;
6056
6057   // 1.1 Track Pattern
6058   ASSERT( theTrack );
6059
6060   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
6061   if ( !pSubMeshDS )
6062     return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
6063                                 theHasAngles, theAngles, theLinearVariation,
6064                                 theHasRefPoint, theRefPoint, theMakeGroups );
6065
6066   aItE = pSubMeshDS->GetElements();
6067   while ( aItE->more() ) {
6068     const SMDS_MeshElement* pE = aItE->next();
6069     aTypeE = pE->GetType();
6070     // Pattern must contain links only
6071     if ( aTypeE != SMDSAbs_Edge )
6072       return EXTR_PATH_NOT_EDGE;
6073   }
6074
6075   list<SMESH_MeshEditor_PathPoint> fullList;
6076
6077   const TopoDS_Shape& aS = theTrack->GetSubShape();
6078   // Sub-shape for the Pattern must be an Edge or Wire
6079   if( aS.ShapeType() == TopAbs_EDGE ) {
6080     aTrackEdge = TopoDS::Edge( aS );
6081     // the Edge must not be degenerated
6082     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6083       return EXTR_BAD_PATH_SHAPE;
6084     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6085     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
6086     const SMDS_MeshNode* aN1 = aItN->next();
6087     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
6088     const SMDS_MeshNode* aN2 = aItN->next();
6089     // starting node must be aN1 or aN2
6090     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6091       return EXTR_BAD_STARTING_NODE;
6092     aItN = pSubMeshDS->GetNodes();
6093     while ( aItN->more() ) {
6094       const SMDS_MeshNode* pNode = aItN->next();
6095       const SMDS_EdgePosition* pEPos =
6096         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6097       double aT = pEPos->GetUParameter();
6098       aPrms.push_back( aT );
6099     }
6100     //Extrusion_Error err =
6101     makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6102   } else if( aS.ShapeType() == TopAbs_WIRE ) {
6103     list< SMESH_subMesh* > LSM;
6104     TopTools_SequenceOfShape Edges;
6105     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6106     while(itSM->more()) {
6107       SMESH_subMesh* SM = itSM->next();
6108       LSM.push_back(SM);
6109       const TopoDS_Shape& aS = SM->GetSubShape();
6110       Edges.Append(aS);
6111     }
6112     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6113     int startNid = theN1->GetID();
6114     TColStd_MapOfInteger UsedNums;
6115
6116     int NbEdges = Edges.Length();
6117     int i = 1;
6118     for(; i<=NbEdges; i++) {
6119       int k = 0;
6120       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6121       for(; itLSM!=LSM.end(); itLSM++) {
6122         k++;
6123         if(UsedNums.Contains(k)) continue;
6124         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6125         SMESH_subMesh* locTrack = *itLSM;
6126         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6127         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6128         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6129         const SMDS_MeshNode* aN1 = aItN->next();
6130         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6131         const SMDS_MeshNode* aN2 = aItN->next();
6132         // starting node must be aN1 or aN2
6133         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6134         // 2. Collect parameters on the track edge
6135         aPrms.clear();
6136         aItN = locMeshDS->GetNodes();
6137         while ( aItN->more() ) {
6138           const SMDS_MeshNode* pNode = aItN->next();
6139           const SMDS_EdgePosition* pEPos =
6140             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6141           double aT = pEPos->GetUParameter();
6142           aPrms.push_back( aT );
6143         }
6144         list<SMESH_MeshEditor_PathPoint> LPP;
6145         //Extrusion_Error err =
6146         makeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6147         LLPPs.push_back(LPP);
6148         UsedNums.Add(k);
6149         // update startN for search following egde
6150         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6151         else startNid = aN1->GetID();
6152         break;
6153       }
6154     }
6155     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6156     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6157     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6158     for(; itPP!=firstList.end(); itPP++) {
6159       fullList.push_back( *itPP );
6160     }
6161     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6162     fullList.pop_back();
6163     itLLPP++;
6164     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6165       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6166       itPP = currList.begin();
6167       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6168       gp_Dir D1 = PP1.Tangent();
6169       gp_Dir D2 = PP2.Tangent();
6170       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6171                            (D1.Z()+D2.Z())/2 ) );
6172       PP1.SetTangent(Dnew);
6173       fullList.push_back(PP1);
6174       itPP++;
6175       for(; itPP!=firstList.end(); itPP++) {
6176         fullList.push_back( *itPP );
6177       }
6178       PP1 = fullList.back();
6179       fullList.pop_back();
6180     }
6181     // if wire not closed
6182     fullList.push_back(PP1);
6183     // else ???
6184   }
6185   else {
6186     return EXTR_BAD_PATH_SHAPE;
6187   }
6188
6189   return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6190                           theHasRefPoint, theRefPoint, theMakeGroups);
6191 }
6192
6193
6194 //=======================================================================
6195 //function : ExtrusionAlongTrack
6196 //purpose  :
6197 //=======================================================================
6198 SMESH_MeshEditor::Extrusion_Error
6199 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
6200                                        SMESH_Mesh*          theTrack,
6201                                        const SMDS_MeshNode* theN1,
6202                                        const bool           theHasAngles,
6203                                        list<double>&        theAngles,
6204                                        const bool           theLinearVariation,
6205                                        const bool           theHasRefPoint,
6206                                        const gp_Pnt&        theRefPoint,
6207                                        const bool           theMakeGroups)
6208 {
6209   myLastCreatedElems.Clear();
6210   myLastCreatedNodes.Clear();
6211
6212   int aNbE;
6213   std::list<double> aPrms;
6214   TIDSortedElemSet::iterator itElem;
6215
6216   gp_XYZ aGC;
6217   TopoDS_Edge aTrackEdge;
6218   TopoDS_Vertex aV1, aV2;
6219
6220   SMDS_ElemIteratorPtr aItE;
6221   SMDS_NodeIteratorPtr aItN;
6222   SMDSAbs_ElementType aTypeE;
6223
6224   TNodeOfNodeListMap mapNewNodes;
6225
6226   // 1. Check data
6227   aNbE = theElements[0].size() + theElements[1].size();
6228   // nothing to do
6229   if ( !aNbE )
6230     return EXTR_NO_ELEMENTS;
6231
6232   // 1.1 Track Pattern
6233   ASSERT( theTrack );
6234
6235   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6236
6237   aItE = pMeshDS->elementsIterator();
6238   while ( aItE->more() ) {
6239     const SMDS_MeshElement* pE = aItE->next();
6240     aTypeE = pE->GetType();
6241     // Pattern must contain links only
6242     if ( aTypeE != SMDSAbs_Edge )
6243       return EXTR_PATH_NOT_EDGE;
6244   }
6245
6246   list<SMESH_MeshEditor_PathPoint> fullList;
6247
6248   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6249
6250   if ( !theTrack->HasShapeToMesh() ) {
6251     //Mesh without shape
6252     const SMDS_MeshNode* currentNode = NULL;
6253     const SMDS_MeshNode* prevNode = theN1;
6254     std::vector<const SMDS_MeshNode*> aNodesList;
6255     aNodesList.push_back(theN1);
6256     int nbEdges = 0, conn=0;
6257     const SMDS_MeshElement* prevElem = NULL;
6258     const SMDS_MeshElement* currentElem = NULL;
6259     int totalNbEdges = theTrack->NbEdges();
6260     SMDS_ElemIteratorPtr nIt;
6261
6262     //check start node
6263     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6264       return EXTR_BAD_STARTING_NODE;
6265     }
6266
6267     conn = nbEdgeConnectivity(theN1);
6268     if( conn != 1 )
6269       return EXTR_PATH_NOT_EDGE;
6270
6271     aItE = theN1->GetInverseElementIterator();
6272     prevElem = aItE->next();
6273     currentElem = prevElem;
6274     //Get all nodes
6275     if(totalNbEdges == 1 ) {
6276       nIt = currentElem->nodesIterator();
6277       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6278       if(currentNode == prevNode)
6279         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6280       aNodesList.push_back(currentNode);
6281     } else {
6282       nIt = currentElem->nodesIterator();
6283       while( nIt->more() ) {
6284         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6285         if(currentNode == prevNode)
6286           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6287         aNodesList.push_back(currentNode);
6288
6289         //case of the closed mesh
6290         if(currentNode == theN1) {
6291           nbEdges++;
6292           break;
6293         }
6294
6295         conn = nbEdgeConnectivity(currentNode);
6296         if(conn > 2) {
6297           return EXTR_PATH_NOT_EDGE;
6298         }else if( conn == 1 && nbEdges > 0 ) {
6299           //End of the path
6300           nbEdges++;
6301           break;
6302         }else {
6303           prevNode = currentNode;
6304           aItE = currentNode->GetInverseElementIterator();
6305           currentElem = aItE->next();
6306           if( currentElem  == prevElem)
6307             currentElem = aItE->next();
6308           nIt = currentElem->nodesIterator();
6309           prevElem = currentElem;
6310           nbEdges++;
6311         }
6312       }
6313     }
6314
6315     if(nbEdges != totalNbEdges)
6316       return EXTR_PATH_NOT_EDGE;
6317
6318     TopTools_SequenceOfShape Edges;
6319     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6320     int startNid = theN1->GetID();
6321     for ( size_t i = 1; i < aNodesList.size(); i++ )
6322     {
6323       gp_Pnt     p1 = SMESH_TNodeXYZ( aNodesList[i-1] );
6324       gp_Pnt     p2 = SMESH_TNodeXYZ( aNodesList[i] );
6325       TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6326       list<SMESH_MeshEditor_PathPoint> LPP;
6327       aPrms.clear();
6328       makeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6329       LLPPs.push_back(LPP);
6330       if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i  ]->GetID();
6331       else                                        startNid = aNodesList[i-1]->GetID();
6332     }
6333
6334     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6335     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6336     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6337     for(; itPP!=firstList.end(); itPP++) {
6338       fullList.push_back( *itPP );
6339     }
6340
6341     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6342     SMESH_MeshEditor_PathPoint PP2;
6343     fullList.pop_back();
6344     itLLPP++;
6345     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6346       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6347       itPP = currList.begin();
6348       PP2 = currList.front();
6349       gp_Dir D1 = PP1.Tangent();
6350       gp_Dir D2 = PP2.Tangent();
6351       gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6352       PP1.SetTangent(Dnew);
6353       fullList.push_back(PP1);
6354       itPP++;
6355       for(; itPP!=currList.end(); itPP++) {
6356         fullList.push_back( *itPP );
6357       }
6358       PP1 = fullList.back();
6359       fullList.pop_back();
6360     }
6361     fullList.push_back(PP1);
6362
6363   } // Sub-shape for the Pattern must be an Edge or Wire
6364   else if ( aS.ShapeType() == TopAbs_EDGE )
6365   {
6366     aTrackEdge = TopoDS::Edge( aS );
6367     // the Edge must not be degenerated
6368     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6369       return EXTR_BAD_PATH_SHAPE;
6370     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6371     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6372     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6373     // starting node must be aN1 or aN2
6374     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6375       return EXTR_BAD_STARTING_NODE;
6376     aItN = pMeshDS->nodesIterator();
6377     while ( aItN->more() ) {
6378       const SMDS_MeshNode* pNode = aItN->next();
6379       if( pNode==aN1 || pNode==aN2 ) continue;
6380       const SMDS_EdgePosition* pEPos =
6381         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6382       double aT = pEPos->GetUParameter();
6383       aPrms.push_back( aT );
6384     }
6385     //Extrusion_Error err =
6386     makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6387   }
6388   else if( aS.ShapeType() == TopAbs_WIRE ) {
6389     list< SMESH_subMesh* > LSM;
6390     TopTools_SequenceOfShape Edges;
6391     TopExp_Explorer eExp(aS, TopAbs_EDGE);
6392     for(; eExp.More(); eExp.Next()) {
6393       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6394       if( SMESH_Algo::isDegenerated(E) ) continue;
6395       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6396       if(SM) {
6397         LSM.push_back(SM);
6398         Edges.Append(E);
6399       }
6400     }
6401     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6402     TopoDS_Vertex aVprev;
6403     TColStd_MapOfInteger UsedNums;
6404     int NbEdges = Edges.Length();
6405     int i = 1;
6406     for(; i<=NbEdges; i++) {
6407       int k = 0;
6408       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6409       for(; itLSM!=LSM.end(); itLSM++) {
6410         k++;
6411         if(UsedNums.Contains(k)) continue;
6412         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6413         SMESH_subMesh* locTrack = *itLSM;
6414         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6415         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6416         bool aN1isOK = false, aN2isOK = false;
6417         if ( aVprev.IsNull() ) {
6418           // if previous vertex is not yet defined, it means that we in the beginning of wire
6419           // and we have to find initial vertex corresponding to starting node theN1
6420           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6421           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6422           // starting node must be aN1 or aN2
6423           aN1isOK = ( aN1 && aN1 == theN1 );
6424           aN2isOK = ( aN2 && aN2 == theN1 );
6425         }
6426         else {
6427           // we have specified ending vertex of the previous edge on the previous iteration
6428           // and we have just to check that it corresponds to any vertex in current segment
6429           aN1isOK = aVprev.IsSame( aV1 );
6430           aN2isOK = aVprev.IsSame( aV2 );
6431         }
6432         if ( !aN1isOK && !aN2isOK ) continue;
6433         // 2. Collect parameters on the track edge
6434         aPrms.clear();
6435         aItN = locMeshDS->GetNodes();
6436         while ( aItN->more() ) {
6437           const SMDS_MeshNode*     pNode = aItN->next();
6438           const SMDS_EdgePosition* pEPos =
6439             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6440           double aT = pEPos->GetUParameter();
6441           aPrms.push_back( aT );
6442         }
6443         list<SMESH_MeshEditor_PathPoint> LPP;
6444         //Extrusion_Error err =
6445         makeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6446         LLPPs.push_back(LPP);
6447         UsedNums.Add(k);
6448         // update startN for search following egde
6449         if ( aN1isOK ) aVprev = aV2;
6450         else           aVprev = aV1;
6451         break;
6452       }
6453     }
6454     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6455     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6456     fullList.splice( fullList.end(), firstList );
6457
6458     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6459     fullList.pop_back();
6460     itLLPP++;
6461     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6462       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6463       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6464       gp_Dir D1 = PP1.Tangent();
6465       gp_Dir D2 = PP2.Tangent();
6466       gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6467       PP1.SetTangent(Dnew);
6468       fullList.push_back(PP1);
6469       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6470       PP1 = fullList.back();
6471       fullList.pop_back();
6472     }
6473     // if wire not closed
6474     fullList.push_back(PP1);
6475     // else ???
6476   }
6477   else {
6478     return EXTR_BAD_PATH_SHAPE;
6479   }
6480
6481   return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6482                           theHasRefPoint, theRefPoint, theMakeGroups);
6483 }
6484
6485
6486 //=======================================================================
6487 //function : makeEdgePathPoints
6488 //purpose  : auxiliary for ExtrusionAlongTrack
6489 //=======================================================================
6490 SMESH_MeshEditor::Extrusion_Error
6491 SMESH_MeshEditor::makeEdgePathPoints(std::list<double>&                aPrms,
6492                                      const TopoDS_Edge&                aTrackEdge,
6493                                      bool                              FirstIsStart,
6494                                      list<SMESH_MeshEditor_PathPoint>& LPP)
6495 {
6496   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6497   aTolVec=1.e-7;
6498   aTolVec2=aTolVec*aTolVec;
6499   double aT1, aT2;
6500   TopoDS_Vertex aV1, aV2;
6501   TopExp::Vertices( aTrackEdge, aV1, aV2 );
6502   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6503   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6504   // 2. Collect parameters on the track edge
6505   aPrms.push_front( aT1 );
6506   aPrms.push_back( aT2 );
6507   // sort parameters
6508   aPrms.sort();
6509   if( FirstIsStart ) {
6510     if ( aT1 > aT2 ) {
6511       aPrms.reverse();
6512     }
6513   }
6514   else {
6515     if ( aT2 > aT1 ) {
6516       aPrms.reverse();
6517     }
6518   }
6519   // 3. Path Points
6520   SMESH_MeshEditor_PathPoint aPP;
6521   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6522   std::list<double>::iterator aItD = aPrms.begin();
6523   for(; aItD != aPrms.end(); ++aItD) {
6524     double aT = *aItD;
6525     gp_Pnt aP3D;
6526     gp_Vec aVec;
6527     aC3D->D1( aT, aP3D, aVec );
6528     aL2 = aVec.SquareMagnitude();
6529     if ( aL2 < aTolVec2 )
6530       return EXTR_CANT_GET_TANGENT;
6531     gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6532     aPP.SetPnt( aP3D );
6533     aPP.SetTangent( aTgt );
6534     aPP.SetParameter( aT );
6535     LPP.push_back(aPP);
6536   }
6537   return EXTR_OK;
6538 }
6539
6540
6541 //=======================================================================
6542 //function : makeExtrElements
6543 //purpose  : auxiliary for ExtrusionAlongTrack
6544 //=======================================================================
6545 SMESH_MeshEditor::Extrusion_Error
6546 SMESH_MeshEditor::makeExtrElements(TIDSortedElemSet                  theElemSets[2],
6547                                    list<SMESH_MeshEditor_PathPoint>& fullList,
6548                                    const bool                        theHasAngles,
6549                                    list<double>&                     theAngles,
6550                                    const bool                        theLinearVariation,
6551                                    const bool                        theHasRefPoint,
6552                                    const gp_Pnt&                     theRefPoint,
6553                                    const bool                        theMakeGroups)
6554 {
6555   const int aNbTP = fullList.size();
6556
6557   // Angles
6558   if( theHasAngles && !theAngles.empty() && theLinearVariation )
6559     linearAngleVariation(aNbTP-1, theAngles);
6560
6561   // fill vector of path points with angles
6562   vector<SMESH_MeshEditor_PathPoint> aPPs;
6563   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6564   list<double>::iterator                 itAngles = theAngles.begin();
6565   aPPs.push_back( *itPP++ );
6566   for( ; itPP != fullList.end(); itPP++) {
6567     aPPs.push_back( *itPP );
6568     if ( theHasAngles && itAngles != theAngles.end() )
6569       aPPs.back().SetAngle( *itAngles++ );
6570   }
6571
6572   TNodeOfNodeListMap   mapNewNodes;
6573   TElemOfVecOfNnlmiMap mapElemNewNodes;
6574   TTElemOfElemListMap  newElemsMap;
6575   TIDSortedElemSet::iterator itElem;
6576   // source elements for each generated one
6577   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6578
6579   // 3. Center of rotation aV0
6580   gp_Pnt aV0 = theRefPoint;
6581   if ( !theHasRefPoint )
6582   {
6583     gp_XYZ aGC( 0.,0.,0. );
6584     TIDSortedElemSet newNodes;
6585
6586     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6587     {
6588       TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6589       itElem = theElements.begin();
6590       for ( ; itElem != theElements.end(); itElem++ )
6591       {
6592         const SMDS_MeshElement* elem = *itElem;
6593         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6594         while ( itN->more() ) {
6595           const SMDS_MeshElement* node = itN->next();
6596           if ( newNodes.insert( node ).second )
6597             aGC += SMESH_TNodeXYZ( node );
6598         }
6599       }
6600     }
6601     aGC /= newNodes.size();
6602     aV0.SetXYZ( aGC );
6603   } // if (!theHasRefPoint) {
6604
6605   // 4. Processing the elements
6606   SMESHDS_Mesh* aMesh = GetMeshDS();
6607   list<const SMDS_MeshNode*> emptyList;
6608
6609   setElemsFirst( theElemSets );
6610   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6611   {
6612     TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6613     for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6614     {
6615       const SMDS_MeshElement* elem = *itElem;
6616
6617       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6618       newNodesItVec.reserve( elem->NbNodes() );
6619
6620       // loop on elem nodes
6621       int nodeIndex = -1;
6622       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6623       while ( itN->more() )
6624       {
6625         ++nodeIndex;
6626         // check if a node has been already processed
6627         const SMDS_MeshNode* node = cast2Node( itN->next() );
6628         TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6629         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6630         if ( listNewNodes.empty() )
6631         {
6632           // make new nodes
6633           Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6634           gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6635           gp_Ax1 anAx1, anAxT1T0;
6636           gp_Dir aDT1x, aDT0x, aDT1T0;
6637
6638           aTolAng=1.e-4;
6639
6640           aV0x = aV0;
6641           aPN0 = SMESH_TNodeXYZ( node );
6642
6643           const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6644           aP0x = aPP0.Pnt();
6645           aDT0x= aPP0.Tangent();
6646
6647           for ( int j = 1; j < aNbTP; ++j ) {
6648             const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6649             aP1x     = aPP1.Pnt();
6650             aDT1x    = aPP1.Tangent();
6651             aAngle1x = aPP1.Angle();
6652
6653             gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6654             // Translation
6655             gp_Vec aV01x( aP0x, aP1x );
6656             aTrsf.SetTranslation( aV01x );
6657
6658             // traslated point
6659             aV1x = aV0x.Transformed( aTrsf );
6660             aPN1 = aPN0.Transformed( aTrsf );
6661
6662             // rotation 1 [ T1,T0 ]
6663             aAngleT1T0=-aDT1x.Angle( aDT0x );
6664             if (fabs(aAngleT1T0) > aTolAng)
6665             {
6666               aDT1T0=aDT1x^aDT0x;
6667               anAxT1T0.SetLocation( aV1x );
6668               anAxT1T0.SetDirection( aDT1T0 );
6669               aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6670
6671               aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6672             }
6673
6674             // rotation 2
6675             if ( theHasAngles ) {
6676               anAx1.SetLocation( aV1x );
6677               anAx1.SetDirection( aDT1x );
6678               aTrsfRot.SetRotation( anAx1, aAngle1x );
6679
6680               aPN1 = aPN1.Transformed( aTrsfRot );
6681             }
6682
6683             // make new node
6684             if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6685             {
6686               // create additional node
6687               gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6688               const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6689               myLastCreatedNodes.Append(newNode);
6690               srcNodes.Append( node );
6691               listNewNodes.push_back( newNode );
6692             }
6693             const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6694             myLastCreatedNodes.Append(newNode);
6695             srcNodes.Append( node );
6696             listNewNodes.push_back( newNode );
6697
6698             aPN0 = aPN1;
6699             aP0x = aP1x;
6700             aV0x = aV1x;
6701             aDT0x = aDT1x;
6702           }
6703         }
6704         else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6705         {
6706           // if current elem is quadratic and current node is not medium
6707           // we have to check - may be it is needed to insert additional nodes
6708           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6709           if ((int) listNewNodes.size() == aNbTP-1 )
6710           {
6711             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6712             gp_XYZ P(node->X(), node->Y(), node->Z());
6713             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6714             int i;
6715             for(i=0; i<aNbTP-1; i++) {
6716               const SMDS_MeshNode* N = *it;
6717               double x = ( N->X() + P.X() )/2.;
6718               double y = ( N->Y() + P.Y() )/2.;
6719               double z = ( N->Z() + P.Z() )/2.;
6720               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6721               srcNodes.Append( node );
6722               myLastCreatedNodes.Append(newN);
6723               aNodes[2*i] = newN;
6724               aNodes[2*i+1] = N;
6725               P = gp_XYZ(N->X(),N->Y(),N->Z());
6726             }
6727             listNewNodes.clear();
6728             for(i=0; i<2*(aNbTP-1); i++) {
6729               listNewNodes.push_back(aNodes[i]);
6730             }
6731           }
6732         }
6733
6734         newNodesItVec.push_back( nIt );
6735       }
6736
6737       // make new elements
6738       sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6739     }
6740   }
6741
6742   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6743
6744   if ( theMakeGroups )
6745     generateGroups( srcNodes, srcElems, "extruded");
6746
6747   return EXTR_OK;
6748 }
6749
6750
6751 //=======================================================================
6752 //function : linearAngleVariation
6753 //purpose  : spread values over nbSteps
6754 //=======================================================================
6755
6756 void SMESH_MeshEditor::linearAngleVariation(const int     nbSteps,
6757                                             list<double>& Angles)
6758 {
6759   int nbAngles = Angles.size();
6760   if( nbSteps > nbAngles && nbAngles > 0 )
6761   {
6762     vector<double> theAngles(nbAngles);
6763     theAngles.assign( Angles.begin(), Angles.end() );
6764
6765     list<double> res;
6766     double rAn2St = double( nbAngles ) / double( nbSteps );
6767     double angPrev = 0, angle;
6768     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6769     {
6770       double angCur = rAn2St * ( iSt+1 );
6771       double angCurFloor  = floor( angCur );
6772       double angPrevFloor = floor( angPrev );
6773       if ( angPrevFloor == angCurFloor )
6774         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6775       else {
6776         int iP = int( angPrevFloor );
6777         double angPrevCeil = ceil(angPrev);
6778         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6779
6780         int iC = int( angCurFloor );
6781         if ( iC < nbAngles )
6782           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6783
6784         iP = int( angPrevCeil );
6785         while ( iC-- > iP )
6786           angle += theAngles[ iC ];
6787       }
6788       res.push_back(angle);
6789       angPrev = angCur;
6790     }
6791     Angles.swap( res );
6792   }
6793 }
6794
6795
6796 //================================================================================
6797 /*!
6798  * \brief Move or copy theElements applying theTrsf to their nodes
6799  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6800  *  \param theTrsf - transformation to apply
6801  *  \param theCopy - if true, create translated copies of theElems
6802  *  \param theMakeGroups - if true and theCopy, create translated groups
6803  *  \param theTargetMesh - mesh to copy translated elements into
6804  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6805  */
6806 //================================================================================
6807
6808 SMESH_MeshEditor::PGroupIDs
6809 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6810                              const gp_Trsf&     theTrsf,
6811                              const bool         theCopy,
6812                              const bool         theMakeGroups,
6813                              SMESH_Mesh*        theTargetMesh)
6814 {
6815   myLastCreatedElems.Clear();
6816   myLastCreatedNodes.Clear();
6817
6818   bool needReverse = false;
6819   string groupPostfix;
6820   switch ( theTrsf.Form() ) {
6821   case gp_PntMirror:
6822     needReverse = true;
6823     groupPostfix = "mirrored";
6824     break;
6825   case gp_Ax1Mirror:
6826     groupPostfix = "mirrored";
6827     break;
6828   case gp_Ax2Mirror:
6829     needReverse = true;
6830     groupPostfix = "mirrored";
6831     break;
6832   case gp_Rotation:
6833     groupPostfix = "rotated";
6834     break;
6835   case gp_Translation:
6836     groupPostfix = "translated";
6837     break;
6838   case gp_Scale:
6839     groupPostfix = "scaled";
6840     break;
6841   case gp_CompoundTrsf: // different scale by axis
6842     groupPostfix = "scaled";
6843     break;
6844   default:
6845     needReverse = false;
6846     groupPostfix = "transformed";
6847   }
6848
6849   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6850   SMESHDS_Mesh* aMesh    = GetMeshDS();
6851
6852   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6853   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6854   SMESH_MeshEditor::ElemFeatures elemType;
6855
6856   // map old node to new one
6857   TNodeNodeMap nodeMap;
6858
6859   // elements sharing moved nodes; those of them which have all
6860   // nodes mirrored but are not in theElems are to be reversed
6861   TIDSortedElemSet inverseElemSet;
6862
6863   // source elements for each generated one
6864   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6865
6866   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6867   TIDSortedElemSet orphanNode;
6868
6869   if ( theElems.empty() ) // transform the whole mesh
6870   {
6871     // add all elements
6872     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6873     while ( eIt->more() ) theElems.insert( eIt->next() );
6874     // add orphan nodes
6875     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6876     while ( nIt->more() )
6877     {
6878       const SMDS_MeshNode* node = nIt->next();
6879       if ( node->NbInverseElements() == 0)
6880         orphanNode.insert( node );
6881     }
6882   }
6883
6884   // loop on elements to transform nodes : first orphan nodes then elems
6885   TIDSortedElemSet::iterator itElem;
6886   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6887   for (int i=0; i<2; i++)
6888     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6889     {
6890       const SMDS_MeshElement* elem = *itElem;
6891       if ( !elem )
6892         continue;
6893
6894       // loop on elem nodes
6895       double coord[3];
6896       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6897       while ( itN->more() )
6898       {
6899         const SMDS_MeshNode* node = cast2Node( itN->next() );
6900         // check if a node has been already transformed
6901         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6902           nodeMap.insert( make_pair ( node, node ));
6903         if ( !n2n_isnew.second )
6904           continue;
6905
6906         node->GetXYZ( coord );
6907         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6908         if ( theTargetMesh ) {
6909           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6910           n2n_isnew.first->second = newNode;
6911           myLastCreatedNodes.Append(newNode);
6912           srcNodes.Append( node );
6913         }
6914         else if ( theCopy ) {
6915           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6916           n2n_isnew.first->second = newNode;
6917           myLastCreatedNodes.Append(newNode);
6918           srcNodes.Append( node );
6919         }
6920         else {
6921           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6922           // node position on shape becomes invalid
6923           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6924             ( SMDS_SpacePosition::originSpacePosition() );
6925         }
6926
6927         // keep inverse elements
6928         if ( !theCopy && !theTargetMesh && needReverse ) {
6929           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6930           while ( invElemIt->more() ) {
6931             const SMDS_MeshElement* iel = invElemIt->next();
6932             inverseElemSet.insert( iel );
6933           }
6934         }
6935       }
6936     } // loop on elems in { &orphanNode, &theElems };
6937
6938   // either create new elements or reverse mirrored ones
6939   if ( !theCopy && !needReverse && !theTargetMesh )
6940     return PGroupIDs();
6941
6942   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6943
6944   // Replicate or reverse elements
6945
6946   std::vector<int> iForw;
6947   vector<const SMDS_MeshNode*> nodes;
6948   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6949   {
6950     const SMDS_MeshElement* elem = *itElem;
6951     if ( !elem ) continue;
6952
6953     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6954     size_t               nbNodes  = elem->NbNodes();
6955     if ( geomType == SMDSGeom_NONE ) continue; // node
6956
6957     nodes.resize( nbNodes );
6958
6959     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6960     {
6961       const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6962       if (!aPolyedre)
6963         continue;
6964       nodes.clear();
6965       bool allTransformed = true;
6966       int nbFaces = aPolyedre->NbFaces();
6967       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6968       {
6969         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6970         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6971         {
6972           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6973           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6974           if ( nodeMapIt == nodeMap.end() )
6975             allTransformed = false; // not all nodes transformed
6976           else
6977             nodes.push_back((*nodeMapIt).second);
6978         }
6979         if ( needReverse && allTransformed )
6980           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6981       }
6982       if ( !allTransformed )
6983         continue; // not all nodes transformed
6984     }
6985     else // ----------------------- the rest element types
6986     {
6987       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6988       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6989       const vector<int>&    i = needReverse ? iRev : iForw;
6990
6991       // find transformed nodes
6992       size_t iNode = 0;
6993       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6994       while ( itN->more() ) {
6995         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6996         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6997         if ( nodeMapIt == nodeMap.end() )
6998           break; // not all nodes transformed
6999         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
7000       }
7001       if ( iNode != nbNodes )
7002         continue; // not all nodes transformed
7003     }
7004
7005     if ( editor ) {
7006       // copy in this or a new mesh
7007       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
7008         srcElems.Append( elem );
7009     }
7010     else {
7011       // reverse element as it was reversed by transformation
7012       if ( nbNodes > 2 )
7013         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
7014     }
7015
7016   } // loop on elements
7017
7018   if ( editor && editor != this )
7019     myLastCreatedElems = editor->myLastCreatedElems;
7020
7021   PGroupIDs newGroupIDs;
7022
7023   if ( ( theMakeGroups && theCopy ) ||
7024        ( theMakeGroups && theTargetMesh ) )
7025     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
7026
7027   return newGroupIDs;
7028 }
7029
7030 //=======================================================================
7031 /*!
7032  * \brief Create groups of elements made during transformation
7033  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
7034  *  \param elemGens - elements making corresponding myLastCreatedElems
7035  *  \param postfix - to append to names of new groups
7036  *  \param targetMesh - mesh to create groups in
7037  *  \param topPresent - is there "top" elements that are created by sweeping
7038  */
7039 //=======================================================================
7040
7041 SMESH_MeshEditor::PGroupIDs
7042 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
7043                                  const SMESH_SequenceOfElemPtr& elemGens,
7044                                  const std::string&             postfix,
7045                                  SMESH_Mesh*                    targetMesh,
7046                                  const bool                     topPresent)
7047 {
7048   PGroupIDs newGroupIDs( new list<int> );
7049   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
7050
7051   // Sort existing groups by types and collect their names
7052
7053   // containers to store an old group and generated new ones;
7054   // 1st new group is for result elems of different type than a source one;
7055   // 2nd new group is for same type result elems ("top" group at extrusion)
7056   using boost::tuple;
7057   using boost::make_tuple;
7058   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
7059   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
7060   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
7061   // group names
7062   set< string > groupNames;
7063
7064   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7065   if ( !groupIt->more() ) return newGroupIDs;
7066
7067   int newGroupID = mesh->GetGroupIds().back()+1;
7068   while ( groupIt->more() )
7069   {
7070     SMESH_Group * group = groupIt->next();
7071     if ( !group ) continue;
7072     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7073     if ( !groupDS || groupDS->IsEmpty() ) continue;
7074     groupNames.insert    ( group->GetName() );
7075     groupDS->SetStoreName( group->GetName() );
7076     const SMDSAbs_ElementType type = groupDS->GetType();
7077     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7078     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7079     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7080     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7081   }
7082
7083   // Loop on nodes and elements to add them in new groups
7084
7085   vector< const SMDS_MeshElement* > resultElems;
7086   for ( int isNodes = 0; isNodes < 2; ++isNodes )
7087   {
7088     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
7089     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7090     if ( gens.Length() != elems.Length() )
7091       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7092
7093     // loop on created elements
7094     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7095     {
7096       const SMDS_MeshElement* sourceElem = gens( iElem );
7097       if ( !sourceElem ) {
7098         MESSAGE("generateGroups(): NULL source element");
7099         continue;
7100       }
7101       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7102       if ( groupsOldNew.empty() ) { // no groups of this type at all
7103         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7104           ++iElem; // skip all elements made by sourceElem
7105         continue;
7106       }
7107       // collect all elements made by the iElem-th sourceElem
7108       resultElems.clear();
7109       if ( const SMDS_MeshElement* resElem = elems( iElem ))
7110         if ( resElem != sourceElem )
7111           resultElems.push_back( resElem );
7112       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7113         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7114           if ( resElem != sourceElem )
7115             resultElems.push_back( resElem );
7116
7117       const SMDS_MeshElement* topElem = 0;
7118       if ( isNodes ) // there must be a top element
7119       {
7120         topElem = resultElems.back();
7121         resultElems.pop_back();
7122       }
7123       else
7124       {
7125         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7126         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7127           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7128           {
7129             topElem = *resElemIt;
7130             *resElemIt = 0; // erase *resElemIt
7131             break;
7132           }
7133       }
7134       // add resultElems to groups originted from ones the sourceElem belongs to
7135       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7136       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7137       {
7138         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7139         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7140         {
7141           // fill in a new group
7142           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7143           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7144           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7145             if ( *resElemIt )
7146               newGroup.Add( *resElemIt );
7147
7148           // fill a "top" group
7149           if ( topElem )
7150           {
7151             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7152             newTopGroup.Add( topElem );
7153          }
7154         }
7155       }
7156     } // loop on created elements
7157   }// loop on nodes and elements
7158
7159   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7160
7161   list<int> topGrouIds;
7162   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7163   {
7164     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
7165     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7166                                       orderedOldNewGroups[i]->get<2>() };
7167     for ( int is2nd = 0; is2nd < 2; ++is2nd )
7168     {
7169       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7170       if ( newGroupDS->IsEmpty() )
7171       {
7172         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7173       }
7174       else
7175       {
7176         // set group type
7177         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7178
7179         // make a name
7180         const bool isTop = ( topPresent &&
7181                              newGroupDS->GetType() == oldGroupDS->GetType() &&
7182                              is2nd );
7183
7184         string name = oldGroupDS->GetStoreName();
7185         { // remove trailing whitespaces (issue 22599)
7186           size_t size = name.size();
7187           while ( size > 1 && isspace( name[ size-1 ]))
7188             --size;
7189           if ( size != name.size() )
7190           {
7191             name.resize( size );
7192             oldGroupDS->SetStoreName( name.c_str() );
7193           }
7194         }
7195         if ( !targetMesh ) {
7196           string suffix = ( isTop ? "top": postfix.c_str() );
7197           name += "_";
7198           name += suffix;
7199           int nb = 1;
7200           while ( !groupNames.insert( name ).second ) // name exists
7201             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7202         }
7203         else if ( isTop ) {
7204           name += "_top";
7205         }
7206         newGroupDS->SetStoreName( name.c_str() );
7207
7208         // make a SMESH_Groups
7209         mesh->AddGroup( newGroupDS );
7210         if ( isTop )
7211           topGrouIds.push_back( newGroupDS->GetID() );
7212         else
7213           newGroupIDs->push_back( newGroupDS->GetID() );
7214       }
7215     }
7216   }
7217   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7218
7219   return newGroupIDs;
7220 }
7221
7222 //================================================================================
7223 /*!
7224  *  * \brief Return list of group of nodes close to each other within theTolerance
7225  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
7226  *  *        an Octree algorithm
7227  *  \param [in,out] theNodes - the nodes to treat
7228  *  \param [in]     theTolerance - the tolerance
7229  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
7230  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
7231  *         corner and medium nodes in separate groups
7232  */
7233 //================================================================================
7234
7235 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
7236                                             const double         theTolerance,
7237                                             TListOfListOfNodes & theGroupsOfNodes,
7238                                             bool                 theSeparateCornersAndMedium)
7239 {
7240   myLastCreatedElems.Clear();
7241   myLastCreatedNodes.Clear();
7242
7243   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
7244        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
7245        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7246     theSeparateCornersAndMedium = false;
7247
7248   TIDSortedNodeSet& corners = theNodes;
7249   TIDSortedNodeSet  medium;
7250
7251   if ( theNodes.empty() ) // get all nodes in the mesh
7252   {
7253     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7254     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7255     if ( theSeparateCornersAndMedium )
7256       while ( nIt->more() )
7257       {
7258         const SMDS_MeshNode* n = nIt->next();
7259         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7260         nodeSet->insert( nodeSet->end(), n );
7261       }
7262     else
7263       while ( nIt->more() )
7264         theNodes.insert( theNodes.end(), nIt->next() );
7265   }
7266   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7267   {
7268     TIDSortedNodeSet::iterator nIt = corners.begin();
7269     while ( nIt != corners.end() )
7270       if ( SMESH_MesherHelper::IsMedium( *nIt ))
7271       {
7272         medium.insert( medium.end(), *nIt );
7273         corners.erase( nIt++ );
7274       }
7275       else
7276       {
7277         ++nIt;
7278       }
7279   }
7280
7281   if ( !corners.empty() )
7282     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7283   if ( !medium.empty() )
7284     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7285 }
7286
7287 //=======================================================================
7288 //function : SimplifyFace
7289 //purpose  : split a chain of nodes into several closed chains
7290 //=======================================================================
7291
7292 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7293                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7294                                     vector<int>&                         quantities) const
7295 {
7296   int nbNodes = faceNodes.size();
7297   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7298     --nbNodes;
7299   if ( nbNodes < 3 )
7300     return 0;
7301   size_t prevNbQuant = quantities.size();
7302
7303   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7304   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7305   map< const SMDS_MeshNode*, int >::iterator nInd;
7306
7307   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7308   simpleNodes.push_back( faceNodes[0] );
7309   for ( int iCur = 1; iCur < nbNodes; iCur++ )
7310   {
7311     if ( faceNodes[ iCur ] != simpleNodes.back() )
7312     {
7313       int index = simpleNodes.size();
7314       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7315       int prevIndex = nInd->second;
7316       if ( prevIndex < index )
7317       {
7318         // a sub-loop found
7319         int loopLen = index - prevIndex;
7320         if ( loopLen > 2 )
7321         {
7322           // store the sub-loop
7323           quantities.push_back( loopLen );
7324           for ( int i = prevIndex; i < index; i++ )
7325             poly_nodes.push_back( simpleNodes[ i ]);
7326         }
7327         simpleNodes.resize( prevIndex+1 );
7328       }
7329       else
7330       {
7331         simpleNodes.push_back( faceNodes[ iCur ]);
7332       }
7333     }
7334   }
7335
7336   if ( simpleNodes.size() > 2 )
7337   {
7338     quantities.push_back( simpleNodes.size() );
7339     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7340   }
7341
7342   return quantities.size() - prevNbQuant;
7343 }
7344
7345 //=======================================================================
7346 //function : MergeNodes
7347 //purpose  : In each group, the cdr of nodes are substituted by the first one
7348 //           in all elements.
7349 //=======================================================================
7350
7351 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7352                                    const bool           theAvoidMakingHoles)
7353 {
7354   myLastCreatedElems.Clear();
7355   myLastCreatedNodes.Clear();
7356
7357   SMESHDS_Mesh* mesh = GetMeshDS();
7358
7359   TNodeNodeMap nodeNodeMap; // node to replace - new node
7360   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7361   list< int > rmElemIds, rmNodeIds;
7362   vector< ElemFeatures > newElemDefs;
7363
7364   // Fill nodeNodeMap and elems
7365
7366   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7367   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7368   {
7369     list<const SMDS_MeshNode*>& nodes = *grIt;
7370     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7371     const SMDS_MeshNode* nToKeep = *nIt;
7372     for ( ++nIt; nIt != nodes.end(); nIt++ )
7373     {
7374       const SMDS_MeshNode* nToRemove = *nIt;
7375       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7376       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7377       while ( invElemIt->more() ) {
7378         const SMDS_MeshElement* elem = invElemIt->next();
7379         elems.insert(elem);
7380       }
7381     }
7382   }
7383
7384   // Apply recursive replacements (BUG 0020185)
7385   TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7386   for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7387   {
7388     const SMDS_MeshNode* nToKeep = nnIt->second;
7389     TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7390     while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7391       nToKeep = nnIt_i->second;
7392     nnIt->second = nToKeep;
7393   }
7394
7395   if ( theAvoidMakingHoles )
7396   {
7397     // find elements whose topology changes
7398
7399     vector<const SMDS_MeshElement*> pbElems;
7400     set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7401     for ( ; eIt != elems.end(); ++eIt )
7402     {
7403       const SMDS_MeshElement* elem = *eIt;
7404       SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
7405       while ( itN->more() )
7406       {
7407         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7408         TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7409         if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7410         {
7411           // several nodes of elem stick
7412           pbElems.push_back( elem );
7413           break;
7414         }
7415       }
7416     }
7417     // exclude from merge nodes causing spoiling element
7418     for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7419     {
7420       bool nodesExcluded = false;
7421       for ( size_t i = 0; i < pbElems.size(); ++i )
7422       {
7423         size_t prevNbMergeNodes = nodeNodeMap.size();
7424         if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7425              prevNbMergeNodes < nodeNodeMap.size() )
7426           nodesExcluded = true;
7427       }
7428       if ( !nodesExcluded )
7429         break;
7430     }
7431   }
7432
7433   for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7434   {
7435     const SMDS_MeshNode* nToRemove = nnIt->first;
7436     const SMDS_MeshNode* nToKeep   = nnIt->second;
7437     if ( nToRemove != nToKeep )
7438     {
7439       rmNodeIds.push_back( nToRemove->GetID() );
7440       AddToSameGroups( nToKeep, nToRemove, mesh );
7441       // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7442       // w/o creating node in place of merged ones.
7443       const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7444       if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7445         if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7446           sm->SetIsAlwaysComputed( true );
7447     }
7448   }
7449
7450   // Change element nodes or remove an element
7451
7452   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7453   for ( ; eIt != elems.end(); eIt++ )
7454   {
7455     const SMDS_MeshElement* elem = *eIt;
7456     SMESHDS_SubMesh*          sm = mesh->MeshElements( elem->getshapeId() );
7457
7458     bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7459     if ( !keepElem )
7460       rmElemIds.push_back( elem->GetID() );
7461
7462     for ( size_t i = 0; i < newElemDefs.size(); ++i )
7463     {
7464       if ( i > 0 || !mesh->ChangeElementNodes( elem,
7465                                                & newElemDefs[i].myNodes[0],
7466                                                newElemDefs[i].myNodes.size() ))
7467       {
7468         if ( i == 0 )
7469         {
7470           newElemDefs[i].SetID( elem->GetID() );
7471           mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7472           if ( !keepElem ) rmElemIds.pop_back();
7473         }
7474         else
7475         {
7476           newElemDefs[i].SetID( -1 );
7477         }
7478         SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7479         if ( sm && newElem )
7480           sm->AddElement( newElem );
7481         if ( elem != newElem )
7482           ReplaceElemInGroups( elem, newElem, mesh );
7483       }
7484     }
7485   }
7486
7487   // Remove bad elements, then equal nodes (order important)
7488   Remove( rmElemIds, /*isNodes=*/false );
7489   Remove( rmNodeIds, /*isNodes=*/true );
7490
7491   return;
7492 }
7493
7494 //=======================================================================
7495 //function : applyMerge
7496 //purpose  : Compute new connectivity of an element after merging nodes
7497 //  \param [in] elems - the element
7498 //  \param [out] newElemDefs - definition(s) of result element(s)
7499 //  \param [inout] nodeNodeMap - nodes to merge
7500 //  \param [in] avoidMakingHoles - if true and and the element becomes invalid
7501 //              after merging (but not degenerated), removes nodes causing
7502 //              the invalidity from \a nodeNodeMap.
7503 //  \return bool - true if the element should be removed
7504 //=======================================================================
7505
7506 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7507                                    vector< ElemFeatures >& newElemDefs,
7508                                    TNodeNodeMap&           nodeNodeMap,
7509                                    const bool              avoidMakingHoles )
7510 {
7511   bool toRemove = false; // to remove elem
7512   int nbResElems = 1;    // nb new elements
7513
7514   newElemDefs.resize(nbResElems);
7515   newElemDefs[0].Init( elem );
7516   newElemDefs[0].myNodes.clear();
7517
7518   set<const SMDS_MeshNode*> nodeSet;
7519   vector< const SMDS_MeshNode*>   curNodes;
7520   vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7521   vector<int> iRepl;
7522
7523   const        int  nbNodes = elem->NbNodes();
7524   SMDSAbs_EntityType entity = elem->GetEntityType();
7525
7526   curNodes.resize( nbNodes );
7527   uniqueNodes.resize( nbNodes );
7528   iRepl.resize( nbNodes );
7529   int iUnique = 0, iCur = 0, nbRepl = 0;
7530
7531   // Get new seq of nodes
7532
7533   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7534   while ( itN->more() )
7535   {
7536     const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7537
7538     TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7539     if ( nnIt != nodeNodeMap.end() ) {
7540       n = (*nnIt).second;
7541     }
7542     curNodes[ iCur ] = n;
7543     bool isUnique = nodeSet.insert( n ).second;
7544     if ( isUnique )
7545       uniqueNodes[ iUnique++ ] = n;
7546     else
7547       iRepl[ nbRepl++ ] = iCur;
7548     iCur++;
7549   }
7550
7551   // Analyse element topology after replacement
7552
7553   int nbUniqueNodes = nodeSet.size();
7554   if ( nbNodes != nbUniqueNodes ) // some nodes stick
7555   {
7556     toRemove = true;
7557     nbResElems = 0;
7558
7559     if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7560     {
7561       // if corner nodes stick, remove medium nodes between them from uniqueNodes
7562       int nbCorners = nbNodes / 2;
7563       for ( int iCur = 0; iCur < nbCorners; ++iCur )
7564       {
7565         int iNext = ( iCur + 1 ) % nbCorners;
7566         if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7567         {
7568           int iMedium = iCur + nbCorners;
7569           vector< const SMDS_MeshNode* >::iterator i =
7570             std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7571                        uniqueNodes.end(),
7572                        curNodes[ iMedium ]);
7573           if ( i != uniqueNodes.end() )
7574           {
7575             --nbUniqueNodes;
7576             for ( ; i+1 != uniqueNodes.end(); ++i )
7577               *i = *(i+1);
7578           }
7579         }
7580       }
7581     }
7582
7583     switch ( entity )
7584     {
7585     case SMDSEntity_Polygon:
7586     case SMDSEntity_Quad_Polygon: // Polygon
7587     {
7588       ElemFeatures* elemType = & newElemDefs[0];
7589       const bool isQuad = elemType->myIsQuad;
7590       if ( isQuad )
7591         SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7592           ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7593
7594       // a polygon can divide into several elements
7595       vector<const SMDS_MeshNode *> polygons_nodes;
7596       vector<int> quantities;
7597       nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7598       newElemDefs.resize( nbResElems );
7599       for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7600       {
7601         ElemFeatures* elemType = & newElemDefs[iface];
7602         if ( iface ) elemType->Init( elem );
7603
7604         vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7605         int nbNewNodes = quantities[iface];
7606         face_nodes.assign( polygons_nodes.begin() + inode,
7607                            polygons_nodes.begin() + inode + nbNewNodes );
7608         inode += nbNewNodes;
7609         if ( isQuad ) // check if a result elem is a valid quadratic polygon
7610         {
7611           bool isValid = ( nbNewNodes % 2 == 0 );
7612           for ( int i = 0; i < nbNewNodes && isValid; ++i )
7613             isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7614           elemType->SetQuad( isValid );
7615           if ( isValid ) // put medium nodes after corners
7616             SMDS_MeshCell::applyInterlaceRev
7617               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7618                                                     nbNewNodes ), face_nodes );
7619         }
7620         elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7621       }
7622       nbUniqueNodes = newElemDefs[0].myNodes.size();
7623       break;
7624     } // Polygon
7625
7626     case SMDSEntity_Polyhedra: // Polyhedral volume
7627     {
7628       if ( nbUniqueNodes >= 4 )
7629       {
7630         // each face has to be analyzed in order to check volume validity
7631         if ( const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem ))
7632         {
7633           int nbFaces = aPolyedre->NbFaces();
7634
7635           vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7636           vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7637           vector<const SMDS_MeshNode *>  faceNodes;
7638           poly_nodes.clear();
7639           quantities.clear();
7640
7641           for (int iface = 1; iface <= nbFaces; iface++)
7642           {
7643             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7644             faceNodes.resize( nbFaceNodes );
7645             for (int inode = 1; inode <= nbFaceNodes; inode++)
7646             {
7647               const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7648               TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7649               if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7650                 faceNode = (*nnIt).second;
7651               faceNodes[inode - 1] = faceNode;
7652             }
7653             SimplifyFace(faceNodes, poly_nodes, quantities);
7654           }
7655
7656           if ( quantities.size() > 3 )
7657           {
7658             // TODO: remove coincident faces
7659             nbResElems = 1;
7660             nbUniqueNodes = newElemDefs[0].myNodes.size();
7661           }
7662         }
7663       }
7664     }
7665     break;
7666
7667     // Regular elements
7668     // TODO not all the possible cases are solved. Find something more generic?
7669     case SMDSEntity_Edge: //////// EDGE
7670     case SMDSEntity_Triangle: //// TRIANGLE
7671     case SMDSEntity_Quad_Triangle:
7672     case SMDSEntity_Tetra:
7673     case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7674     {
7675       break;
7676     }
7677     case SMDSEntity_Quad_Edge:
7678     {
7679       break;
7680     }
7681     case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7682     {
7683       if ( nbUniqueNodes < 3 )
7684         toRemove = true;
7685       else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7686         toRemove = true; // opposite nodes stick
7687       else
7688         toRemove = false;
7689       break;
7690     }
7691     case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7692     {
7693       //   1    5    2
7694       //    +---+---+
7695       //    |       |
7696       //   4+       +6
7697       //    |       |
7698       //    +---+---+
7699       //   0    7    3
7700       if ( nbUniqueNodes == 6 &&
7701            iRepl[0] < 4       &&
7702            ( nbRepl == 1 || iRepl[1] >= 4 ))
7703       {
7704         toRemove = false;
7705       }
7706       break;
7707     }
7708     case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7709     {
7710       //   1    5    2
7711       //    +---+---+
7712       //    |       |
7713       //   4+  8+   +6
7714       //    |       |
7715       //    +---+---+
7716       //   0    7    3
7717       if ( nbUniqueNodes == 7 &&
7718            iRepl[0] < 4       &&
7719            ( nbRepl == 1 || iRepl[1] != 8 ))
7720       {
7721         toRemove = false;
7722       }
7723       break;
7724     }
7725     case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7726     {
7727       if ( nbUniqueNodes == 4 ) {
7728         // ---------------------------------> tetrahedron
7729         if ( curNodes[3] == curNodes[4] &&
7730              curNodes[3] == curNodes[5] ) {
7731           // top nodes stick
7732           toRemove = false;
7733         }
7734         else if ( curNodes[0] == curNodes[1] &&
7735                   curNodes[0] == curNodes[2] ) {
7736           // bottom nodes stick: set a top before
7737           uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7738           uniqueNodes[ 0 ] = curNodes [ 5 ];
7739           uniqueNodes[ 1 ] = curNodes [ 4 ];
7740           uniqueNodes[ 2 ] = curNodes [ 3 ];
7741           toRemove = false;
7742         }
7743         else if (( curNodes[0] == curNodes[3] ) +
7744                  ( curNodes[1] == curNodes[4] ) +
7745                  ( curNodes[2] == curNodes[5] ) == 2 ) {
7746           // a lateral face turns into a line
7747           toRemove = false;
7748         }
7749       }
7750       else if ( nbUniqueNodes == 5 ) {
7751         // PENTAHEDRON --------------------> pyramid
7752         if ( curNodes[0] == curNodes[3] )
7753         {
7754           uniqueNodes[ 0 ] = curNodes[ 1 ];
7755           uniqueNodes[ 1 ] = curNodes[ 4 ];
7756           uniqueNodes[ 2 ] = curNodes[ 5 ];
7757           uniqueNodes[ 3 ] = curNodes[ 2 ];
7758           uniqueNodes[ 4 ] = curNodes[ 0 ];
7759           toRemove = false;
7760         }
7761         if ( curNodes[1] == curNodes[4] )
7762         {
7763           uniqueNodes[ 0 ] = curNodes[ 0 ];
7764           uniqueNodes[ 1 ] = curNodes[ 2 ];
7765           uniqueNodes[ 2 ] = curNodes[ 5 ];
7766           uniqueNodes[ 3 ] = curNodes[ 3 ];
7767           uniqueNodes[ 4 ] = curNodes[ 1 ];
7768           toRemove = false;
7769         }
7770         if ( curNodes[2] == curNodes[5] )
7771         {
7772           uniqueNodes[ 0 ] = curNodes[ 0 ];
7773           uniqueNodes[ 1 ] = curNodes[ 3 ];
7774           uniqueNodes[ 2 ] = curNodes[ 4 ];
7775           uniqueNodes[ 3 ] = curNodes[ 1 ];
7776           uniqueNodes[ 4 ] = curNodes[ 2 ];
7777           toRemove = false;
7778         }
7779       }
7780       break;
7781     }
7782     case SMDSEntity_Hexa:
7783     {
7784       //////////////////////////////////// HEXAHEDRON
7785       SMDS_VolumeTool hexa (elem);
7786       hexa.SetExternalNormal();
7787       if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7788         //////////////////////// HEX ---> tetrahedron
7789         for ( int iFace = 0; iFace < 6; iFace++ ) {
7790           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7791           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7792               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7793               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7794             // one face turns into a point ...
7795             int  pickInd = ind[ 0 ];
7796             int iOppFace = hexa.GetOppFaceIndex( iFace );
7797             ind = hexa.GetFaceNodesIndices( iOppFace );
7798             int nbStick = 0;
7799             uniqueNodes.clear();
7800             for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7801               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7802                 nbStick++;
7803               else
7804                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7805             }
7806             if ( nbStick == 1 ) {
7807               // ... and the opposite one - into a triangle.
7808               // set a top node
7809               uniqueNodes.push_back( curNodes[ pickInd ]);
7810               toRemove = false;
7811             }
7812             break;
7813           }
7814         }
7815       }
7816       else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7817         //////////////////////// HEX ---> prism
7818         int nbTria = 0, iTria[3];
7819         const int *ind; // indices of face nodes
7820         // look for triangular faces
7821         for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7822           ind = hexa.GetFaceNodesIndices( iFace );
7823           TIDSortedNodeSet faceNodes;
7824           for ( iCur = 0; iCur < 4; iCur++ )
7825             faceNodes.insert( curNodes[ind[iCur]] );
7826           if ( faceNodes.size() == 3 )
7827             iTria[ nbTria++ ] = iFace;
7828         }
7829         // check if triangles are opposite
7830         if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7831         {
7832           // set nodes of the bottom triangle
7833           ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7834           vector<int> indB;
7835           for ( iCur = 0; iCur < 4; iCur++ )
7836             if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7837               indB.push_back( ind[iCur] );
7838           if ( !hexa.IsForward() )
7839             std::swap( indB[0], indB[2] );
7840           for ( iCur = 0; iCur < 3; iCur++ )
7841             uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7842           // set nodes of the top triangle
7843           const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7844           for ( iCur = 0; iCur < 3; ++iCur )
7845             for ( int j = 0; j < 4; ++j )
7846               if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7847               {
7848                 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7849                 break;
7850               }
7851           toRemove = false;
7852           break;
7853         }
7854       }
7855       else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7856         //////////////////// HEXAHEDRON ---> pyramid
7857         for ( int iFace = 0; iFace < 6; iFace++ ) {
7858           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7859           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7860               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7861               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7862             // one face turns into a point ...
7863             int iOppFace = hexa.GetOppFaceIndex( iFace );
7864             ind = hexa.GetFaceNodesIndices( iOppFace );
7865             uniqueNodes.clear();
7866             for ( iCur = 0; iCur < 4; iCur++ ) {
7867               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7868                 break;
7869               else
7870                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7871             }
7872             if ( uniqueNodes.size() == 4 ) {
7873               // ... and the opposite one is a quadrangle
7874               // set a top node
7875               const int* indTop = hexa.GetFaceNodesIndices( iFace );
7876               uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7877               toRemove = false;
7878             }
7879             break;
7880           }
7881         }
7882       }
7883
7884       if ( toRemove && nbUniqueNodes > 4 ) {
7885         ////////////////// HEXAHEDRON ---> polyhedron
7886         hexa.SetExternalNormal();
7887         vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7888         vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7889         poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7890         quantities.reserve( 6 );     quantities.clear();
7891         for ( int iFace = 0; iFace < 6; iFace++ )
7892         {
7893           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7894           if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7895                curNodes[ind[1]] == curNodes[ind[3]] )
7896           {
7897             quantities.clear();
7898             break; // opposite nodes stick
7899           }
7900           nodeSet.clear();
7901           for ( iCur = 0; iCur < 4; iCur++ )
7902           {
7903             if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7904               poly_nodes.push_back( curNodes[ind[ iCur ]]);
7905           }
7906           if ( nodeSet.size() < 3 )
7907             poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7908           else
7909             quantities.push_back( nodeSet.size() );
7910         }
7911         if ( quantities.size() >= 4 )
7912         {
7913           nbResElems = 1;
7914           nbUniqueNodes = poly_nodes.size();
7915           newElemDefs[0].SetPoly(true);
7916         }
7917       }
7918       break;
7919     } // case HEXAHEDRON
7920
7921     default:
7922       toRemove = true;
7923
7924     } // switch ( entity )
7925
7926     if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7927     {
7928       // erase from nodeNodeMap nodes whose merge spoils elem
7929       vector< const SMDS_MeshNode* > noMergeNodes;
7930       SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7931       for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7932         nodeNodeMap.erase( noMergeNodes[i] );
7933     }
7934     
7935   } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7936
7937   uniqueNodes.resize( nbUniqueNodes );
7938
7939   if ( !toRemove && nbResElems == 0 )
7940     nbResElems = 1;
7941
7942   newElemDefs.resize( nbResElems );
7943
7944   return !toRemove;
7945 }
7946
7947
7948 // ========================================================
7949 // class   : SortableElement
7950 // purpose : allow sorting elements basing on their nodes
7951 // ========================================================
7952 class SortableElement : public set <const SMDS_MeshElement*>
7953 {
7954 public:
7955
7956   SortableElement( const SMDS_MeshElement* theElem )
7957   {
7958     myElem = theElem;
7959     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7960     while ( nodeIt->more() )
7961       this->insert( nodeIt->next() );
7962   }
7963
7964   const SMDS_MeshElement* Get() const
7965   { return myElem; }
7966
7967 private:
7968   mutable const SMDS_MeshElement* myElem;
7969 };
7970
7971 //=======================================================================
7972 //function : FindEqualElements
7973 //purpose  : Return list of group of elements built on the same nodes.
7974 //           Search among theElements or in the whole mesh if theElements is empty
7975 //=======================================================================
7976
7977 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
7978                                          TListOfListOfElementsID & theGroupsOfElementsID)
7979 {
7980   myLastCreatedElems.Clear();
7981   myLastCreatedNodes.Clear();
7982
7983   typedef map< SortableElement, int > TMapOfNodeSet;
7984   typedef list<int> TGroupOfElems;
7985
7986   if ( theElements.empty() )
7987   { // get all elements in the mesh
7988     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7989     while ( eIt->more() )
7990       theElements.insert( theElements.end(), eIt->next() );
7991   }
7992
7993   vector< TGroupOfElems > arrayOfGroups;
7994   TGroupOfElems groupOfElems;
7995   TMapOfNodeSet mapOfNodeSet;
7996
7997   TIDSortedElemSet::iterator elemIt = theElements.begin();
7998   for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7999   {
8000     const SMDS_MeshElement* curElem = *elemIt;
8001     SortableElement SE(curElem);
8002     // check uniqueness
8003     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8004     if ( !pp.second ) { // one more coincident elem
8005       TMapOfNodeSet::iterator& itSE = pp.first;
8006       int ind = (*itSE).second;
8007       arrayOfGroups[ind].push_back( curElem->GetID() );
8008     }
8009     else {
8010       arrayOfGroups.push_back( groupOfElems );
8011       arrayOfGroups.back().push_back( curElem->GetID() );
8012       i++;
8013     }
8014   }
8015
8016   groupOfElems.clear();
8017   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8018   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8019   {
8020     if ( groupIt->size() > 1 ) {
8021       //groupOfElems.sort(); -- theElements is sorted already
8022       theGroupsOfElementsID.push_back( groupOfElems );
8023       theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
8024     }
8025   }
8026 }
8027
8028 //=======================================================================
8029 //function : MergeElements
8030 //purpose  : In each given group, substitute all elements by the first one.
8031 //=======================================================================
8032
8033 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8034 {
8035   myLastCreatedElems.Clear();
8036   myLastCreatedNodes.Clear();
8037
8038   typedef list<int> TListOfIDs;
8039   TListOfIDs rmElemIds; // IDs of elems to remove
8040
8041   SMESHDS_Mesh* aMesh = GetMeshDS();
8042
8043   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8044   while ( groupsIt != theGroupsOfElementsID.end() ) {
8045     TListOfIDs& aGroupOfElemID = *groupsIt;
8046     aGroupOfElemID.sort();
8047     int elemIDToKeep = aGroupOfElemID.front();
8048     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8049     aGroupOfElemID.pop_front();
8050     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8051     while ( idIt != aGroupOfElemID.end() ) {
8052       int elemIDToRemove = *idIt;
8053       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8054       // add the kept element in groups of removed one (PAL15188)
8055       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8056       rmElemIds.push_back( elemIDToRemove );
8057       ++idIt;
8058     }
8059     ++groupsIt;
8060   }
8061
8062   Remove( rmElemIds, false );
8063 }
8064
8065 //=======================================================================
8066 //function : MergeEqualElements
8067 //purpose  : Remove all but one of elements built on the same nodes.
8068 //=======================================================================
8069
8070 void SMESH_MeshEditor::MergeEqualElements()
8071 {
8072   TIDSortedElemSet aMeshElements; /* empty input ==
8073                                      to merge equal elements in the whole mesh */
8074   TListOfListOfElementsID aGroupsOfElementsID;
8075   FindEqualElements(aMeshElements, aGroupsOfElementsID);
8076   MergeElements(aGroupsOfElementsID);
8077 }
8078
8079 //=======================================================================
8080 //function : findAdjacentFace
8081 //purpose  :
8082 //=======================================================================
8083
8084 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8085                                                 const SMDS_MeshNode* n2,
8086                                                 const SMDS_MeshElement* elem)
8087 {
8088   TIDSortedElemSet elemSet, avoidSet;
8089   if ( elem )
8090     avoidSet.insert ( elem );
8091   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8092 }
8093
8094 //=======================================================================
8095 //function : findSegment
8096 //purpose  : Return a mesh segment by two nodes one of which can be medium
8097 //=======================================================================
8098
8099 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8100                                            const SMDS_MeshNode* n2)
8101 {
8102   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8103   while ( it->more() )
8104   {
8105     const SMDS_MeshElement* seg = it->next();
8106     if ( seg->GetNodeIndex( n2 ) >= 0 )
8107       return seg;
8108   }
8109   return 0;
8110 }
8111
8112 //=======================================================================
8113 //function : FindFreeBorder
8114 //purpose  :
8115 //=======================================================================
8116
8117 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8118
8119 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
8120                                        const SMDS_MeshNode*             theSecondNode,
8121                                        const SMDS_MeshNode*             theLastNode,
8122                                        list< const SMDS_MeshNode* > &   theNodes,
8123                                        list< const SMDS_MeshElement* >& theFaces)
8124 {
8125   if ( !theFirstNode || !theSecondNode )
8126     return false;
8127   // find border face between theFirstNode and theSecondNode
8128   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8129   if ( !curElem )
8130     return false;
8131
8132   theFaces.push_back( curElem );
8133   theNodes.push_back( theFirstNode );
8134   theNodes.push_back( theSecondNode );
8135
8136   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8137   TIDSortedElemSet foundElems;
8138   bool needTheLast = ( theLastNode != 0 );
8139
8140   while ( nStart != theLastNode ) {
8141     if ( nStart == theFirstNode )
8142       return !needTheLast;
8143
8144     // find all free border faces sharing form nStart
8145
8146     list< const SMDS_MeshElement* > curElemList;
8147     list< const SMDS_MeshNode* >    nStartList;
8148     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8149     while ( invElemIt->more() ) {
8150       const SMDS_MeshElement* e = invElemIt->next();
8151       if ( e == curElem || foundElems.insert( e ).second ) {
8152         // get nodes
8153         int iNode = 0, nbNodes = e->NbNodes();
8154         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8155
8156         if ( e->IsQuadratic() ) {
8157           const SMDS_VtkFace* F =
8158             dynamic_cast<const SMDS_VtkFace*>(e);
8159           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8160           // use special nodes iterator
8161           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8162           while( anIter->more() ) {
8163             nodes[ iNode++ ] = cast2Node(anIter->next());
8164           }
8165         }
8166         else {
8167           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8168           while ( nIt->more() )
8169             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8170         }
8171         nodes[ iNode ] = nodes[ 0 ];
8172         // check 2 links
8173         for ( iNode = 0; iNode < nbNodes; iNode++ )
8174           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8175                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8176               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8177           {
8178             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8179             curElemList.push_back( e );
8180           }
8181       }
8182     }
8183     // analyse the found
8184
8185     int nbNewBorders = curElemList.size();
8186     if ( nbNewBorders == 0 ) {
8187       // no free border furthermore
8188       return !needTheLast;
8189     }
8190     else if ( nbNewBorders == 1 ) {
8191       // one more element found
8192       nIgnore = nStart;
8193       nStart = nStartList.front();
8194       curElem = curElemList.front();
8195       theFaces.push_back( curElem );
8196       theNodes.push_back( nStart );
8197     }
8198     else {
8199       // several continuations found
8200       list< const SMDS_MeshElement* >::iterator curElemIt;
8201       list< const SMDS_MeshNode* >::iterator nStartIt;
8202       // check if one of them reached the last node
8203       if ( needTheLast ) {
8204         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8205              curElemIt!= curElemList.end();
8206              curElemIt++, nStartIt++ )
8207           if ( *nStartIt == theLastNode ) {
8208             theFaces.push_back( *curElemIt );
8209             theNodes.push_back( *nStartIt );
8210             return true;
8211           }
8212       }
8213       // find the best free border by the continuations
8214       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8215       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8216       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8217            curElemIt!= curElemList.end();
8218            curElemIt++, nStartIt++ )
8219       {
8220         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8221         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8222         // find one more free border
8223         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8224           cNL->clear();
8225           cFL->clear();
8226         }
8227         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8228           // choice: clear a worse one
8229           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8230           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8231           contNodes[ iWorse ].clear();
8232           contFaces[ iWorse ].clear();
8233         }
8234       }
8235       if ( contNodes[0].empty() && contNodes[1].empty() )
8236         return false;
8237
8238       // append the best free border
8239       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8240       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8241       theNodes.pop_back(); // remove nIgnore
8242       theNodes.pop_back(); // remove nStart
8243       theFaces.pop_back(); // remove curElem
8244       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8245       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8246       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8247       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8248       return true;
8249
8250     } // several continuations found
8251   } // while ( nStart != theLastNode )
8252
8253   return true;
8254 }
8255
8256 //=======================================================================
8257 //function : CheckFreeBorderNodes
8258 //purpose  : Return true if the tree nodes are on a free border
8259 //=======================================================================
8260
8261 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8262                                             const SMDS_MeshNode* theNode2,
8263                                             const SMDS_MeshNode* theNode3)
8264 {
8265   list< const SMDS_MeshNode* > nodes;
8266   list< const SMDS_MeshElement* > faces;
8267   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8268 }
8269
8270 //=======================================================================
8271 //function : SewFreeBorder
8272 //purpose  :
8273 //warning  : for border-to-side sewing theSideSecondNode is considered as
8274 //           the last side node and theSideThirdNode is not used
8275 //=======================================================================
8276
8277 SMESH_MeshEditor::Sew_Error
8278 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8279                                  const SMDS_MeshNode* theBordSecondNode,
8280                                  const SMDS_MeshNode* theBordLastNode,
8281                                  const SMDS_MeshNode* theSideFirstNode,
8282                                  const SMDS_MeshNode* theSideSecondNode,
8283                                  const SMDS_MeshNode* theSideThirdNode,
8284                                  const bool           theSideIsFreeBorder,
8285                                  const bool           toCreatePolygons,
8286                                  const bool           toCreatePolyedrs)
8287 {
8288   myLastCreatedElems.Clear();
8289   myLastCreatedNodes.Clear();
8290
8291   Sew_Error aResult = SEW_OK;
8292
8293   // ====================================
8294   //    find side nodes and elements
8295   // ====================================
8296
8297   list< const SMDS_MeshNode* >    nSide[ 2 ];
8298   list< const SMDS_MeshElement* > eSide[ 2 ];
8299   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
8300   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8301
8302   // Free border 1
8303   // --------------
8304   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8305                       nSide[0], eSide[0])) {
8306     MESSAGE(" Free Border 1 not found " );
8307     aResult = SEW_BORDER1_NOT_FOUND;
8308   }
8309   if (theSideIsFreeBorder) {
8310     // Free border 2
8311     // --------------
8312     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8313                         nSide[1], eSide[1])) {
8314       MESSAGE(" Free Border 2 not found " );
8315       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8316     }
8317   }
8318   if ( aResult != SEW_OK )
8319     return aResult;
8320
8321   if (!theSideIsFreeBorder) {
8322     // Side 2
8323     // --------------
8324
8325     // -------------------------------------------------------------------------
8326     // Algo:
8327     // 1. If nodes to merge are not coincident, move nodes of the free border
8328     //    from the coord sys defined by the direction from the first to last
8329     //    nodes of the border to the correspondent sys of the side 2
8330     // 2. On the side 2, find the links most co-directed with the correspondent
8331     //    links of the free border
8332     // -------------------------------------------------------------------------
8333
8334     // 1. Since sewing may break if there are volumes to split on the side 2,
8335     //    we won't move nodes but just compute new coordinates for them
8336     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8337     TNodeXYZMap nBordXYZ;
8338     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8339     list< const SMDS_MeshNode* >::iterator nBordIt;
8340
8341     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8342     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8343     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8344     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8345     double tol2 = 1.e-8;
8346     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8347     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8348       // Need node movement.
8349
8350       // find X and Z axes to create trsf
8351       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8352       gp_Vec X = Zs ^ Zb;
8353       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8354         // Zb || Zs
8355         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8356
8357       // coord systems
8358       gp_Ax3 toBordAx( Pb1, Zb, X );
8359       gp_Ax3 fromSideAx( Ps1, Zs, X );
8360       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8361       // set trsf
8362       gp_Trsf toBordSys, fromSide2Sys;
8363       toBordSys.SetTransformation( toBordAx );
8364       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8365       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8366
8367       // move
8368       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8369         const SMDS_MeshNode* n = *nBordIt;
8370         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8371         toBordSys.Transforms( xyz );
8372         fromSide2Sys.Transforms( xyz );
8373         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8374       }
8375     }
8376     else {
8377       // just insert nodes XYZ in the nBordXYZ map
8378       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8379         const SMDS_MeshNode* n = *nBordIt;
8380         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8381       }
8382     }
8383
8384     // 2. On the side 2, find the links most co-directed with the correspondent
8385     //    links of the free border
8386
8387     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8388     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8389     sideNodes.push_back( theSideFirstNode );
8390
8391     bool hasVolumes = false;
8392     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8393     set<long> foundSideLinkIDs, checkedLinkIDs;
8394     SMDS_VolumeTool volume;
8395     //const SMDS_MeshNode* faceNodes[ 4 ];
8396
8397     const SMDS_MeshNode*    sideNode;
8398     const SMDS_MeshElement* sideElem  = 0;
8399     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8400     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8401     nBordIt = bordNodes.begin();
8402     nBordIt++;
8403     // border node position and border link direction to compare with
8404     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8405     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8406     // choose next side node by link direction or by closeness to
8407     // the current border node:
8408     bool searchByDir = ( *nBordIt != theBordLastNode );
8409     do {
8410       // find the next node on the Side 2
8411       sideNode = 0;
8412       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8413       long linkID;
8414       checkedLinkIDs.clear();
8415       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8416
8417       // loop on inverse elements of current node (prevSideNode) on the Side 2
8418       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8419       while ( invElemIt->more() )
8420       {
8421         const SMDS_MeshElement* elem = invElemIt->next();
8422         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8423         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8424         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8425         bool isVolume = volume.Set( elem );
8426         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8427         if ( isVolume ) // --volume
8428           hasVolumes = true;
8429         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8430           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8431           if(elem->IsQuadratic()) {
8432             const SMDS_VtkFace* F =
8433               dynamic_cast<const SMDS_VtkFace*>(elem);
8434             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8435             // use special nodes iterator
8436             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8437             while( anIter->more() ) {
8438               nodes[ iNode ] = cast2Node(anIter->next());
8439               if ( nodes[ iNode++ ] == prevSideNode )
8440                 iPrevNode = iNode - 1;
8441             }
8442           }
8443           else {
8444             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8445             while ( nIt->more() ) {
8446               nodes[ iNode ] = cast2Node( nIt->next() );
8447               if ( nodes[ iNode++ ] == prevSideNode )
8448                 iPrevNode = iNode - 1;
8449             }
8450           }
8451           // there are 2 links to check
8452           nbNodes = 2;
8453         }
8454         else // --edge
8455           continue;
8456         // loop on links, to be precise, on the second node of links
8457         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8458           const SMDS_MeshNode* n = nodes[ iNode ];
8459           if ( isVolume ) {
8460             if ( !volume.IsLinked( n, prevSideNode ))
8461               continue;
8462           }
8463           else {
8464             if ( iNode ) // a node before prevSideNode
8465               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8466             else         // a node after prevSideNode
8467               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8468           }
8469           // check if this link was already used
8470           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8471           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8472           if (!isJustChecked &&
8473               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8474           {
8475             // test a link geometrically
8476             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8477             bool linkIsBetter = false;
8478             double dot = 0.0, dist = 0.0;
8479             if ( searchByDir ) { // choose most co-directed link
8480               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8481               linkIsBetter = ( dot > maxDot );
8482             }
8483             else { // choose link with the node closest to bordPos
8484               dist = ( nextXYZ - bordPos ).SquareModulus();
8485               linkIsBetter = ( dist < minDist );
8486             }
8487             if ( linkIsBetter ) {
8488               maxDot = dot;
8489               minDist = dist;
8490               linkID = iLink;
8491               sideNode = n;
8492               sideElem = elem;
8493             }
8494           }
8495         }
8496       } // loop on inverse elements of prevSideNode
8497
8498       if ( !sideNode ) {
8499         MESSAGE(" Can't find path by links of the Side 2 ");
8500         return SEW_BAD_SIDE_NODES;
8501       }
8502       sideNodes.push_back( sideNode );
8503       sideElems.push_back( sideElem );
8504       foundSideLinkIDs.insert ( linkID );
8505       prevSideNode = sideNode;
8506
8507       if ( *nBordIt == theBordLastNode )
8508         searchByDir = false;
8509       else {
8510         // find the next border link to compare with
8511         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8512         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8513         // move to next border node if sideNode is before forward border node (bordPos)
8514         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8515           prevBordNode = *nBordIt;
8516           nBordIt++;
8517           bordPos = nBordXYZ[ *nBordIt ];
8518           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8519           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8520         }
8521       }
8522     }
8523     while ( sideNode != theSideSecondNode );
8524
8525     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8526       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8527       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8528     }
8529   } // end nodes search on the side 2
8530
8531   // ============================
8532   // sew the border to the side 2
8533   // ============================
8534
8535   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8536   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8537
8538   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8539   if ( toMergeConformal && toCreatePolygons )
8540   {
8541     // do not merge quadrangles if polygons are OK (IPAL0052824)
8542     eIt[0] = eSide[0].begin();
8543     eIt[1] = eSide[1].begin();
8544     bool allQuads[2] = { true, true };
8545     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8546       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8547         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8548     }
8549     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8550   }
8551
8552   TListOfListOfNodes nodeGroupsToMerge;
8553   if (( toMergeConformal ) ||
8554       ( theSideIsFreeBorder && !theSideThirdNode )) {
8555
8556     // all nodes are to be merged
8557
8558     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8559          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8560          nIt[0]++, nIt[1]++ )
8561     {
8562       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8563       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8564       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8565     }
8566   }
8567   else {
8568
8569     // insert new nodes into the border and the side to get equal nb of segments
8570
8571     // get normalized parameters of nodes on the borders
8572     vector< double > param[ 2 ];
8573     param[0].resize( maxNbNodes );
8574     param[1].resize( maxNbNodes );
8575     int iNode, iBord;
8576     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8577       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8578       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8579       const SMDS_MeshNode* nPrev = *nIt;
8580       double bordLength = 0;
8581       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8582         const SMDS_MeshNode* nCur = *nIt;
8583         gp_XYZ segment (nCur->X() - nPrev->X(),
8584                         nCur->Y() - nPrev->Y(),
8585                         nCur->Z() - nPrev->Z());
8586         double segmentLen = segment.Modulus();
8587         bordLength += segmentLen;
8588         param[ iBord ][ iNode ] = bordLength;
8589         nPrev = nCur;
8590       }
8591       // normalize within [0,1]
8592       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8593         param[ iBord ][ iNode ] /= bordLength;
8594       }
8595     }
8596
8597     // loop on border segments
8598     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8599     int i[ 2 ] = { 0, 0 };
8600     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8601     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8602
8603     TElemOfNodeListMap insertMap;
8604     TElemOfNodeListMap::iterator insertMapIt;
8605     // insertMap is
8606     // key:   elem to insert nodes into
8607     // value: 2 nodes to insert between + nodes to be inserted
8608     do {
8609       bool next[ 2 ] = { false, false };
8610
8611       // find min adjacent segment length after sewing
8612       double nextParam = 10., prevParam = 0;
8613       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8614         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8615           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8616         if ( i[ iBord ] > 0 )
8617           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8618       }
8619       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8620       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8621       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8622
8623       // choose to insert or to merge nodes
8624       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8625       if ( Abs( du ) <= minSegLen * 0.2 ) {
8626         // merge
8627         // ------
8628         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8629         const SMDS_MeshNode* n0 = *nIt[0];
8630         const SMDS_MeshNode* n1 = *nIt[1];
8631         nodeGroupsToMerge.back().push_back( n1 );
8632         nodeGroupsToMerge.back().push_back( n0 );
8633         // position of node of the border changes due to merge
8634         param[ 0 ][ i[0] ] += du;
8635         // move n1 for the sake of elem shape evaluation during insertion.
8636         // n1 will be removed by MergeNodes() anyway
8637         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8638         next[0] = next[1] = true;
8639       }
8640       else {
8641         // insert
8642         // ------
8643         int intoBord = ( du < 0 ) ? 0 : 1;
8644         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8645         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8646         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8647         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8648         if ( intoBord == 1 ) {
8649           // move node of the border to be on a link of elem of the side
8650           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8651           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8652           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8653           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8654           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8655         }
8656         insertMapIt = insertMap.find( elem );
8657         bool  notFound = ( insertMapIt == insertMap.end() );
8658         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8659         if ( otherLink ) {
8660           // insert into another link of the same element:
8661           // 1. perform insertion into the other link of the elem
8662           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8663           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8664           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8665           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8666           // 2. perform insertion into the link of adjacent faces
8667           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8668             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8669           }
8670           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8671             InsertNodesIntoLink( seg, n12, n22, nodeList );
8672           }
8673           if (toCreatePolyedrs) {
8674             // perform insertion into the links of adjacent volumes
8675             UpdateVolumes(n12, n22, nodeList);
8676           }
8677           // 3. find an element appeared on n1 and n2 after the insertion
8678           insertMap.erase( elem );
8679           elem = findAdjacentFace( n1, n2, 0 );
8680         }
8681         if ( notFound || otherLink ) {
8682           // add element and nodes of the side into the insertMap
8683           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8684           (*insertMapIt).second.push_back( n1 );
8685           (*insertMapIt).second.push_back( n2 );
8686         }
8687         // add node to be inserted into elem
8688         (*insertMapIt).second.push_back( nIns );
8689         next[ 1 - intoBord ] = true;
8690       }
8691
8692       // go to the next segment
8693       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8694         if ( next[ iBord ] ) {
8695           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8696             eIt[ iBord ]++;
8697           nPrev[ iBord ] = *nIt[ iBord ];
8698           nIt[ iBord ]++; i[ iBord ]++;
8699         }
8700       }
8701     }
8702     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8703
8704     // perform insertion of nodes into elements
8705
8706     for (insertMapIt = insertMap.begin();
8707          insertMapIt != insertMap.end();
8708          insertMapIt++ )
8709     {
8710       const SMDS_MeshElement* elem = (*insertMapIt).first;
8711       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8712       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8713       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8714
8715       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8716
8717       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8718         InsertNodesIntoLink( seg, n1, n2, nodeList );
8719       }
8720
8721       if ( !theSideIsFreeBorder ) {
8722         // look for and insert nodes into the faces adjacent to elem
8723         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8724           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8725         }
8726       }
8727       if (toCreatePolyedrs) {
8728         // perform insertion into the links of adjacent volumes
8729         UpdateVolumes(n1, n2, nodeList);
8730       }
8731     }
8732   } // end: insert new nodes
8733
8734   MergeNodes ( nodeGroupsToMerge );
8735
8736
8737   // Remove coincident segments
8738
8739   // get new segments
8740   TIDSortedElemSet segments;
8741   SMESH_SequenceOfElemPtr newFaces;
8742   for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8743   {
8744     if ( !myLastCreatedElems(i) ) continue;
8745     if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8746       segments.insert( segments.end(), myLastCreatedElems(i) );
8747     else
8748       newFaces.Append( myLastCreatedElems(i) );
8749   }
8750   // get segments adjacent to merged nodes
8751   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8752   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8753   {
8754     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8755     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8756     while ( segIt->more() )
8757       segments.insert( segIt->next() );
8758   }
8759
8760   // find coincident
8761   TListOfListOfElementsID equalGroups;
8762   if ( !segments.empty() )
8763     FindEqualElements( segments, equalGroups );
8764   if ( !equalGroups.empty() )
8765   {
8766     // remove from segments those that will be removed
8767     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8768     for ( ; itGroups != equalGroups.end(); ++itGroups )
8769     {
8770       list< int >& group = *itGroups;
8771       list< int >::iterator id = group.begin();
8772       for ( ++id; id != group.end(); ++id )
8773         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8774           segments.erase( seg );
8775     }
8776     // remove equal segments
8777     MergeElements( equalGroups );
8778
8779     // restore myLastCreatedElems
8780     myLastCreatedElems = newFaces;
8781     TIDSortedElemSet::iterator seg = segments.begin();
8782     for ( ; seg != segments.end(); ++seg )
8783       myLastCreatedElems.Append( *seg );
8784   }
8785
8786   return aResult;
8787 }
8788
8789 //=======================================================================
8790 //function : InsertNodesIntoLink
8791 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8792 //           and theBetweenNode2 and split theElement
8793 //=======================================================================
8794
8795 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8796                                            const SMDS_MeshNode*        theBetweenNode1,
8797                                            const SMDS_MeshNode*        theBetweenNode2,
8798                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8799                                            const bool                  toCreatePoly)
8800 {
8801   if ( !theElement ) return;
8802
8803   SMESHDS_Mesh *aMesh = GetMeshDS();
8804   vector<const SMDS_MeshElement*> newElems;
8805
8806   if ( theElement->GetType() == SMDSAbs_Edge )
8807   {
8808     theNodesToInsert.push_front( theBetweenNode1 );
8809     theNodesToInsert.push_back ( theBetweenNode2 );
8810     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8811     const SMDS_MeshNode* n1 = *n;
8812     for ( ++n; n != theNodesToInsert.end(); ++n )
8813     {
8814       const SMDS_MeshNode* n2 = *n;
8815       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8816         AddToSameGroups( seg, theElement, aMesh );
8817       else
8818         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8819       n1 = n2;
8820     }
8821     theNodesToInsert.pop_front();
8822     theNodesToInsert.pop_back();
8823
8824     if ( theElement->IsQuadratic() ) // add a not split part
8825     {
8826       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8827                                           theElement->end_nodes() );
8828       int iOther = 0, nbN = nodes.size();
8829       for ( ; iOther < nbN; ++iOther )
8830         if ( nodes[iOther] != theBetweenNode1 &&
8831              nodes[iOther] != theBetweenNode2 )
8832           break;
8833       if      ( iOther == 0 )
8834       {
8835         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8836           AddToSameGroups( seg, theElement, aMesh );
8837         else
8838           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8839       }
8840       else if ( iOther == 2 )
8841       {
8842         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8843           AddToSameGroups( seg, theElement, aMesh );
8844         else
8845           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8846       }
8847     }
8848     // treat new elements
8849     for ( size_t i = 0; i < newElems.size(); ++i )
8850       if ( newElems[i] )
8851       {
8852         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8853         myLastCreatedElems.Append( newElems[i] );
8854       }
8855     ReplaceElemInGroups( theElement, newElems, aMesh );
8856     aMesh->RemoveElement( theElement );
8857     return;
8858
8859   } // if ( theElement->GetType() == SMDSAbs_Edge )
8860
8861   const SMDS_MeshElement* theFace = theElement;
8862   if ( theFace->GetType() != SMDSAbs_Face ) return;
8863
8864   // find indices of 2 link nodes and of the rest nodes
8865   int iNode = 0, il1, il2, i3, i4;
8866   il1 = il2 = i3 = i4 = -1;
8867   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8868
8869   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8870   while ( nodeIt->more() ) {
8871     const SMDS_MeshNode* n = nodeIt->next();
8872     if ( n == theBetweenNode1 )
8873       il1 = iNode;
8874     else if ( n == theBetweenNode2 )
8875       il2 = iNode;
8876     else if ( i3 < 0 )
8877       i3 = iNode;
8878     else
8879       i4 = iNode;
8880     nodes[ iNode++ ] = n;
8881   }
8882   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8883     return ;
8884
8885   // arrange link nodes to go one after another regarding the face orientation
8886   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8887   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8888   if ( reverse ) {
8889     iNode = il1;
8890     il1 = il2;
8891     il2 = iNode;
8892     aNodesToInsert.reverse();
8893   }
8894   // check that not link nodes of a quadrangles are in good order
8895   int nbFaceNodes = theFace->NbNodes();
8896   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8897     iNode = i3;
8898     i3 = i4;
8899     i4 = iNode;
8900   }
8901
8902   if (toCreatePoly || theFace->IsPoly()) {
8903
8904     iNode = 0;
8905     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8906
8907     // add nodes of face up to first node of link
8908     bool isFLN = false;
8909
8910     if ( theFace->IsQuadratic() ) {
8911       const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8912       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8913       // use special nodes iterator
8914       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8915       while( anIter->more()  && !isFLN ) {
8916         const SMDS_MeshNode* n = cast2Node(anIter->next());
8917         poly_nodes[iNode++] = n;
8918         if (n == nodes[il1]) {
8919           isFLN = true;
8920         }
8921       }
8922       // add nodes to insert
8923       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8924       for (; nIt != aNodesToInsert.end(); nIt++) {
8925         poly_nodes[iNode++] = *nIt;
8926       }
8927       // add nodes of face starting from last node of link
8928       while ( anIter->more() ) {
8929         poly_nodes[iNode++] = cast2Node(anIter->next());
8930       }
8931     }
8932     else {
8933       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8934       while ( nodeIt->more() && !isFLN ) {
8935         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8936         poly_nodes[iNode++] = n;
8937         if (n == nodes[il1]) {
8938           isFLN = true;
8939         }
8940       }
8941       // add nodes to insert
8942       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8943       for (; nIt != aNodesToInsert.end(); nIt++) {
8944         poly_nodes[iNode++] = *nIt;
8945       }
8946       // add nodes of face starting from last node of link
8947       while ( nodeIt->more() ) {
8948         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8949         poly_nodes[iNode++] = n;
8950       }
8951     }
8952
8953     // make a new face
8954     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8955   }
8956
8957   else if ( !theFace->IsQuadratic() )
8958   {
8959     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8960     int nbLinkNodes = 2 + aNodesToInsert.size();
8961     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8962     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8963     linkNodes[ 0 ] = nodes[ il1 ];
8964     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8965     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8966     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8967       linkNodes[ iNode++ ] = *nIt;
8968     }
8969     // decide how to split a quadrangle: compare possible variants
8970     // and choose which of splits to be a quadrangle
8971     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8972     if ( nbFaceNodes == 3 ) {
8973       iBestQuad = nbSplits;
8974       i4 = i3;
8975     }
8976     else if ( nbFaceNodes == 4 ) {
8977       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8978       double aBestRate = DBL_MAX;
8979       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8980         i1 = 0; i2 = 1;
8981         double aBadRate = 0;
8982         // evaluate elements quality
8983         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8984           if ( iSplit == iQuad ) {
8985             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8986                                    linkNodes[ i2++ ],
8987                                    nodes[ i3 ],
8988                                    nodes[ i4 ]);
8989             aBadRate += getBadRate( &quad, aCrit );
8990           }
8991           else {
8992             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8993                                    linkNodes[ i2++ ],
8994                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8995             aBadRate += getBadRate( &tria, aCrit );
8996           }
8997         }
8998         // choice
8999         if ( aBadRate < aBestRate ) {
9000           iBestQuad = iQuad;
9001           aBestRate = aBadRate;
9002         }
9003       }
9004     }
9005
9006     // create new elements
9007     i1 = 0; i2 = 1;
9008     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
9009     {
9010       if ( iSplit == iBestQuad )
9011         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9012                                             linkNodes[ i2++ ],
9013                                             nodes[ i3 ],
9014                                             nodes[ i4 ]));
9015       else
9016         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9017                                             linkNodes[ i2++ ],
9018                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
9019     }
9020
9021     const SMDS_MeshNode* newNodes[ 4 ];
9022     newNodes[ 0 ] = linkNodes[ i1 ];
9023     newNodes[ 1 ] = linkNodes[ i2 ];
9024     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9025     newNodes[ 3 ] = nodes[ i4 ];
9026     if (iSplit == iBestQuad)
9027       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
9028     else
9029       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
9030
9031   } // end if(!theFace->IsQuadratic())
9032
9033   else { // theFace is quadratic
9034     // we have to split theFace on simple triangles and one simple quadrangle
9035     int tmp = il1/2;
9036     int nbshift = tmp*2;
9037     // shift nodes in nodes[] by nbshift
9038     int i,j;
9039     for(i=0; i<nbshift; i++) {
9040       const SMDS_MeshNode* n = nodes[0];
9041       for(j=0; j<nbFaceNodes-1; j++) {
9042         nodes[j] = nodes[j+1];
9043       }
9044       nodes[nbFaceNodes-1] = n;
9045     }
9046     il1 = il1 - nbshift;
9047     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9048     //   n0      n1     n2    n0      n1     n2
9049     //     +-----+-----+        +-----+-----+
9050     //      \         /         |           |
9051     //       \       /          |           |
9052     //      n5+     +n3       n7+           +n3
9053     //         \   /            |           |
9054     //          \ /             |           |
9055     //           +              +-----+-----+
9056     //           n4           n6      n5     n4
9057
9058     // create new elements
9059     int n1,n2,n3;
9060     if ( nbFaceNodes == 6 ) { // quadratic triangle
9061       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9062       if ( theFace->IsMediumNode(nodes[il1]) ) {
9063         // create quadrangle
9064         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
9065         n1 = 1;
9066         n2 = 2;
9067         n3 = 3;
9068       }
9069       else {
9070         // create quadrangle
9071         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
9072         n1 = 0;
9073         n2 = 1;
9074         n3 = 5;
9075       }
9076     }
9077     else { // nbFaceNodes==8 - quadratic quadrangle
9078       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9079       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9080       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9081       if ( theFace->IsMediumNode( nodes[ il1 ])) {
9082         // create quadrangle
9083         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9084         n1 = 1;
9085         n2 = 2;
9086         n3 = 3;
9087       }
9088       else {
9089         // create quadrangle
9090         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9091         n1 = 0;
9092         n2 = 1;
9093         n3 = 7;
9094       }
9095     }
9096     // create needed triangles using n1,n2,n3 and inserted nodes
9097     int nbn = 2 + aNodesToInsert.size();
9098     vector<const SMDS_MeshNode*> aNodes(nbn);
9099     aNodes[0    ] = nodes[n1];
9100     aNodes[nbn-1] = nodes[n2];
9101     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9102     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9103       aNodes[iNode++] = *nIt;
9104     }
9105     for ( i = 1; i < nbn; i++ )
9106       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9107   }
9108
9109   // remove the old face
9110   for ( size_t i = 0; i < newElems.size(); ++i )
9111     if ( newElems[i] )
9112     {
9113       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9114       myLastCreatedElems.Append( newElems[i] );
9115     }
9116   ReplaceElemInGroups( theFace, newElems, aMesh );
9117   aMesh->RemoveElement(theFace);
9118
9119 } // InsertNodesIntoLink()
9120
9121 //=======================================================================
9122 //function : UpdateVolumes
9123 //purpose  :
9124 //=======================================================================
9125
9126 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
9127                                       const SMDS_MeshNode*        theBetweenNode2,
9128                                       list<const SMDS_MeshNode*>& theNodesToInsert)
9129 {
9130   myLastCreatedElems.Clear();
9131   myLastCreatedNodes.Clear();
9132
9133   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9134   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9135     const SMDS_MeshElement* elem = invElemIt->next();
9136
9137     // check, if current volume has link theBetweenNode1 - theBetweenNode2
9138     SMDS_VolumeTool aVolume (elem);
9139     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9140       continue;
9141
9142     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9143     int iface, nbFaces = aVolume.NbFaces();
9144     vector<const SMDS_MeshNode *> poly_nodes;
9145     vector<int> quantities (nbFaces);
9146
9147     for (iface = 0; iface < nbFaces; iface++) {
9148       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9149       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9150       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9151
9152       for (int inode = 0; inode < nbFaceNodes; inode++) {
9153         poly_nodes.push_back(faceNodes[inode]);
9154
9155         if (nbInserted == 0) {
9156           if (faceNodes[inode] == theBetweenNode1) {
9157             if (faceNodes[inode + 1] == theBetweenNode2) {
9158               nbInserted = theNodesToInsert.size();
9159
9160               // add nodes to insert
9161               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9162               for (; nIt != theNodesToInsert.end(); nIt++) {
9163                 poly_nodes.push_back(*nIt);
9164               }
9165             }
9166           }
9167           else if (faceNodes[inode] == theBetweenNode2) {
9168             if (faceNodes[inode + 1] == theBetweenNode1) {
9169               nbInserted = theNodesToInsert.size();
9170
9171               // add nodes to insert in reversed order
9172               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9173               nIt--;
9174               for (; nIt != theNodesToInsert.begin(); nIt--) {
9175                 poly_nodes.push_back(*nIt);
9176               }
9177               poly_nodes.push_back(*nIt);
9178             }
9179           }
9180           else {
9181           }
9182         }
9183       }
9184       quantities[iface] = nbFaceNodes + nbInserted;
9185     }
9186
9187     // Replace the volume
9188     SMESHDS_Mesh *aMesh = GetMeshDS();
9189
9190     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9191     {
9192       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9193       myLastCreatedElems.Append( newElem );
9194       ReplaceElemInGroups( elem, newElem, aMesh );
9195     }
9196     aMesh->RemoveElement( elem );
9197   }
9198 }
9199
9200 namespace
9201 {
9202   //================================================================================
9203   /*!
9204    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9205    */
9206   //================================================================================
9207
9208   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9209                            vector<const SMDS_MeshNode *> & nodes,
9210                            vector<int> &                   nbNodeInFaces )
9211   {
9212     nodes.clear();
9213     nbNodeInFaces.clear();
9214     SMDS_VolumeTool vTool ( elem );
9215     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9216     {
9217       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9218       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9219       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9220     }
9221   }
9222 }
9223
9224 //=======================================================================
9225 /*!
9226  * \brief Convert elements contained in a sub-mesh to quadratic
9227  * \return int - nb of checked elements
9228  */
9229 //=======================================================================
9230
9231 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9232                                              SMESH_MesherHelper& theHelper,
9233                                              const bool          theForce3d)
9234 {
9235   int nbElem = 0;
9236   if( !theSm ) return nbElem;
9237
9238   vector<int> nbNodeInFaces;
9239   vector<const SMDS_MeshNode *> nodes;
9240   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9241   while(ElemItr->more())
9242   {
9243     nbElem++;
9244     const SMDS_MeshElement* elem = ElemItr->next();
9245     if( !elem ) continue;
9246
9247     // analyse a necessity of conversion
9248     const SMDSAbs_ElementType aType = elem->GetType();
9249     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9250       continue;
9251     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9252     bool hasCentralNodes = false;
9253     if ( elem->IsQuadratic() )
9254     {
9255       bool alreadyOK;
9256       switch ( aGeomType ) {
9257       case SMDSEntity_Quad_Triangle:
9258       case SMDSEntity_Quad_Quadrangle:
9259       case SMDSEntity_Quad_Hexa:
9260         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9261
9262       case SMDSEntity_BiQuad_Triangle:
9263       case SMDSEntity_BiQuad_Quadrangle:
9264       case SMDSEntity_TriQuad_Hexa:
9265         alreadyOK = theHelper.GetIsBiQuadratic();
9266         hasCentralNodes = true;
9267         break;
9268       default:
9269         alreadyOK = true;
9270       }
9271       // take into account already present modium nodes
9272       switch ( aType ) {
9273       case SMDSAbs_Volume:
9274         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9275       case SMDSAbs_Face:
9276         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9277       case SMDSAbs_Edge:
9278         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9279       default:;
9280       }
9281       if ( alreadyOK )
9282         continue;
9283     }
9284     // get elem data needed to re-create it
9285     //
9286     const int id      = elem->GetID();
9287     const int nbNodes = elem->NbCornerNodes();
9288     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9289     if ( aGeomType == SMDSEntity_Polyhedra )
9290       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9291     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9292       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9293
9294     // remove a linear element
9295     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9296
9297     // remove central nodes of biquadratic elements (biquad->quad conversion)
9298     if ( hasCentralNodes )
9299       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9300         if ( nodes[i]->NbInverseElements() == 0 )
9301           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9302
9303     const SMDS_MeshElement* NewElem = 0;
9304
9305     switch( aType )
9306     {
9307     case SMDSAbs_Edge :
9308       {
9309         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9310         break;
9311       }
9312     case SMDSAbs_Face :
9313       {
9314         switch(nbNodes)
9315         {
9316         case 3:
9317           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9318           break;
9319         case 4:
9320           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9321           break;
9322         default:
9323           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9324         }
9325         break;
9326       }
9327     case SMDSAbs_Volume :
9328       {
9329         switch( aGeomType )
9330         {
9331         case SMDSEntity_Tetra:
9332           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9333           break;
9334         case SMDSEntity_Pyramid:
9335           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9336           break;
9337         case SMDSEntity_Penta:
9338           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9339           break;
9340         case SMDSEntity_Hexa:
9341         case SMDSEntity_Quad_Hexa:
9342         case SMDSEntity_TriQuad_Hexa:
9343           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9344                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9345           break;
9346         case SMDSEntity_Hexagonal_Prism:
9347         default:
9348           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9349         }
9350         break;
9351       }
9352     default :
9353       continue;
9354     }
9355     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9356     if( NewElem && NewElem->getshapeId() < 1 )
9357       theSm->AddElement( NewElem );
9358   }
9359   return nbElem;
9360 }
9361 //=======================================================================
9362 //function : ConvertToQuadratic
9363 //purpose  :
9364 //=======================================================================
9365
9366 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9367 {
9368   SMESHDS_Mesh* meshDS = GetMeshDS();
9369
9370   SMESH_MesherHelper aHelper(*myMesh);
9371
9372   aHelper.SetIsQuadratic( true );
9373   aHelper.SetIsBiQuadratic( theToBiQuad );
9374   aHelper.SetElementsOnShape(true);
9375   aHelper.ToFixNodeParameters( true );
9376
9377   // convert elements assigned to sub-meshes
9378   int nbCheckedElems = 0;
9379   if ( myMesh->HasShapeToMesh() )
9380   {
9381     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9382     {
9383       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9384       while ( smIt->more() ) {
9385         SMESH_subMesh* sm = smIt->next();
9386         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9387           aHelper.SetSubShape( sm->GetSubShape() );
9388           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9389         }
9390       }
9391     }
9392   }
9393
9394   // convert elements NOT assigned to sub-meshes
9395   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9396   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9397   {
9398     aHelper.SetElementsOnShape(false);
9399     SMESHDS_SubMesh *smDS = 0;
9400
9401     // convert edges
9402     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9403     while( aEdgeItr->more() )
9404     {
9405       const SMDS_MeshEdge* edge = aEdgeItr->next();
9406       if ( !edge->IsQuadratic() )
9407       {
9408         int                  id = edge->GetID();
9409         const SMDS_MeshNode* n1 = edge->GetNode(0);
9410         const SMDS_MeshNode* n2 = edge->GetNode(1);
9411
9412         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9413
9414         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9415         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9416       }
9417       else
9418       {
9419         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9420       }
9421     }
9422
9423     // convert faces
9424     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9425     while( aFaceItr->more() )
9426     {
9427       const SMDS_MeshFace* face = aFaceItr->next();
9428       if ( !face ) continue;
9429       
9430       const SMDSAbs_EntityType type = face->GetEntityType();
9431       bool alreadyOK;
9432       switch( type )
9433       {
9434       case SMDSEntity_Quad_Triangle:
9435       case SMDSEntity_Quad_Quadrangle:
9436         alreadyOK = !theToBiQuad;
9437         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9438         break;
9439       case SMDSEntity_BiQuad_Triangle:
9440       case SMDSEntity_BiQuad_Quadrangle:
9441         alreadyOK = theToBiQuad;
9442         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9443         break;
9444       default: alreadyOK = false;
9445       }
9446       if ( alreadyOK )
9447         continue;
9448
9449       const int id = face->GetID();
9450       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9451
9452       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9453
9454       SMDS_MeshFace * NewFace = 0;
9455       switch( type )
9456       {
9457       case SMDSEntity_Triangle:
9458       case SMDSEntity_Quad_Triangle:
9459       case SMDSEntity_BiQuad_Triangle:
9460         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9461         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9462           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9463         break;
9464
9465       case SMDSEntity_Quadrangle:
9466       case SMDSEntity_Quad_Quadrangle:
9467       case SMDSEntity_BiQuad_Quadrangle:
9468         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9469         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9470           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9471         break;
9472
9473       default:;
9474         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9475       }
9476       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9477     }
9478
9479     // convert volumes
9480     vector<int> nbNodeInFaces;
9481     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9482     while(aVolumeItr->more())
9483     {
9484       const SMDS_MeshVolume* volume = aVolumeItr->next();
9485       if ( !volume ) continue;
9486
9487       const SMDSAbs_EntityType type = volume->GetEntityType();
9488       if ( volume->IsQuadratic() )
9489       {
9490         bool alreadyOK;
9491         switch ( type )
9492         {
9493         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9494         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9495         default:                      alreadyOK = true;
9496         }
9497         if ( alreadyOK )
9498         {
9499           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9500           continue;
9501         }
9502       }
9503       const int id = volume->GetID();
9504       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9505       if ( type == SMDSEntity_Polyhedra )
9506         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9507       else if ( type == SMDSEntity_Hexagonal_Prism )
9508         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9509
9510       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9511
9512       SMDS_MeshVolume * NewVolume = 0;
9513       switch ( type )
9514       {
9515       case SMDSEntity_Tetra:
9516         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9517         break;
9518       case SMDSEntity_Hexa:
9519       case SMDSEntity_Quad_Hexa:
9520       case SMDSEntity_TriQuad_Hexa:
9521         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9522                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9523         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9524           if ( nodes[i]->NbInverseElements() == 0 )
9525             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9526         break;
9527       case SMDSEntity_Pyramid:
9528         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9529                                       nodes[3], nodes[4], id, theForce3d);
9530         break;
9531       case SMDSEntity_Penta:
9532         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9533                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9534         break;
9535       case SMDSEntity_Hexagonal_Prism:
9536       default:
9537         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9538       }
9539       ReplaceElemInGroups(volume, NewVolume, meshDS);
9540     }
9541   }
9542
9543   if ( !theForce3d )
9544   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9545     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9546     // aHelper.FixQuadraticElements(myError);
9547     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9548   }
9549 }
9550
9551 //================================================================================
9552 /*!
9553  * \brief Makes given elements quadratic
9554  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9555  *  \param theElements - elements to make quadratic
9556  */
9557 //================================================================================
9558
9559 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9560                                           TIDSortedElemSet& theElements,
9561                                           const bool        theToBiQuad)
9562 {
9563   if ( theElements.empty() ) return;
9564
9565   // we believe that all theElements are of the same type
9566   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9567
9568   // get all nodes shared by theElements
9569   TIDSortedNodeSet allNodes;
9570   TIDSortedElemSet::iterator eIt = theElements.begin();
9571   for ( ; eIt != theElements.end(); ++eIt )
9572     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9573
9574   // complete theElements with elements of lower dim whose all nodes are in allNodes
9575
9576   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9577   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9578   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9579   for ( ; nIt != allNodes.end(); ++nIt )
9580   {
9581     const SMDS_MeshNode* n = *nIt;
9582     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9583     while ( invIt->more() )
9584     {
9585       const SMDS_MeshElement*      e = invIt->next();
9586       const SMDSAbs_ElementType type = e->GetType();
9587       if ( e->IsQuadratic() )
9588       {
9589         quadAdjacentElems[ type ].insert( e );
9590
9591         bool alreadyOK;
9592         switch ( e->GetEntityType() ) {
9593         case SMDSEntity_Quad_Triangle:
9594         case SMDSEntity_Quad_Quadrangle:
9595         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9596         case SMDSEntity_BiQuad_Triangle:
9597         case SMDSEntity_BiQuad_Quadrangle:
9598         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9599         default:                           alreadyOK = true;
9600         }
9601         if ( alreadyOK )
9602           continue;
9603       }
9604       if ( type >= elemType )
9605         continue; // same type or more complex linear element
9606
9607       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9608         continue; // e is already checked
9609
9610       // check nodes
9611       bool allIn = true;
9612       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9613       while ( nodeIt->more() && allIn )
9614         allIn = allNodes.count( nodeIt->next() );
9615       if ( allIn )
9616         theElements.insert(e );
9617     }
9618   }
9619
9620   SMESH_MesherHelper helper(*myMesh);
9621   helper.SetIsQuadratic( true );
9622   helper.SetIsBiQuadratic( theToBiQuad );
9623
9624   // add links of quadratic adjacent elements to the helper
9625
9626   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9627     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9628           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9629     {
9630       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9631     }
9632   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9633     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9634           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9635     {
9636       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9637     }
9638   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9639     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9640           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9641     {
9642       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9643     }
9644
9645   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9646
9647   SMESHDS_Mesh*  meshDS = GetMeshDS();
9648   SMESHDS_SubMesh* smDS = 0;
9649   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9650   {
9651     const SMDS_MeshElement* elem = *eIt;
9652
9653     bool alreadyOK;
9654     int nbCentralNodes = 0;
9655     switch ( elem->GetEntityType() ) {
9656       // linear convertible
9657     case SMDSEntity_Edge:
9658     case SMDSEntity_Triangle:
9659     case SMDSEntity_Quadrangle:
9660     case SMDSEntity_Tetra:
9661     case SMDSEntity_Pyramid:
9662     case SMDSEntity_Hexa:
9663     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9664       // quadratic that can become bi-quadratic
9665     case SMDSEntity_Quad_Triangle:
9666     case SMDSEntity_Quad_Quadrangle:
9667     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9668       // bi-quadratic
9669     case SMDSEntity_BiQuad_Triangle:
9670     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9671     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9672       // the rest
9673     default:                           alreadyOK = true;
9674     }
9675     if ( alreadyOK ) continue;
9676
9677     const SMDSAbs_ElementType type = elem->GetType();
9678     const int                   id = elem->GetID();
9679     const int              nbNodes = elem->NbCornerNodes();
9680     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9681
9682     helper.SetSubShape( elem->getshapeId() );
9683
9684     if ( !smDS || !smDS->Contains( elem ))
9685       smDS = meshDS->MeshElements( elem->getshapeId() );
9686     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9687
9688     SMDS_MeshElement * newElem = 0;
9689     switch( nbNodes )
9690     {
9691     case 4: // cases for most frequently used element types go first (for optimization)
9692       if ( type == SMDSAbs_Volume )
9693         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9694       else
9695         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9696       break;
9697     case 8:
9698       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9699                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9700       break;
9701     case 3:
9702       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9703       break;
9704     case 2:
9705       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9706       break;
9707     case 5:
9708       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9709                                  nodes[4], id, theForce3d);
9710       break;
9711     case 6:
9712       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9713                                  nodes[4], nodes[5], id, theForce3d);
9714       break;
9715     default:;
9716     }
9717     ReplaceElemInGroups( elem, newElem, meshDS);
9718     if( newElem && smDS )
9719       smDS->AddElement( newElem );
9720
9721      // remove central nodes
9722     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9723       if ( nodes[i]->NbInverseElements() == 0 )
9724         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9725
9726   } // loop on theElements
9727
9728   if ( !theForce3d )
9729   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9730     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9731     // helper.FixQuadraticElements( myError );
9732     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9733   }
9734 }
9735
9736 //=======================================================================
9737 /*!
9738  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9739  * \return int - nb of checked elements
9740  */
9741 //=======================================================================
9742
9743 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9744                                      SMDS_ElemIteratorPtr theItr,
9745                                      const int            theShapeID)
9746 {
9747   int nbElem = 0;
9748   SMESHDS_Mesh* meshDS = GetMeshDS();
9749   ElemFeatures elemType;
9750   vector<const SMDS_MeshNode *> nodes;
9751
9752   while( theItr->more() )
9753   {
9754     const SMDS_MeshElement* elem = theItr->next();
9755     nbElem++;
9756     if( elem && elem->IsQuadratic())
9757     {
9758       // get elem data
9759       int nbCornerNodes = elem->NbCornerNodes();
9760       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9761
9762       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9763
9764       //remove a quadratic element
9765       if ( !theSm || !theSm->Contains( elem ))
9766         theSm = meshDS->MeshElements( elem->getshapeId() );
9767       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9768
9769       // remove medium nodes
9770       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9771         if ( nodes[i]->NbInverseElements() == 0 )
9772           meshDS->RemoveFreeNode( nodes[i], theSm );
9773
9774       // add a linear element
9775       nodes.resize( nbCornerNodes );
9776       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9777       ReplaceElemInGroups(elem, newElem, meshDS);
9778       if( theSm && newElem )
9779         theSm->AddElement( newElem );
9780     }
9781   }
9782   return nbElem;
9783 }
9784
9785 //=======================================================================
9786 //function : ConvertFromQuadratic
9787 //purpose  :
9788 //=======================================================================
9789
9790 bool SMESH_MeshEditor::ConvertFromQuadratic()
9791 {
9792   int nbCheckedElems = 0;
9793   if ( myMesh->HasShapeToMesh() )
9794   {
9795     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9796     {
9797       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9798       while ( smIt->more() ) {
9799         SMESH_subMesh* sm = smIt->next();
9800         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9801           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9802       }
9803     }
9804   }
9805
9806   int totalNbElems =
9807     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9808   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9809   {
9810     SMESHDS_SubMesh *aSM = 0;
9811     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9812   }
9813
9814   return true;
9815 }
9816
9817 namespace
9818 {
9819   //================================================================================
9820   /*!
9821    * \brief Return true if all medium nodes of the element are in the node set
9822    */
9823   //================================================================================
9824
9825   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9826   {
9827     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9828       if ( !nodeSet.count( elem->GetNode(i) ))
9829         return false;
9830     return true;
9831   }
9832 }
9833
9834 //================================================================================
9835 /*!
9836  * \brief Makes given elements linear
9837  */
9838 //================================================================================
9839
9840 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9841 {
9842   if ( theElements.empty() ) return;
9843
9844   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9845   set<int> mediumNodeIDs;
9846   TIDSortedElemSet::iterator eIt = theElements.begin();
9847   for ( ; eIt != theElements.end(); ++eIt )
9848   {
9849     const SMDS_MeshElement* e = *eIt;
9850     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9851       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9852   }
9853
9854   // replace given elements by linear ones
9855   SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9856   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9857
9858   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9859   // except those elements sharing medium nodes of quadratic element whose medium nodes
9860   // are not all in mediumNodeIDs
9861
9862   // get remaining medium nodes
9863   TIDSortedNodeSet mediumNodes;
9864   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9865   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9866     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9867       mediumNodes.insert( mediumNodes.end(), n );
9868
9869   // find more quadratic elements to convert
9870   TIDSortedElemSet moreElemsToConvert;
9871   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9872   for ( ; nIt != mediumNodes.end(); ++nIt )
9873   {
9874     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9875     while ( invIt->more() )
9876     {
9877       const SMDS_MeshElement* e = invIt->next();
9878       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9879       {
9880         // find a more complex element including e and
9881         // whose medium nodes are not in mediumNodes
9882         bool complexFound = false;
9883         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9884         {
9885           SMDS_ElemIteratorPtr invIt2 =
9886             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9887           while ( invIt2->more() )
9888           {
9889             const SMDS_MeshElement* eComplex = invIt2->next();
9890             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9891             {
9892               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9893               if ( nbCommonNodes == e->NbNodes())
9894               {
9895                 complexFound = true;
9896                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9897                 break;
9898               }
9899             }
9900           }
9901         }
9902         if ( !complexFound )
9903           moreElemsToConvert.insert( e );
9904       }
9905     }
9906   }
9907   elemIt = elemSetIterator( moreElemsToConvert );
9908   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9909 }
9910
9911 //=======================================================================
9912 //function : SewSideElements
9913 //purpose  :
9914 //=======================================================================
9915
9916 SMESH_MeshEditor::Sew_Error
9917 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9918                                    TIDSortedElemSet&    theSide2,
9919                                    const SMDS_MeshNode* theFirstNode1,
9920                                    const SMDS_MeshNode* theFirstNode2,
9921                                    const SMDS_MeshNode* theSecondNode1,
9922                                    const SMDS_MeshNode* theSecondNode2)
9923 {
9924   myLastCreatedElems.Clear();
9925   myLastCreatedNodes.Clear();
9926
9927   if ( theSide1.size() != theSide2.size() )
9928     return SEW_DIFF_NB_OF_ELEMENTS;
9929
9930   Sew_Error aResult = SEW_OK;
9931   // Algo:
9932   // 1. Build set of faces representing each side
9933   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9934   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9935
9936   // =======================================================================
9937   // 1. Build set of faces representing each side:
9938   // =======================================================================
9939   // a. build set of nodes belonging to faces
9940   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9941   // c. create temporary faces representing side of volumes if correspondent
9942   //    face does not exist
9943
9944   SMESHDS_Mesh* aMesh = GetMeshDS();
9945   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9946   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9947   TIDSortedElemSet             faceSet1, faceSet2;
9948   set<const SMDS_MeshElement*> volSet1,  volSet2;
9949   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9950   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9951   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9952   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9953   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9954   int iSide, iFace, iNode;
9955
9956   list<const SMDS_MeshElement* > tempFaceList;
9957   for ( iSide = 0; iSide < 2; iSide++ ) {
9958     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9959     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9960     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9961     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9962     set<const SMDS_MeshElement*>::iterator vIt;
9963     TIDSortedElemSet::iterator eIt;
9964     set<const SMDS_MeshNode*>::iterator    nIt;
9965
9966     // check that given nodes belong to given elements
9967     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9968     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9969     int firstIndex = -1, secondIndex = -1;
9970     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9971       const SMDS_MeshElement* elem = *eIt;
9972       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9973       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9974       if ( firstIndex > -1 && secondIndex > -1 ) break;
9975     }
9976     if ( firstIndex < 0 || secondIndex < 0 ) {
9977       // we can simply return until temporary faces created
9978       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9979     }
9980
9981     // -----------------------------------------------------------
9982     // 1a. Collect nodes of existing faces
9983     //     and build set of face nodes in order to detect missing
9984     //     faces corresponding to sides of volumes
9985     // -----------------------------------------------------------
9986
9987     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9988
9989     // loop on the given element of a side
9990     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9991       //const SMDS_MeshElement* elem = *eIt;
9992       const SMDS_MeshElement* elem = *eIt;
9993       if ( elem->GetType() == SMDSAbs_Face ) {
9994         faceSet->insert( elem );
9995         set <const SMDS_MeshNode*> faceNodeSet;
9996         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9997         while ( nodeIt->more() ) {
9998           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9999           nodeSet->insert( n );
10000           faceNodeSet.insert( n );
10001         }
10002         setOfFaceNodeSet.insert( faceNodeSet );
10003       }
10004       else if ( elem->GetType() == SMDSAbs_Volume )
10005         volSet->insert( elem );
10006     }
10007     // ------------------------------------------------------------------------------
10008     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10009     // ------------------------------------------------------------------------------
10010
10011     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10012       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10013       while ( fIt->more() ) { // loop on faces sharing a node
10014         const SMDS_MeshElement* f = fIt->next();
10015         if ( faceSet->find( f ) == faceSet->end() ) {
10016           // check if all nodes are in nodeSet and
10017           // complete setOfFaceNodeSet if they are
10018           set <const SMDS_MeshNode*> faceNodeSet;
10019           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10020           bool allInSet = true;
10021           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10022             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10023             if ( nodeSet->find( n ) == nodeSet->end() )
10024               allInSet = false;
10025             else
10026               faceNodeSet.insert( n );
10027           }
10028           if ( allInSet ) {
10029             faceSet->insert( f );
10030             setOfFaceNodeSet.insert( faceNodeSet );
10031           }
10032         }
10033       }
10034     }
10035
10036     // -------------------------------------------------------------------------
10037     // 1c. Create temporary faces representing sides of volumes if correspondent
10038     //     face does not exist
10039     // -------------------------------------------------------------------------
10040
10041     if ( !volSet->empty() ) {
10042       //int nodeSetSize = nodeSet->size();
10043
10044       // loop on given volumes
10045       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10046         SMDS_VolumeTool vol (*vIt);
10047         // loop on volume faces: find free faces
10048         // --------------------------------------
10049         list<const SMDS_MeshElement* > freeFaceList;
10050         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10051           if ( !vol.IsFreeFace( iFace ))
10052             continue;
10053           // check if there is already a face with same nodes in a face set
10054           const SMDS_MeshElement* aFreeFace = 0;
10055           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10056           int nbNodes = vol.NbFaceNodes( iFace );
10057           set <const SMDS_MeshNode*> faceNodeSet;
10058           vol.GetFaceNodes( iFace, faceNodeSet );
10059           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10060           if ( isNewFace ) {
10061             // no such a face is given but it still can exist, check it
10062             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10063             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10064           }
10065           if ( !aFreeFace ) {
10066             // create a temporary face
10067             if ( nbNodes == 3 ) {
10068               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10069               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10070             }
10071             else if ( nbNodes == 4 ) {
10072               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10073               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10074             }
10075             else {
10076               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10077               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10078               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10079             }
10080             if ( aFreeFace )
10081               tempFaceList.push_back( aFreeFace );
10082           }
10083
10084           if ( aFreeFace )
10085             freeFaceList.push_back( aFreeFace );
10086
10087         } // loop on faces of a volume
10088
10089         // choose one of several free faces of a volume
10090         // --------------------------------------------
10091         if ( freeFaceList.size() > 1 ) {
10092           // choose a face having max nb of nodes shared by other elems of a side
10093           int maxNbNodes = -1;
10094           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10095           while ( fIt != freeFaceList.end() ) { // loop on free faces
10096             int nbSharedNodes = 0;
10097             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10098             while ( nodeIt->more() ) { // loop on free face nodes
10099               const SMDS_MeshNode* n =
10100                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10101               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10102               while ( invElemIt->more() ) {
10103                 const SMDS_MeshElement* e = invElemIt->next();
10104                 nbSharedNodes += faceSet->count( e );
10105                 nbSharedNodes += elemSet->count( e );
10106               }
10107             }
10108             if ( nbSharedNodes > maxNbNodes ) {
10109               maxNbNodes = nbSharedNodes;
10110               freeFaceList.erase( freeFaceList.begin(), fIt++ );
10111             }
10112             else if ( nbSharedNodes == maxNbNodes ) {
10113               fIt++;
10114             }
10115             else {
10116               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10117             }
10118           }
10119           if ( freeFaceList.size() > 1 )
10120           {
10121             // could not choose one face, use another way
10122             // choose a face most close to the bary center of the opposite side
10123             gp_XYZ aBC( 0., 0., 0. );
10124             set <const SMDS_MeshNode*> addedNodes;
10125             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10126             eIt = elemSet2->begin();
10127             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10128               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10129               while ( nodeIt->more() ) { // loop on free face nodes
10130                 const SMDS_MeshNode* n =
10131                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10132                 if ( addedNodes.insert( n ).second )
10133                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10134               }
10135             }
10136             aBC /= addedNodes.size();
10137             double minDist = DBL_MAX;
10138             fIt = freeFaceList.begin();
10139             while ( fIt != freeFaceList.end() ) { // loop on free faces
10140               double dist = 0;
10141               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10142               while ( nodeIt->more() ) { // loop on free face nodes
10143                 const SMDS_MeshNode* n =
10144                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10145                 gp_XYZ p( n->X(),n->Y(),n->Z() );
10146                 dist += ( aBC - p ).SquareModulus();
10147               }
10148               if ( dist < minDist ) {
10149                 minDist = dist;
10150                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10151               }
10152               else
10153                 fIt = freeFaceList.erase( fIt++ );
10154             }
10155           }
10156         } // choose one of several free faces of a volume
10157
10158         if ( freeFaceList.size() == 1 ) {
10159           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10160           faceSet->insert( aFreeFace );
10161           // complete a node set with nodes of a found free face
10162           //           for ( iNode = 0; iNode < ; iNode++ )
10163           //             nodeSet->insert( fNodes[ iNode ] );
10164         }
10165
10166       } // loop on volumes of a side
10167
10168       //       // complete a set of faces if new nodes in a nodeSet appeared
10169       //       // ----------------------------------------------------------
10170       //       if ( nodeSetSize != nodeSet->size() ) {
10171       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10172       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10173       //           while ( fIt->more() ) { // loop on faces sharing a node
10174       //             const SMDS_MeshElement* f = fIt->next();
10175       //             if ( faceSet->find( f ) == faceSet->end() ) {
10176       //               // check if all nodes are in nodeSet and
10177       //               // complete setOfFaceNodeSet if they are
10178       //               set <const SMDS_MeshNode*> faceNodeSet;
10179       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10180       //               bool allInSet = true;
10181       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10182       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10183       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10184       //                   allInSet = false;
10185       //                 else
10186       //                   faceNodeSet.insert( n );
10187       //               }
10188       //               if ( allInSet ) {
10189       //                 faceSet->insert( f );
10190       //                 setOfFaceNodeSet.insert( faceNodeSet );
10191       //               }
10192       //             }
10193       //           }
10194       //         }
10195       //       }
10196     } // Create temporary faces, if there are volumes given
10197   } // loop on sides
10198
10199   if ( faceSet1.size() != faceSet2.size() ) {
10200     // delete temporary faces: they are in reverseElements of actual nodes
10201 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10202 //    while ( tmpFaceIt->more() )
10203 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10204 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10205 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10206 //      aMesh->RemoveElement(*tmpFaceIt);
10207     MESSAGE("Diff nb of faces");
10208     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10209   }
10210
10211   // ============================================================
10212   // 2. Find nodes to merge:
10213   //              bind a node to remove to a node to put instead
10214   // ============================================================
10215
10216   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10217   if ( theFirstNode1 != theFirstNode2 )
10218     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10219   if ( theSecondNode1 != theSecondNode2 )
10220     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10221
10222   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10223   set< long > linkIdSet; // links to process
10224   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10225
10226   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10227   list< NLink > linkList[2];
10228   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10229   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10230   // loop on links in linkList; find faces by links and append links
10231   // of the found faces to linkList
10232   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10233   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10234   {
10235     NLink link[] = { *linkIt[0], *linkIt[1] };
10236     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10237     if ( !linkIdSet.count( linkID ) )
10238       continue;
10239
10240     // by links, find faces in the face sets,
10241     // and find indices of link nodes in the found faces;
10242     // in a face set, there is only one or no face sharing a link
10243     // ---------------------------------------------------------------
10244
10245     const SMDS_MeshElement* face[] = { 0, 0 };
10246     vector<const SMDS_MeshNode*> fnodes[2];
10247     int iLinkNode[2][2];
10248     TIDSortedElemSet avoidSet;
10249     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10250       const SMDS_MeshNode* n1 = link[iSide].first;
10251       const SMDS_MeshNode* n2 = link[iSide].second;
10252       //cout << "Side " << iSide << " ";
10253       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10254       // find a face by two link nodes
10255       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10256                                                       *faceSetPtr[ iSide ], avoidSet,
10257                                                       &iLinkNode[iSide][0],
10258                                                       &iLinkNode[iSide][1] );
10259       if ( face[ iSide ])
10260       {
10261         //cout << " F " << face[ iSide]->GetID() <<endl;
10262         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10263         // put face nodes to fnodes
10264         if ( face[ iSide ]->IsQuadratic() )
10265         {
10266           // use interlaced nodes iterator
10267           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10268           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10269           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10270           while ( nIter->more() )
10271             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10272         }
10273         else
10274         {
10275           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10276                                   face[ iSide ]->end_nodes() );
10277         }
10278         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10279       }
10280     }
10281
10282     // check similarity of elements of the sides
10283     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10284       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10285       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10286         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10287       }
10288       else {
10289         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10290       }
10291       break; // do not return because it's necessary to remove tmp faces
10292     }
10293
10294     // set nodes to merge
10295     // -------------------
10296
10297     if ( face[0] && face[1] )  {
10298       const int nbNodes = face[0]->NbNodes();
10299       if ( nbNodes != face[1]->NbNodes() ) {
10300         MESSAGE("Diff nb of face nodes");
10301         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10302         break; // do not return because it s necessary to remove tmp faces
10303       }
10304       bool reverse[] = { false, false }; // order of nodes in the link
10305       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10306         // analyse link orientation in faces
10307         int i1 = iLinkNode[ iSide ][ 0 ];
10308         int i2 = iLinkNode[ iSide ][ 1 ];
10309         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10310       }
10311       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10312       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10313       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10314       {
10315         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10316                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10317       }
10318
10319       // add other links of the faces to linkList
10320       // -----------------------------------------
10321
10322       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10323         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10324         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10325         if ( !iter_isnew.second ) { // already in a set: no need to process
10326           linkIdSet.erase( iter_isnew.first );
10327         }
10328         else // new in set == encountered for the first time: add
10329         {
10330           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10331           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10332           linkList[0].push_back ( NLink( n1, n2 ));
10333           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10334         }
10335       }
10336     } // 2 faces found
10337
10338     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10339       break;
10340
10341   } // loop on link lists
10342
10343   if ( aResult == SEW_OK &&
10344        ( //linkIt[0] != linkList[0].end() ||
10345          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10346     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10347              " " << (faceSetPtr[1]->empty()));
10348     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10349   }
10350
10351   // ====================================================================
10352   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10353   // ====================================================================
10354
10355   // delete temporary faces
10356 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10357 //  while ( tmpFaceIt->more() )
10358 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10359   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10360   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10361     aMesh->RemoveElement(*tmpFaceIt);
10362
10363   if ( aResult != SEW_OK)
10364     return aResult;
10365
10366   list< int > nodeIDsToRemove;
10367   vector< const SMDS_MeshNode*> nodes;
10368   ElemFeatures elemType;
10369
10370   // loop on nodes replacement map
10371   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10372   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10373     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10374     {
10375       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10376       nodeIDsToRemove.push_back( nToRemove->GetID() );
10377       // loop on elements sharing nToRemove
10378       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10379       while ( invElemIt->more() ) {
10380         const SMDS_MeshElement* e = invElemIt->next();
10381         // get a new suite of nodes: make replacement
10382         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10383         nodes.resize( nbNodes );
10384         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10385         while ( nIt->more() ) {
10386           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10387           nnIt = nReplaceMap.find( n );
10388           if ( nnIt != nReplaceMap.end() ) {
10389             nbReplaced++;
10390             n = (*nnIt).second;
10391           }
10392           nodes[ i++ ] = n;
10393         }
10394         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10395         //         elemIDsToRemove.push_back( e->GetID() );
10396         //       else
10397         if ( nbReplaced )
10398         {
10399           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10400           aMesh->RemoveElement( e );
10401
10402           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10403           {
10404             AddToSameGroups( newElem, e, aMesh );
10405             if ( int aShapeId = e->getshapeId() )
10406               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10407           }
10408         }
10409       }
10410     }
10411
10412   Remove( nodeIDsToRemove, true );
10413
10414   return aResult;
10415 }
10416
10417 //================================================================================
10418 /*!
10419  * \brief Find corresponding nodes in two sets of faces
10420  * \param theSide1 - first face set
10421  * \param theSide2 - second first face
10422  * \param theFirstNode1 - a boundary node of set 1
10423  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10424  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10425  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10426  * \param nReplaceMap - output map of corresponding nodes
10427  * \return bool  - is a success or not
10428  */
10429 //================================================================================
10430
10431 #ifdef _DEBUG_
10432 //#define DEBUG_MATCHING_NODES
10433 #endif
10434
10435 SMESH_MeshEditor::Sew_Error
10436 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10437                                     set<const SMDS_MeshElement*>& theSide2,
10438                                     const SMDS_MeshNode*          theFirstNode1,
10439                                     const SMDS_MeshNode*          theFirstNode2,
10440                                     const SMDS_MeshNode*          theSecondNode1,
10441                                     const SMDS_MeshNode*          theSecondNode2,
10442                                     TNodeNodeMap &                nReplaceMap)
10443 {
10444   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10445
10446   nReplaceMap.clear();
10447   if ( theFirstNode1 != theFirstNode2 )
10448     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10449   if ( theSecondNode1 != theSecondNode2 )
10450     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10451
10452   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10453   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10454
10455   list< NLink > linkList[2];
10456   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10457   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10458
10459   // loop on links in linkList; find faces by links and append links
10460   // of the found faces to linkList
10461   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10462   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10463     NLink link[] = { *linkIt[0], *linkIt[1] };
10464     if ( linkSet.find( link[0] ) == linkSet.end() )
10465       continue;
10466
10467     // by links, find faces in the face sets,
10468     // and find indices of link nodes in the found faces;
10469     // in a face set, there is only one or no face sharing a link
10470     // ---------------------------------------------------------------
10471
10472     const SMDS_MeshElement* face[] = { 0, 0 };
10473     list<const SMDS_MeshNode*> notLinkNodes[2];
10474     //bool reverse[] = { false, false }; // order of notLinkNodes
10475     int nbNodes[2];
10476     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10477     {
10478       const SMDS_MeshNode* n1 = link[iSide].first;
10479       const SMDS_MeshNode* n2 = link[iSide].second;
10480       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10481       set< const SMDS_MeshElement* > facesOfNode1;
10482       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10483       {
10484         // during a loop of the first node, we find all faces around n1,
10485         // during a loop of the second node, we find one face sharing both n1 and n2
10486         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10487         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10488         while ( fIt->more() ) { // loop on faces sharing a node
10489           const SMDS_MeshElement* f = fIt->next();
10490           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10491               ! facesOfNode1.insert( f ).second ) // f encounters twice
10492           {
10493             if ( face[ iSide ] ) {
10494               MESSAGE( "2 faces per link " );
10495               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10496             }
10497             face[ iSide ] = f;
10498             faceSet->erase( f );
10499
10500             // get not link nodes
10501             int nbN = f->NbNodes();
10502             if ( f->IsQuadratic() )
10503               nbN /= 2;
10504             nbNodes[ iSide ] = nbN;
10505             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10506             int i1 = f->GetNodeIndex( n1 );
10507             int i2 = f->GetNodeIndex( n2 );
10508             int iEnd = nbN, iBeg = -1, iDelta = 1;
10509             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10510             if ( reverse ) {
10511               std::swap( iEnd, iBeg ); iDelta = -1;
10512             }
10513             int i = i2;
10514             while ( true ) {
10515               i += iDelta;
10516               if ( i == iEnd ) i = iBeg + iDelta;
10517               if ( i == i1 ) break;
10518               nodes.push_back ( f->GetNode( i ) );
10519             }
10520           }
10521         }
10522       }
10523     }
10524     // check similarity of elements of the sides
10525     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10526       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10527       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10528         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10529       }
10530       else {
10531         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10532       }
10533     }
10534
10535     // set nodes to merge
10536     // -------------------
10537
10538     if ( face[0] && face[1] )  {
10539       if ( nbNodes[0] != nbNodes[1] ) {
10540         MESSAGE("Diff nb of face nodes");
10541         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10542       }
10543 #ifdef DEBUG_MATCHING_NODES
10544       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10545                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10546                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10547 #endif
10548       int nbN = nbNodes[0];
10549       {
10550         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10551         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10552         for ( int i = 0 ; i < nbN - 2; ++i ) {
10553 #ifdef DEBUG_MATCHING_NODES
10554           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10555 #endif
10556           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10557         }
10558       }
10559
10560       // add other links of the face 1 to linkList
10561       // -----------------------------------------
10562
10563       const SMDS_MeshElement* f0 = face[0];
10564       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10565       for ( int i = 0; i < nbN; i++ )
10566       {
10567         const SMDS_MeshNode* n2 = f0->GetNode( i );
10568         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10569           linkSet.insert( SMESH_TLink( n1, n2 ));
10570         if ( !iter_isnew.second ) { // already in a set: no need to process
10571           linkSet.erase( iter_isnew.first );
10572         }
10573         else // new in set == encountered for the first time: add
10574         {
10575 #ifdef DEBUG_MATCHING_NODES
10576           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10577                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10578 #endif
10579           linkList[0].push_back ( NLink( n1, n2 ));
10580           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10581         }
10582         n1 = n2;
10583       }
10584     } // 2 faces found
10585   } // loop on link lists
10586
10587   return SEW_OK;
10588 }
10589
10590 //================================================================================
10591 /*!
10592  * \brief Create elements equal (on same nodes) to given ones
10593  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10594  *              elements of the uppest dimension are duplicated.
10595  */
10596 //================================================================================
10597
10598 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10599 {
10600   ClearLastCreated();
10601   SMESHDS_Mesh* mesh = GetMeshDS();
10602
10603   // get an element type and an iterator over elements
10604
10605   SMDSAbs_ElementType type = SMDSAbs_All;
10606   SMDS_ElemIteratorPtr elemIt;
10607   vector< const SMDS_MeshElement* > allElems;
10608   if ( theElements.empty() )
10609   {
10610     if ( mesh->NbNodes() == 0 )
10611       return;
10612     // get most complex type
10613     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10614       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10615       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10616     };
10617     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10618       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10619       {
10620         type = types[i];
10621         break;
10622       }
10623     // put all elements in the vector <allElems>
10624     allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10625     elemIt = mesh->elementsIterator( type );
10626     while ( elemIt->more() )
10627       allElems.push_back( elemIt->next());
10628     elemIt = elemSetIterator( allElems );
10629   }
10630   else
10631   {
10632     type = (*theElements.begin())->GetType();
10633     elemIt = elemSetIterator( theElements );
10634   }
10635
10636   // duplicate elements
10637
10638   ElemFeatures elemType;
10639
10640   vector< const SMDS_MeshNode* > nodes;
10641   while ( elemIt->more() )
10642   {
10643     const SMDS_MeshElement* elem = elemIt->next();
10644     if ( elem->GetType() != type )
10645       continue;
10646
10647     elemType.Init( elem, /*basicOnly=*/false );
10648     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10649
10650     AddElement( nodes, elemType );
10651   }
10652 }
10653
10654 //================================================================================
10655 /*!
10656   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10657   \param theElems - the list of elements (edges or faces) to be replicated
10658   The nodes for duplication could be found from these elements
10659   \param theNodesNot - list of nodes to NOT replicate
10660   \param theAffectedElems - the list of elements (cells and edges) to which the
10661   replicated nodes should be associated to.
10662   \return TRUE if operation has been completed successfully, FALSE otherwise
10663 */
10664 //================================================================================
10665
10666 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10667                                     const TIDSortedElemSet& theNodesNot,
10668                                     const TIDSortedElemSet& theAffectedElems )
10669 {
10670   myLastCreatedElems.Clear();
10671   myLastCreatedNodes.Clear();
10672
10673   if ( theElems.size() == 0 )
10674     return false;
10675
10676   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10677   if ( !aMeshDS )
10678     return false;
10679
10680   bool res = false;
10681   TNodeNodeMap anOldNodeToNewNode;
10682   // duplicate elements and nodes
10683   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10684   // replce nodes by duplications
10685   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10686   return res;
10687 }
10688
10689 //================================================================================
10690 /*!
10691   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10692   \param theMeshDS - mesh instance
10693   \param theElems - the elements replicated or modified (nodes should be changed)
10694   \param theNodesNot - nodes to NOT replicate
10695   \param theNodeNodeMap - relation of old node to new created node
10696   \param theIsDoubleElem - flag os to replicate element or modify
10697   \return TRUE if operation has been completed successfully, FALSE otherwise
10698 */
10699 //================================================================================
10700
10701 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10702                                    const TIDSortedElemSet& theElems,
10703                                    const TIDSortedElemSet& theNodesNot,
10704                                    TNodeNodeMap&           theNodeNodeMap,
10705                                    const bool              theIsDoubleElem )
10706 {
10707   // iterate through element and duplicate them (by nodes duplication)
10708   bool res = false;
10709   std::vector<const SMDS_MeshNode*> newNodes;
10710   ElemFeatures elemType;
10711
10712   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10713   for ( ;  elemItr != theElems.end(); ++elemItr )
10714   {
10715     const SMDS_MeshElement* anElem = *elemItr;
10716     if (!anElem)
10717       continue;
10718
10719     // duplicate nodes to duplicate element
10720     bool isDuplicate = false;
10721     newNodes.resize( anElem->NbNodes() );
10722     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10723     int ind = 0;
10724     while ( anIter->more() )
10725     {
10726       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10727       const SMDS_MeshNode*  aNewNode = aCurrNode;
10728       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10729       if ( n2n != theNodeNodeMap.end() )
10730       {
10731         aNewNode = n2n->second;
10732       }
10733       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10734       {
10735         // duplicate node
10736         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10737         copyPosition( aCurrNode, aNewNode );
10738         theNodeNodeMap[ aCurrNode ] = aNewNode;
10739         myLastCreatedNodes.Append( aNewNode );
10740       }
10741       isDuplicate |= (aCurrNode != aNewNode);
10742       newNodes[ ind++ ] = aNewNode;
10743     }
10744     if ( !isDuplicate )
10745       continue;
10746
10747     if ( theIsDoubleElem )
10748       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10749     else
10750       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10751
10752     res = true;
10753   }
10754   return res;
10755 }
10756
10757 //================================================================================
10758 /*!
10759   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10760   \param theNodes - identifiers of nodes to be doubled
10761   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10762   nodes. If list of element identifiers is empty then nodes are doubled but
10763   they not assigned to elements
10764   \return TRUE if operation has been completed successfully, FALSE otherwise
10765 */
10766 //================================================================================
10767
10768 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10769                                     const std::list< int >& theListOfModifiedElems )
10770 {
10771   myLastCreatedElems.Clear();
10772   myLastCreatedNodes.Clear();
10773
10774   if ( theListOfNodes.size() == 0 )
10775     return false;
10776
10777   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10778   if ( !aMeshDS )
10779     return false;
10780
10781   // iterate through nodes and duplicate them
10782
10783   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10784
10785   std::list< int >::const_iterator aNodeIter;
10786   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10787   {
10788     int aCurr = *aNodeIter;
10789     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10790     if ( !aNode )
10791       continue;
10792
10793     // duplicate node
10794
10795     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10796     if ( aNewNode )
10797     {
10798       copyPosition( aNode, aNewNode );
10799       anOldNodeToNewNode[ aNode ] = aNewNode;
10800       myLastCreatedNodes.Append( aNewNode );
10801     }
10802   }
10803
10804   // Create map of new nodes for modified elements
10805
10806   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10807
10808   std::list< int >::const_iterator anElemIter;
10809   for ( anElemIter = theListOfModifiedElems.begin();
10810         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10811   {
10812     int aCurr = *anElemIter;
10813     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10814     if ( !anElem )
10815       continue;
10816
10817     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10818
10819     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10820     int ind = 0;
10821     while ( anIter->more() )
10822     {
10823       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10824       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10825       {
10826         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10827         aNodeArr[ ind++ ] = aNewNode;
10828       }
10829       else
10830         aNodeArr[ ind++ ] = aCurrNode;
10831     }
10832     anElemToNodes[ anElem ] = aNodeArr;
10833   }
10834
10835   // Change nodes of elements
10836
10837   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10838     anElemToNodesIter = anElemToNodes.begin();
10839   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10840   {
10841     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10842     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10843     if ( anElem )
10844     {
10845       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10846     }
10847   }
10848
10849   return true;
10850 }
10851
10852 namespace {
10853
10854   //================================================================================
10855   /*!
10856   \brief Check if element located inside shape
10857   \return TRUE if IN or ON shape, FALSE otherwise
10858   */
10859   //================================================================================
10860
10861   template<class Classifier>
10862   bool isInside(const SMDS_MeshElement* theElem,
10863                 Classifier&             theClassifier,
10864                 const double            theTol)
10865   {
10866     gp_XYZ centerXYZ (0, 0, 0);
10867     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10868     while (aNodeItr->more())
10869       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10870
10871     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10872     theClassifier.Perform(aPnt, theTol);
10873     TopAbs_State aState = theClassifier.State();
10874     return (aState == TopAbs_IN || aState == TopAbs_ON );
10875   }
10876
10877   //================================================================================
10878   /*!
10879    * \brief Classifier of the 3D point on the TopoDS_Face
10880    *        with interaface suitable for isInside()
10881    */
10882   //================================================================================
10883
10884   struct _FaceClassifier
10885   {
10886     Extrema_ExtPS       _extremum;
10887     BRepAdaptor_Surface _surface;
10888     TopAbs_State        _state;
10889
10890     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10891     {
10892       _extremum.Initialize( _surface,
10893                             _surface.FirstUParameter(), _surface.LastUParameter(),
10894                             _surface.FirstVParameter(), _surface.LastVParameter(),
10895                             _surface.Tolerance(), _surface.Tolerance() );
10896     }
10897     void Perform(const gp_Pnt& aPnt, double theTol)
10898     {
10899       theTol *= theTol;
10900       _state = TopAbs_OUT;
10901       _extremum.Perform(aPnt);
10902       if ( _extremum.IsDone() )
10903         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10904           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10905     }
10906     TopAbs_State State() const
10907     {
10908       return _state;
10909     }
10910   };
10911 }
10912
10913 //================================================================================
10914 /*!
10915   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10916   This method is the first step of DoubleNodeElemGroupsInRegion.
10917   \param theElems - list of groups of elements (edges or faces) to be replicated
10918   \param theNodesNot - list of groups of nodes not to replicated
10919   \param theShape - shape to detect affected elements (element which geometric center
10920          located on or inside shape). If the shape is null, detection is done on faces orientations
10921          (select elements with a gravity center on the side given by faces normals).
10922          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10923          The replicated nodes should be associated to affected elements.
10924   \return groups of affected elements
10925   \sa DoubleNodeElemGroupsInRegion()
10926  */
10927 //================================================================================
10928
10929 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10930                                                    const TIDSortedElemSet& theNodesNot,
10931                                                    const TopoDS_Shape&     theShape,
10932                                                    TIDSortedElemSet&       theAffectedElems)
10933 {
10934   if ( theShape.IsNull() )
10935   {
10936     std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10937     std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10938     std::set<const SMDS_MeshElement*> edgesToCheck;
10939     alreadyCheckedNodes.clear();
10940     alreadyCheckedElems.clear();
10941     edgesToCheck.clear();
10942
10943     // --- iterates on elements to be replicated and get elements by back references from their nodes
10944
10945     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10946     for ( ;  elemItr != theElems.end(); ++elemItr )
10947     {
10948       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10949       if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10950         continue;
10951       gp_XYZ normal;
10952       SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10953       std::set<const SMDS_MeshNode*> nodesElem;
10954       nodesElem.clear();
10955       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10956       while ( nodeItr->more() )
10957       {
10958         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10959         nodesElem.insert(aNode);
10960       }
10961       std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10962       for (; nodit != nodesElem.end(); nodit++)
10963       {
10964         const SMDS_MeshNode* aNode = *nodit;
10965         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10966           continue;
10967         if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10968           continue;
10969         alreadyCheckedNodes.insert(aNode);
10970         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10971         while ( backElemItr->more() )
10972         {
10973           const SMDS_MeshElement* curElem = backElemItr->next();
10974           if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10975             continue;
10976           if (theElems.find(curElem) != theElems.end())
10977             continue;
10978           alreadyCheckedElems.insert(curElem);
10979           double x=0, y=0, z=0;
10980           int nb = 0;
10981           SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10982           while ( nodeItr2->more() )
10983           {
10984             const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10985             x += anotherNode->X();
10986             y += anotherNode->Y();
10987             z += anotherNode->Z();
10988             nb++;
10989           }
10990           gp_XYZ p;
10991           p.SetCoord( x/nb -aNode->X(),
10992                       y/nb -aNode->Y(),
10993                       z/nb -aNode->Z() );
10994           if (normal*p > 0)
10995           {
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         theAffectedElems.insert(anEdge);
11022       }
11023     }
11024   }
11025   else
11026   {
11027     const double aTol = Precision::Confusion();
11028     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11029     auto_ptr<_FaceClassifier>              aFaceClassifier;
11030     if ( theShape.ShapeType() == TopAbs_SOLID )
11031     {
11032       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11033       bsc3d->PerformInfinitePoint(aTol);
11034     }
11035     else if (theShape.ShapeType() == TopAbs_FACE )
11036     {
11037       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11038     }
11039
11040     // iterates on indicated elements and get elements by back references from their nodes
11041     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11042     for ( ;  elemItr != theElems.end(); ++elemItr )
11043     {
11044       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11045       if (!anElem)
11046         continue;
11047       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11048       while ( nodeItr->more() )
11049       {
11050         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11051         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11052           continue;
11053         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11054         while ( backElemItr->more() )
11055         {
11056           const SMDS_MeshElement* curElem = backElemItr->next();
11057           if ( curElem && theElems.find(curElem) == theElems.end() &&
11058               ( bsc3d.get() ?
11059                 isInside( curElem, *bsc3d, aTol ) :
11060                 isInside( curElem, *aFaceClassifier, aTol )))
11061             theAffectedElems.insert( curElem );
11062         }
11063       }
11064     }
11065   }
11066   return true;
11067 }
11068
11069 //================================================================================
11070 /*!
11071   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11072   \param theElems - group of of elements (edges or faces) to be replicated
11073   \param theNodesNot - group of nodes not to replicate
11074   \param theShape - shape to detect affected elements (element which geometric center
11075   located on or inside shape).
11076   The replicated nodes should be associated to affected elements.
11077   \return TRUE if operation has been completed successfully, FALSE otherwise
11078 */
11079 //================================================================================
11080
11081 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11082                                             const TIDSortedElemSet& theNodesNot,
11083                                             const TopoDS_Shape&     theShape )
11084 {
11085   if ( theShape.IsNull() )
11086     return false;
11087
11088   const double aTol = Precision::Confusion();
11089   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11090   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
11091   if ( theShape.ShapeType() == TopAbs_SOLID )
11092   {
11093     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11094     bsc3d->PerformInfinitePoint(aTol);
11095   }
11096   else if (theShape.ShapeType() == TopAbs_FACE )
11097   {
11098     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11099   }
11100
11101   // iterates on indicated elements and get elements by back references from their nodes
11102   TIDSortedElemSet anAffected;
11103   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11104   for ( ;  elemItr != theElems.end(); ++elemItr )
11105   {
11106     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11107     if (!anElem)
11108       continue;
11109
11110     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11111     while ( nodeItr->more() )
11112     {
11113       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11114       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11115         continue;
11116       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11117       while ( backElemItr->more() )
11118       {
11119         const SMDS_MeshElement* curElem = backElemItr->next();
11120         if ( curElem && theElems.find(curElem) == theElems.end() &&
11121              ( bsc3d ?
11122                isInside( curElem, *bsc3d, aTol ) :
11123                isInside( curElem, *aFaceClassifier, aTol )))
11124           anAffected.insert( curElem );
11125       }
11126     }
11127   }
11128   return DoubleNodes( theElems, theNodesNot, anAffected );
11129 }
11130
11131 /*!
11132  *  \brief compute an oriented angle between two planes defined by four points.
11133  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11134  *  @param p0 base of the rotation axe
11135  *  @param p1 extremity of the rotation axe
11136  *  @param g1 belongs to the first plane
11137  *  @param g2 belongs to the second plane
11138  */
11139 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11140 {
11141   gp_Vec vref(p0, p1);
11142   gp_Vec v1(p0, g1);
11143   gp_Vec v2(p0, g2);
11144   gp_Vec n1 = vref.Crossed(v1);
11145   gp_Vec n2 = vref.Crossed(v2);
11146   try {
11147     return n2.AngleWithRef(n1, vref);
11148   }
11149   catch ( Standard_Failure ) {
11150   }
11151   return Max( v1.Magnitude(), v2.Magnitude() );
11152 }
11153
11154 /*!
11155  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11156  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11157  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11158  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11159  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11160  * 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.
11161  * 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.
11162  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11163  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11164  * \param theElems - list of groups of volumes, where a group of volume is a set of
11165  *        SMDS_MeshElements sorted by Id.
11166  * \param createJointElems - if TRUE, create the elements
11167  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11168  *        the boundary between \a theDomains and the rest mesh
11169  * \return TRUE if operation has been completed successfully, FALSE otherwise
11170  */
11171 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11172                                                      bool                                 createJointElems,
11173                                                      bool                                 onAllBoundaries)
11174 {
11175   // MESSAGE("----------------------------------------------");
11176   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11177   // MESSAGE("----------------------------------------------");
11178
11179   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11180   meshDS->BuildDownWardConnectivity(true);
11181   CHRONO(50);
11182   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11183
11184   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11185   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11186   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11187
11188   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11189   std::map<int,int>celldom; // cell vtkId --> domain
11190   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11191   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11192   faceDomains.clear();
11193   celldom.clear();
11194   cellDomains.clear();
11195   nodeDomains.clear();
11196   std::map<int,int> emptyMap;
11197   std::set<int> emptySet;
11198   emptyMap.clear();
11199
11200   //MESSAGE(".. Number of domains :"<<theElems.size());
11201
11202   TIDSortedElemSet theRestDomElems;
11203   const int iRestDom  = -1;
11204   const int idom0     = onAllBoundaries ? iRestDom : 0;
11205   const int nbDomains = theElems.size();
11206
11207   // Check if the domains do not share an element
11208   for (int idom = 0; idom < nbDomains-1; idom++)
11209   {
11210     //       MESSAGE("... Check of domain #" << idom);
11211     const TIDSortedElemSet& domain = theElems[idom];
11212     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11213     for (; elemItr != domain.end(); ++elemItr)
11214     {
11215       const SMDS_MeshElement* anElem = *elemItr;
11216       int idombisdeb = idom + 1 ;
11217       // check if the element belongs to a domain further in the list
11218       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11219       {
11220         const TIDSortedElemSet& domainbis = theElems[idombis];
11221         if ( domainbis.count( anElem ))
11222         {
11223           MESSAGE(".... Domain #" << idom);
11224           MESSAGE(".... Domain #" << idombis);
11225           throw SALOME_Exception("The domains are not disjoint.");
11226           return false ;
11227         }
11228       }
11229     }
11230   }
11231
11232   for (int idom = 0; idom < nbDomains; idom++)
11233   {
11234
11235     // --- build a map (face to duplicate --> volume to modify)
11236     //     with all the faces shared by 2 domains (group of elements)
11237     //     and corresponding volume of this domain, for each shared face.
11238     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11239
11240     //MESSAGE("... Neighbors of domain #" << idom);
11241     const TIDSortedElemSet& domain = theElems[idom];
11242     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11243     for (; elemItr != domain.end(); ++elemItr)
11244     {
11245       const SMDS_MeshElement* anElem = *elemItr;
11246       if (!anElem)
11247         continue;
11248       int vtkId = anElem->getVtkId();
11249       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11250       int neighborsVtkIds[NBMAXNEIGHBORS];
11251       int downIds[NBMAXNEIGHBORS];
11252       unsigned char downTypes[NBMAXNEIGHBORS];
11253       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11254       for (int n = 0; n < nbNeighbors; n++)
11255       {
11256         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11257         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11258         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11259         {
11260           bool ok = false;
11261           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11262           {
11263             // MESSAGE("Domain " << idombis);
11264             const TIDSortedElemSet& domainbis = theElems[idombis];
11265             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11266           }
11267           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11268           {
11269             DownIdType face(downIds[n], downTypes[n]);
11270             if (!faceDomains[face].count(idom))
11271             {
11272               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11273               celldom[vtkId] = idom;
11274               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11275             }
11276             if ( !ok )
11277             {
11278               theRestDomElems.insert( elem );
11279               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11280               celldom[neighborsVtkIds[n]] = iRestDom;
11281             }
11282           }
11283         }
11284       }
11285     }
11286   }
11287
11288   //MESSAGE("Number of shared faces " << faceDomains.size());
11289   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11290
11291   // --- explore the shared faces domain by domain,
11292   //     explore the nodes of the face and see if they belong to a cell in the domain,
11293   //     which has only a node or an edge on the border (not a shared face)
11294
11295   for (int idomain = idom0; idomain < nbDomains; idomain++)
11296   {
11297     //MESSAGE("Domain " << idomain);
11298     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11299     itface = faceDomains.begin();
11300     for (; itface != faceDomains.end(); ++itface)
11301     {
11302       const std::map<int, int>& domvol = itface->second;
11303       if (!domvol.count(idomain))
11304         continue;
11305       DownIdType face = itface->first;
11306       //MESSAGE(" --- face " << face.cellId);
11307       std::set<int> oldNodes;
11308       oldNodes.clear();
11309       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11310       std::set<int>::iterator itn = oldNodes.begin();
11311       for (; itn != oldNodes.end(); ++itn)
11312       {
11313         int oldId = *itn;
11314         //MESSAGE("     node " << oldId);
11315         vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11316         for (int i=0; i<l.ncells; i++)
11317         {
11318           int vtkId = l.cells[i];
11319           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11320           if (!domain.count(anElem))
11321             continue;
11322           int vtkType = grid->GetCellType(vtkId);
11323           int downId = grid->CellIdToDownId(vtkId);
11324           if (downId < 0)
11325           {
11326             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11327             continue; // not OK at this stage of the algorithm:
11328             //no cells created after BuildDownWardConnectivity
11329           }
11330           DownIdType aCell(downId, vtkType);
11331           cellDomains[aCell][idomain] = vtkId;
11332           celldom[vtkId] = idomain;
11333           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11334         }
11335       }
11336     }
11337   }
11338
11339   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11340   //     for each shared face, get the nodes
11341   //     for each node, for each domain of the face, create a clone of the node
11342
11343   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11344   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11345   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11346
11347   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11348   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11349   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11350
11351   //MESSAGE(".. Duplication of the nodes");
11352   for (int idomain = idom0; idomain < nbDomains; idomain++)
11353   {
11354     itface = faceDomains.begin();
11355     for (; itface != faceDomains.end(); ++itface)
11356     {
11357       const std::map<int, int>& domvol = itface->second;
11358       if (!domvol.count(idomain))
11359         continue;
11360       DownIdType face = itface->first;
11361       //MESSAGE(" --- face " << face.cellId);
11362       std::set<int> oldNodes;
11363       oldNodes.clear();
11364       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11365       std::set<int>::iterator itn = oldNodes.begin();
11366       for (; itn != oldNodes.end(); ++itn)
11367       {
11368         int oldId = *itn;
11369         if (nodeDomains[oldId].empty())
11370         {
11371           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11372           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11373         }
11374         std::map<int, int>::const_iterator itdom = domvol.begin();
11375         for (; itdom != domvol.end(); ++itdom)
11376         {
11377           int idom = itdom->first;
11378           //MESSAGE("         domain " << idom);
11379           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11380           {
11381             if (nodeDomains[oldId].size() >= 2) // a multiple node
11382             {
11383               vector<int> orderedDoms;
11384               //MESSAGE("multiple node " << oldId);
11385               if (mutipleNodes.count(oldId))
11386                 orderedDoms = mutipleNodes[oldId];
11387               else
11388               {
11389                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11390                 for (; it != nodeDomains[oldId].end(); ++it)
11391                   orderedDoms.push_back(it->first);
11392               }
11393               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11394               //stringstream txt;
11395               //for (int i=0; i<orderedDoms.size(); i++)
11396               //  txt << orderedDoms[i] << " ";
11397               //MESSAGE("orderedDoms " << txt.str());
11398               mutipleNodes[oldId] = orderedDoms;
11399             }
11400             double *coords = grid->GetPoint(oldId);
11401             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11402             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11403             int newId = newNode->getVtkId();
11404             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11405             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11406           }
11407         }
11408       }
11409     }
11410   }
11411
11412   //MESSAGE(".. Creation of elements");
11413   for (int idomain = idom0; idomain < nbDomains; idomain++)
11414   {
11415     itface = faceDomains.begin();
11416     for (; itface != faceDomains.end(); ++itface)
11417     {
11418       std::map<int, int> domvol = itface->second;
11419       if (!domvol.count(idomain))
11420         continue;
11421       DownIdType face = itface->first;
11422       //MESSAGE(" --- face " << face.cellId);
11423       std::set<int> oldNodes;
11424       oldNodes.clear();
11425       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11426       int nbMultipleNodes = 0;
11427       std::set<int>::iterator itn = oldNodes.begin();
11428       for (; itn != oldNodes.end(); ++itn)
11429       {
11430         int oldId = *itn;
11431         if (mutipleNodes.count(oldId))
11432           nbMultipleNodes++;
11433       }
11434       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11435       {
11436         //MESSAGE("multiple Nodes detected on a shared face");
11437         int downId = itface->first.cellId;
11438         unsigned char cellType = itface->first.cellType;
11439         // --- shared edge or shared face ?
11440         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11441         {
11442           int nodes[3];
11443           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11444           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11445             if (mutipleNodes.count(nodes[i]))
11446               if (!mutipleNodesToFace.count(nodes[i]))
11447                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11448         }
11449         else // shared face (between two volumes)
11450         {
11451           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11452           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11453           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11454           for (int ie =0; ie < nbEdges; ie++)
11455           {
11456             int nodes[3];
11457             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11458             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11459             {
11460               vector<int> vn0 = mutipleNodes[nodes[0]];
11461               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11462               vector<int> doms;
11463               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11464                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11465                   if ( vn0[i0] == vn1[i1] )
11466                     doms.push_back( vn0[ i0 ]);
11467               if ( doms.size() > 2 )
11468               {
11469                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11470                 double *coords = grid->GetPoint(nodes[0]);
11471                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11472                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11473                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11474                 gp_Pnt gref;
11475                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11476                 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11477                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11478                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11479                 for ( size_t id = 0; id < doms.size(); id++ )
11480                 {
11481                   int idom = doms[id];
11482                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11483                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11484                   {
11485                     int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11486                     SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11487                     if (domain.count(elem))
11488                     {
11489                       SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11490                       domvol[idom] = svol;
11491                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11492                       double values[3];
11493                       vtkIdType npts = 0;
11494                       vtkIdType* pts = 0;
11495                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11496                       SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11497                       if (id ==0)
11498                       {
11499                         gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11500                         angleDom[idom] = 0;
11501                       }
11502                       else
11503                       {
11504                         gp_Pnt g(values[0], values[1], values[2]);
11505                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11506                         //MESSAGE("  angle=" << angleDom[idom]);
11507                       }
11508                       break;
11509                     }
11510                   }
11511                 }
11512                 map<double, int> sortedDom; // sort domains by angle
11513                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11514                   sortedDom[ia->second] = ia->first;
11515                 vector<int> vnodes;
11516                 vector<int> vdom;
11517                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11518                 {
11519                   vdom.push_back(ib->second);
11520                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11521                 }
11522                 for (int ino = 0; ino < nbNodes; ino++)
11523                   vnodes.push_back(nodes[ino]);
11524                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11525               }
11526             }
11527           }
11528         }
11529       }
11530     }
11531   }
11532
11533   // --- iterate on shared faces (volumes to modify, face to extrude)
11534   //     get node id's of the face (id SMDS = id VTK)
11535   //     create flat element with old and new nodes if requested
11536
11537   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11538   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11539
11540   std::map<int, std::map<long,int> > nodeQuadDomains;
11541   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11542
11543   //MESSAGE(".. Creation of elements: simple junction");
11544   if (createJointElems)
11545   {
11546     int idg;
11547     string joints2DName = "joints2D";
11548     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11549     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11550     string joints3DName = "joints3D";
11551     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11552     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11553
11554     itface = faceDomains.begin();
11555     for (; itface != faceDomains.end(); ++itface)
11556     {
11557       DownIdType face = itface->first;
11558       std::set<int> oldNodes;
11559       std::set<int>::iterator itn;
11560       oldNodes.clear();
11561       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11562
11563       std::map<int, int> domvol = itface->second;
11564       std::map<int, int>::iterator itdom = domvol.begin();
11565       int dom1 = itdom->first;
11566       int vtkVolId = itdom->second;
11567       itdom++;
11568       int dom2 = itdom->first;
11569       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11570                                                        nodeQuadDomains);
11571       stringstream grpname;
11572       grpname << "j_";
11573       if (dom1 < dom2)
11574         grpname << dom1 << "_" << dom2;
11575       else
11576         grpname << dom2 << "_" << dom1;
11577       string namegrp = grpname.str();
11578       if (!mapOfJunctionGroups.count(namegrp))
11579         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11580       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11581       if (sgrp)
11582         sgrp->Add(vol->GetID());
11583       if (vol->GetType() == SMDSAbs_Volume)
11584         joints3DGrp->Add(vol->GetID());
11585       else if (vol->GetType() == SMDSAbs_Face)
11586         joints2DGrp->Add(vol->GetID());
11587     }
11588   }
11589
11590   // --- create volumes on multiple domain intersection if requested
11591   //     iterate on mutipleNodesToFace
11592   //     iterate on edgesMultiDomains
11593
11594   //MESSAGE(".. Creation of elements: multiple junction");
11595   if (createJointElems)
11596   {
11597     // --- iterate on mutipleNodesToFace
11598
11599     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11600     for (; itn != mutipleNodesToFace.end(); ++itn)
11601     {
11602       int node = itn->first;
11603       vector<int> orderDom = itn->second;
11604       vector<vtkIdType> orderedNodes;
11605       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11606         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11607       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11608
11609       stringstream grpname;
11610       grpname << "m2j_";
11611       grpname << 0 << "_" << 0;
11612       int idg;
11613       string namegrp = grpname.str();
11614       if (!mapOfJunctionGroups.count(namegrp))
11615         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11616       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11617       if (sgrp)
11618         sgrp->Add(face->GetID());
11619     }
11620
11621     // --- iterate on edgesMultiDomains
11622
11623     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11624     for (; ite != edgesMultiDomains.end(); ++ite)
11625     {
11626       vector<int> nodes = ite->first;
11627       vector<int> orderDom = ite->second;
11628       vector<vtkIdType> orderedNodes;
11629       if (nodes.size() == 2)
11630       {
11631         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11632         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11633           if ( orderDom.size() == 3 )
11634             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11635               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11636           else
11637             for (int idom = orderDom.size()-1; idom >=0; idom--)
11638               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11639         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11640
11641         int idg;
11642         string namegrp = "jointsMultiples";
11643         if (!mapOfJunctionGroups.count(namegrp))
11644           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11645         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11646         if (sgrp)
11647           sgrp->Add(vol->GetID());
11648       }
11649       else
11650       {
11651         //INFOS("Quadratic multiple joints not implemented");
11652         // TODO quadratic nodes
11653       }
11654     }
11655   }
11656
11657   // --- list the explicit faces and edges of the mesh that need to be modified,
11658   //     i.e. faces and edges built with one or more duplicated nodes.
11659   //     associate these faces or edges to their corresponding domain.
11660   //     only the first domain found is kept when a face or edge is shared
11661
11662   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11663   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11664   faceOrEdgeDom.clear();
11665   feDom.clear();
11666
11667   //MESSAGE(".. Modification of elements");
11668   for (int idomain = idom0; idomain < nbDomains; idomain++)
11669   {
11670     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11671     for (; itnod != nodeDomains.end(); ++itnod)
11672     {
11673       int oldId = itnod->first;
11674       //MESSAGE("     node " << oldId);
11675       vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11676       for (int i = 0; i < l.ncells; i++)
11677       {
11678         int vtkId = l.cells[i];
11679         int vtkType = grid->GetCellType(vtkId);
11680         int downId = grid->CellIdToDownId(vtkId);
11681         if (downId < 0)
11682           continue; // new cells: not to be modified
11683         DownIdType aCell(downId, vtkType);
11684         int volParents[1000];
11685         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11686         for (int j = 0; j < nbvol; j++)
11687           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11688             if (!feDom.count(vtkId))
11689             {
11690               feDom[vtkId] = idomain;
11691               faceOrEdgeDom[aCell] = emptyMap;
11692               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11693               //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11694               //        << " type " << vtkType << " downId " << downId);
11695             }
11696       }
11697     }
11698   }
11699
11700   // --- iterate on shared faces (volumes to modify, face to extrude)
11701   //     get node id's of the face
11702   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11703
11704   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11705   for (int m=0; m<3; m++)
11706   {
11707     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11708     itface = (*amap).begin();
11709     for (; itface != (*amap).end(); ++itface)
11710     {
11711       DownIdType face = itface->first;
11712       std::set<int> oldNodes;
11713       std::set<int>::iterator itn;
11714       oldNodes.clear();
11715       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11716       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11717       std::map<int, int> localClonedNodeIds;
11718
11719       std::map<int, int> domvol = itface->second;
11720       std::map<int, int>::iterator itdom = domvol.begin();
11721       for (; itdom != domvol.end(); ++itdom)
11722       {
11723         int idom = itdom->first;
11724         int vtkVolId = itdom->second;
11725         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11726         localClonedNodeIds.clear();
11727         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11728         {
11729           int oldId = *itn;
11730           if (nodeDomains[oldId].count(idom))
11731           {
11732             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11733             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11734           }
11735         }
11736         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11737       }
11738     }
11739   }
11740
11741   // Remove empty groups (issue 0022812)
11742   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11743   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11744   {
11745     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11746       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11747   }
11748
11749   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11750   grid->DeleteLinks();
11751
11752   CHRONOSTOP(50);
11753   counters::stats();
11754   return true;
11755 }
11756
11757 /*!
11758  * \brief Double nodes on some external faces and create flat elements.
11759  * Flat elements are mainly used by some types of mechanic calculations.
11760  *
11761  * Each group of the list must be constituted of faces.
11762  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11763  * @param theElems - list of groups of faces, where a group of faces is a set of
11764  * SMDS_MeshElements sorted by Id.
11765  * @return TRUE if operation has been completed successfully, FALSE otherwise
11766  */
11767 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11768 {
11769   // MESSAGE("-------------------------------------------------");
11770   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11771   // MESSAGE("-------------------------------------------------");
11772
11773   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11774
11775   // --- For each group of faces
11776   //     duplicate the nodes, create a flat element based on the face
11777   //     replace the nodes of the faces by their clones
11778
11779   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11780   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11781   clonedNodes.clear();
11782   intermediateNodes.clear();
11783   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11784   mapOfJunctionGroups.clear();
11785
11786   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11787   {
11788     const TIDSortedElemSet&           domain = theElems[idom];
11789     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11790     for ( ; elemItr != domain.end(); ++elemItr )
11791     {
11792       SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11793       SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11794       if (!aFace)
11795         continue;
11796       // MESSAGE("aFace=" << aFace->GetID());
11797       bool isQuad = aFace->IsQuadratic();
11798       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11799
11800       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11801
11802       SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11803       while (nodeIt->more())
11804       {
11805         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11806         bool isMedium = isQuad && (aFace->IsMediumNode(node));
11807         if (isMedium)
11808           ln2.push_back(node);
11809         else
11810           ln0.push_back(node);
11811
11812         const SMDS_MeshNode* clone = 0;
11813         if (!clonedNodes.count(node))
11814         {
11815           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11816           copyPosition( node, clone );
11817           clonedNodes[node] = clone;
11818         }
11819         else
11820           clone = clonedNodes[node];
11821
11822         if (isMedium)
11823           ln3.push_back(clone);
11824         else
11825           ln1.push_back(clone);
11826
11827         const SMDS_MeshNode* inter = 0;
11828         if (isQuad && (!isMedium))
11829         {
11830           if (!intermediateNodes.count(node))
11831           {
11832             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11833             copyPosition( node, inter );
11834             intermediateNodes[node] = inter;
11835           }
11836           else
11837             inter = intermediateNodes[node];
11838           ln4.push_back(inter);
11839         }
11840       }
11841
11842       // --- extrude the face
11843
11844       vector<const SMDS_MeshNode*> ln;
11845       SMDS_MeshVolume* vol = 0;
11846       vtkIdType aType = aFace->GetVtkType();
11847       switch (aType)
11848       {
11849       case VTK_TRIANGLE:
11850         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11851         // MESSAGE("vol prism " << vol->GetID());
11852         ln.push_back(ln1[0]);
11853         ln.push_back(ln1[1]);
11854         ln.push_back(ln1[2]);
11855         break;
11856       case VTK_QUAD:
11857         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11858         // MESSAGE("vol hexa " << vol->GetID());
11859         ln.push_back(ln1[0]);
11860         ln.push_back(ln1[1]);
11861         ln.push_back(ln1[2]);
11862         ln.push_back(ln1[3]);
11863         break;
11864       case VTK_QUADRATIC_TRIANGLE:
11865         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11866                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11867         // MESSAGE("vol quad prism " << vol->GetID());
11868         ln.push_back(ln1[0]);
11869         ln.push_back(ln1[1]);
11870         ln.push_back(ln1[2]);
11871         ln.push_back(ln3[0]);
11872         ln.push_back(ln3[1]);
11873         ln.push_back(ln3[2]);
11874         break;
11875       case VTK_QUADRATIC_QUAD:
11876         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11877         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11878         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11879         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11880                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11881                                 ln4[0], ln4[1], ln4[2], ln4[3]);
11882         // MESSAGE("vol quad hexa " << vol->GetID());
11883         ln.push_back(ln1[0]);
11884         ln.push_back(ln1[1]);
11885         ln.push_back(ln1[2]);
11886         ln.push_back(ln1[3]);
11887         ln.push_back(ln3[0]);
11888         ln.push_back(ln3[1]);
11889         ln.push_back(ln3[2]);
11890         ln.push_back(ln3[3]);
11891         break;
11892       case VTK_POLYGON:
11893         break;
11894       default:
11895         break;
11896       }
11897
11898       if (vol)
11899       {
11900         stringstream grpname;
11901         grpname << "jf_";
11902         grpname << idom;
11903         int idg;
11904         string namegrp = grpname.str();
11905         if (!mapOfJunctionGroups.count(namegrp))
11906           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11907         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11908         if (sgrp)
11909           sgrp->Add(vol->GetID());
11910       }
11911
11912       // --- modify the face
11913
11914       aFace->ChangeNodes(&ln[0], ln.size());
11915     }
11916   }
11917   return true;
11918 }
11919
11920 /*!
11921  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11922  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11923  *  groups of faces to remove inside the object, (idem edges).
11924  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11925  */
11926 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
11927                                       const TopoDS_Shape&             theShape,
11928                                       SMESH_NodeSearcher*             theNodeSearcher,
11929                                       const char*                     groupName,
11930                                       std::vector<double>&            nodesCoords,
11931                                       std::vector<std::vector<int> >& listOfListOfNodes)
11932 {
11933   // MESSAGE("--------------------------------");
11934   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11935   // MESSAGE("--------------------------------");
11936
11937   // --- zone of volumes to remove is given :
11938   //     1 either by a geom shape (one or more vertices) and a radius,
11939   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11940   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11941   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11942   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11943   //     defined by it's name.
11944
11945   SMESHDS_GroupBase* groupDS = 0;
11946   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11947   while ( groupIt->more() )
11948   {
11949     groupDS = 0;
11950     SMESH_Group * group = groupIt->next();
11951     if ( !group ) continue;
11952     groupDS = group->GetGroupDS();
11953     if ( !groupDS || groupDS->IsEmpty() ) continue;
11954     std::string grpName = group->GetName();
11955     //MESSAGE("grpName=" << grpName);
11956     if (grpName == groupName)
11957       break;
11958     else
11959       groupDS = 0;
11960   }
11961
11962   bool isNodeGroup = false;
11963   bool isNodeCoords = false;
11964   if (groupDS)
11965   {
11966     if (groupDS->GetType() != SMDSAbs_Node)
11967       return;
11968     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11969   }
11970
11971   if (nodesCoords.size() > 0)
11972     isNodeCoords = true; // a list o nodes given by their coordinates
11973   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11974
11975   // --- define groups to build
11976
11977   int idg; // --- group of SMDS volumes
11978   string grpvName = groupName;
11979   grpvName += "_vol";
11980   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11981   if (!grp)
11982   {
11983     MESSAGE("group not created " << grpvName);
11984     return;
11985   }
11986   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11987
11988   int idgs; // --- group of SMDS faces on the skin
11989   string grpsName = groupName;
11990   grpsName += "_skin";
11991   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11992   if (!grps)
11993   {
11994     MESSAGE("group not created " << grpsName);
11995     return;
11996   }
11997   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11998
11999   int idgi; // --- group of SMDS faces internal (several shapes)
12000   string grpiName = groupName;
12001   grpiName += "_internalFaces";
12002   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12003   if (!grpi)
12004   {
12005     MESSAGE("group not created " << grpiName);
12006     return;
12007   }
12008   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12009
12010   int idgei; // --- group of SMDS faces internal (several shapes)
12011   string grpeiName = groupName;
12012   grpeiName += "_internalEdges";
12013   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12014   if (!grpei)
12015   {
12016     MESSAGE("group not created " << grpeiName);
12017     return;
12018   }
12019   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12020
12021   // --- build downward connectivity
12022
12023   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12024   meshDS->BuildDownWardConnectivity(true);
12025   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12026
12027   // --- set of volumes detected inside
12028
12029   std::set<int> setOfInsideVol;
12030   std::set<int> setOfVolToCheck;
12031
12032   std::vector<gp_Pnt> gpnts;
12033   gpnts.clear();
12034
12035   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12036   {
12037     //MESSAGE("group of nodes provided");
12038     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12039     while ( elemIt->more() )
12040     {
12041       const SMDS_MeshElement* elem = elemIt->next();
12042       if (!elem)
12043         continue;
12044       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12045       if (!node)
12046         continue;
12047       SMDS_MeshElement* vol = 0;
12048       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12049       while (volItr->more())
12050       {
12051         vol = (SMDS_MeshElement*)volItr->next();
12052         setOfInsideVol.insert(vol->getVtkId());
12053         sgrp->Add(vol->GetID());
12054       }
12055     }
12056   }
12057   else if (isNodeCoords)
12058   {
12059     //MESSAGE("list of nodes coordinates provided");
12060     size_t i = 0;
12061     int k = 0;
12062     while ( i < nodesCoords.size()-2 )
12063     {
12064       double x = nodesCoords[i++];
12065       double y = nodesCoords[i++];
12066       double z = nodesCoords[i++];
12067       gp_Pnt p = gp_Pnt(x, y ,z);
12068       gpnts.push_back(p);
12069       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12070       k++;
12071     }
12072   }
12073   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12074   {
12075     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12076     TopTools_IndexedMapOfShape vertexMap;
12077     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12078     gp_Pnt p = gp_Pnt(0,0,0);
12079     if (vertexMap.Extent() < 1)
12080       return;
12081
12082     for ( int i = 1; i <= vertexMap.Extent(); ++i )
12083     {
12084       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12085       p = BRep_Tool::Pnt(vertex);
12086       gpnts.push_back(p);
12087       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12088     }
12089   }
12090
12091   if (gpnts.size() > 0)
12092   {
12093     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12094     //MESSAGE("startNode->nodeId " << nodeId);
12095
12096     double radius2 = radius*radius;
12097     //MESSAGE("radius2 " << radius2);
12098
12099     // --- volumes on start node
12100
12101     setOfVolToCheck.clear();
12102     SMDS_MeshElement* startVol = 0;
12103     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12104     while (volItr->more())
12105     {
12106       startVol = (SMDS_MeshElement*)volItr->next();
12107       setOfVolToCheck.insert(startVol->getVtkId());
12108     }
12109     if (setOfVolToCheck.empty())
12110     {
12111       MESSAGE("No volumes found");
12112       return;
12113     }
12114
12115     // --- starting with central volumes then their neighbors, check if they are inside
12116     //     or outside the domain, until no more new neighbor volume is inside.
12117     //     Fill the group of inside volumes
12118
12119     std::map<int, double> mapOfNodeDistance2;
12120     mapOfNodeDistance2.clear();
12121     std::set<int> setOfOutsideVol;
12122     while (!setOfVolToCheck.empty())
12123     {
12124       std::set<int>::iterator it = setOfVolToCheck.begin();
12125       int vtkId = *it;
12126       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12127       bool volInside = false;
12128       vtkIdType npts = 0;
12129       vtkIdType* pts = 0;
12130       grid->GetCellPoints(vtkId, npts, pts);
12131       for (int i=0; i<npts; i++)
12132       {
12133         double distance2 = 0;
12134         if (mapOfNodeDistance2.count(pts[i]))
12135         {
12136           distance2 = mapOfNodeDistance2[pts[i]];
12137           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12138         }
12139         else
12140         {
12141           double *coords = grid->GetPoint(pts[i]);
12142           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12143           distance2 = 1.E40;
12144           for ( size_t j = 0; j < gpnts.size(); j++ )
12145           {
12146             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12147             if (d2 < distance2)
12148             {
12149               distance2 = d2;
12150               if (distance2 < radius2)
12151                 break;
12152             }
12153           }
12154           mapOfNodeDistance2[pts[i]] = distance2;
12155           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12156         }
12157         if (distance2 < radius2)
12158         {
12159           volInside = true; // one or more nodes inside the domain
12160           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12161           break;
12162         }
12163       }
12164       if (volInside)
12165       {
12166         setOfInsideVol.insert(vtkId);
12167         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12168         int neighborsVtkIds[NBMAXNEIGHBORS];
12169         int downIds[NBMAXNEIGHBORS];
12170         unsigned char downTypes[NBMAXNEIGHBORS];
12171         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12172         for (int n = 0; n < nbNeighbors; n++)
12173           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12174             setOfVolToCheck.insert(neighborsVtkIds[n]);
12175       }
12176       else
12177       {
12178         setOfOutsideVol.insert(vtkId);
12179         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12180       }
12181       setOfVolToCheck.erase(vtkId);
12182     }
12183   }
12184
12185   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12186   //     If yes, add the volume to the inside set
12187
12188   bool addedInside = true;
12189   std::set<int> setOfVolToReCheck;
12190   while (addedInside)
12191   {
12192     //MESSAGE(" --------------------------- re check");
12193     addedInside = false;
12194     std::set<int>::iterator itv = setOfInsideVol.begin();
12195     for (; itv != setOfInsideVol.end(); ++itv)
12196     {
12197       int vtkId = *itv;
12198       int neighborsVtkIds[NBMAXNEIGHBORS];
12199       int downIds[NBMAXNEIGHBORS];
12200       unsigned char downTypes[NBMAXNEIGHBORS];
12201       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12202       for (int n = 0; n < nbNeighbors; n++)
12203         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12204           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12205     }
12206     setOfVolToCheck = setOfVolToReCheck;
12207     setOfVolToReCheck.clear();
12208     while  (!setOfVolToCheck.empty())
12209     {
12210       std::set<int>::iterator it = setOfVolToCheck.begin();
12211       int vtkId = *it;
12212       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12213       {
12214         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12215         int countInside = 0;
12216         int neighborsVtkIds[NBMAXNEIGHBORS];
12217         int downIds[NBMAXNEIGHBORS];
12218         unsigned char downTypes[NBMAXNEIGHBORS];
12219         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12220         for (int n = 0; n < nbNeighbors; n++)
12221           if (setOfInsideVol.count(neighborsVtkIds[n]))
12222             countInside++;
12223         //MESSAGE("countInside " << countInside);
12224         if (countInside > 1)
12225         {
12226           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12227           setOfInsideVol.insert(vtkId);
12228           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12229           addedInside = true;
12230         }
12231         else
12232           setOfVolToReCheck.insert(vtkId);
12233       }
12234       setOfVolToCheck.erase(vtkId);
12235     }
12236   }
12237
12238   // --- map of Downward faces at the boundary, inside the global volume
12239   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12240   //     fill group of SMDS faces inside the volume (when several volume shapes)
12241   //     fill group of SMDS faces on the skin of the global volume (if skin)
12242
12243   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12244   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12245   std::set<int>::iterator it = setOfInsideVol.begin();
12246   for (; it != setOfInsideVol.end(); ++it)
12247   {
12248     int vtkId = *it;
12249     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12250     int neighborsVtkIds[NBMAXNEIGHBORS];
12251     int downIds[NBMAXNEIGHBORS];
12252     unsigned char downTypes[NBMAXNEIGHBORS];
12253     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12254     for (int n = 0; n < nbNeighbors; n++)
12255     {
12256       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12257       if (neighborDim == 3)
12258       {
12259         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12260         {
12261           DownIdType face(downIds[n], downTypes[n]);
12262           boundaryFaces[face] = vtkId;
12263         }
12264         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12265         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12266         if (vtkFaceId >= 0)
12267         {
12268           sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12269           // find also the smds edges on this face
12270           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12271           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12272           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12273           for (int i = 0; i < nbEdges; i++)
12274           {
12275             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12276             if (vtkEdgeId >= 0)
12277               sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12278           }
12279         }
12280       }
12281       else if (neighborDim == 2) // skin of the volume
12282       {
12283         DownIdType face(downIds[n], downTypes[n]);
12284         skinFaces[face] = vtkId;
12285         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12286         if (vtkFaceId >= 0)
12287           sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12288       }
12289     }
12290   }
12291
12292   // --- identify the edges constituting the wire of each subshape on the skin
12293   //     define polylines with the nodes of edges, equivalent to wires
12294   //     project polylines on subshapes, and partition, to get geom faces
12295
12296   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12297   std::set<int> emptySet;
12298   emptySet.clear();
12299   std::set<int> shapeIds;
12300
12301   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12302   while (itelem->more())
12303   {
12304     const SMDS_MeshElement *elem = itelem->next();
12305     int shapeId = elem->getshapeId();
12306     int vtkId = elem->getVtkId();
12307     if (!shapeIdToVtkIdSet.count(shapeId))
12308     {
12309       shapeIdToVtkIdSet[shapeId] = emptySet;
12310       shapeIds.insert(shapeId);
12311     }
12312     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12313   }
12314
12315   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12316   std::set<DownIdType, DownIdCompare> emptyEdges;
12317   emptyEdges.clear();
12318
12319   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12320   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12321   {
12322     int shapeId = itShape->first;
12323     //MESSAGE(" --- Shape ID --- "<< shapeId);
12324     shapeIdToEdges[shapeId] = emptyEdges;
12325
12326     std::vector<int> nodesEdges;
12327
12328     std::set<int>::iterator its = itShape->second.begin();
12329     for (; its != itShape->second.end(); ++its)
12330     {
12331       int vtkId = *its;
12332       //MESSAGE("     " << vtkId);
12333       int neighborsVtkIds[NBMAXNEIGHBORS];
12334       int downIds[NBMAXNEIGHBORS];
12335       unsigned char downTypes[NBMAXNEIGHBORS];
12336       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12337       for (int n = 0; n < nbNeighbors; n++)
12338       {
12339         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12340           continue;
12341         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12342         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12343         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12344         {
12345           DownIdType edge(downIds[n], downTypes[n]);
12346           if (!shapeIdToEdges[shapeId].count(edge))
12347           {
12348             shapeIdToEdges[shapeId].insert(edge);
12349             int vtkNodeId[3];
12350             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12351             nodesEdges.push_back(vtkNodeId[0]);
12352             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12353             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12354           }
12355         }
12356       }
12357     }
12358
12359     std::list<int> order;
12360     order.clear();
12361     if (nodesEdges.size() > 0)
12362     {
12363       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12364       nodesEdges[0] = -1;
12365       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12366       nodesEdges[1] = -1; // do not reuse this edge
12367       bool found = true;
12368       while (found)
12369       {
12370         int nodeTofind = order.back(); // try first to push back
12371         int i = 0;
12372         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12373           if (nodesEdges[i] == nodeTofind)
12374             break;
12375         if ( i == (int) nodesEdges.size() )
12376           found = false; // no follower found on back
12377         else
12378         {
12379           if (i%2) // odd ==> use the previous one
12380             if (nodesEdges[i-1] < 0)
12381               found = false;
12382             else
12383             {
12384               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12385               nodesEdges[i-1] = -1;
12386             }
12387           else // even ==> use the next one
12388             if (nodesEdges[i+1] < 0)
12389               found = false;
12390             else
12391             {
12392               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12393               nodesEdges[i+1] = -1;
12394             }
12395         }
12396         if (found)
12397           continue;
12398         // try to push front
12399         found = true;
12400         nodeTofind = order.front(); // try to push front
12401         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12402           if ( nodesEdges[i] == nodeTofind )
12403             break;
12404         if ( i == (int)nodesEdges.size() )
12405         {
12406           found = false; // no predecessor found on front
12407           continue;
12408         }
12409         if (i%2) // odd ==> use the previous one
12410           if (nodesEdges[i-1] < 0)
12411             found = false;
12412           else
12413           {
12414             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12415             nodesEdges[i-1] = -1;
12416           }
12417         else // even ==> use the next one
12418           if (nodesEdges[i+1] < 0)
12419             found = false;
12420           else
12421           {
12422             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12423             nodesEdges[i+1] = -1;
12424           }
12425       }
12426     }
12427
12428
12429     std::vector<int> nodes;
12430     nodes.push_back(shapeId);
12431     std::list<int>::iterator itl = order.begin();
12432     for (; itl != order.end(); itl++)
12433     {
12434       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12435       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12436     }
12437     listOfListOfNodes.push_back(nodes);
12438   }
12439
12440   //     partition geom faces with blocFissure
12441   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12442   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12443
12444   return;
12445 }
12446
12447
12448 //================================================================================
12449 /*!
12450  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12451  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12452  * \return TRUE if operation has been completed successfully, FALSE otherwise
12453  */
12454 //================================================================================
12455
12456 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12457 {
12458   // iterates on volume elements and detect all free faces on them
12459   SMESHDS_Mesh* aMesh = GetMeshDS();
12460   if (!aMesh)
12461     return false;
12462
12463   ElemFeatures faceType( SMDSAbs_Face );
12464   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12465   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12466   while(vIt->more())
12467   {
12468     const SMDS_MeshVolume* volume = vIt->next();
12469     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12470     vTool.SetExternalNormal();
12471     const int iQuad = volume->IsQuadratic();
12472     faceType.SetQuad( iQuad );
12473     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12474     {
12475       if (!vTool.IsFreeFace(iface))
12476         continue;
12477       nbFree++;
12478       vector<const SMDS_MeshNode *> nodes;
12479       int nbFaceNodes = vTool.NbFaceNodes(iface);
12480       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12481       int inode = 0;
12482       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12483         nodes.push_back(faceNodes[inode]);
12484
12485       if (iQuad) // add medium nodes
12486       {
12487         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12488           nodes.push_back(faceNodes[inode]);
12489         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12490           nodes.push_back(faceNodes[8]);
12491       }
12492       // add new face based on volume nodes
12493       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12494       {
12495         nbExisted++; // face already exsist
12496       }
12497       else
12498       {
12499         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12500         nbCreated++;
12501       }
12502     }
12503   }
12504   return ( nbFree == ( nbExisted + nbCreated ));
12505 }
12506
12507 namespace
12508 {
12509   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12510   {
12511     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12512       return n;
12513     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12514   }
12515 }
12516 //================================================================================
12517 /*!
12518  * \brief Creates missing boundary elements
12519  *  \param elements - elements whose boundary is to be checked
12520  *  \param dimension - defines type of boundary elements to create
12521  *  \param group - a group to store created boundary elements in
12522  *  \param targetMesh - a mesh to store created boundary elements in
12523  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12524  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12525  *                                boundary elements will be copied into the targetMesh
12526  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12527  *                                boundary elements will be added into the new group
12528  *  \param aroundElements - if true, elements will be created on boundary of given
12529  *                          elements else, on boundary of the whole mesh.
12530  * \return nb of added boundary elements
12531  */
12532 //================================================================================
12533
12534 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12535                                        Bnd_Dimension           dimension,
12536                                        SMESH_Group*            group/*=0*/,
12537                                        SMESH_Mesh*             targetMesh/*=0*/,
12538                                        bool                    toCopyElements/*=false*/,
12539                                        bool                    toCopyExistingBoundary/*=false*/,
12540                                        bool                    toAddExistingBondary/*= false*/,
12541                                        bool                    aroundElements/*= false*/)
12542 {
12543   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12544   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12545   // hope that all elements are of the same type, do not check them all
12546   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12547     throw SALOME_Exception(LOCALIZED("wrong element type"));
12548
12549   if ( !targetMesh )
12550     toCopyElements = toCopyExistingBoundary = false;
12551
12552   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12553   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12554   int nbAddedBnd = 0;
12555
12556   // editor adding present bnd elements and optionally holding elements to add to the group
12557   SMESH_MeshEditor* presentEditor;
12558   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12559   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12560
12561   SMESH_MesherHelper helper( *myMesh );
12562   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12563   SMDS_VolumeTool vTool;
12564   TIDSortedElemSet avoidSet;
12565   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12566   size_t inode;
12567
12568   typedef vector<const SMDS_MeshNode*> TConnectivity;
12569   TConnectivity tgtNodes;
12570   ElemFeatures elemKind( missType ), elemToCopy;
12571
12572   vector<const SMDS_MeshElement*> presentBndElems;
12573   vector<TConnectivity>           missingBndElems;
12574   vector<int>                     freeFacets;
12575   TConnectivity nodes, elemNodes;
12576
12577   SMDS_ElemIteratorPtr eIt;
12578   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12579   else                  eIt = elemSetIterator( elements );
12580
12581   while (eIt->more())
12582   {
12583     const SMDS_MeshElement* elem = eIt->next();
12584     const int              iQuad = elem->IsQuadratic();
12585     elemKind.SetQuad( iQuad );
12586
12587     // ------------------------------------------------------------------------------------
12588     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12589     // ------------------------------------------------------------------------------------
12590     presentBndElems.clear();
12591     missingBndElems.clear();
12592     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12593     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12594     {
12595       const SMDS_MeshElement* otherVol = 0;
12596       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12597       {
12598         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12599              ( !aroundElements || elements.count( otherVol )))
12600           continue;
12601         freeFacets.push_back( iface );
12602       }
12603       if ( missType == SMDSAbs_Face )
12604         vTool.SetExternalNormal();
12605       for ( size_t i = 0; i < freeFacets.size(); ++i )
12606       {
12607         int                iface = freeFacets[i];
12608         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12609         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12610         if ( missType == SMDSAbs_Edge ) // boundary edges
12611         {
12612           nodes.resize( 2+iQuad );
12613           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12614           {
12615             for ( size_t j = 0; j < nodes.size(); ++j )
12616               nodes[ j ] = nn[ i+j ];
12617             if ( const SMDS_MeshElement* edge =
12618                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12619               presentBndElems.push_back( edge );
12620             else
12621               missingBndElems.push_back( nodes );
12622           }
12623         }
12624         else // boundary face
12625         {
12626           nodes.clear();
12627           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12628             nodes.push_back( nn[inode] ); // add corner nodes
12629           if (iQuad)
12630             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12631               nodes.push_back( nn[inode] ); // add medium nodes
12632           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12633           if ( iCenter > 0 )
12634             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12635
12636           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12637                                                                SMDSAbs_Face, /*noMedium=*/false ))
12638             presentBndElems.push_back( f );
12639           else
12640             missingBndElems.push_back( nodes );
12641
12642           if ( targetMesh != myMesh )
12643           {
12644             // add 1D elements on face boundary to be added to a new mesh
12645             const SMDS_MeshElement* edge;
12646             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12647             {
12648               if ( iQuad )
12649                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12650               else
12651                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12652               if ( edge && avoidSet.insert( edge ).second )
12653                 presentBndElems.push_back( edge );
12654             }
12655           }
12656         }
12657       }
12658     }
12659     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12660     {
12661       avoidSet.clear(), avoidSet.insert( elem );
12662       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12663                         SMDS_MeshElement::iterator() );
12664       elemNodes.push_back( elemNodes[0] );
12665       nodes.resize( 2 + iQuad );
12666       const int nbLinks = elem->NbCornerNodes();
12667       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12668       {
12669         nodes[0] = elemNodes[iN];
12670         nodes[1] = elemNodes[iN+1+iQuad];
12671         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12672           continue; // not free link
12673
12674         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12675         if ( const SMDS_MeshElement* edge =
12676              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12677           presentBndElems.push_back( edge );
12678         else
12679           missingBndElems.push_back( nodes );
12680       }
12681     }
12682
12683     // ---------------------------------
12684     // 2. Add missing boundary elements
12685     // ---------------------------------
12686     if ( targetMesh != myMesh )
12687       // instead of making a map of nodes in this mesh and targetMesh,
12688       // we create nodes with same IDs.
12689       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12690       {
12691         TConnectivity& srcNodes = missingBndElems[i];
12692         tgtNodes.resize( srcNodes.size() );
12693         for ( inode = 0; inode < srcNodes.size(); ++inode )
12694           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12695         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12696                                                                    missType,
12697                                                                    /*noMedium=*/false))
12698           continue;
12699         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12700         ++nbAddedBnd;
12701       }
12702     else
12703       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12704       {
12705         TConnectivity& nodes = missingBndElems[ i ];
12706         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12707                                                                    missType,
12708                                                                    /*noMedium=*/false))
12709           continue;
12710         SMDS_MeshElement* newElem =
12711           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12712         nbAddedBnd += bool( newElem );
12713
12714         // try to set a new element to a shape
12715         if ( myMesh->HasShapeToMesh() )
12716         {
12717           bool ok = true;
12718           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12719           const size_t nbN = nodes.size() / (iQuad+1 );
12720           for ( inode = 0; inode < nbN && ok; ++inode )
12721           {
12722             pair<int, TopAbs_ShapeEnum> i_stype =
12723               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12724             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12725               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12726           }
12727           if ( ok && mediumShapes.size() > 1 )
12728           {
12729             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12730             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12731             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12732             {
12733               if (( ok = ( stype_i->first != stype_i_0.first )))
12734                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12735                                         aMesh->IndexToShape( stype_i_0.second ));
12736             }
12737           }
12738           if ( ok && mediumShapes.begin()->first == missShapeType )
12739             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12740         }
12741       }
12742
12743     // ----------------------------------
12744     // 3. Copy present boundary elements
12745     // ----------------------------------
12746     if ( toCopyExistingBoundary )
12747       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12748       {
12749         const SMDS_MeshElement* e = presentBndElems[i];
12750         tgtNodes.resize( e->NbNodes() );
12751         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12752           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12753         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12754       }
12755     else // store present elements to add them to a group
12756       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12757       {
12758         presentEditor->myLastCreatedElems.Append( presentBndElems[ i ]);
12759       }
12760
12761   } // loop on given elements
12762
12763   // ---------------------------------------------
12764   // 4. Fill group with boundary elements
12765   // ---------------------------------------------
12766   if ( group )
12767   {
12768     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12769       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12770         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12771   }
12772   tgtEditor.myLastCreatedElems.Clear();
12773   tgtEditor2.myLastCreatedElems.Clear();
12774
12775   // -----------------------
12776   // 5. Copy given elements
12777   // -----------------------
12778   if ( toCopyElements && targetMesh != myMesh )
12779   {
12780     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12781     else                  eIt = elemSetIterator( elements );
12782     while (eIt->more())
12783     {
12784       const SMDS_MeshElement* elem = eIt->next();
12785       tgtNodes.resize( elem->NbNodes() );
12786       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12787         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12788       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12789
12790       tgtEditor.myLastCreatedElems.Clear();
12791     }
12792   }
12793   return nbAddedBnd;
12794 }
12795
12796 //================================================================================
12797 /*!
12798  * \brief Copy node position and set \a to node on the same geometry
12799  */
12800 //================================================================================
12801
12802 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12803                                      const SMDS_MeshNode* to )
12804 {
12805   if ( !from || !to ) return;
12806
12807   SMDS_PositionPtr pos = from->GetPosition();
12808   if ( !pos || from->getshapeId() < 1 ) return;
12809
12810   switch ( pos->GetTypeOfPosition() )
12811   {
12812   case SMDS_TOP_3DSPACE: break;
12813
12814   case SMDS_TOP_FACE:
12815   {
12816     const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12817     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12818                                 fPos->GetUParameter(), fPos->GetVParameter() );
12819     break;
12820   }
12821   case SMDS_TOP_EDGE:
12822   {
12823     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12824     const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12825     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12826     break;
12827   }
12828   case SMDS_TOP_VERTEX:
12829   {
12830     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12831     break;
12832   }
12833   case SMDS_TOP_UNSPEC:
12834   default:;
12835   }
12836 }
12837
12838 namespace // utils for MakePolyLine
12839 {
12840   //================================================================================
12841   /*!
12842    * \brief Sequence of found points and a current point data
12843    */
12844   struct Path
12845   {
12846     std::vector< gp_XYZ >   myPoints;
12847     double                  myLength;
12848
12849     int                     mySrcPntInd; //!< start point index
12850     const SMDS_MeshElement* myFace;
12851     SMESH_NodeXYZ           myNode1;
12852     SMESH_NodeXYZ           myNode2;
12853     int                     myNodeInd1;
12854     int                     myNodeInd2;
12855     double                  myDot1;
12856     double                  myDot2;
12857     TIDSortedElemSet        myElemSet, myAvoidSet;
12858
12859     Path(): myLength(0.0), myFace(0) {}
12860
12861     bool SetCutAtCorner( const SMESH_NodeXYZ&    cornerNode,
12862                          const SMDS_MeshElement* face,
12863                          const gp_XYZ&           plnNorm,
12864                          const gp_XYZ&           plnOrig );
12865
12866     void AddPoint( const gp_XYZ& p );
12867
12868     bool Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig );
12869
12870     static void Remove( std::vector< Path > & paths, size_t& i );
12871   };
12872
12873   //================================================================================
12874   /*!
12875    * \brief Remove a path from a vector
12876    */
12877   //================================================================================
12878
12879   void Path::Remove( std::vector< Path > & paths, size_t& i )
12880   {
12881     if ( paths.size() > 1 )
12882     {
12883       size_t j = paths.size() - 1; // last item to be removed
12884       if ( i < j )
12885       {
12886         paths[ i ].myPoints.swap( paths[ j ].myPoints );
12887         paths[ i ].myFace     = paths[ j ].myFace;
12888         paths[ i ].myNodeInd1 = paths[ j ].myNodeInd1;
12889         paths[ i ].myNodeInd2 = paths[ j ].myNodeInd2;
12890         paths[ i ].myNode1    = paths[ j ].myNode1;
12891         paths[ i ].myNode2    = paths[ j ].myNode2;
12892         paths[ i ].myLength   = paths[ j ].myLength;
12893       }
12894     }
12895     paths.pop_back();
12896     if ( i > 0 )
12897       --i;
12898   }
12899
12900   //================================================================================
12901   /*!
12902    * \brief Store a point that is at a node of a face if the face is intersected by plane.
12903    *        Return false if the node is a sole intersection point of the face and the plane
12904    */
12905   //================================================================================
12906
12907   bool Path::SetCutAtCorner( const SMESH_NodeXYZ&    cornerNode,
12908                              const SMDS_MeshElement* face,
12909                              const gp_XYZ&           plnNorm,
12910                              const gp_XYZ&           plnOrig )
12911   {
12912     if ( face == myFace )
12913       return false;
12914     myNodeInd1 = face->GetNodeIndex( cornerNode._node );
12915     myNodeInd2 = ( myNodeInd1 + 1 ) % face->NbCornerNodes();
12916     int   ind3 = ( myNodeInd1 + 2 ) % face->NbCornerNodes();
12917     myNode1.Set( face->GetNode( ind3 ));
12918     myNode2.Set( face->GetNode( myNodeInd2 ));
12919
12920     myDot1 = plnNorm * ( myNode1 - plnOrig );
12921     myDot2 = plnNorm * ( myNode2 - plnOrig );
12922
12923     bool ok = ( myDot1 * myDot2 < 0 );
12924     if ( !ok && myDot1 * myDot2 == 0 )
12925     {
12926       ok = ( myDot1 != myDot2 );
12927       if ( ok && myFace )
12928         ok = ( myFace->GetNodeIndex(( myDot1 == 0 ? myNode1 : myNode2 )._node ) < 0 );
12929     }
12930     if ( ok )
12931     {
12932       myFace = face;
12933       myDot1 = 0;
12934       AddPoint( cornerNode );
12935     }
12936     return ok;
12937   }
12938
12939   //================================================================================
12940   /*!
12941    * \brief Store a point and update myLength
12942    */
12943   //================================================================================
12944
12945   void Path::AddPoint( const gp_XYZ& p )
12946   {
12947     if ( !myPoints.empty() )
12948       myLength += ( p - myPoints.back() ).Modulus();
12949     else
12950       myLength = 0;
12951     myPoints.push_back( p );
12952   }
12953
12954   //================================================================================
12955   /*!
12956    * \brief Try to find the next point
12957    *  \param [in] plnNorm - cutting plane normal
12958    *  \param [in] plnOrig - cutting plane origin
12959    */
12960   //================================================================================
12961
12962   bool Path::Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig )
12963   {
12964     int nodeInd3 = ( myNodeInd1 + 1 ) % myFace->NbCornerNodes();
12965     if ( myNodeInd2 == nodeInd3 )
12966       nodeInd3 = ( myNodeInd1 + 2 ) % myFace->NbCornerNodes();
12967
12968     SMESH_NodeXYZ node3 = myFace->GetNode( nodeInd3 );
12969     double         dot3 = plnNorm * ( node3 - plnOrig );
12970
12971     if ( dot3 * myDot1 < 0. )
12972     {
12973       myNode2    = node3;
12974       myNodeInd2 = nodeInd3;
12975       myDot2     = dot3;
12976     }
12977     else if ( dot3 * myDot2 < 0. )
12978     {
12979       myNode1    = node3;
12980       myNodeInd1 = nodeInd3;
12981       myDot1     = dot3;
12982     }
12983     else if ( dot3 == 0. )
12984     {
12985       SMDS_ElemIteratorPtr fIt = node3._node->GetInverseElementIterator(SMDSAbs_Face);
12986       while ( fIt->more() )
12987         if ( SetCutAtCorner( node3, fIt->next(), plnNorm, plnOrig ))
12988           return true;
12989       return false;
12990     }
12991     else if ( myDot2 == 0. )
12992     {
12993       SMESH_NodeXYZ node2 = myNode2; // copy as myNode2 changes in SetCutAtCorner()
12994       SMDS_ElemIteratorPtr fIt = node2._node->GetInverseElementIterator(SMDSAbs_Face);
12995       while ( fIt->more() )
12996         if ( SetCutAtCorner( node2, fIt->next(), plnNorm, plnOrig ))
12997           return true;
12998       return false;
12999     }
13000
13001     double r = Abs( myDot1 / ( myDot2 - myDot1 ));
13002     AddPoint( myNode1 * ( 1 - r ) + myNode2 * r );
13003
13004     myAvoidSet.clear();
13005     myAvoidSet.insert( myFace );
13006     myFace = SMESH_MeshAlgos::FindFaceInSet( myNode1._node, myNode2._node,
13007                                              myElemSet,   myAvoidSet,
13008                                              &myNodeInd1, &myNodeInd2 );
13009     return myFace;
13010   }
13011
13012   //================================================================================
13013   /*!
13014    * \brief Compute a path between two points of PolySegment
13015    */
13016   struct PolyPathCompute
13017   {
13018     SMESH_MeshEditor::TListOfPolySegments& mySegments; //!< inout PolySegment's
13019     std::vector< Path >&                   myPaths;    //!< path of each of segments to compute
13020     SMESH_Mesh*                            myMesh;
13021     mutable std::vector< std::string >     myErrors;
13022
13023     PolyPathCompute( SMESH_MeshEditor::TListOfPolySegments& theSegments,
13024                      std::vector< Path >&                   thePaths,
13025                      SMESH_Mesh*                            theMesh):
13026       mySegments( theSegments ),
13027       myPaths( thePaths ),
13028       myMesh( theMesh ),
13029       myErrors( theSegments.size() )
13030     {
13031     }
13032 #undef SMESH_CAUGHT
13033 #define SMESH_CAUGHT myErrors[i] =
13034     void operator() ( const int i ) const
13035     {
13036       SMESH_TRY;
13037       const_cast< PolyPathCompute* >( this )->Compute( i );
13038       SMESH_CATCH( SMESH::returnError );
13039     }
13040 #undef SMESH_CAUGHT
13041     //================================================================================
13042     /*!
13043      * \brief Compute a path of a given segment
13044      */
13045     //================================================================================
13046
13047     void Compute( const int iSeg )
13048     {
13049       SMESH_MeshEditor::PolySegment& polySeg = mySegments[ iSeg ];
13050
13051       // get a cutting plane
13052
13053       gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13054       gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13055       if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13056       if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13057
13058       gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13059       gp_XYZ plnOrig = SMESH_NodeXYZ( polySeg.myNode1[0] );
13060
13061       // find paths connecting the 2 end points of polySeg
13062
13063       std::vector< Path > paths; paths.reserve(10);
13064
13065       // initialize paths
13066
13067       for ( int iP = 0; iP < 2; ++iP ) // loop on the polySeg end points
13068       {
13069         Path path;
13070         path.mySrcPntInd = iP;
13071         size_t nbPaths = paths.size();
13072
13073         if ( polySeg.myNode2[ iP ] && polySeg.myNode2[ iP ] != polySeg.myNode1[ iP ] )
13074         {
13075           while (( path.myFace = SMESH_MeshAlgos::FindFaceInSet( polySeg.myNode1[ iP ],
13076                                                                  polySeg.myNode2[ iP ],
13077                                                                  path.myElemSet,
13078                                                                  path.myAvoidSet,
13079                                                                  &path.myNodeInd1,
13080                                                                  &path.myNodeInd2 )))
13081           {
13082             path.myNode1.Set( polySeg.myNode1[ iP ]);
13083             path.myNode2.Set( polySeg.myNode2[ iP ]);
13084             path.myDot1 = plnNorm * ( path.myNode1 - plnOrig );
13085             path.myDot2 = plnNorm * ( path.myNode2 - plnOrig );
13086             path.myPoints.clear();
13087             path.AddPoint( 0.5 * ( path.myNode1 + path.myNode2 ));
13088             path.myAvoidSet.insert( path.myFace );
13089             paths.push_back( path );
13090           }
13091           if ( nbPaths == paths.size() )
13092             throw SALOME_Exception ( SMESH_Comment("No face edge found by point ") << iP+1
13093                                      << " in a PolySegment " << iSeg );
13094         }
13095         else // an end point is at node
13096         {
13097           std::set<const SMDS_MeshNode* > nodes;
13098           SMDS_ElemIteratorPtr fIt = polySeg.myNode1[ iP ]->GetInverseElementIterator(SMDSAbs_Face);
13099           while ( fIt->more() )
13100           {
13101             path.myPoints.clear();
13102             if ( path.SetCutAtCorner( polySeg.myNode1[ iP ], fIt->next(), plnNorm, plnOrig ))
13103             {
13104               if (( path.myDot1 * path.myDot2 != 0 ) ||
13105                   ( nodes.insert( path.myNode1._node ).second &&
13106                     nodes.insert( path.myNode2._node ).second ))
13107                 paths.push_back( path );
13108             }
13109           }
13110         }
13111
13112         // look for a one-segment path
13113         for ( size_t i = 0; i < nbPaths; ++i )
13114           for ( size_t j = nbPaths; j < paths.size(); ++j )
13115             if ( paths[i].myFace == paths[j].myFace )
13116             {
13117               myPaths[ iSeg ].myPoints.push_back( paths[i].myPoints[0] );
13118               myPaths[ iSeg ].myPoints.push_back( paths[j].myPoints[0] );
13119               paths.clear();
13120             }
13121       }
13122
13123       // extend paths
13124
13125       myPaths[ iSeg ].myLength = 1e100;
13126
13127       while ( paths.size() >= 2 )
13128       {
13129         for ( size_t i = 0; i < paths.size(); ++i )
13130         {
13131           Path& path = paths[ i ];
13132           if ( !path.Extend( plnNorm, plnOrig ) ||         // path reached a mesh boundary
13133                path.myLength > myPaths[ iSeg ].myLength ) // path is longer than others
13134           {
13135             Path::Remove( paths, i );
13136             continue;
13137           }
13138
13139           // join paths that reach same point
13140           for ( size_t j = 0; j < paths.size(); ++j )
13141           {
13142             if ( i != j &&
13143                  paths[i].myFace == paths[j].myFace &&
13144                  paths[i].mySrcPntInd != paths[j].mySrcPntInd )
13145             {
13146               double   distLast = ( paths[i].myPoints.back() - paths[j].myPoints.back() ).Modulus();
13147               double fullLength = ( paths[i].myLength + paths[j].myLength + distLast );
13148               if ( fullLength < myPaths[ iSeg ].myLength )
13149               {
13150                 myPaths[ iSeg ].myLength = fullLength;
13151                 std::vector< gp_XYZ > & allPoints = myPaths[ iSeg ].myPoints;
13152                 allPoints.swap( paths[i].myPoints );
13153                 allPoints.insert( allPoints.end(),
13154                                   paths[j].myPoints.rbegin(),
13155                                   paths[j].myPoints.rend() );
13156               }
13157               Path::Remove( paths, i );
13158               Path::Remove( paths, j );
13159             }
13160           }
13161         }
13162         if ( !paths.empty() && (int) paths[0].myPoints.size() > myMesh->NbFaces() )
13163           throw SALOME_Exception(LOCALIZED( "Infinite loop in MakePolyLine()"));
13164       }
13165
13166       return;
13167
13168     } // PolyPathCompute::Compute()
13169
13170   }; // struct PolyPathCompute
13171
13172 } // namespace
13173
13174 //=======================================================================
13175 //function : MakePolyLine
13176 //purpose  : Create a polyline consisting of 1D mesh elements each lying on a 2D element of
13177 //           the initial mesh
13178 //=======================================================================
13179
13180 void SMESH_MeshEditor::MakePolyLine( TListOfPolySegments&   theSegments,
13181                                      SMESHDS_Group*         theGroup,
13182                                      SMESH_ElementSearcher* theSearcher)
13183 {
13184   std::vector< Path > segPaths( theSegments.size() ); // path of each of segments
13185
13186   SMESH_ElementSearcher* searcher = theSearcher;
13187   SMESHUtils::Deleter<SMESH_ElementSearcher> delSearcher;
13188   if ( !searcher )
13189   {
13190     searcher = SMESH_MeshAlgos::GetElementSearcher( *GetMeshDS() );
13191     delSearcher._obj = searcher;
13192   }
13193
13194   // get cutting planes
13195
13196   std::vector< bool > isVectorOK( theSegments.size(), true );
13197
13198   for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13199   {
13200     PolySegment& polySeg = theSegments[ iSeg ];
13201
13202     gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13203     gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13204     if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13205     if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13206
13207     gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13208
13209     isVectorOK[ iSeg ] = ( plnNorm.Modulus() > std::numeric_limits<double>::min() );
13210     if ( !isVectorOK[ iSeg ])
13211     {
13212       gp_XYZ pMid = 0.5 * ( p1 + p2 );
13213       const SMDS_MeshElement* face;
13214       polySeg.myMidProjPoint = searcher->Project( pMid, SMDSAbs_Face, &face );
13215
13216       if ( polySeg.myMidProjPoint.Distance( pMid ) < Precision::Confusion() )
13217       {
13218         SMESH_MeshAlgos::FaceNormal( face, const_cast< gp_XYZ& >( polySeg.myVector.XYZ() ));
13219         polySeg.myMidProjPoint = pMid + polySeg.myVector.XYZ();
13220       }
13221     }
13222   }
13223
13224   // assure that inverse elements are constructed, avoid their concurrent building in threads
13225   GetMeshDS()->nodesIterator()->next()->NbInverseElements();
13226
13227   // find paths
13228
13229   PolyPathCompute algo( theSegments, segPaths, myMesh );
13230   OSD_Parallel::For( 0, theSegments.size(), algo, theSegments.size() == 1 );
13231
13232   for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13233     if ( !algo.myErrors[ iSeg ].empty() )
13234       throw SALOME_Exception( algo.myErrors[ iSeg ].c_str() );
13235
13236   // create an 1D mesh
13237
13238   const SMDS_MeshNode *n, *nPrev = 0;
13239   SMESHDS_Mesh* mesh = GetMeshDS();
13240
13241   for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13242   {
13243     const Path& path = segPaths[iSeg];
13244     if ( path.myPoints.size() < 2 )
13245       continue;
13246
13247     double tol = path.myLength / path.myPoints.size() / 1000.;
13248     if ( !nPrev || ( SMESH_NodeXYZ( nPrev ) - path.myPoints[0] ).SquareModulus() > tol*tol )
13249     {
13250       nPrev = mesh->AddNode( path.myPoints[0].X(), path.myPoints[0].Y(), path.myPoints[0].Z() );
13251       myLastCreatedNodes.Append( nPrev );
13252     }
13253     for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13254     {
13255       n = mesh->AddNode( path.myPoints[iP].X(), path.myPoints[iP].Y(), path.myPoints[iP].Z() );
13256       myLastCreatedNodes.Append( n );
13257
13258       const SMDS_MeshElement* elem = mesh->AddEdge( nPrev, n );
13259       myLastCreatedElems.Append( elem );
13260       if ( theGroup )
13261         theGroup->Add( elem );
13262
13263       nPrev = n;
13264     }
13265
13266     // return a vector
13267
13268     if ( isVectorOK[ iSeg ])
13269     {
13270       // find the most distance point of a path
13271       double maxDist = 0;
13272       for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13273       {
13274         double dist = theSegments[iSeg].myVector * ( path.myPoints[iP] - path.myPoints[0] );
13275         if ( dist > maxDist )
13276         {
13277           maxDist = dist;
13278           theSegments[iSeg].myMidProjPoint = path.myPoints[iP];
13279         }
13280       }
13281     }
13282     gp_XYZ pMid = 0.5 * ( path.myPoints[0] + path.myPoints.back() );
13283     theSegments[iSeg].myVector = gp_Vec( pMid, theSegments[iSeg].myMidProjPoint );
13284   }
13285
13286   return;
13287 }