Salome HOME
Regression of smesh/mesh_Projection_2D_01/B7
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2015  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // File      : SMESH_MeshEditor.cxx
24 // Created   : Mon Apr 12 16:10:22 2004
25 // Author    : Edward AGAPOV (eap)
26
27 #include "SMESH_MeshEditor.hxx"
28
29 #include "SMDS_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
38
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
41
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MeshAlgos.hxx"
46 #include "SMESH_MesherHelper.hxx"
47 #include "SMESH_OctreeNode.hxx"
48 #include "SMESH_subMesh.hxx"
49
50 #include <Basics_OCCTVersion.hxx>
51
52 #include "utilities.h"
53 #include "chrono.hxx"
54
55 #include <BRepAdaptor_Surface.hxx>
56 #include <BRepBuilderAPI_MakeEdge.hxx>
57 #include <BRepClass3d_SolidClassifier.hxx>
58 #include <BRep_Tool.hxx>
59 #include <ElCLib.hxx>
60 #include <Extrema_GenExtPS.hxx>
61 #include <Extrema_POnCurv.hxx>
62 #include <Extrema_POnSurf.hxx>
63 #include <Geom2d_Curve.hxx>
64 #include <GeomAdaptor_Surface.hxx>
65 #include <Geom_Curve.hxx>
66 #include <Geom_Surface.hxx>
67 #include <Precision.hxx>
68 #include <TColStd_ListOfInteger.hxx>
69 #include <TopAbs_State.hxx>
70 #include <TopExp.hxx>
71 #include <TopExp_Explorer.hxx>
72 #include <TopTools_ListIteratorOfListOfShape.hxx>
73 #include <TopTools_ListOfShape.hxx>
74 #include <TopTools_SequenceOfShape.hxx>
75 #include <TopoDS.hxx>
76 #include <TopoDS_Face.hxx>
77 #include <TopoDS_Solid.hxx>
78 #include <gp.hxx>
79 #include <gp_Ax1.hxx>
80 #include <gp_Dir.hxx>
81 #include <gp_Lin.hxx>
82 #include <gp_Pln.hxx>
83 #include <gp_Trsf.hxx>
84 #include <gp_Vec.hxx>
85 #include <gp_XY.hxx>
86 #include <gp_XYZ.hxx>
87
88 #include <cmath>
89
90 #include <map>
91 #include <set>
92 #include <numeric>
93 #include <limits>
94 #include <algorithm>
95 #include <sstream>
96
97 #include <boost/tuple/tuple.hpp>
98
99 #include <Standard_Failure.hxx>
100 #include <Standard_ErrorHandler.hxx>
101
102 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
103
104 using namespace std;
105 using namespace SMESH::Controls;
106
107 namespace
108 {
109   template < class ELEM_SET >
110   SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
111   {
112     typedef SMDS_SetIterator
113       < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
114     return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
115   }
116 }
117
118 //=======================================================================
119 //function : SMESH_MeshEditor
120 //purpose  :
121 //=======================================================================
122
123 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
124   :myMesh( theMesh ) // theMesh may be NULL
125 {
126 }
127
128 //================================================================================
129 /*!
130  * \brief Clears myLastCreatedNodes and myLastCreatedElems
131  */
132 //================================================================================
133
134 void SMESH_MeshEditor::ClearLastCreated()
135 {
136   myLastCreatedNodes.Clear();
137   myLastCreatedElems.Clear();
138 }
139
140
141 //=======================================================================
142 /*!
143  * \brief Add element
144  */
145 //=======================================================================
146
147 SMDS_MeshElement*
148 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
149                              const SMDSAbs_ElementType            type,
150                              const bool                           isPoly,
151                              const int                            ID,
152                              const double                         ballDiameter)
153 {
154   //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
155   SMDS_MeshElement* e = 0;
156   int nbnode = node.size();
157   SMESHDS_Mesh* mesh = GetMeshDS();
158   switch ( type ) {
159   case SMDSAbs_Face:
160     if ( !isPoly ) {
161       if      (nbnode == 3) {
162         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
163         else           e = mesh->AddFace      (node[0], node[1], node[2] );
164       }
165       else if (nbnode == 4) {
166         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
167         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
168       }
169       else if (nbnode == 6) {
170         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
171                                                node[4], node[5], ID);
172         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
173                                                node[4], node[5] );
174       }
175       else if (nbnode == 7) {
176         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
177                                                node[4], node[5], node[6], ID);
178         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
179                                                node[4], node[5], node[6] );
180       }
181       else if (nbnode == 8) {
182         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
183                                                node[4], node[5], node[6], node[7], ID);
184         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
185                                                node[4], node[5], node[6], node[7] );
186       }
187       else if (nbnode == 9) {
188         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
189                                                node[4], node[5], node[6], node[7], node[8], ID);
190         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
191                                                node[4], node[5], node[6], node[7], node[8] );
192       }
193     } else {
194       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
195       else           e = mesh->AddPolygonalFace      (node    );
196     }
197     break;
198
199   case SMDSAbs_Volume:
200     if ( !isPoly ) {
201       if      (nbnode == 4) {
202         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
203         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
204       }
205       else if (nbnode == 5) {
206         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
207                                                  node[4], ID);
208         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
209                                                  node[4] );
210       }
211       else if (nbnode == 6) {
212         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
213                                                  node[4], node[5], ID);
214         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
215                                                  node[4], node[5] );
216       }
217       else if (nbnode == 8) {
218         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
219                                                  node[4], node[5], node[6], node[7], ID);
220         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
221                                                  node[4], node[5], node[6], node[7] );
222       }
223       else if (nbnode == 10) {
224         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
225                                                  node[4], node[5], node[6], node[7],
226                                                  node[8], node[9], ID);
227         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
228                                                  node[4], node[5], node[6], node[7],
229                                                  node[8], node[9] );
230       }
231       else if (nbnode == 12) {
232         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
233                                                  node[4], node[5], node[6], node[7],
234                                                  node[8], node[9], node[10], node[11], ID);
235         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
236                                                  node[4], node[5], node[6], node[7],
237                                                  node[8], node[9], node[10], node[11] );
238       }
239       else if (nbnode == 13) {
240         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
241                                                  node[4], node[5], node[6], node[7],
242                                                  node[8], node[9], node[10],node[11],
243                                                  node[12],ID);
244         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
245                                                  node[4], node[5], node[6], node[7],
246                                                  node[8], node[9], node[10],node[11],
247                                                  node[12] );
248       }
249       else if (nbnode == 15) {
250         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
251                                                  node[4], node[5], node[6], node[7],
252                                                  node[8], node[9], node[10],node[11],
253                                                  node[12],node[13],node[14],ID);
254         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
255                                                  node[4], node[5], node[6], node[7],
256                                                  node[8], node[9], node[10],node[11],
257                                                  node[12],node[13],node[14] );
258       }
259       else if (nbnode == 20) {
260         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
261                                                  node[4], node[5], node[6], node[7],
262                                                  node[8], node[9], node[10],node[11],
263                                                  node[12],node[13],node[14],node[15],
264                                                  node[16],node[17],node[18],node[19],ID);
265         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
266                                                  node[4], node[5], node[6], node[7],
267                                                  node[8], node[9], node[10],node[11],
268                                                  node[12],node[13],node[14],node[15],
269                                                  node[16],node[17],node[18],node[19] );
270       }
271       else if (nbnode == 27) {
272         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
273                                                  node[4], node[5], node[6], node[7],
274                                                  node[8], node[9], node[10],node[11],
275                                                  node[12],node[13],node[14],node[15],
276                                                  node[16],node[17],node[18],node[19],
277                                                  node[20],node[21],node[22],node[23],
278                                                  node[24],node[25],node[26], ID);
279         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
280                                                  node[4], node[5], node[6], node[7],
281                                                  node[8], node[9], node[10],node[11],
282                                                  node[12],node[13],node[14],node[15],
283                                                  node[16],node[17],node[18],node[19],
284                                                  node[20],node[21],node[22],node[23],
285                                                  node[24],node[25],node[26] );
286       }
287     }
288     break;
289
290   case SMDSAbs_Edge:
291     if ( nbnode == 2 ) {
292       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
293       else           e = mesh->AddEdge      (node[0], node[1] );
294     }
295     else if ( nbnode == 3 ) {
296       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
297       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
298     }
299     break;
300
301   case SMDSAbs_0DElement:
302     if ( nbnode == 1 ) {
303       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
304       else           e = mesh->Add0DElement      (node[0] );
305     }
306     break;
307
308   case SMDSAbs_Node:
309     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
310     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z());
311     break;
312
313   case SMDSAbs_Ball:
314     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
315     else           e = mesh->AddBall      (node[0], ballDiameter);
316     break;
317
318   default:;
319   }
320   if ( e ) myLastCreatedElems.Append( e );
321   return e;
322 }
323
324 //=======================================================================
325 /*!
326  * \brief Add element
327  */
328 //=======================================================================
329
330 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> &       nodeIDs,
331                                                const SMDSAbs_ElementType type,
332                                                const bool                isPoly,
333                                                const int                 ID)
334 {
335   vector<const SMDS_MeshNode*> nodes;
336   nodes.reserve( nodeIDs.size() );
337   vector<int>::const_iterator id = nodeIDs.begin();
338   while ( id != nodeIDs.end() ) {
339     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
340       nodes.push_back( node );
341     else
342       return 0;
343   }
344   return AddElement( nodes, type, isPoly, ID );
345 }
346
347 //=======================================================================
348 //function : Remove
349 //purpose  : Remove a node or an element.
350 //           Modify a compute state of sub-meshes which become empty
351 //=======================================================================
352
353 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
354                               const bool         isNodes )
355 {
356   myLastCreatedElems.Clear();
357   myLastCreatedNodes.Clear();
358
359   SMESHDS_Mesh* aMesh = GetMeshDS();
360   set< SMESH_subMesh *> smmap;
361
362   int removed = 0;
363   list<int>::const_iterator it = theIDs.begin();
364   for ( ; it != theIDs.end(); it++ ) {
365     const SMDS_MeshElement * elem;
366     if ( isNodes )
367       elem = aMesh->FindNode( *it );
368     else
369       elem = aMesh->FindElement( *it );
370     if ( !elem )
371       continue;
372
373     // Notify VERTEX sub-meshes about modification
374     if ( isNodes ) {
375       const SMDS_MeshNode* node = cast2Node( elem );
376       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
377         if ( int aShapeID = node->getshapeId() )
378           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
379             smmap.insert( sm );
380     }
381     // Find sub-meshes to notify about modification
382     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
383     //     while ( nodeIt->more() ) {
384     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
385     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
386     //       if ( aPosition.get() ) {
387     //         if ( int aShapeID = aPosition->GetShapeId() ) {
388     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
389     //             smmap.insert( sm );
390     //         }
391     //       }
392     //     }
393
394     // Do remove
395     if ( isNodes )
396       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
397     else
398       aMesh->RemoveElement( elem );
399     removed++;
400   }
401
402   // Notify sub-meshes about modification
403   if ( !smmap.empty() ) {
404     set< SMESH_subMesh *>::iterator smIt;
405     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
406       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
407   }
408
409   //   // Check if the whole mesh becomes empty
410   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
411   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
412
413   return removed;
414 }
415
416 //================================================================================
417 /*!
418  * \brief Create 0D elements on all nodes of the given object except those
419  *        nodes on which a 0D element already exists.
420  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
421  *                    the all mesh is treated
422  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
423  */
424 //================================================================================
425
426 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
427                                                    TIDSortedElemSet&       all0DElems )
428 {
429   SMDS_ElemIteratorPtr elemIt;
430   vector< const SMDS_MeshElement* > allNodes;
431   if ( elements.empty() )
432   {
433     allNodes.reserve( GetMeshDS()->NbNodes() );
434     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
435     while ( elemIt->more() )
436       allNodes.push_back( elemIt->next() );
437
438     elemIt = elemSetIterator( allNodes );
439   }
440   else
441   {
442     elemIt = elemSetIterator( elements );
443   }
444
445   while ( elemIt->more() )
446   {
447     const SMDS_MeshElement* e = elemIt->next();
448     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
449     while ( nodeIt->more() )
450     {
451       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
452       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
453       if ( it0D->more() )
454         all0DElems.insert( it0D->next() );
455       else {
456         myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
457         all0DElems.insert( myLastCreatedElems.Last() );
458       }
459     }
460   }
461 }
462
463 //=======================================================================
464 //function : FindShape
465 //purpose  : Return an index of the shape theElem is on
466 //           or zero if a shape not found
467 //=======================================================================
468
469 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
470 {
471   myLastCreatedElems.Clear();
472   myLastCreatedNodes.Clear();
473
474   SMESHDS_Mesh * aMesh = GetMeshDS();
475   if ( aMesh->ShapeToMesh().IsNull() )
476     return 0;
477
478   int aShapeID = theElem->getshapeId();
479   if ( aShapeID < 1 )
480     return 0;
481
482   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
483     if ( sm->Contains( theElem ))
484       return aShapeID;
485
486   if ( theElem->GetType() == SMDSAbs_Node ) {
487     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
488   }
489   else {
490     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
491   }
492
493   TopoDS_Shape aShape; // the shape a node of theElem is on
494   if ( theElem->GetType() != SMDSAbs_Node )
495   {
496     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
497     while ( nodeIt->more() ) {
498       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
499       if ((aShapeID = node->getshapeId()) > 0) {
500         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
501           if ( sm->Contains( theElem ))
502             return aShapeID;
503           if ( aShape.IsNull() )
504             aShape = aMesh->IndexToShape( aShapeID );
505         }
506       }
507     }
508   }
509
510   // None of nodes is on a proper shape,
511   // find the shape among ancestors of aShape on which a node is
512   if ( !aShape.IsNull() ) {
513     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
514     for ( ; ancIt.More(); ancIt.Next() ) {
515       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
516       if ( sm && sm->Contains( theElem ))
517         return aMesh->ShapeToIndex( ancIt.Value() );
518     }
519   }
520   else
521   {
522     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
523     while ( const SMESHDS_SubMesh* sm = smIt->next() )
524       if ( sm->Contains( theElem ))
525         return sm->GetID();
526   }
527
528   return 0;
529 }
530
531 //=======================================================================
532 //function : IsMedium
533 //purpose  :
534 //=======================================================================
535
536 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
537                                 const SMDSAbs_ElementType typeToCheck)
538 {
539   bool isMedium = false;
540   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
541   while (it->more() && !isMedium ) {
542     const SMDS_MeshElement* elem = it->next();
543     isMedium = elem->IsMediumNode(node);
544   }
545   return isMedium;
546 }
547
548 //=======================================================================
549 //function : shiftNodesQuadTria
550 //purpose  : Shift nodes in the array corresponded to quadratic triangle
551 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
552 //=======================================================================
553
554 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
555 {
556   const SMDS_MeshNode* nd1 = aNodes[0];
557   aNodes[0] = aNodes[1];
558   aNodes[1] = aNodes[2];
559   aNodes[2] = nd1;
560   const SMDS_MeshNode* nd2 = aNodes[3];
561   aNodes[3] = aNodes[4];
562   aNodes[4] = aNodes[5];
563   aNodes[5] = nd2;
564 }
565
566 //=======================================================================
567 //function : nbEdgeConnectivity
568 //purpose  : return number of the edges connected with the theNode.
569 //           if theEdges has connections with the other type of the
570 //           elements, return -1
571 //=======================================================================
572
573 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
574 {
575   // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
576   // int nb=0;
577   // while(elemIt->more()) {
578   //   elemIt->next();
579   //   nb++;
580   // }
581   // return nb;
582   return theNode->NbInverseElements();
583 }
584
585 //=======================================================================
586 //function : getNodesFromTwoTria
587 //purpose  : 
588 //=======================================================================
589
590 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
591                                 const SMDS_MeshElement * theTria2,
592                                 vector< const SMDS_MeshNode*>& N1,
593                                 vector< const SMDS_MeshNode*>& N2)
594 {
595   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
596   if ( N1.size() < 6 ) return false;
597   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
598   if ( N2.size() < 6 ) return false;
599
600   int sames[3] = {-1,-1,-1};
601   int nbsames = 0;
602   int i, j;
603   for(i=0; i<3; i++) {
604     for(j=0; j<3; j++) {
605       if(N1[i]==N2[j]) {
606         sames[i] = j;
607         nbsames++;
608         break;
609       }
610     }
611   }
612   if(nbsames!=2) return false;
613   if(sames[0]>-1) {
614     shiftNodesQuadTria(N1);
615     if(sames[1]>-1) {
616       shiftNodesQuadTria(N1);
617     }
618   }
619   i = sames[0] + sames[1] + sames[2];
620   for(; i<2; i++) {
621     shiftNodesQuadTria(N2);
622   }
623   // now we receive following N1 and N2 (using numeration as in the image below)
624   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
625   // i.e. first nodes from both arrays form a new diagonal
626   return true;
627 }
628
629 //=======================================================================
630 //function : InverseDiag
631 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
632 //           but having other common link.
633 //           Return False if args are improper
634 //=======================================================================
635
636 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
637                                     const SMDS_MeshElement * theTria2 )
638 {
639   MESSAGE("InverseDiag");
640   myLastCreatedElems.Clear();
641   myLastCreatedNodes.Clear();
642
643   if (!theTria1 || !theTria2)
644     return false;
645
646   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
647   if (!F1) return false;
648   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
649   if (!F2) return false;
650   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
651       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
652
653     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
654     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
655     //    |/ |                                         | \|
656     //  B +--+ 2                                     B +--+ 2
657
658     // put nodes in array and find out indices of the same ones
659     const SMDS_MeshNode* aNodes [6];
660     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
661     int i = 0;
662     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
663     while ( it->more() ) {
664       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
665
666       if ( i > 2 ) // theTria2
667         // find same node of theTria1
668         for ( int j = 0; j < 3; j++ )
669           if ( aNodes[ i ] == aNodes[ j ]) {
670             sameInd[ j ] = i;
671             sameInd[ i ] = j;
672             break;
673           }
674       // next
675       i++;
676       if ( i == 3 ) {
677         if ( it->more() )
678           return false; // theTria1 is not a triangle
679         it = theTria2->nodesIterator();
680       }
681       if ( i == 6 && it->more() )
682         return false; // theTria2 is not a triangle
683     }
684
685     // find indices of 1,2 and of A,B in theTria1
686     int iA = -1, iB = 0, i1 = 0, i2 = 0;
687     for ( i = 0; i < 6; i++ ) {
688       if ( sameInd [ i ] == -1 ) {
689         if ( i < 3 ) i1 = i;
690         else         i2 = i;
691       }
692       else if (i < 3) {
693         if ( iA >= 0) iB = i;
694         else          iA = i;
695       }
696     }
697     // nodes 1 and 2 should not be the same
698     if ( aNodes[ i1 ] == aNodes[ i2 ] )
699       return false;
700
701     // theTria1: A->2
702     aNodes[ iA ] = aNodes[ i2 ];
703     // theTria2: B->1
704     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
705
706     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
707     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
708
709     return true;
710
711   } // end if(F1 && F2)
712
713   // check case of quadratic faces
714   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
715       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
716     return false;
717   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
718       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
719     return false;
720
721   //       5
722   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
723   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
724   //    |   / |
725   //  7 +  +  + 6
726   //    | /9  |
727   //    |/    |
728   //  4 +--+--+ 3
729   //       8
730
731   vector< const SMDS_MeshNode* > N1;
732   vector< const SMDS_MeshNode* > N2;
733   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
734     return false;
735   // now we receive following N1 and N2 (using numeration as above image)
736   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
737   // i.e. first nodes from both arrays determ new diagonal
738
739   vector< const SMDS_MeshNode*> N1new( N1.size() );
740   vector< const SMDS_MeshNode*> N2new( N2.size() );
741   N1new.back() = N1.back(); // central node of biquadratic
742   N2new.back() = N2.back();
743   N1new[0] = N1[0];  N2new[0] = N1[0];
744   N1new[1] = N2[0];  N2new[1] = N1[1];
745   N1new[2] = N2[1];  N2new[2] = N2[0];
746   N1new[3] = N1[4];  N2new[3] = N1[3];
747   N1new[4] = N2[3];  N2new[4] = N2[5];
748   N1new[5] = N1[5];  N2new[5] = N1[4];
749   // change nodes in faces
750   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
751   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
752
753   // move the central node of biquadratic triangle
754   SMESH_MesherHelper helper( *GetMesh() );
755   for ( int is2nd = 0; is2nd < 2; ++is2nd )
756   {
757     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
758     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
759     if ( nodes.size() < 7 )
760       continue;
761     helper.SetSubShape( tria->getshapeId() );
762     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
763     gp_Pnt xyz;
764     if ( F.IsNull() )
765     {
766       xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
767               SMESH_TNodeXYZ( nodes[4] ) +
768               SMESH_TNodeXYZ( nodes[5] )) / 3.;
769     }
770     else
771     {
772       bool checkUV;
773       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
774                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
775                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
776       TopLoc_Location loc;
777       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
778       xyz = S->Value( uv.X(), uv.Y() );
779       xyz.Transform( loc );
780       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
781            nodes[6]->getshapeId() > 0 )
782         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
783     }
784     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
785   }
786   return true;
787 }
788
789 //=======================================================================
790 //function : findTriangles
791 //purpose  : find triangles sharing theNode1-theNode2 link
792 //=======================================================================
793
794 static bool findTriangles(const SMDS_MeshNode *    theNode1,
795                           const SMDS_MeshNode *    theNode2,
796                           const SMDS_MeshElement*& theTria1,
797                           const SMDS_MeshElement*& theTria2)
798 {
799   if ( !theNode1 || !theNode2 ) return false;
800
801   theTria1 = theTria2 = 0;
802
803   set< const SMDS_MeshElement* > emap;
804   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
805   while (it->more()) {
806     const SMDS_MeshElement* elem = it->next();
807     if ( elem->NbCornerNodes() == 3 )
808       emap.insert( elem );
809   }
810   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
811   while (it->more()) {
812     const SMDS_MeshElement* elem = it->next();
813     if ( emap.count( elem )) {
814       if ( !theTria1 )
815       {
816         theTria1 = elem;
817       }
818       else  
819       {
820         theTria2 = elem;
821         // theTria1 must be element with minimum ID
822         if ( theTria2->GetID() < theTria1->GetID() )
823           std::swap( theTria2, theTria1 );
824         return true;
825       }
826     }
827   }
828   return false;
829 }
830
831 //=======================================================================
832 //function : InverseDiag
833 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
834 //           with ones built on the same 4 nodes but having other common link.
835 //           Return false if proper faces not found
836 //=======================================================================
837
838 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
839                                     const SMDS_MeshNode * theNode2)
840 {
841   myLastCreatedElems.Clear();
842   myLastCreatedNodes.Clear();
843
844   MESSAGE( "::InverseDiag()" );
845
846   const SMDS_MeshElement *tr1, *tr2;
847   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
848     return false;
849
850   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
851   if (!F1) return false;
852   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
853   if (!F2) return false;
854   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
855       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
856
857     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
858     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
859     //    |/ |                                    | \|
860     //  B +--+ 2                                B +--+ 2
861
862     // put nodes in array
863     // and find indices of 1,2 and of A in tr1 and of B in tr2
864     int i, iA1 = 0, i1 = 0;
865     const SMDS_MeshNode* aNodes1 [3];
866     SMDS_ElemIteratorPtr it;
867     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
868       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
869       if ( aNodes1[ i ] == theNode1 )
870         iA1 = i; // node A in tr1
871       else if ( aNodes1[ i ] != theNode2 )
872         i1 = i;  // node 1
873     }
874     int iB2 = 0, i2 = 0;
875     const SMDS_MeshNode* aNodes2 [3];
876     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
877       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
878       if ( aNodes2[ i ] == theNode2 )
879         iB2 = i; // node B in tr2
880       else if ( aNodes2[ i ] != theNode1 )
881         i2 = i;  // node 2
882     }
883
884     // nodes 1 and 2 should not be the same
885     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
886       return false;
887
888     // tr1: A->2
889     aNodes1[ iA1 ] = aNodes2[ i2 ];
890     // tr2: B->1
891     aNodes2[ iB2 ] = aNodes1[ i1 ];
892
893     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
894     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
895
896     return true;
897   }
898
899   // check case of quadratic faces
900   return InverseDiag(tr1,tr2);
901 }
902
903 //=======================================================================
904 //function : getQuadrangleNodes
905 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
906 //           fusion of triangles tr1 and tr2 having shared link on
907 //           theNode1 and theNode2
908 //=======================================================================
909
910 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
911                         const SMDS_MeshNode *    theNode1,
912                         const SMDS_MeshNode *    theNode2,
913                         const SMDS_MeshElement * tr1,
914                         const SMDS_MeshElement * tr2 )
915 {
916   if( tr1->NbNodes() != tr2->NbNodes() )
917     return false;
918   // find the 4-th node to insert into tr1
919   const SMDS_MeshNode* n4 = 0;
920   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
921   int i=0;
922   while ( !n4 && i<3 ) {
923     const SMDS_MeshNode * n = cast2Node( it->next() );
924     i++;
925     bool isDiag = ( n == theNode1 || n == theNode2 );
926     if ( !isDiag )
927       n4 = n;
928   }
929   // Make an array of nodes to be in a quadrangle
930   int iNode = 0, iFirstDiag = -1;
931   it = tr1->nodesIterator();
932   i=0;
933   while ( i<3 ) {
934     const SMDS_MeshNode * n = cast2Node( it->next() );
935     i++;
936     bool isDiag = ( n == theNode1 || n == theNode2 );
937     if ( isDiag ) {
938       if ( iFirstDiag < 0 )
939         iFirstDiag = iNode;
940       else if ( iNode - iFirstDiag == 1 )
941         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
942     }
943     else if ( n == n4 ) {
944       return false; // tr1 and tr2 should not have all the same nodes
945     }
946     theQuadNodes[ iNode++ ] = n;
947   }
948   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
949     theQuadNodes[ iNode ] = n4;
950
951   return true;
952 }
953
954 //=======================================================================
955 //function : DeleteDiag
956 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
957 //           with a quadrangle built on the same 4 nodes.
958 //           Return false if proper faces not found
959 //=======================================================================
960
961 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
962                                    const SMDS_MeshNode * theNode2)
963 {
964   myLastCreatedElems.Clear();
965   myLastCreatedNodes.Clear();
966
967   MESSAGE( "::DeleteDiag()" );
968
969   const SMDS_MeshElement *tr1, *tr2;
970   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
971     return false;
972
973   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
974   if (!F1) return false;
975   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
976   if (!F2) return false;
977   SMESHDS_Mesh * aMesh = GetMeshDS();
978
979   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
980       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
981
982     const SMDS_MeshNode* aNodes [ 4 ];
983     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
984       return false;
985
986     const SMDS_MeshElement* newElem = 0;
987     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
988     myLastCreatedElems.Append(newElem);
989     AddToSameGroups( newElem, tr1, aMesh );
990     int aShapeId = tr1->getshapeId();
991     if ( aShapeId )
992       {
993         aMesh->SetMeshElementOnShape( newElem, aShapeId );
994       }
995     aMesh->RemoveElement( tr1 );
996     aMesh->RemoveElement( tr2 );
997
998     return true;
999   }
1000
1001   // check case of quadratic faces
1002   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1003     return false;
1004   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1005     return false;
1006
1007   //       5
1008   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1009   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1010   //    |   / |
1011   //  7 +  +  + 6
1012   //    | /9  |
1013   //    |/    |
1014   //  4 +--+--+ 3
1015   //       8
1016
1017   vector< const SMDS_MeshNode* > N1;
1018   vector< const SMDS_MeshNode* > N2;
1019   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1020     return false;
1021   // now we receive following N1 and N2 (using numeration as above image)
1022   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1023   // i.e. first nodes from both arrays determ new diagonal
1024
1025   const SMDS_MeshNode* aNodes[8];
1026   aNodes[0] = N1[0];
1027   aNodes[1] = N1[1];
1028   aNodes[2] = N2[0];
1029   aNodes[3] = N2[1];
1030   aNodes[4] = N1[3];
1031   aNodes[5] = N2[5];
1032   aNodes[6] = N2[3];
1033   aNodes[7] = N1[5];
1034
1035   const SMDS_MeshElement* newElem = 0;
1036   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1037                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1038   myLastCreatedElems.Append(newElem);
1039   AddToSameGroups( newElem, tr1, aMesh );
1040   int aShapeId = tr1->getshapeId();
1041   if ( aShapeId )
1042     {
1043       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1044     }
1045   aMesh->RemoveElement( tr1 );
1046   aMesh->RemoveElement( tr2 );
1047
1048   // remove middle node (9)
1049   GetMeshDS()->RemoveNode( N1[4] );
1050
1051   return true;
1052 }
1053
1054 //=======================================================================
1055 //function : Reorient
1056 //purpose  : Reverse theElement orientation
1057 //=======================================================================
1058
1059 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1060 {
1061   MESSAGE("Reorient");
1062   myLastCreatedElems.Clear();
1063   myLastCreatedNodes.Clear();
1064
1065   if (!theElem)
1066     return false;
1067   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1068   if ( !it || !it->more() )
1069     return false;
1070
1071   const SMDSAbs_ElementType type = theElem->GetType();
1072   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1073     return false;
1074
1075   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1076   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1077   {
1078     const SMDS_VtkVolume* aPolyedre =
1079       dynamic_cast<const SMDS_VtkVolume*>( theElem );
1080     if (!aPolyedre) {
1081       MESSAGE("Warning: bad volumic element");
1082       return false;
1083     }
1084     const int nbFaces = aPolyedre->NbFaces();
1085     vector<const SMDS_MeshNode *> poly_nodes;
1086     vector<int> quantities (nbFaces);
1087
1088     // reverse each face of the polyedre
1089     for (int iface = 1; iface <= nbFaces; iface++) {
1090       int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1091       quantities[iface - 1] = nbFaceNodes;
1092
1093       for (inode = nbFaceNodes; inode >= 1; inode--) {
1094         const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1095         poly_nodes.push_back(curNode);
1096       }
1097     }
1098     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1099   }
1100   else // other elements
1101   {
1102     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1103     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType );
1104     if ( interlace.empty() )
1105     {
1106       std::reverse( nodes.begin(), nodes.end() ); // polygon
1107     }
1108     else if ( interlace.size() > 1 )
1109     {
1110       SMDS_MeshCell::applyInterlace( interlace, nodes );
1111     }
1112     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1113   }
1114   return false;
1115 }
1116
1117 //================================================================================
1118 /*!
1119  * \brief Reorient faces.
1120  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1121  * \param theDirection - desired direction of normal of \a theFace
1122  * \param theFace - one of \a theFaces that sould be oriented according to
1123  *        \a theDirection and whose orientation defines orientation of other faces
1124  * \return number of reoriented faces.
1125  */
1126 //================================================================================
1127
1128 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1129                                   const gp_Dir&            theDirection,
1130                                   const SMDS_MeshElement * theFace)
1131 {
1132   int nbReori = 0;
1133   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1134
1135   if ( theFaces.empty() )
1136   {
1137     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1138     while ( fIt->more() )
1139       theFaces.insert( theFaces.end(), fIt->next() );
1140   }
1141
1142   // orient theFace according to theDirection
1143   gp_XYZ normal;
1144   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1145   if ( normal * theDirection.XYZ() < 0 )
1146     nbReori += Reorient( theFace );
1147
1148   // Orient other faces
1149
1150   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1151   TIDSortedElemSet avoidSet;
1152   set< SMESH_TLink > checkedLinks;
1153   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1154
1155   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1156     theFaces.erase( theFace );
1157   startFaces.insert( theFace );
1158
1159   int nodeInd1, nodeInd2;
1160   const SMDS_MeshElement*           otherFace;
1161   vector< const SMDS_MeshElement* > facesNearLink;
1162   vector< std::pair< int, int > >   nodeIndsOfFace;
1163
1164   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1165   while ( !startFaces.empty() )
1166   {
1167     startFace = startFaces.begin();
1168     theFace = *startFace;
1169     startFaces.erase( startFace );
1170     if ( !visitedFaces.insert( theFace ).second )
1171       continue;
1172
1173     avoidSet.clear();
1174     avoidSet.insert(theFace);
1175
1176     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1177
1178     const int nbNodes = theFace->NbCornerNodes();
1179     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1180     {
1181       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1182       linkIt_isNew = checkedLinks.insert( link );
1183       if ( !linkIt_isNew.second )
1184       {
1185         // link has already been checked and won't be encountered more
1186         // if the group (theFaces) is manifold
1187         //checkedLinks.erase( linkIt_isNew.first );
1188       }
1189       else
1190       {
1191         facesNearLink.clear();
1192         nodeIndsOfFace.clear();
1193         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1194                                                              theFaces, avoidSet,
1195                                                              &nodeInd1, &nodeInd2 )))
1196           if ( otherFace != theFace)
1197           {
1198             facesNearLink.push_back( otherFace );
1199             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1200             avoidSet.insert( otherFace );
1201           }
1202         if ( facesNearLink.size() > 1 )
1203         {
1204           // NON-MANIFOLD mesh shell !
1205           // select a face most co-directed with theFace,
1206           // other faces won't be visited this time
1207           gp_XYZ NF, NOF;
1208           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1209           double proj, maxProj = -1;
1210           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1211             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1212             if (( proj = Abs( NF * NOF )) > maxProj ) {
1213               maxProj = proj;
1214               otherFace = facesNearLink[i];
1215               nodeInd1  = nodeIndsOfFace[i].first;
1216               nodeInd2  = nodeIndsOfFace[i].second;
1217             }
1218           }
1219           // not to visit rejected faces
1220           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1221             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1222               visitedFaces.insert( facesNearLink[i] );
1223         }
1224         else if ( facesNearLink.size() == 1 )
1225         {
1226           otherFace = facesNearLink[0];
1227           nodeInd1  = nodeIndsOfFace.back().first;
1228           nodeInd2  = nodeIndsOfFace.back().second;
1229         }
1230         if ( otherFace && otherFace != theFace)
1231         {
1232           // link must be reverse in otherFace if orientation ot otherFace
1233           // is same as that of theFace
1234           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1235           {
1236             nbReori += Reorient( otherFace );
1237           }
1238           startFaces.insert( otherFace );
1239         }
1240       }
1241       std::swap( link.first, link.second ); // reverse the link
1242     }
1243   }
1244   return nbReori;
1245 }
1246
1247 //================================================================================
1248 /*!
1249  * \brief Reorient faces basing on orientation of adjacent volumes.
1250  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1251  * \param theVolumes - reference volumes.
1252  * \param theOutsideNormal - to orient faces to have their normal
1253  *        pointing either \a outside or \a inside the adjacent volumes.
1254  * \return number of reoriented faces.
1255  */
1256 //================================================================================
1257
1258 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1259                                       TIDSortedElemSet & theVolumes,
1260                                       const bool         theOutsideNormal)
1261 {
1262   int nbReori = 0;
1263
1264   SMDS_ElemIteratorPtr faceIt;
1265   if ( theFaces.empty() )
1266     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1267   else
1268     faceIt = elemSetIterator( theFaces );
1269
1270   vector< const SMDS_MeshNode* > faceNodes;
1271   TIDSortedElemSet checkedVolumes;
1272   set< const SMDS_MeshNode* > faceNodesSet;
1273   SMDS_VolumeTool volumeTool;
1274
1275   while ( faceIt->more() ) // loop on given faces
1276   {
1277     const SMDS_MeshElement* face = faceIt->next();
1278     if ( face->GetType() != SMDSAbs_Face )
1279       continue;
1280
1281     const int nbCornersNodes = face->NbCornerNodes();
1282     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1283
1284     checkedVolumes.clear();
1285     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1286     while ( vIt->more() )
1287     {
1288       const SMDS_MeshElement* volume = vIt->next();
1289
1290       if ( !checkedVolumes.insert( volume ).second )
1291         continue;
1292       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1293         continue;
1294
1295       // is volume adjacent?
1296       bool allNodesCommon = true;
1297       for ( int iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1298         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1299       if ( !allNodesCommon )
1300         continue;
1301
1302       // get nodes of a corresponding volume facet
1303       faceNodesSet.clear();
1304       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1305       volumeTool.Set( volume );
1306       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1307       if ( facetID < 0 ) continue;
1308       volumeTool.SetExternalNormal();
1309       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1310
1311       // compare order of faceNodes and facetNodes
1312       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1313       int iNN[2];
1314       for ( int i = 0; i < 2; ++i )
1315       {
1316         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1317         for ( int iN = 0; iN < nbCornersNodes; ++iN )
1318           if ( faceNodes[ iN ] == n )
1319           {
1320             iNN[ i ] = iN;
1321             break;
1322           }
1323       }
1324       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1325       if ( isOutside != theOutsideNormal )
1326         nbReori += Reorient( face );
1327     }
1328   }  // loop on given faces
1329
1330   return nbReori;
1331 }
1332
1333 //=======================================================================
1334 //function : getBadRate
1335 //purpose  :
1336 //=======================================================================
1337
1338 static double getBadRate (const SMDS_MeshElement*               theElem,
1339                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1340 {
1341   SMESH::Controls::TSequenceOfXYZ P;
1342   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1343     return 1e100;
1344   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1345   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1346 }
1347
1348 //=======================================================================
1349 //function : QuadToTri
1350 //purpose  : Cut quadrangles into triangles.
1351 //           theCrit is used to select a diagonal to cut
1352 //=======================================================================
1353
1354 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1355                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1356 {
1357   myLastCreatedElems.Clear();
1358   myLastCreatedNodes.Clear();
1359
1360   if ( !theCrit.get() )
1361     return false;
1362
1363   SMESHDS_Mesh * aMesh = GetMeshDS();
1364
1365   Handle(Geom_Surface) surface;
1366   SMESH_MesherHelper   helper( *GetMesh() );
1367
1368   TIDSortedElemSet::iterator itElem;
1369   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1370   {
1371     const SMDS_MeshElement* elem = *itElem;
1372     if ( !elem || elem->GetType() != SMDSAbs_Face )
1373       continue;
1374     if ( elem->NbCornerNodes() != 4 )
1375       continue;
1376
1377     // retrieve element nodes
1378     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1379
1380     // compare two sets of possible triangles
1381     double aBadRate1, aBadRate2; // to what extent a set is bad
1382     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1383     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1384     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1385
1386     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1387     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1388     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1389
1390     const int aShapeId = FindShape( elem );
1391     const SMDS_MeshElement* newElem1 = 0;
1392     const SMDS_MeshElement* newElem2 = 0;
1393
1394     if ( !elem->IsQuadratic() ) // split liner quadrangle
1395     {
1396       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1397       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1398       if ( aBadRate1 <= aBadRate2 ) {
1399         // tr1 + tr2 is better
1400         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1401         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1402       }
1403       else {
1404         // tr3 + tr4 is better
1405         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1406         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1407       }
1408     }
1409     else // split quadratic quadrangle
1410     {
1411       helper.SetIsQuadratic( true );
1412       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1413
1414       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1415       if ( aNodes.size() == 9 )
1416       {
1417         helper.SetIsBiQuadratic( true );
1418         if ( aBadRate1 <= aBadRate2 )
1419           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1420         else
1421           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1422       }
1423       // create a new element
1424       if ( aBadRate1 <= aBadRate2 ) {
1425         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1426         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1427       }
1428       else {
1429         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1430         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1431       }
1432     } // quadratic case
1433
1434     // care of a new element
1435
1436     myLastCreatedElems.Append(newElem1);
1437     myLastCreatedElems.Append(newElem2);
1438     AddToSameGroups( newElem1, elem, aMesh );
1439     AddToSameGroups( newElem2, elem, aMesh );
1440
1441     // put a new triangle on the same shape
1442     if ( aShapeId )
1443       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1444     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1445
1446     aMesh->RemoveElement( elem );
1447   }
1448   return true;
1449 }
1450
1451 //=======================================================================
1452 /*!
1453  * \brief Split each of given quadrangles into 4 triangles.
1454  * \param theElems - The faces to be splitted. If empty all faces are split.
1455  */
1456 //=======================================================================
1457
1458 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1459 {
1460   myLastCreatedElems.Clear();
1461   myLastCreatedNodes.Clear();
1462
1463   SMESH_MesherHelper helper( *GetMesh() );
1464   helper.SetElementsOnShape( true );
1465
1466   SMDS_ElemIteratorPtr faceIt;
1467   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1468   else                    faceIt = elemSetIterator( theElems );
1469
1470   bool   checkUV;
1471   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1472   gp_XYZ xyz[9];
1473   vector< const SMDS_MeshNode* > nodes;
1474   SMESHDS_SubMesh*               subMeshDS;
1475   TopoDS_Face                    F;
1476   Handle(Geom_Surface)           surface;
1477   TopLoc_Location                loc;
1478
1479   while ( faceIt->more() )
1480   {
1481     const SMDS_MeshElement* quad = faceIt->next();
1482     if ( !quad || quad->NbCornerNodes() != 4 )
1483       continue;
1484
1485     // get a surface the quad is on
1486
1487     if ( quad->getshapeId() < 1 )
1488     {
1489       F.Nullify();
1490       helper.SetSubShape( 0 );
1491       subMeshDS = 0;
1492     }
1493     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1494     {
1495       helper.SetSubShape( quad->getshapeId() );
1496       if ( !helper.GetSubShape().IsNull() &&
1497            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1498       {
1499         F = TopoDS::Face( helper.GetSubShape() );
1500         surface = BRep_Tool::Surface( F, loc );
1501         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1502       }
1503       else
1504       {
1505         helper.SetSubShape( 0 );
1506         subMeshDS = 0;
1507       }
1508     }
1509
1510     // create a central node
1511
1512     const SMDS_MeshNode* nCentral;
1513     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1514
1515     if ( nodes.size() == 9 )
1516     {
1517       nCentral = nodes.back();
1518     }
1519     else
1520     {
1521       size_t iN = 0;
1522       if ( F.IsNull() )
1523       {
1524         for ( ; iN < nodes.size(); ++iN )
1525           xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1526
1527         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1528           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1529
1530         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1531                                    xyz[0], xyz[1], xyz[2], xyz[3],
1532                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1533       }
1534       else
1535       {
1536         for ( ; iN < nodes.size(); ++iN )
1537           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1538
1539         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1540           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1541
1542         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1543                                   uv[0], uv[1], uv[2], uv[3],
1544                                   uv[4], uv[5], uv[6], uv[7] );
1545
1546         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1547         xyz[ 8 ] = p.XYZ();
1548       }
1549
1550       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1551                                  uv[8].X(), uv[8].Y() );
1552       myLastCreatedNodes.Append( nCentral );
1553     }
1554
1555     // create 4 triangles
1556
1557     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1558     
1559     helper.SetIsQuadratic  ( nodes.size() > 4 );
1560     helper.SetIsBiQuadratic( nodes.size() == 9 );
1561     if ( helper.GetIsQuadratic() )
1562       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1563
1564     for ( int i = 0; i < 4; ++i )
1565     {
1566       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1567                                                nodes[(i+1)%4],
1568                                                nCentral );
1569       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1570       myLastCreatedElems.Append( tria );
1571     }
1572   }
1573 }
1574
1575 //=======================================================================
1576 //function : BestSplit
1577 //purpose  : Find better diagonal for cutting.
1578 //=======================================================================
1579
1580 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1581                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1582 {
1583   myLastCreatedElems.Clear();
1584   myLastCreatedNodes.Clear();
1585
1586   if (!theCrit.get())
1587     return -1;
1588
1589   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1590     return -1;
1591
1592   if( theQuad->NbNodes()==4 ||
1593       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1594
1595     // retrieve element nodes
1596     const SMDS_MeshNode* aNodes [4];
1597     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1598     int i = 0;
1599     //while (itN->more())
1600     while (i<4) {
1601       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1602     }
1603     // compare two sets of possible triangles
1604     double aBadRate1, aBadRate2; // to what extent a set is bad
1605     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1606     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1607     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1608
1609     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1610     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1611     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1612     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1613     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1614     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1615       return 1; // diagonal 1-3
1616
1617     return 2; // diagonal 2-4
1618   }
1619   return -1;
1620 }
1621
1622 namespace
1623 {
1624   // Methods of splitting volumes into tetra
1625
1626   const int theHexTo5_1[5*4+1] =
1627     {
1628       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1629     };
1630   const int theHexTo5_2[5*4+1] =
1631     {
1632       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1633     };
1634   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1635
1636   const int theHexTo6_1[6*4+1] =
1637     {
1638       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
1639     };
1640   const int theHexTo6_2[6*4+1] =
1641     {
1642       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
1643     };
1644   const int theHexTo6_3[6*4+1] =
1645     {
1646       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
1647     };
1648   const int theHexTo6_4[6*4+1] =
1649     {
1650       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
1651     };
1652   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1653
1654   const int thePyraTo2_1[2*4+1] =
1655     {
1656       0, 1, 2, 4,    0, 2, 3, 4,   -1
1657     };
1658   const int thePyraTo2_2[2*4+1] =
1659     {
1660       1, 2, 3, 4,    1, 3, 0, 4,   -1
1661     };
1662   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1663
1664   const int thePentaTo3_1[3*4+1] =
1665     {
1666       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1667     };
1668   const int thePentaTo3_2[3*4+1] =
1669     {
1670       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1671     };
1672   const int thePentaTo3_3[3*4+1] =
1673     {
1674       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1675     };
1676   const int thePentaTo3_4[3*4+1] =
1677     {
1678       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1679     };
1680   const int thePentaTo3_5[3*4+1] =
1681     {
1682       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1683     };
1684   const int thePentaTo3_6[3*4+1] =
1685     {
1686       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1687     };
1688   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1689                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1690
1691   // Methods of splitting hexahedron into prisms
1692
1693   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1694     {
1695       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
1696     };
1697   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1698     {
1699       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
1700     };
1701   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1702     {
1703       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
1704     };
1705
1706   const int theHexTo2Prisms_BT_1[6*2+1] =
1707     {
1708       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1709     };
1710   const int theHexTo2Prisms_BT_2[6*2+1] =
1711     {
1712       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1713     };
1714   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1715
1716   const int theHexTo2Prisms_LR_1[6*2+1] =
1717     {
1718       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1719     };
1720   const int theHexTo2Prisms_LR_2[6*2+1] =
1721     {
1722       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1723     };
1724   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1725
1726   const int theHexTo2Prisms_FB_1[6*2+1] =
1727     {
1728       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1729     };
1730   const int theHexTo2Prisms_FB_2[6*2+1] =
1731     {
1732       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1733     };
1734   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1735
1736
1737   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1738   {
1739     int _n1, _n2, _n3;
1740     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1741     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1742     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1743                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1744   };
1745   struct TSplitMethod
1746   {
1747     int        _nbSplits;
1748     int        _nbCorners;
1749     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1750     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1751     bool       _ownConn;      //!< to delete _connectivity in destructor
1752     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1753
1754     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1755       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1756     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1757     bool hasFacet( const TTriangleFacet& facet ) const
1758     {
1759       if ( _nbCorners == 4 )
1760       {
1761         const int* tetConn = _connectivity;
1762         for ( ; tetConn[0] >= 0; tetConn += 4 )
1763           if (( facet.contains( tetConn[0] ) +
1764                 facet.contains( tetConn[1] ) +
1765                 facet.contains( tetConn[2] ) +
1766                 facet.contains( tetConn[3] )) == 3 )
1767             return true;
1768       }
1769       else // prism, _nbCorners == 6
1770       {
1771         const int* prismConn = _connectivity;
1772         for ( ; prismConn[0] >= 0; prismConn += 6 )
1773         {
1774           if (( facet.contains( prismConn[0] ) &&
1775                 facet.contains( prismConn[1] ) &&
1776                 facet.contains( prismConn[2] ))
1777               ||
1778               ( facet.contains( prismConn[3] ) &&
1779                 facet.contains( prismConn[4] ) &&
1780                 facet.contains( prismConn[5] )))
1781             return true;
1782         }
1783       }
1784       return false;
1785     }
1786   };
1787
1788   //=======================================================================
1789   /*!
1790    * \brief return TSplitMethod for the given element to split into tetrahedra
1791    */
1792   //=======================================================================
1793
1794   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1795   {
1796     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1797
1798     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1799     // an edge and a face barycenter; tertaherdons are based on triangles and
1800     // a volume barycenter
1801     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1802
1803     // Find out how adjacent volumes are split
1804
1805     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1806     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1807     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1808     {
1809       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1810       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1811       if ( nbNodes < 4 ) continue;
1812
1813       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1814       const int* nInd = vol.GetFaceNodesIndices( iF );
1815       if ( nbNodes == 4 )
1816       {
1817         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1818         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1819         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1820         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1821       }
1822       else
1823       {
1824         int iCom = 0; // common node of triangle faces to split into
1825         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1826         {
1827           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1828                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1829                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1830           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1831                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1832                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1833           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1834           {
1835             triaSplits.push_back( t012 );
1836             triaSplits.push_back( t023 );
1837             break;
1838           }
1839         }
1840       }
1841       if ( !triaSplits.empty() )
1842         hasAdjacentSplits = true;
1843     }
1844
1845     // Among variants of split method select one compliant with adjacent volumes
1846
1847     TSplitMethod method;
1848     if ( !vol.Element()->IsPoly() && !is24TetMode )
1849     {
1850       int nbVariants = 2, nbTet = 0;
1851       const int** connVariants = 0;
1852       switch ( vol.Element()->GetEntityType() )
1853       {
1854       case SMDSEntity_Hexa:
1855       case SMDSEntity_Quad_Hexa:
1856       case SMDSEntity_TriQuad_Hexa:
1857         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1858           connVariants = theHexTo5, nbTet = 5;
1859         else
1860           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1861         break;
1862       case SMDSEntity_Pyramid:
1863       case SMDSEntity_Quad_Pyramid:
1864         connVariants = thePyraTo2;  nbTet = 2;
1865         break;
1866       case SMDSEntity_Penta:
1867       case SMDSEntity_Quad_Penta:
1868         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1869         break;
1870       default:
1871         nbVariants = 0;
1872       }
1873       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1874       {
1875         // check method compliancy with adjacent tetras,
1876         // all found splits must be among facets of tetras described by this method
1877         method = TSplitMethod( nbTet, connVariants[variant] );
1878         if ( hasAdjacentSplits && method._nbSplits > 0 )
1879         {
1880           bool facetCreated = true;
1881           for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1882           {
1883             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1884             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1885               facetCreated = method.hasFacet( *facet );
1886           }
1887           if ( !facetCreated )
1888             method = TSplitMethod(0); // incompatible method
1889         }
1890       }
1891     }
1892     if ( method._nbSplits < 1 )
1893     {
1894       // No standard method is applicable, use a generic solution:
1895       // each facet of a volume is split into triangles and
1896       // each of triangles and a volume barycenter form a tetrahedron.
1897
1898       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1899
1900       int* connectivity = new int[ maxTetConnSize + 1 ];
1901       method._connectivity = connectivity;
1902       method._ownConn = true;
1903       method._baryNode = !isHex27; // to create central node or not
1904
1905       int connSize = 0;
1906       int baryCenInd = vol.NbNodes() - int( isHex27 );
1907       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1908       {
1909         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1910         const int*   nInd = vol.GetFaceNodesIndices( iF );
1911         // find common node of triangle facets of tetra to create
1912         int iCommon = 0; // index in linear numeration
1913         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1914         if ( !triaSplits.empty() )
1915         {
1916           // by found facets
1917           const TTriangleFacet* facet = &triaSplits.front();
1918           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1919             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1920                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1921               break;
1922         }
1923         else if ( nbNodes > 3 && !is24TetMode )
1924         {
1925           // find the best method of splitting into triangles by aspect ratio
1926           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1927           map< double, int > badness2iCommon;
1928           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1929           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1930           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1931           {
1932             double badness = 0;
1933             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1934             {
1935               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1936                                       nodes[ iQ*((iLast-1)%nbNodes)],
1937                                       nodes[ iQ*((iLast  )%nbNodes)]);
1938               badness += getBadRate( &tria, aspectRatio );
1939             }
1940             badness2iCommon.insert( make_pair( badness, iCommon ));
1941           }
1942           // use iCommon with lowest badness
1943           iCommon = badness2iCommon.begin()->second;
1944         }
1945         if ( iCommon >= nbNodes )
1946           iCommon = 0; // something wrong
1947
1948         // fill connectivity of tetrahedra based on a current face
1949         int nbTet = nbNodes - 2;
1950         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1951         {
1952           int faceBaryCenInd;
1953           if ( isHex27 )
1954           {
1955             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1956             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1957           }
1958           else
1959           {
1960             method._faceBaryNode[ iF ] = 0;
1961             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1962           }
1963           nbTet = nbNodes;
1964           for ( int i = 0; i < nbTet; ++i )
1965           {
1966             int i1 = i, i2 = (i+1) % nbNodes;
1967             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1968             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1969             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1970             connectivity[ connSize++ ] = faceBaryCenInd;
1971             connectivity[ connSize++ ] = baryCenInd;
1972           }
1973         }
1974         else
1975         {
1976           for ( int i = 0; i < nbTet; ++i )
1977           {
1978             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1979             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1980             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1981             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1982             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1983             connectivity[ connSize++ ] = baryCenInd;
1984           }
1985         }
1986         method._nbSplits += nbTet;
1987
1988       } // loop on volume faces
1989
1990       connectivity[ connSize++ ] = -1;
1991
1992     } // end of generic solution
1993
1994     return method;
1995   }
1996   //=======================================================================
1997   /*!
1998    * \brief return TSplitMethod to split haxhedron into prisms
1999    */
2000   //=======================================================================
2001
2002   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2003                                     const int        methodFlags,
2004                                     const int        facetToSplit)
2005   {
2006     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2007     // B, T, L, B, R, F
2008     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2009
2010     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2011     {
2012       static TSplitMethod to4methods[4]; // order BT, LR, FB
2013       if ( to4methods[iF]._nbSplits == 0 )
2014       {
2015         switch ( iF ) {
2016         case 0:
2017           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2018           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2019           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2020           break;
2021         case 1:
2022           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2023           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2024           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2025           break;
2026         case 2:
2027           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2028           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2029           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2030           break;
2031         default: return to4methods[3];
2032         }
2033         to4methods[iF]._nbSplits  = 4;
2034         to4methods[iF]._nbCorners = 6;
2035       }
2036       return to4methods[iF];
2037     }
2038     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2039
2040     TSplitMethod method;
2041
2042     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2043
2044     const int nbVariants = 2, nbSplits = 2;
2045     const int** connVariants = 0;
2046     switch ( iF ) {
2047     case 0: connVariants = theHexTo2Prisms_BT; break;
2048     case 1: connVariants = theHexTo2Prisms_LR; break;
2049     case 2: connVariants = theHexTo2Prisms_FB; break;
2050     default: return method;
2051     }
2052
2053     // look for prisms adjacent via facetToSplit and an opposite one
2054     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2055     {
2056       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2057       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2058       if ( nbNodes != 4 ) return method;
2059
2060       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2061       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2062       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2063       TTriangleFacet* t;
2064       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2065         t = &t012;
2066       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2067         t = &t123;
2068       else
2069         continue;
2070
2071       // there are adjacent prism
2072       for ( int variant = 0; variant < nbVariants; ++variant )
2073       {
2074         // check method compliancy with adjacent prisms,
2075         // the found prism facets must be among facets of prisms described by current method
2076         method._nbSplits     = nbSplits;
2077         method._nbCorners    = 6;
2078         method._connectivity = connVariants[ variant ];
2079         if ( method.hasFacet( *t ))
2080           return method;
2081       }
2082     }
2083
2084     // No adjacent prisms. Select a variant with a best aspect ratio.
2085
2086     double badness[2] = { 0, 0 };
2087     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2088     const SMDS_MeshNode** nodes = vol.GetNodes();
2089     for ( int variant = 0; variant < nbVariants; ++variant )
2090       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2091       {
2092         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2093         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2094
2095         method._connectivity = connVariants[ variant ];
2096         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2097         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2098         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2099
2100         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2101                                 nodes[ t->_n2 ],
2102                                 nodes[ t->_n3 ] );
2103         badness[ variant ] += getBadRate( &tria, aspectRatio );
2104       }
2105     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2106
2107     method._nbSplits     = nbSplits;
2108     method._nbCorners    = 6;
2109     method._connectivity = connVariants[ iBetter ];
2110
2111     return method;
2112   }
2113
2114   //================================================================================
2115   /*!
2116    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2117    */
2118   //================================================================================
2119
2120   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2121                                        const SMDSAbs_GeometryType geom ) const
2122   {
2123     // find the tetrahedron including the three nodes of facet
2124     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2125     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2126     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2127     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2128     while ( volIt1->more() )
2129     {
2130       const SMDS_MeshElement* v = volIt1->next();
2131       if ( v->GetGeomType() != geom )
2132         continue;
2133       const int lastCornerInd = v->NbCornerNodes() - 1;
2134       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2135         continue; // medium node not allowed
2136       const int ind2 = v->GetNodeIndex( n2 );
2137       if ( ind2 < 0 || lastCornerInd < ind2 )
2138         continue;
2139       const int ind3 = v->GetNodeIndex( n3 );
2140       if ( ind3 < 0 || lastCornerInd < ind3 )
2141         continue;
2142       return true;
2143     }
2144     return false;
2145   }
2146
2147   //=======================================================================
2148   /*!
2149    * \brief A key of a face of volume
2150    */
2151   //=======================================================================
2152
2153   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2154   {
2155     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2156     {
2157       TIDSortedNodeSet sortedNodes;
2158       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2159       int nbNodes = vol.NbFaceNodes( iF );
2160       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2161       for ( int i = 0; i < nbNodes; i += iQ )
2162         sortedNodes.insert( fNodes[i] );
2163       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2164       first.first   = (*(n++))->GetID();
2165       first.second  = (*(n++))->GetID();
2166       second.first  = (*(n++))->GetID();
2167       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2168     }
2169   };
2170 } // namespace
2171
2172 //=======================================================================
2173 //function : SplitVolumes
2174 //purpose  : Split volume elements into tetrahedra or prisms.
2175 //           If facet ID < 0, element is split into tetrahedra,
2176 //           else a hexahedron is split into prisms so that the given facet is
2177 //           split into triangles
2178 //=======================================================================
2179
2180 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2181                                      const int            theMethodFlags)
2182 {
2183   SMDS_VolumeTool    volTool;
2184   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2185   fHelper.ToFixNodeParameters( true );
2186
2187   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2188   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2189
2190   SMESH_SequenceOfElemPtr newNodes, newElems;
2191
2192   // map face of volume to it's baricenrtic node
2193   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2194   double bc[3];
2195
2196   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2197   for ( ; elem2facet != theElems.end(); ++elem2facet )
2198   {
2199     const SMDS_MeshElement* elem = elem2facet->first;
2200     const int       facetToSplit = elem2facet->second;
2201     if ( elem->GetType() != SMDSAbs_Volume )
2202       continue;
2203     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2204     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2205       continue;
2206
2207     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2208
2209     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2210                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2211                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2212     if ( splitMethod._nbSplits < 1 ) continue;
2213
2214     // find submesh to add new tetras to
2215     if ( !subMesh || !subMesh->Contains( elem ))
2216     {
2217       int shapeID = FindShape( elem );
2218       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2219       subMesh = GetMeshDS()->MeshElements( shapeID );
2220     }
2221     int iQ;
2222     if ( elem->IsQuadratic() )
2223     {
2224       iQ = 2;
2225       // add quadratic links to the helper
2226       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2227       {
2228         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2229         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2230         for ( int iN = 0; iN < nbN; iN += iQ )
2231           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2232       }
2233       helper.SetIsQuadratic( true );
2234     }
2235     else
2236     {
2237       iQ = 1;
2238       helper.SetIsQuadratic( false );
2239     }
2240     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2241                                         volTool.GetNodes() + elem->NbNodes() );
2242     helper.SetElementsOnShape( true );
2243     if ( splitMethod._baryNode )
2244     {
2245       // make a node at barycenter
2246       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2247       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2248       nodes.push_back( gcNode );
2249       newNodes.Append( gcNode );
2250     }
2251     if ( !splitMethod._faceBaryNode.empty() )
2252     {
2253       // make or find baricentric nodes of faces
2254       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2255       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2256       {
2257         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2258           volFace2BaryNode.insert
2259           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2260         if ( !f_n->second )
2261         {
2262           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2263           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2264         }
2265         nodes.push_back( iF_n->second = f_n->second );
2266       }
2267     }
2268
2269     // make new volumes
2270     vector<const SMDS_MeshElement* > splitVols( splitMethod._nbSplits ); // splits of a volume
2271     const int* volConn = splitMethod._connectivity;
2272     if ( splitMethod._nbCorners == 4 ) // tetra
2273       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2274         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2275                                                             nodes[ volConn[1] ],
2276                                                             nodes[ volConn[2] ],
2277                                                             nodes[ volConn[3] ]));
2278     else // prisms
2279       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2280         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2281                                                             nodes[ volConn[1] ],
2282                                                             nodes[ volConn[2] ],
2283                                                             nodes[ volConn[3] ],
2284                                                             nodes[ volConn[4] ],
2285                                                             nodes[ volConn[5] ]));
2286
2287     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2288
2289     // Split faces on sides of the split volume
2290
2291     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2292     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2293     {
2294       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2295       if ( nbNodes < 4 ) continue;
2296
2297       // find an existing face
2298       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2299                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2300       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2301                                                                        /*noMedium=*/false))
2302       {
2303         // make triangles
2304         helper.SetElementsOnShape( false );
2305         vector< const SMDS_MeshElement* > triangles;
2306
2307         // find submesh to add new triangles in
2308         if ( !fSubMesh || !fSubMesh->Contains( face ))
2309         {
2310           int shapeID = FindShape( face );
2311           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2312         }
2313         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2314         if ( iF_n != splitMethod._faceBaryNode.end() )
2315         {
2316           const SMDS_MeshNode *baryNode = iF_n->second;
2317           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2318           {
2319             const SMDS_MeshNode* n1 = fNodes[iN];
2320             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2321             const SMDS_MeshNode *n3 = baryNode;
2322             if ( !volTool.IsFaceExternal( iF ))
2323               swap( n2, n3 );
2324             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2325           }
2326           if ( fSubMesh ) // update position of the bary node on geometry
2327           {
2328             if ( subMesh )
2329               subMesh->RemoveNode( baryNode, false );
2330             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2331             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2332             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2333             {
2334               fHelper.SetSubShape( s );
2335               gp_XY uv( 1e100, 1e100 );
2336               double distXYZ[4];
2337               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2338                                         uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2339                    uv.X() < 1e100 )
2340               {
2341                 // node is too far from the surface
2342                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2343                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2344                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2345               }
2346             }
2347           }
2348         }
2349         else
2350         {
2351           // among possible triangles create ones discribed by split method
2352           const int* nInd = volTool.GetFaceNodesIndices( iF );
2353           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2354           int iCom = 0; // common node of triangle faces to split into
2355           list< TTriangleFacet > facets;
2356           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2357           {
2358             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2359                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2360                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2361             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2362                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2363                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2364             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2365             {
2366               facets.push_back( t012 );
2367               facets.push_back( t023 );
2368               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2369                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2370                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2371                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2372               break;
2373             }
2374           }
2375           list< TTriangleFacet >::iterator facet = facets.begin();
2376           if ( facet == facets.end() )
2377             break;
2378           for ( ; facet != facets.end(); ++facet )
2379           {
2380             if ( !volTool.IsFaceExternal( iF ))
2381               swap( facet->_n2, facet->_n3 );
2382             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2383                                                  volNodes[ facet->_n2 ],
2384                                                  volNodes[ facet->_n3 ]));
2385           }
2386         }
2387         for ( int i = 0; i < triangles.size(); ++i )
2388         {
2389           if ( !triangles[i] ) continue;
2390           if ( fSubMesh )
2391             fSubMesh->AddElement( triangles[i]);
2392           newElems.Append( triangles[i] );
2393         }
2394         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2395         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2396
2397       } // while a face based on facet nodes exists
2398     } // loop on volume faces to split them into triangles
2399
2400     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2401
2402     if ( geomType == SMDSEntity_TriQuad_Hexa )
2403     {
2404       // remove medium nodes that could become free
2405       for ( int i = 20; i < volTool.NbNodes(); ++i )
2406         if ( volNodes[i]->NbInverseElements() == 0 )
2407           GetMeshDS()->RemoveNode( volNodes[i] );
2408     }
2409   } // loop on volumes to split
2410   
2411   myLastCreatedNodes = newNodes;
2412   myLastCreatedElems = newElems;
2413 }
2414
2415 //=======================================================================
2416 //function : GetHexaFacetsToSplit
2417 //purpose  : For hexahedra that will be split into prisms, finds facets to
2418 //           split into triangles. Only hexahedra adjacent to the one closest
2419 //           to theFacetNormal.Location() are returned.
2420 //param [in,out] theHexas - the hexahedra
2421 //param [in]     theFacetNormal - facet normal
2422 //param [out]    theFacets - the hexahedra and found facet IDs
2423 //=======================================================================
2424
2425 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2426                                              const gp_Ax1&     theFacetNormal,
2427                                              TFacetOfElem &    theFacets)
2428 {
2429   #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2430
2431   // Find a hexa closest to the location of theFacetNormal
2432
2433   const SMDS_MeshElement* startHex;
2434   {
2435     // get SMDS_ElemIteratorPtr on theHexas
2436     typedef const SMDS_MeshElement*                                      TValue;
2437     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2438     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2439     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2440     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2441     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2442       ( new TElemSetIter( theHexas.begin(),
2443                           theHexas.end(),
2444                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2445
2446     SMESH_ElementSearcher* searcher =
2447       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2448
2449     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2450
2451     delete searcher;
2452
2453     if ( !startHex )
2454       throw SALOME_Exception( THIS_METHOD "startHex not found");
2455   }
2456
2457   // Select a facet of startHex by theFacetNormal
2458
2459   SMDS_VolumeTool vTool( startHex );
2460   double norm[3], dot, maxDot = 0;
2461   int facetID = -1;
2462   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2463     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2464     {
2465       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2466       if ( dot > maxDot )
2467       {
2468         facetID = iF;
2469         maxDot = dot;
2470       }
2471     }
2472   if ( facetID < 0 )
2473     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2474
2475   // Fill theFacets starting from facetID of startHex
2476
2477   // facets used for seach of volumes adjacent to already treated ones
2478   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2479   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2480   TFacetMap facetsToCheck;
2481
2482   set<const SMDS_MeshNode*> facetNodes;
2483   const SMDS_MeshElement*   curHex;
2484
2485   const bool allHex = ( theHexas.size() == myMesh->NbHexas() );
2486
2487   while ( startHex )
2488   {
2489     // move in two directions from startHex via facetID
2490     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2491     {
2492       curHex       = startHex;
2493       int curFacet = facetID;
2494       if ( is2nd ) // do not treat startHex twice
2495       {
2496         vTool.Set( curHex );
2497         if ( vTool.IsFreeFace( curFacet, &curHex ))
2498         {
2499           curHex = 0;
2500         }
2501         else
2502         {
2503           vTool.GetFaceNodes( curFacet, facetNodes );
2504           vTool.Set( curHex );
2505           curFacet = vTool.GetFaceIndex( facetNodes );
2506         }
2507       }
2508       while ( curHex )
2509       {
2510         // store a facet to split
2511         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2512         {
2513           theFacets.insert( make_pair( curHex, -1 ));
2514           break;
2515         }
2516         if ( !allHex && !theHexas.count( curHex ))
2517           break;
2518
2519         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2520           theFacets.insert( make_pair( curHex, curFacet ));
2521         if ( !facetIt2isNew.second )
2522           break;
2523
2524         // remember not-to-split facets in facetsToCheck
2525         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2526         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2527         {
2528           if ( iF == curFacet && iF == oppFacet )
2529             continue;
2530           TVolumeFaceKey facetKey ( vTool, iF );
2531           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2532           pair< TFacetMap::iterator, bool > it2isnew =
2533             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2534           if ( !it2isnew.second )
2535             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2536         }
2537         // pass to a volume adjacent via oppFacet
2538         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2539         {
2540           curHex = 0;
2541         }
2542         else
2543         {
2544           // get a new curFacet
2545           vTool.GetFaceNodes( oppFacet, facetNodes );
2546           vTool.Set( curHex );
2547           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2548         }
2549       }
2550     } // move in two directions from startHex via facetID
2551
2552     // Find a new startHex by facetsToCheck
2553
2554     startHex = 0;
2555     facetID  = -1;
2556     TFacetMap::iterator fIt = facetsToCheck.begin();
2557     while ( !startHex && fIt != facetsToCheck.end() )
2558     {
2559       const TElemFacets&  elemFacets = fIt->second;
2560       const SMDS_MeshElement*    hex = elemFacets.first->first;
2561       int                 splitFacet = elemFacets.first->second;
2562       int               lateralFacet = elemFacets.second;
2563       facetsToCheck.erase( fIt );
2564       fIt = facetsToCheck.begin();
2565
2566       vTool.Set( hex );
2567       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2568            curHex->GetGeomType() != SMDSGeom_HEXA )
2569         continue;
2570       if ( !allHex && !theHexas.count( curHex ))
2571         continue;
2572
2573       startHex = curHex;
2574
2575       // find a facet of startHex to split 
2576
2577       set<const SMDS_MeshNode*> lateralNodes;
2578       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2579       vTool.GetFaceNodes( splitFacet,   facetNodes );
2580       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2581       vTool.Set( startHex );
2582       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2583
2584       // look for a facet of startHex having common nodes with facetNodes
2585       // but not lateralFacet
2586       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2587       {
2588         if ( iF == lateralFacet )
2589           continue;
2590         int nbCommonNodes = 0;
2591         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2592         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2593           nbCommonNodes += facetNodes.count( nn[ iN ]);
2594
2595         if ( nbCommonNodes >= 2 )
2596         {
2597           facetID = iF;
2598           break;
2599         }
2600       }
2601       if ( facetID < 0 )
2602         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2603     }
2604   } //   while ( startHex )
2605 }
2606
2607 //=======================================================================
2608 //function : AddToSameGroups
2609 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2610 //=======================================================================
2611
2612 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2613                                         const SMDS_MeshElement* elemInGroups,
2614                                         SMESHDS_Mesh *          aMesh)
2615 {
2616   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2617   if (!groups.empty()) {
2618     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2619     for ( ; grIt != groups.end(); grIt++ ) {
2620       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2621       if ( group && group->Contains( elemInGroups ))
2622         group->SMDSGroup().Add( elemToAdd );
2623     }
2624   }
2625 }
2626
2627
2628 //=======================================================================
2629 //function : RemoveElemFromGroups
2630 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2631 //=======================================================================
2632 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2633                                              SMESHDS_Mesh *          aMesh)
2634 {
2635   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2636   if (!groups.empty())
2637   {
2638     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2639     for (; GrIt != groups.end(); GrIt++)
2640     {
2641       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2642       if (!grp || grp->IsEmpty()) continue;
2643       grp->SMDSGroup().Remove(removeelem);
2644     }
2645   }
2646 }
2647
2648 //================================================================================
2649 /*!
2650  * \brief Replace elemToRm by elemToAdd in the all groups
2651  */
2652 //================================================================================
2653
2654 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2655                                             const SMDS_MeshElement* elemToAdd,
2656                                             SMESHDS_Mesh *          aMesh)
2657 {
2658   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2659   if (!groups.empty()) {
2660     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2661     for ( ; grIt != groups.end(); grIt++ ) {
2662       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2663       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2664         group->SMDSGroup().Add( elemToAdd );
2665     }
2666   }
2667 }
2668
2669 //================================================================================
2670 /*!
2671  * \brief Replace elemToRm by elemToAdd in the all groups
2672  */
2673 //================================================================================
2674
2675 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2676                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2677                                             SMESHDS_Mesh *                         aMesh)
2678 {
2679   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2680   if (!groups.empty())
2681   {
2682     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2683     for ( ; grIt != groups.end(); grIt++ ) {
2684       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2685       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2686         for ( int i = 0; i < elemToAdd.size(); ++i )
2687           group->SMDSGroup().Add( elemToAdd[ i ] );
2688     }
2689   }
2690 }
2691
2692 //=======================================================================
2693 //function : QuadToTri
2694 //purpose  : Cut quadrangles into triangles.
2695 //           theCrit is used to select a diagonal to cut
2696 //=======================================================================
2697
2698 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2699                                   const bool         the13Diag)
2700 {
2701   myLastCreatedElems.Clear();
2702   myLastCreatedNodes.Clear();
2703
2704   MESSAGE( "::QuadToTri()" );
2705
2706   SMESHDS_Mesh * aMesh = GetMeshDS();
2707
2708   Handle(Geom_Surface) surface;
2709   SMESH_MesherHelper   helper( *GetMesh() );
2710
2711   TIDSortedElemSet::iterator itElem;
2712   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2713     const SMDS_MeshElement* elem = *itElem;
2714     if ( !elem || elem->GetType() != SMDSAbs_Face )
2715       continue;
2716     bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2717     if(!isquad) continue;
2718
2719     if(elem->NbNodes()==4) {
2720       // retrieve element nodes
2721       const SMDS_MeshNode* aNodes [4];
2722       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2723       int i = 0;
2724       while ( itN->more() )
2725         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2726
2727       int aShapeId = FindShape( elem );
2728       const SMDS_MeshElement* newElem1 = 0;
2729       const SMDS_MeshElement* newElem2 = 0;
2730       if ( the13Diag ) {
2731         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2732         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2733       }
2734       else {
2735         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2736         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2737       }
2738       myLastCreatedElems.Append(newElem1);
2739       myLastCreatedElems.Append(newElem2);
2740       // put a new triangle on the same shape and add to the same groups
2741       if ( aShapeId )
2742         {
2743           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2744           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2745         }
2746       AddToSameGroups( newElem1, elem, aMesh );
2747       AddToSameGroups( newElem2, elem, aMesh );
2748       //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2749       aMesh->RemoveElement( elem );
2750     }
2751
2752     // Quadratic quadrangle
2753
2754     if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2755
2756       // get surface elem is on
2757       int aShapeId = FindShape( elem );
2758       if ( aShapeId != helper.GetSubShapeID() ) {
2759         surface.Nullify();
2760         TopoDS_Shape shape;
2761         if ( aShapeId > 0 )
2762           shape = aMesh->IndexToShape( aShapeId );
2763         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2764           TopoDS_Face face = TopoDS::Face( shape );
2765           surface = BRep_Tool::Surface( face );
2766           if ( !surface.IsNull() )
2767             helper.SetSubShape( shape );
2768         }
2769       }
2770
2771       const SMDS_MeshNode* aNodes [8];
2772       const SMDS_MeshNode* inFaceNode = 0;
2773       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2774       int i = 0;
2775       while ( itN->more() ) {
2776         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2777         if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2778              aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2779         {
2780           inFaceNode = aNodes[ i-1 ];
2781         }
2782       }
2783
2784       // find middle point for (0,1,2,3)
2785       // and create a node in this point;
2786       gp_XYZ p( 0,0,0 );
2787       if ( surface.IsNull() ) {
2788         for(i=0; i<4; i++)
2789           p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2790         p /= 4;
2791       }
2792       else {
2793         TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2794         gp_XY uv( 0,0 );
2795         for(i=0; i<4; i++)
2796           uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2797         uv /= 4.;
2798         p = surface->Value( uv.X(), uv.Y() ).XYZ();
2799       }
2800       const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2801       myLastCreatedNodes.Append(newN);
2802
2803       // create a new element
2804       const SMDS_MeshElement* newElem1 = 0;
2805       const SMDS_MeshElement* newElem2 = 0;
2806       if ( the13Diag ) {
2807         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2808                                   aNodes[6], aNodes[7], newN );
2809         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2810                                   newN,      aNodes[4], aNodes[5] );
2811       }
2812       else {
2813         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2814                                   aNodes[7], aNodes[4], newN );
2815         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2816                                   newN,      aNodes[5], aNodes[6] );
2817       }
2818       myLastCreatedElems.Append(newElem1);
2819       myLastCreatedElems.Append(newElem2);
2820       // put a new triangle on the same shape and add to the same groups
2821       if ( aShapeId )
2822         {
2823           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2824           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2825         }
2826       AddToSameGroups( newElem1, elem, aMesh );
2827       AddToSameGroups( newElem2, elem, aMesh );
2828       aMesh->RemoveElement( elem );
2829     }
2830   }
2831
2832   return true;
2833 }
2834
2835 //=======================================================================
2836 //function : getAngle
2837 //purpose  :
2838 //=======================================================================
2839
2840 double getAngle(const SMDS_MeshElement * tr1,
2841                 const SMDS_MeshElement * tr2,
2842                 const SMDS_MeshNode *    n1,
2843                 const SMDS_MeshNode *    n2)
2844 {
2845   double angle = 2. * M_PI; // bad angle
2846
2847   // get normals
2848   SMESH::Controls::TSequenceOfXYZ P1, P2;
2849   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2850        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2851     return angle;
2852   gp_Vec N1,N2;
2853   if(!tr1->IsQuadratic())
2854     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2855   else
2856     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2857   if ( N1.SquareMagnitude() <= gp::Resolution() )
2858     return angle;
2859   if(!tr2->IsQuadratic())
2860     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2861   else
2862     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2863   if ( N2.SquareMagnitude() <= gp::Resolution() )
2864     return angle;
2865
2866   // find the first diagonal node n1 in the triangles:
2867   // take in account a diagonal link orientation
2868   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2869   for ( int t = 0; t < 2; t++ ) {
2870     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2871     int i = 0, iDiag = -1;
2872     while ( it->more()) {
2873       const SMDS_MeshElement *n = it->next();
2874       if ( n == n1 || n == n2 ) {
2875         if ( iDiag < 0)
2876           iDiag = i;
2877         else {
2878           if ( i - iDiag == 1 )
2879             nFirst[ t ] = ( n == n1 ? n2 : n1 );
2880           else
2881             nFirst[ t ] = n;
2882           break;
2883         }
2884       }
2885       i++;
2886     }
2887   }
2888   if ( nFirst[ 0 ] == nFirst[ 1 ] )
2889     N2.Reverse();
2890
2891   angle = N1.Angle( N2 );
2892   //SCRUTE( angle );
2893   return angle;
2894 }
2895
2896 // =================================================
2897 // class generating a unique ID for a pair of nodes
2898 // and able to return nodes by that ID
2899 // =================================================
2900 class LinkID_Gen {
2901 public:
2902
2903   LinkID_Gen( const SMESHDS_Mesh* theMesh )
2904     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2905   {}
2906
2907   long GetLinkID (const SMDS_MeshNode * n1,
2908                   const SMDS_MeshNode * n2) const
2909   {
2910     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2911   }
2912
2913   bool GetNodes (const long             theLinkID,
2914                  const SMDS_MeshNode* & theNode1,
2915                  const SMDS_MeshNode* & theNode2) const
2916   {
2917     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2918     if ( !theNode1 ) return false;
2919     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2920     if ( !theNode2 ) return false;
2921     return true;
2922   }
2923
2924 private:
2925   LinkID_Gen();
2926   const SMESHDS_Mesh* myMesh;
2927   long                myMaxID;
2928 };
2929
2930
2931 //=======================================================================
2932 //function : TriToQuad
2933 //purpose  : Fuse neighbour triangles into quadrangles.
2934 //           theCrit is used to select a neighbour to fuse with.
2935 //           theMaxAngle is a max angle between element normals at which
2936 //           fusion is still performed.
2937 //=======================================================================
2938
2939 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
2940                                   SMESH::Controls::NumericalFunctorPtr theCrit,
2941                                   const double                         theMaxAngle)
2942 {
2943   myLastCreatedElems.Clear();
2944   myLastCreatedNodes.Clear();
2945
2946   MESSAGE( "::TriToQuad()" );
2947
2948   if ( !theCrit.get() )
2949     return false;
2950
2951   SMESHDS_Mesh * aMesh = GetMeshDS();
2952
2953   // Prepare data for algo: build
2954   // 1. map of elements with their linkIDs
2955   // 2. map of linkIDs with their elements
2956
2957   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2958   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2959   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
2960   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2961
2962   TIDSortedElemSet::iterator itElem;
2963   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2964   {
2965     const SMDS_MeshElement* elem = *itElem;
2966     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2967     bool IsTria = ( elem->NbCornerNodes()==3 );
2968     if (!IsTria) continue;
2969
2970     // retrieve element nodes
2971     const SMDS_MeshNode* aNodes [4];
2972     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
2973     int i = 0;
2974     while ( i < 3 )
2975       aNodes[ i++ ] = itN->next();
2976     aNodes[ 3 ] = aNodes[ 0 ];
2977
2978     // fill maps
2979     for ( i = 0; i < 3; i++ ) {
2980       SMESH_TLink link( aNodes[i], aNodes[i+1] );
2981       // check if elements sharing a link can be fused
2982       itLE = mapLi_listEl.find( link );
2983       if ( itLE != mapLi_listEl.end() ) {
2984         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2985           continue;
2986         const SMDS_MeshElement* elem2 = (*itLE).second.front();
2987         //if ( FindShape( elem ) != FindShape( elem2 ))
2988         //  continue; // do not fuse triangles laying on different shapes
2989         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2990           continue; // avoid making badly shaped quads
2991         (*itLE).second.push_back( elem );
2992       }
2993       else {
2994         mapLi_listEl[ link ].push_back( elem );
2995       }
2996       mapEl_setLi [ elem ].insert( link );
2997     }
2998   }
2999   // Clean the maps from the links shared by a sole element, ie
3000   // links to which only one element is bound in mapLi_listEl
3001
3002   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3003     int nbElems = (*itLE).second.size();
3004     if ( nbElems < 2  ) {
3005       const SMDS_MeshElement* elem = (*itLE).second.front();
3006       SMESH_TLink link = (*itLE).first;
3007       mapEl_setLi[ elem ].erase( link );
3008       if ( mapEl_setLi[ elem ].empty() )
3009         mapEl_setLi.erase( elem );
3010     }
3011   }
3012
3013   // Algo: fuse triangles into quadrangles
3014
3015   while ( ! mapEl_setLi.empty() ) {
3016     // Look for the start element:
3017     // the element having the least nb of shared links
3018     const SMDS_MeshElement* startElem = 0;
3019     int minNbLinks = 4;
3020     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3021       int nbLinks = (*itEL).second.size();
3022       if ( nbLinks < minNbLinks ) {
3023         startElem = (*itEL).first;
3024         minNbLinks = nbLinks;
3025         if ( minNbLinks == 1 )
3026           break;
3027       }
3028     }
3029
3030     // search elements to fuse starting from startElem or links of elements
3031     // fused earlyer - startLinks
3032     list< SMESH_TLink > startLinks;
3033     while ( startElem || !startLinks.empty() ) {
3034       while ( !startElem && !startLinks.empty() ) {
3035         // Get an element to start, by a link
3036         SMESH_TLink linkId = startLinks.front();
3037         startLinks.pop_front();
3038         itLE = mapLi_listEl.find( linkId );
3039         if ( itLE != mapLi_listEl.end() ) {
3040           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3041           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3042           for ( ; itE != listElem.end() ; itE++ )
3043             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3044               startElem = (*itE);
3045           mapLi_listEl.erase( itLE );
3046         }
3047       }
3048
3049       if ( startElem ) {
3050         // Get candidates to be fused
3051         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3052         const SMESH_TLink *link12, *link13;
3053         startElem = 0;
3054         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3055         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3056         ASSERT( !setLi.empty() );
3057         set< SMESH_TLink >::iterator itLi;
3058         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3059         {
3060           const SMESH_TLink & link = (*itLi);
3061           itLE = mapLi_listEl.find( link );
3062           if ( itLE == mapLi_listEl.end() )
3063             continue;
3064
3065           const SMDS_MeshElement* elem = (*itLE).second.front();
3066           if ( elem == tr1 )
3067             elem = (*itLE).second.back();
3068           mapLi_listEl.erase( itLE );
3069           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3070             continue;
3071           if ( tr2 ) {
3072             tr3 = elem;
3073             link13 = &link;
3074           }
3075           else {
3076             tr2 = elem;
3077             link12 = &link;
3078           }
3079
3080           // add other links of elem to list of links to re-start from
3081           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3082           set< SMESH_TLink >::iterator it;
3083           for ( it = links.begin(); it != links.end(); it++ ) {
3084             const SMESH_TLink& link2 = (*it);
3085             if ( link2 != link )
3086               startLinks.push_back( link2 );
3087           }
3088         }
3089
3090         // Get nodes of possible quadrangles
3091         const SMDS_MeshNode *n12 [4], *n13 [4];
3092         bool Ok12 = false, Ok13 = false;
3093         const SMDS_MeshNode *linkNode1, *linkNode2;
3094         if(tr2) {
3095           linkNode1 = link12->first;
3096           linkNode2 = link12->second;
3097           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3098             Ok12 = true;
3099         }
3100         if(tr3) {
3101           linkNode1 = link13->first;
3102           linkNode2 = link13->second;
3103           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3104             Ok13 = true;
3105         }
3106
3107         // Choose a pair to fuse
3108         if ( Ok12 && Ok13 ) {
3109           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3110           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3111           double aBadRate12 = getBadRate( &quad12, theCrit );
3112           double aBadRate13 = getBadRate( &quad13, theCrit );
3113           if (  aBadRate13 < aBadRate12 )
3114             Ok12 = false;
3115           else
3116             Ok13 = false;
3117         }
3118
3119         // Make quadrangles
3120         // and remove fused elems and remove links from the maps
3121         mapEl_setLi.erase( tr1 );
3122         if ( Ok12 )
3123         {
3124           mapEl_setLi.erase( tr2 );
3125           mapLi_listEl.erase( *link12 );
3126           if ( tr1->NbNodes() == 3 )
3127           {
3128             const SMDS_MeshElement* newElem = 0;
3129             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3130             myLastCreatedElems.Append(newElem);
3131             AddToSameGroups( newElem, tr1, aMesh );
3132             int aShapeId = tr1->getshapeId();
3133             if ( aShapeId )
3134               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3135             aMesh->RemoveElement( tr1 );
3136             aMesh->RemoveElement( tr2 );
3137           }
3138           else {
3139             vector< const SMDS_MeshNode* > N1;
3140             vector< const SMDS_MeshNode* > N2;
3141             getNodesFromTwoTria(tr1,tr2,N1,N2);
3142             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3143             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3144             // i.e. first nodes from both arrays form a new diagonal
3145             const SMDS_MeshNode* aNodes[8];
3146             aNodes[0] = N1[0];
3147             aNodes[1] = N1[1];
3148             aNodes[2] = N2[0];
3149             aNodes[3] = N2[1];
3150             aNodes[4] = N1[3];
3151             aNodes[5] = N2[5];
3152             aNodes[6] = N2[3];
3153             aNodes[7] = N1[5];
3154             const SMDS_MeshElement* newElem = 0;
3155             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3156               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3157                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3158             else
3159               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3160                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3161             myLastCreatedElems.Append(newElem);
3162             AddToSameGroups( newElem, tr1, aMesh );
3163             int aShapeId = tr1->getshapeId();
3164             if ( aShapeId )
3165               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3166             aMesh->RemoveElement( tr1 );
3167             aMesh->RemoveElement( tr2 );
3168             // remove middle node (9)
3169             if ( N1[4]->NbInverseElements() == 0 )
3170               aMesh->RemoveNode( N1[4] );
3171             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3172               aMesh->RemoveNode( N1[6] );
3173             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3174               aMesh->RemoveNode( N2[6] );
3175           }
3176         }
3177         else if ( Ok13 )
3178         {
3179           mapEl_setLi.erase( tr3 );
3180           mapLi_listEl.erase( *link13 );
3181           if ( tr1->NbNodes() == 3 ) {
3182             const SMDS_MeshElement* newElem = 0;
3183             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3184             myLastCreatedElems.Append(newElem);
3185             AddToSameGroups( newElem, tr1, aMesh );
3186             int aShapeId = tr1->getshapeId();
3187             if ( aShapeId )
3188               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3189             aMesh->RemoveElement( tr1 );
3190             aMesh->RemoveElement( tr3 );
3191           }
3192           else {
3193             vector< const SMDS_MeshNode* > N1;
3194             vector< const SMDS_MeshNode* > N2;
3195             getNodesFromTwoTria(tr1,tr3,N1,N2);
3196             // now we receive following N1 and N2 (using numeration as above image)
3197             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3198             // i.e. first nodes from both arrays form a new diagonal
3199             const SMDS_MeshNode* aNodes[8];
3200             aNodes[0] = N1[0];
3201             aNodes[1] = N1[1];
3202             aNodes[2] = N2[0];
3203             aNodes[3] = N2[1];
3204             aNodes[4] = N1[3];
3205             aNodes[5] = N2[5];
3206             aNodes[6] = N2[3];
3207             aNodes[7] = N1[5];
3208             const SMDS_MeshElement* newElem = 0;
3209             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3210               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3211                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3212             else
3213               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3214                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3215             myLastCreatedElems.Append(newElem);
3216             AddToSameGroups( newElem, tr1, aMesh );
3217             int aShapeId = tr1->getshapeId();
3218             if ( aShapeId )
3219               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3220             aMesh->RemoveElement( tr1 );
3221             aMesh->RemoveElement( tr3 );
3222             // remove middle node (9)
3223             if ( N1[4]->NbInverseElements() == 0 )
3224               aMesh->RemoveNode( N1[4] );
3225             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3226               aMesh->RemoveNode( N1[6] );
3227             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3228               aMesh->RemoveNode( N2[6] );
3229           }
3230         }
3231
3232         // Next element to fuse: the rejected one
3233         if ( tr3 )
3234           startElem = Ok12 ? tr3 : tr2;
3235
3236       } // if ( startElem )
3237     } // while ( startElem || !startLinks.empty() )
3238   } // while ( ! mapEl_setLi.empty() )
3239
3240   return true;
3241 }
3242
3243
3244 /*#define DUMPSO(txt) \
3245 //  cout << txt << endl;
3246 //=============================================================================
3247 //
3248 //
3249 //
3250 //=============================================================================
3251 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3252 {
3253 if ( i1 == i2 )
3254 return;
3255 int tmp = idNodes[ i1 ];
3256 idNodes[ i1 ] = idNodes[ i2 ];
3257 idNodes[ i2 ] = tmp;
3258 gp_Pnt Ptmp = P[ i1 ];
3259 P[ i1 ] = P[ i2 ];
3260 P[ i2 ] = Ptmp;
3261 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3262 }
3263
3264 //=======================================================================
3265 //function : SortQuadNodes
3266 //purpose  : Set 4 nodes of a quadrangle face in a good order.
3267 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
3268 //           1 or 2 else 0.
3269 //=======================================================================
3270
3271 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3272 int               idNodes[] )
3273 {
3274   gp_Pnt P[4];
3275   int i;
3276   for ( i = 0; i < 4; i++ ) {
3277     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3278     if ( !n ) return 0;
3279     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3280   }
3281
3282   gp_Vec V1(P[0], P[1]);
3283   gp_Vec V2(P[0], P[2]);
3284   gp_Vec V3(P[0], P[3]);
3285
3286   gp_Vec Cross1 = V1 ^ V2;
3287   gp_Vec Cross2 = V2 ^ V3;
3288
3289   i = 0;
3290   if (Cross1.Dot(Cross2) < 0)
3291   {
3292     Cross1 = V2 ^ V1;
3293     Cross2 = V1 ^ V3;
3294
3295     if (Cross1.Dot(Cross2) < 0)
3296       i = 2;
3297     else
3298       i = 1;
3299     swap ( i, i + 1, idNodes, P );
3300
3301     //     for ( int ii = 0; ii < 4; ii++ ) {
3302     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3303     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3304     //     }
3305   }
3306   return i;
3307 }
3308
3309 //=======================================================================
3310 //function : SortHexaNodes
3311 //purpose  : Set 8 nodes of a hexahedron in a good order.
3312 //           Return success status
3313 //=======================================================================
3314
3315 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3316                                       int               idNodes[] )
3317 {
3318   gp_Pnt P[8];
3319   int i;
3320   DUMPSO( "INPUT: ========================================");
3321   for ( i = 0; i < 8; i++ ) {
3322     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3323     if ( !n ) return false;
3324     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3325     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3326   }
3327   DUMPSO( "========================================");
3328
3329
3330   set<int> faceNodes;  // ids of bottom face nodes, to be found
3331   set<int> checkedId1; // ids of tried 2-nd nodes
3332   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3333   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
3334   int iMin, iLoop1 = 0;
3335
3336   // Loop to try the 2-nd nodes
3337
3338   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3339   {
3340     // Find not checked 2-nd node
3341     for ( i = 1; i < 8; i++ )
3342       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3343         int id1 = idNodes[i];
3344         swap ( 1, i, idNodes, P );
3345         checkedId1.insert ( id1 );
3346         break;
3347       }
3348
3349     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3350     // ie that all but meybe one (id3 which is on the same face) nodes
3351     // lay on the same side from the triangle plane.
3352
3353     bool manyInPlane = false; // more than 4 nodes lay in plane
3354     int iLoop2 = 0;
3355     while ( ++iLoop2 < 6 ) {
3356
3357       // get 1-2-3 plane coeffs
3358       Standard_Real A, B, C, D;
3359       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3360       if ( N.SquareMagnitude() > gp::Resolution() )
3361       {
3362         gp_Pln pln ( P[0], N );
3363         pln.Coefficients( A, B, C, D );
3364
3365         // find the node (iMin) closest to pln
3366         Standard_Real dist[ 8 ], minDist = DBL_MAX;
3367         set<int> idInPln;
3368         for ( i = 3; i < 8; i++ ) {
3369           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3370           if ( fabs( dist[i] ) < minDist ) {
3371             minDist = fabs( dist[i] );
3372             iMin = i;
3373           }
3374           if ( fabs( dist[i] ) <= tol )
3375             idInPln.insert( idNodes[i] );
3376         }
3377
3378         // there should not be more than 4 nodes in bottom plane
3379         if ( idInPln.size() > 1 )
3380         {
3381           DUMPSO( "### idInPln.size() = " << idInPln.size());
3382           // idInPlane does not contain the first 3 nodes
3383           if ( manyInPlane || idInPln.size() == 5)
3384             return false; // all nodes in one plane
3385           manyInPlane = true;
3386
3387           // set the 1-st node to be not in plane
3388           for ( i = 3; i < 8; i++ ) {
3389             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3390               DUMPSO( "### Reset 0-th node");
3391               swap( 0, i, idNodes, P );
3392               break;
3393             }
3394           }
3395
3396           // reset to re-check second nodes
3397           leastDist = DBL_MAX;
3398           faceNodes.clear();
3399           checkedId1.clear();
3400           iLoop1 = 0;
3401           break; // from iLoop2;
3402         }
3403
3404         // check that the other 4 nodes are on the same side
3405         bool sameSide = true;
3406         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3407         for ( i = 3; sameSide && i < 8; i++ ) {
3408           if ( i != iMin )
3409             sameSide = ( isNeg == dist[i] <= 0.);
3410         }
3411
3412         // keep best solution
3413         if ( sameSide && minDist < leastDist ) {
3414           leastDist = minDist;
3415           faceNodes.clear();
3416           faceNodes.insert( idNodes[ 1 ] );
3417           faceNodes.insert( idNodes[ 2 ] );
3418           faceNodes.insert( idNodes[ iMin ] );
3419           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3420                   << " leastDist = " << leastDist);
3421           if ( leastDist <= DBL_MIN )
3422             break;
3423         }
3424       }
3425
3426       // set next 3-d node to check
3427       int iNext = 2 + iLoop2;
3428       if ( iNext < 8 ) {
3429         DUMPSO( "Try 2-nd");
3430         swap ( 2, iNext, idNodes, P );
3431       }
3432     } // while ( iLoop2 < 6 )
3433   } // iLoop1
3434
3435   if ( faceNodes.empty() ) return false;
3436
3437   // Put the faceNodes in proper places
3438   for ( i = 4; i < 8; i++ ) {
3439     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3440       // find a place to put
3441       int iTo = 1;
3442       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3443         iTo++;
3444       DUMPSO( "Set faceNodes");
3445       swap ( iTo, i, idNodes, P );
3446     }
3447   }
3448
3449
3450   // Set nodes of the found bottom face in good order
3451   DUMPSO( " Found bottom face: ");
3452   i = SortQuadNodes( theMesh, idNodes );
3453   if ( i ) {
3454     gp_Pnt Ptmp = P[ i ];
3455     P[ i ] = P[ i+1 ];
3456     P[ i+1 ] = Ptmp;
3457   }
3458   //   else
3459   //     for ( int ii = 0; ii < 4; ii++ ) {
3460   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3461   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3462   //    }
3463
3464   // Gravity center of the top and bottom faces
3465   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3466   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3467
3468   // Get direction from the bottom to the top face
3469   gp_Vec upDir ( aGCb, aGCt );
3470   Standard_Real upDirSize = upDir.Magnitude();
3471   if ( upDirSize <= gp::Resolution() ) return false;
3472   upDir / upDirSize;
3473
3474   // Assure that the bottom face normal points up
3475   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3476   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3477   if ( Nb.Dot( upDir ) < 0 ) {
3478     DUMPSO( "Reverse bottom face");
3479     swap( 1, 3, idNodes, P );
3480   }
3481
3482   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3483   Standard_Real minDist = DBL_MAX;
3484   for ( i = 4; i < 8; i++ ) {
3485     // projection of P[i] to the plane defined by P[0] and upDir
3486     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3487     Standard_Real sqDist = P[0].SquareDistance( Pp );
3488     if ( sqDist < minDist ) {
3489       minDist = sqDist;
3490       iMin = i;
3491     }
3492   }
3493   DUMPSO( "Set 4-th");
3494   swap ( 4, iMin, idNodes, P );
3495
3496   // Set nodes of the top face in good order
3497   DUMPSO( "Sort top face");
3498   i = SortQuadNodes( theMesh, &idNodes[4] );
3499   if ( i ) {
3500     i += 4;
3501     gp_Pnt Ptmp = P[ i ];
3502     P[ i ] = P[ i+1 ];
3503     P[ i+1 ] = Ptmp;
3504   }
3505
3506   // Assure that direction of the top face normal is from the bottom face
3507   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3508   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3509   if ( Nt.Dot( upDir ) < 0 ) {
3510     DUMPSO( "Reverse top face");
3511     swap( 5, 7, idNodes, P );
3512   }
3513
3514   //   DUMPSO( "OUTPUT: ========================================");
3515   //   for ( i = 0; i < 8; i++ ) {
3516   //     float *p = ugrid->GetPoint(idNodes[i]);
3517   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3518   //   }
3519
3520   return true;
3521 }*/
3522
3523 //================================================================================
3524 /*!
3525  * \brief Return nodes linked to the given one
3526  * \param theNode - the node
3527  * \param linkedNodes - the found nodes
3528  * \param type - the type of elements to check
3529  *
3530  * Medium nodes are ignored
3531  */
3532 //================================================================================
3533
3534 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3535                                        TIDSortedElemSet &   linkedNodes,
3536                                        SMDSAbs_ElementType  type )
3537 {
3538   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3539   while ( elemIt->more() )
3540   {
3541     const SMDS_MeshElement* elem = elemIt->next();
3542     if(elem->GetType() == SMDSAbs_0DElement)
3543       continue;
3544
3545     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3546     if ( elem->GetType() == SMDSAbs_Volume )
3547     {
3548       SMDS_VolumeTool vol( elem );
3549       while ( nodeIt->more() ) {
3550         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3551         if ( theNode != n && vol.IsLinked( theNode, n ))
3552           linkedNodes.insert( n );
3553       }
3554     }
3555     else
3556     {
3557       for ( int i = 0; nodeIt->more(); ++i ) {
3558         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3559         if ( n == theNode ) {
3560           int iBefore = i - 1;
3561           int iAfter  = i + 1;
3562           if ( elem->IsQuadratic() ) {
3563             int nb = elem->NbNodes() / 2;
3564             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3565             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3566           }
3567           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3568           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3569         }
3570       }
3571     }
3572   }
3573 }
3574
3575 //=======================================================================
3576 //function : laplacianSmooth
3577 //purpose  : pulls theNode toward the center of surrounding nodes directly
3578 //           connected to that node along an element edge
3579 //=======================================================================
3580
3581 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3582                      const Handle(Geom_Surface)&          theSurface,
3583                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3584 {
3585   // find surrounding nodes
3586
3587   TIDSortedElemSet nodeSet;
3588   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3589
3590   // compute new coodrs
3591
3592   double coord[] = { 0., 0., 0. };
3593   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3594   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3595     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3596     if ( theSurface.IsNull() ) { // smooth in 3D
3597       coord[0] += node->X();
3598       coord[1] += node->Y();
3599       coord[2] += node->Z();
3600     }
3601     else { // smooth in 2D
3602       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3603       gp_XY* uv = theUVMap[ node ];
3604       coord[0] += uv->X();
3605       coord[1] += uv->Y();
3606     }
3607   }
3608   int nbNodes = nodeSet.size();
3609   if ( !nbNodes )
3610     return;
3611   coord[0] /= nbNodes;
3612   coord[1] /= nbNodes;
3613
3614   if ( !theSurface.IsNull() ) {
3615     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3616     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3617     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3618     coord[0] = p3d.X();
3619     coord[1] = p3d.Y();
3620     coord[2] = p3d.Z();
3621   }
3622   else
3623     coord[2] /= nbNodes;
3624
3625   // move node
3626
3627   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3628 }
3629
3630 //=======================================================================
3631 //function : centroidalSmooth
3632 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3633 //           surrounding elements
3634 //=======================================================================
3635
3636 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3637                       const Handle(Geom_Surface)&          theSurface,
3638                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3639 {
3640   gp_XYZ aNewXYZ(0.,0.,0.);
3641   SMESH::Controls::Area anAreaFunc;
3642   double totalArea = 0.;
3643   int nbElems = 0;
3644
3645   // compute new XYZ
3646
3647   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3648   while ( elemIt->more() )
3649   {
3650     const SMDS_MeshElement* elem = elemIt->next();
3651     nbElems++;
3652
3653     gp_XYZ elemCenter(0.,0.,0.);
3654     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3655     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3656     int nn = elem->NbNodes();
3657     if(elem->IsQuadratic()) nn = nn/2;
3658     int i=0;
3659     //while ( itN->more() ) {
3660     while ( i<nn ) {
3661       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3662       i++;
3663       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3664       aNodePoints.push_back( aP );
3665       if ( !theSurface.IsNull() ) { // smooth in 2D
3666         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3667         gp_XY* uv = theUVMap[ aNode ];
3668         aP.SetCoord( uv->X(), uv->Y(), 0. );
3669       }
3670       elemCenter += aP;
3671     }
3672     double elemArea = anAreaFunc.GetValue( aNodePoints );
3673     totalArea += elemArea;
3674     elemCenter /= nn;
3675     aNewXYZ += elemCenter * elemArea;
3676   }
3677   aNewXYZ /= totalArea;
3678   if ( !theSurface.IsNull() ) {
3679     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3680     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3681   }
3682
3683   // move node
3684
3685   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3686 }
3687
3688 //=======================================================================
3689 //function : getClosestUV
3690 //purpose  : return UV of closest projection
3691 //=======================================================================
3692
3693 static bool getClosestUV (Extrema_GenExtPS& projector,
3694                           const gp_Pnt&     point,
3695                           gp_XY &           result)
3696 {
3697   projector.Perform( point );
3698   if ( projector.IsDone() ) {
3699     double u, v, minVal = DBL_MAX;
3700     for ( int i = projector.NbExt(); i > 0; i-- )
3701       if ( projector.SquareDistance( i ) < minVal ) {
3702         minVal = projector.SquareDistance( i );
3703         projector.Point( i ).Parameter( u, v );
3704       }
3705     result.SetCoord( u, v );
3706     return true;
3707   }
3708   return false;
3709 }
3710
3711 //=======================================================================
3712 //function : Smooth
3713 //purpose  : Smooth theElements during theNbIterations or until a worst
3714 //           element has aspect ratio <= theTgtAspectRatio.
3715 //           Aspect Ratio varies in range [1.0, inf].
3716 //           If theElements is empty, the whole mesh is smoothed.
3717 //           theFixedNodes contains additionally fixed nodes. Nodes built
3718 //           on edges and boundary nodes are always fixed.
3719 //=======================================================================
3720
3721 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3722                                set<const SMDS_MeshNode*> & theFixedNodes,
3723                                const SmoothMethod          theSmoothMethod,
3724                                const int                   theNbIterations,
3725                                double                      theTgtAspectRatio,
3726                                const bool                  the2D)
3727 {
3728   myLastCreatedElems.Clear();
3729   myLastCreatedNodes.Clear();
3730
3731   MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3732
3733   if ( theTgtAspectRatio < 1.0 )
3734     theTgtAspectRatio = 1.0;
3735
3736   const double disttol = 1.e-16;
3737
3738   SMESH::Controls::AspectRatio aQualityFunc;
3739
3740   SMESHDS_Mesh* aMesh = GetMeshDS();
3741
3742   if ( theElems.empty() ) {
3743     // add all faces to theElems
3744     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3745     while ( fIt->more() ) {
3746       const SMDS_MeshElement* face = fIt->next();
3747       theElems.insert( theElems.end(), face );
3748     }
3749   }
3750   // get all face ids theElems are on
3751   set< int > faceIdSet;
3752   TIDSortedElemSet::iterator itElem;
3753   if ( the2D )
3754     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3755       int fId = FindShape( *itElem );
3756       // check that corresponding submesh exists and a shape is face
3757       if (fId &&
3758           faceIdSet.find( fId ) == faceIdSet.end() &&
3759           aMesh->MeshElements( fId )) {
3760         TopoDS_Shape F = aMesh->IndexToShape( fId );
3761         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3762           faceIdSet.insert( fId );
3763       }
3764     }
3765   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3766
3767   // ===============================================
3768   // smooth elements on each TopoDS_Face separately
3769   // ===============================================
3770
3771   SMESH_MesherHelper helper( *GetMesh() );
3772
3773   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3774   for ( ; fId != faceIdSet.rend(); ++fId )
3775   {
3776     // get face surface and submesh
3777     Handle(Geom_Surface) surface;
3778     SMESHDS_SubMesh* faceSubMesh = 0;
3779     TopoDS_Face face;
3780     double fToler2 = 0, f,l;
3781     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3782     bool isUPeriodic = false, isVPeriodic = false;
3783     if ( *fId )
3784     {
3785       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3786       surface = BRep_Tool::Surface( face );
3787       faceSubMesh = aMesh->MeshElements( *fId );
3788       fToler2 = BRep_Tool::Tolerance( face );
3789       fToler2 *= fToler2 * 10.;
3790       isUPeriodic = surface->IsUPeriodic();
3791       if ( isUPeriodic )
3792         surface->UPeriod();
3793       isVPeriodic = surface->IsVPeriodic();
3794       if ( isVPeriodic )
3795         surface->VPeriod();
3796       surface->Bounds( u1, u2, v1, v2 );
3797       helper.SetSubShape( face );
3798     }
3799     // ---------------------------------------------------------
3800     // for elements on a face, find movable and fixed nodes and
3801     // compute UV for them
3802     // ---------------------------------------------------------
3803     bool checkBoundaryNodes = false;
3804     bool isQuadratic = false;
3805     set<const SMDS_MeshNode*> setMovableNodes;
3806     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3807     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3808     list< const SMDS_MeshElement* > elemsOnFace;
3809
3810     Extrema_GenExtPS projector;
3811     GeomAdaptor_Surface surfAdaptor;
3812     if ( !surface.IsNull() ) {
3813       surfAdaptor.Load( surface );
3814       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3815     }
3816     int nbElemOnFace = 0;
3817     itElem = theElems.begin();
3818     // loop on not yet smoothed elements: look for elems on a face
3819     while ( itElem != theElems.end() )
3820     {
3821       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3822         break; // all elements found
3823
3824       const SMDS_MeshElement* elem = *itElem;
3825       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3826            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3827         ++itElem;
3828         continue;
3829       }
3830       elemsOnFace.push_back( elem );
3831       theElems.erase( itElem++ );
3832       nbElemOnFace++;
3833
3834       if ( !isQuadratic )
3835         isQuadratic = elem->IsQuadratic();
3836
3837       // get movable nodes of elem
3838       const SMDS_MeshNode* node;
3839       SMDS_TypeOfPosition posType;
3840       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3841       int nn = 0, nbn =  elem->NbNodes();
3842       if(elem->IsQuadratic())
3843         nbn = nbn/2;
3844       while ( nn++ < nbn ) {
3845         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3846         const SMDS_PositionPtr& pos = node->GetPosition();
3847         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3848         if (posType != SMDS_TOP_EDGE &&
3849             posType != SMDS_TOP_VERTEX &&
3850             theFixedNodes.find( node ) == theFixedNodes.end())
3851         {
3852           // check if all faces around the node are on faceSubMesh
3853           // because a node on edge may be bound to face
3854           SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3855           bool all = true;
3856           if ( faceSubMesh ) {
3857             while ( eIt->more() && all ) {
3858               const SMDS_MeshElement* e = eIt->next();
3859               all = faceSubMesh->Contains( e );
3860             }
3861           }
3862           if ( all )
3863             setMovableNodes.insert( node );
3864           else
3865             checkBoundaryNodes = true;
3866         }
3867         if ( posType == SMDS_TOP_3DSPACE )
3868           checkBoundaryNodes = true;
3869       }
3870
3871       if ( surface.IsNull() )
3872         continue;
3873
3874       // get nodes to check UV
3875       list< const SMDS_MeshNode* > uvCheckNodes;
3876       const SMDS_MeshNode* nodeInFace = 0;
3877       itN = elem->nodesIterator();
3878       nn = 0; nbn =  elem->NbNodes();
3879       if(elem->IsQuadratic())
3880         nbn = nbn/2;
3881       while ( nn++ < nbn ) {
3882         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3883         if ( node->GetPosition()->GetDim() == 2 )
3884           nodeInFace = node;
3885         if ( uvMap.find( node ) == uvMap.end() )
3886           uvCheckNodes.push_back( node );
3887         // add nodes of elems sharing node
3888         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3889         //         while ( eIt->more() ) {
3890         //           const SMDS_MeshElement* e = eIt->next();
3891         //           if ( e != elem ) {
3892         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3893         //             while ( nIt->more() ) {
3894         //               const SMDS_MeshNode* n =
3895         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3896         //               if ( uvMap.find( n ) == uvMap.end() )
3897         //                 uvCheckNodes.push_back( n );
3898         //             }
3899         //           }
3900         //         }
3901       }
3902       // check UV on face
3903       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3904       for ( ; n != uvCheckNodes.end(); ++n ) {
3905         node = *n;
3906         gp_XY uv( 0, 0 );
3907         const SMDS_PositionPtr& pos = node->GetPosition();
3908         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3909         // get existing UV
3910         if ( pos )
3911         {
3912           bool toCheck = true;
3913           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3914         }
3915         // compute not existing UV
3916         bool project = ( posType == SMDS_TOP_3DSPACE );
3917         // double dist1 = DBL_MAX, dist2 = 0;
3918         // if ( posType != SMDS_TOP_3DSPACE ) {
3919         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3920         //   project = dist1 > fToler2;
3921         // }
3922         if ( project ) { // compute new UV
3923           gp_XY newUV;
3924           gp_Pnt pNode = SMESH_TNodeXYZ( node );
3925           if ( !getClosestUV( projector, pNode, newUV )) {
3926             MESSAGE("Node Projection Failed " << node);
3927           }
3928           else {
3929             if ( isUPeriodic )
3930               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3931             if ( isVPeriodic )
3932               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3933             // check new UV
3934             // if ( posType != SMDS_TOP_3DSPACE )
3935             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3936             // if ( dist2 < dist1 )
3937               uv = newUV;
3938           }
3939         }
3940         // store UV in the map
3941         listUV.push_back( uv );
3942         uvMap.insert( make_pair( node, &listUV.back() ));
3943       }
3944     } // loop on not yet smoothed elements
3945
3946     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3947       checkBoundaryNodes = true;
3948
3949     // fix nodes on mesh boundary
3950
3951     if ( checkBoundaryNodes ) {
3952       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3953       map< SMESH_TLink, int >::iterator link_nb;
3954       // put all elements links to linkNbMap
3955       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3956       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3957         const SMDS_MeshElement* elem = (*elemIt);
3958         int nbn =  elem->NbCornerNodes();
3959         // loop on elem links: insert them in linkNbMap
3960         for ( int iN = 0; iN < nbn; ++iN ) {
3961           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3962           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3963           SMESH_TLink link( n1, n2 );
3964           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3965           link_nb->second++;
3966         }
3967       }
3968       // remove nodes that are in links encountered only once from setMovableNodes
3969       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3970         if ( link_nb->second == 1 ) {
3971           setMovableNodes.erase( link_nb->first.node1() );
3972           setMovableNodes.erase( link_nb->first.node2() );
3973         }
3974       }
3975     }
3976
3977     // -----------------------------------------------------
3978     // for nodes on seam edge, compute one more UV ( uvMap2 );
3979     // find movable nodes linked to nodes on seam and which
3980     // are to be smoothed using the second UV ( uvMap2 )
3981     // -----------------------------------------------------
3982
3983     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3984     if ( !surface.IsNull() ) {
3985       TopExp_Explorer eExp( face, TopAbs_EDGE );
3986       for ( ; eExp.More(); eExp.Next() ) {
3987         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3988         if ( !BRep_Tool::IsClosed( edge, face ))
3989           continue;
3990         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3991         if ( !sm ) continue;
3992         // find out which parameter varies for a node on seam
3993         double f,l;
3994         gp_Pnt2d uv1, uv2;
3995         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3996         if ( pcurve.IsNull() ) continue;
3997         uv1 = pcurve->Value( f );
3998         edge.Reverse();
3999         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4000         if ( pcurve.IsNull() ) continue;
4001         uv2 = pcurve->Value( f );
4002         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4003         // assure uv1 < uv2
4004         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4005           std::swap( uv1, uv2 );
4006         // get nodes on seam and its vertices
4007         list< const SMDS_MeshNode* > seamNodes;
4008         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4009         while ( nSeamIt->more() ) {
4010           const SMDS_MeshNode* node = nSeamIt->next();
4011           if ( !isQuadratic || !IsMedium( node ))
4012             seamNodes.push_back( node );
4013         }
4014         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4015         for ( ; vExp.More(); vExp.Next() ) {
4016           sm = aMesh->MeshElements( vExp.Current() );
4017           if ( sm ) {
4018             nSeamIt = sm->GetNodes();
4019             while ( nSeamIt->more() )
4020               seamNodes.push_back( nSeamIt->next() );
4021           }
4022         }
4023         // loop on nodes on seam
4024         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4025         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4026           const SMDS_MeshNode* nSeam = *noSeIt;
4027           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4028           if ( n_uv == uvMap.end() )
4029             continue;
4030           // set the first UV
4031           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4032           // set the second UV
4033           listUV.push_back( *n_uv->second );
4034           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4035           if ( uvMap2.empty() )
4036             uvMap2 = uvMap; // copy the uvMap contents
4037           uvMap2[ nSeam ] = &listUV.back();
4038
4039           // collect movable nodes linked to ones on seam in nodesNearSeam
4040           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4041           while ( eIt->more() ) {
4042             const SMDS_MeshElement* e = eIt->next();
4043             int nbUseMap1 = 0, nbUseMap2 = 0;
4044             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4045             int nn = 0, nbn =  e->NbNodes();
4046             if(e->IsQuadratic()) nbn = nbn/2;
4047             while ( nn++ < nbn )
4048             {
4049               const SMDS_MeshNode* n =
4050                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4051               if (n == nSeam ||
4052                   setMovableNodes.find( n ) == setMovableNodes.end() )
4053                 continue;
4054               // add only nodes being closer to uv2 than to uv1
4055               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4056               //              0.5 * ( n->Y() + nSeam->Y() ),
4057               //              0.5 * ( n->Z() + nSeam->Z() ));
4058               // gp_XY uv;
4059               // getClosestUV( projector, pMid, uv );
4060               double x = uvMap[ n ]->Coord( iPar );
4061               if ( Abs( uv1.Coord( iPar ) - x ) >
4062                    Abs( uv2.Coord( iPar ) - x )) {
4063                 nodesNearSeam.insert( n );
4064                 nbUseMap2++;
4065               }
4066               else
4067                 nbUseMap1++;
4068             }
4069             // for centroidalSmooth all element nodes must
4070             // be on one side of a seam
4071             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4072               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4073               nn = 0;
4074               while ( nn++ < nbn ) {
4075                 const SMDS_MeshNode* n =
4076                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4077                 setMovableNodes.erase( n );
4078               }
4079             }
4080           }
4081         } // loop on nodes on seam
4082       } // loop on edge of a face
4083     } // if ( !face.IsNull() )
4084
4085     if ( setMovableNodes.empty() ) {
4086       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4087       continue; // goto next face
4088     }
4089
4090     // -------------
4091     // SMOOTHING //
4092     // -------------
4093
4094     int it = -1;
4095     double maxRatio = -1., maxDisplacement = -1.;
4096     set<const SMDS_MeshNode*>::iterator nodeToMove;
4097     for ( it = 0; it < theNbIterations; it++ ) {
4098       maxDisplacement = 0.;
4099       nodeToMove = setMovableNodes.begin();
4100       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4101         const SMDS_MeshNode* node = (*nodeToMove);
4102         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4103
4104         // smooth
4105         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4106         if ( theSmoothMethod == LAPLACIAN )
4107           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4108         else
4109           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4110
4111         // node displacement
4112         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4113         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4114         if ( aDispl > maxDisplacement )
4115           maxDisplacement = aDispl;
4116       }
4117       // no node movement => exit
4118       //if ( maxDisplacement < 1.e-16 ) {
4119       if ( maxDisplacement < disttol ) {
4120         MESSAGE("-- no node movement --");
4121         break;
4122       }
4123
4124       // check elements quality
4125       maxRatio  = 0;
4126       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4127       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4128         const SMDS_MeshElement* elem = (*elemIt);
4129         if ( !elem || elem->GetType() != SMDSAbs_Face )
4130           continue;
4131         SMESH::Controls::TSequenceOfXYZ aPoints;
4132         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4133           double aValue = aQualityFunc.GetValue( aPoints );
4134           if ( aValue > maxRatio )
4135             maxRatio = aValue;
4136         }
4137       }
4138       if ( maxRatio <= theTgtAspectRatio ) {
4139         MESSAGE("-- quality achived --");
4140         break;
4141       }
4142       if (it+1 == theNbIterations) {
4143         MESSAGE("-- Iteration limit exceeded --");
4144       }
4145     } // smoothing iterations
4146
4147     MESSAGE(" Face id: " << *fId <<
4148             " Nb iterstions: " << it <<
4149             " Displacement: " << maxDisplacement <<
4150             " Aspect Ratio " << maxRatio);
4151
4152     // ---------------------------------------
4153     // new nodes positions are computed,
4154     // record movement in DS and set new UV
4155     // ---------------------------------------
4156     nodeToMove = setMovableNodes.begin();
4157     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4158       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4159       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4160       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4161       if ( node_uv != uvMap.end() ) {
4162         gp_XY* uv = node_uv->second;
4163         node->SetPosition
4164           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4165       }
4166     }
4167
4168     // move medium nodes of quadratic elements
4169     if ( isQuadratic )
4170     {
4171       vector<const SMDS_MeshNode*> nodes;
4172       bool checkUV;
4173       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4174       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4175       {
4176         const SMDS_MeshElement* QF = *elemIt;
4177         if ( QF->IsQuadratic() )
4178         {
4179           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4180                         SMDS_MeshElement::iterator() );
4181           nodes.push_back( nodes[0] );
4182           gp_Pnt xyz;
4183           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4184           {
4185             if ( !surface.IsNull() )
4186             {
4187               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4188               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4189               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4190               xyz = surface->Value( uv.X(), uv.Y() );
4191             }
4192             else {
4193               xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4194             }
4195             if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4196               // we have to move a medium node
4197               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4198           }
4199         }
4200       }
4201     }
4202
4203   } // loop on face ids
4204
4205 }
4206
4207 namespace
4208 {
4209   //=======================================================================
4210   //function : isReverse
4211   //purpose  : Return true if normal of prevNodes is not co-directied with
4212   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4213   //           iNotSame is where prevNodes and nextNodes are different.
4214   //           If result is true then future volume orientation is OK
4215   //=======================================================================
4216
4217   bool isReverse(const SMDS_MeshElement*             face,
4218                  const vector<const SMDS_MeshNode*>& prevNodes,
4219                  const vector<const SMDS_MeshNode*>& nextNodes,
4220                  const int                           iNotSame)
4221   {
4222
4223     SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4224     SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4225     gp_XYZ extrDir( pN - pP ), faceNorm;
4226     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4227
4228     return faceNorm * extrDir < 0.0;
4229   }
4230
4231   //================================================================================
4232   /*!
4233    * \brief Assure that theElemSets[0] holds elements, not nodes
4234    */
4235   //================================================================================
4236
4237   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4238   {
4239     if ( !theElemSets[0].empty() &&
4240          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4241     {
4242       std::swap( theElemSets[0], theElemSets[1] );
4243     }
4244     else if ( !theElemSets[1].empty() &&
4245               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4246     {
4247       std::swap( theElemSets[0], theElemSets[1] );
4248     }
4249   }
4250 }
4251
4252 //=======================================================================
4253 /*!
4254  * \brief Create elements by sweeping an element
4255  * \param elem - element to sweep
4256  * \param newNodesItVec - nodes generated from each node of the element
4257  * \param newElems - generated elements
4258  * \param nbSteps - number of sweeping steps
4259  * \param srcElements - to append elem for each generated element
4260  */
4261 //=======================================================================
4262
4263 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4264                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4265                                     list<const SMDS_MeshElement*>&        newElems,
4266                                     const int                             nbSteps,
4267                                     SMESH_SequenceOfElemPtr&              srcElements)
4268 {
4269   //MESSAGE("sweepElement " << nbSteps);
4270   SMESHDS_Mesh* aMesh = GetMeshDS();
4271
4272   const int           nbNodes = elem->NbNodes();
4273   const int         nbCorners = elem->NbCornerNodes();
4274   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4275                                                           polyhedron creation !!! */
4276   // Loop on elem nodes:
4277   // find new nodes and detect same nodes indices
4278   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4279   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4280   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4281   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4282
4283   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4284   vector<int> sames(nbNodes);
4285   vector<bool> isSingleNode(nbNodes);
4286
4287   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4288     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4289     const SMDS_MeshNode*                         node = nnIt->first;
4290     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4291     if ( listNewNodes.empty() )
4292       return;
4293
4294     itNN   [ iNode ] = listNewNodes.begin();
4295     prevNod[ iNode ] = node;
4296     nextNod[ iNode ] = listNewNodes.front();
4297
4298     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4299                                                              corner node of linear */
4300     if ( prevNod[ iNode ] != nextNod [ iNode ])
4301       nbDouble += !isSingleNode[iNode];
4302
4303     if( iNode < nbCorners ) { // check corners only
4304       if ( prevNod[ iNode ] == nextNod [ iNode ])
4305         sames[nbSame++] = iNode;
4306       else
4307         iNotSameNode = iNode;
4308     }
4309   }
4310
4311   if ( nbSame == nbNodes || nbSame > 2) {
4312     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4313     return;
4314   }
4315
4316   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4317   {
4318     // fix nodes order to have bottom normal external
4319     if ( baseType == SMDSEntity_Polygon )
4320     {
4321       std::reverse( itNN.begin(), itNN.end() );
4322       std::reverse( prevNod.begin(), prevNod.end() );
4323       std::reverse( midlNod.begin(), midlNod.end() );
4324       std::reverse( nextNod.begin(), nextNod.end() );
4325       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4326     }
4327     else
4328     {
4329       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
4330       SMDS_MeshCell::applyInterlace( ind, itNN );
4331       SMDS_MeshCell::applyInterlace( ind, prevNod );
4332       SMDS_MeshCell::applyInterlace( ind, nextNod );
4333       SMDS_MeshCell::applyInterlace( ind, midlNod );
4334       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4335       if ( nbSame > 0 )
4336       {
4337         sames[nbSame] = iNotSameNode;
4338         for ( int j = 0; j <= nbSame; ++j )
4339           for ( size_t i = 0; i < ind.size(); ++i )
4340             if ( ind[i] == sames[j] )
4341             {
4342               sames[j] = i;
4343               break;
4344             }
4345         iNotSameNode = sames[nbSame];
4346       }
4347     }
4348   }
4349   else if ( elem->GetType() == SMDSAbs_Edge )
4350   {
4351     // orient a new face same as adjacent one
4352     int i1, i2;
4353     const SMDS_MeshElement* e;
4354     TIDSortedElemSet dummy;
4355     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4356         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4357         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4358     {
4359       // there is an adjacent face, check order of nodes in it
4360       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4361       if ( sameOrder )
4362       {
4363         std::swap( itNN[0],    itNN[1] );
4364         std::swap( prevNod[0], prevNod[1] );
4365         std::swap( nextNod[0], nextNod[1] );
4366         isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4367         if ( nbSame > 0 )
4368           sames[0] = 1 - sames[0];
4369         iNotSameNode = 1 - iNotSameNode;
4370       }
4371     }
4372   }
4373
4374   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4375   if ( nbSame > 0 ) {
4376     iSameNode    = sames[ nbSame-1 ];
4377     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4378     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4379     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4380   }
4381
4382   // make new elements
4383   for (int iStep = 0; iStep < nbSteps; iStep++ )
4384   {
4385     // get next nodes
4386     for ( iNode = 0; iNode < nbNodes; iNode++ )
4387     {
4388       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4389       nextNod[ iNode ] = *itNN[ iNode ]++;
4390     }
4391
4392     SMDS_MeshElement* aNewElem = 0;
4393     /*if(!elem->IsPoly())*/ {
4394       switch ( baseType ) {
4395       case SMDSEntity_0D:
4396       case SMDSEntity_Node: { // sweep NODE
4397         if ( nbSame == 0 ) {
4398           if ( isSingleNode[0] )
4399             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4400           else
4401             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4402         }
4403         else
4404           return;
4405         break;
4406       }
4407       case SMDSEntity_Edge: { // sweep EDGE
4408         if ( nbDouble == 0 )
4409         {
4410           if ( nbSame == 0 ) // ---> quadrangle
4411             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4412                                       nextNod[ 1 ], nextNod[ 0 ] );
4413           else               // ---> triangle
4414             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4415                                       nextNod[ iNotSameNode ] );
4416         }
4417         else                 // ---> polygon
4418         {
4419           vector<const SMDS_MeshNode*> poly_nodes;
4420           poly_nodes.push_back( prevNod[0] );
4421           poly_nodes.push_back( prevNod[1] );
4422           if ( prevNod[1] != nextNod[1] )
4423           {
4424             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4425             poly_nodes.push_back( nextNod[1] );
4426           }
4427           if ( prevNod[0] != nextNod[0] )
4428           {
4429             poly_nodes.push_back( nextNod[0] );
4430             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4431           }
4432           switch ( poly_nodes.size() ) {
4433           case 3:
4434             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4435             break;
4436           case 4:
4437             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4438                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4439             break;
4440           default:
4441             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4442           }
4443         }
4444         break;
4445       }
4446       case SMDSEntity_Triangle: // TRIANGLE --->
4447         {
4448           if ( nbDouble > 0 ) break;
4449           if ( nbSame == 0 )       // ---> pentahedron
4450             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4451                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4452
4453           else if ( nbSame == 1 )  // ---> pyramid
4454             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4455                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4456                                          nextNod[ iSameNode ]);
4457
4458           else // 2 same nodes:       ---> tetrahedron
4459             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4460                                          nextNod[ iNotSameNode ]);
4461           break;
4462         }
4463       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4464         {
4465           if ( nbSame == 2 )
4466             return;
4467           if ( nbDouble+nbSame == 2 )
4468           {
4469             if(nbSame==0) {      // ---> quadratic quadrangle
4470               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4471                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4472             }
4473             else { //(nbSame==1) // ---> quadratic triangle
4474               if(sames[0]==2) {
4475                 return; // medium node on axis
4476               }
4477               else if(sames[0]==0)
4478                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4479                                           prevNod[2], midlNod[1], nextNod[2] );
4480               else // sames[0]==1
4481                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4482                                           prevNod[2], nextNod[2], midlNod[0]);
4483             }
4484           }
4485           else if ( nbDouble == 3 )
4486           {
4487             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4488               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4489                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4490             }
4491           }
4492           else
4493             return;
4494           break;
4495         }
4496       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4497         if ( nbDouble > 0 ) break;
4498
4499         if ( nbSame == 0 )       // ---> hexahedron
4500           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4501                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4502
4503         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4504           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4505                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4506                                        nextNod[ iSameNode ]);
4507           newElems.push_back( aNewElem );
4508           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4509                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4510                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4511         }
4512         else if ( nbSame == 2 ) { // ---> pentahedron
4513           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4514             // iBeforeSame is same too
4515             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4516                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4517                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4518           else
4519             // iAfterSame is same too
4520             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4521                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4522                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4523         }
4524         break;
4525       }
4526       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4527       case SMDSEntity_BiQuad_Triangle: /* ??? */ { 
4528         if ( nbDouble+nbSame != 3 ) break;
4529         if(nbSame==0) {
4530           // --->  pentahedron with 15 nodes
4531           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4532                                        nextNod[0], nextNod[1], nextNod[2],
4533                                        prevNod[3], prevNod[4], prevNod[5],
4534                                        nextNod[3], nextNod[4], nextNod[5],
4535                                        midlNod[0], midlNod[1], midlNod[2]);
4536         }
4537         else if(nbSame==1) {
4538           // --->  2d order pyramid of 13 nodes
4539           int apex = iSameNode;
4540           int i0 = ( apex + 1 ) % nbCorners;
4541           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4542           int i0a = apex + 3;
4543           int i1a = i1 + 3;
4544           int i01 = i0 + 3;
4545           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4546                                       nextNod[i0], nextNod[i1], prevNod[apex],
4547                                       prevNod[i01], midlNod[i0],
4548                                       nextNod[i01], midlNod[i1],
4549                                       prevNod[i1a], prevNod[i0a],
4550                                       nextNod[i0a], nextNod[i1a]);
4551         }
4552         else if(nbSame==2) {
4553           // --->  2d order tetrahedron of 10 nodes
4554           int n1 = iNotSameNode;
4555           int n2 = ( n1 + 1             ) % nbCorners;
4556           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4557           int n12 = n1 + 3;
4558           int n23 = n2 + 3;
4559           int n31 = n3 + 3;
4560           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4561                                        prevNod[n12], prevNod[n23], prevNod[n31],
4562                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4563         }
4564         break;
4565       }
4566       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4567         if( nbSame == 0 ) {
4568           if ( nbDouble != 4 ) break;
4569           // --->  hexahedron with 20 nodes
4570           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4571                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4572                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4573                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4574                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4575         }
4576         else if(nbSame==1) {
4577           // ---> pyramid + pentahedron - can not be created since it is needed
4578           // additional middle node at the center of face
4579           INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4580           return;
4581         }
4582         else if( nbSame == 2 ) {
4583           if ( nbDouble != 2 ) break;
4584           // --->  2d order Pentahedron with 15 nodes
4585           int n1,n2,n4,n5;
4586           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4587             // iBeforeSame is same too
4588             n1 = iBeforeSame;
4589             n2 = iOpposSame;
4590             n4 = iSameNode;
4591             n5 = iAfterSame;
4592           }
4593           else {
4594             // iAfterSame is same too
4595             n1 = iSameNode;
4596             n2 = iBeforeSame;
4597             n4 = iAfterSame;
4598             n5 = iOpposSame;
4599           }
4600           int n12 = n2 + 4;
4601           int n45 = n4 + 4;
4602           int n14 = n1 + 4;
4603           int n25 = n5 + 4;
4604           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4605                                        prevNod[n4], prevNod[n5], nextNod[n5],
4606                                        prevNod[n12], midlNod[n2], nextNod[n12],
4607                                        prevNod[n45], midlNod[n5], nextNod[n45],
4608                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4609         }
4610         break;
4611       }
4612       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4613
4614         if( nbSame == 0 && nbDouble == 9 ) {
4615           // --->  tri-quadratic hexahedron with 27 nodes
4616           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4617                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4618                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4619                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4620                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4621                                        prevNod[8], // bottom center
4622                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4623                                        nextNod[8], // top center
4624                                        midlNod[8]);// elem center
4625         }
4626         else
4627         {
4628           return;
4629         }
4630         break;
4631       }
4632       case SMDSEntity_Polygon: { // sweep POLYGON
4633
4634         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4635           // --->  hexagonal prism
4636           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4637                                        prevNod[3], prevNod[4], prevNod[5],
4638                                        nextNod[0], nextNod[1], nextNod[2],
4639                                        nextNod[3], nextNod[4], nextNod[5]);
4640         }
4641         break;
4642       }
4643       case SMDSEntity_Ball:
4644         return;
4645
4646       default:
4647         break;
4648       } // switch ( baseType )
4649     } // scope
4650
4651     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4652     {
4653       if ( baseType != SMDSEntity_Polygon )
4654       {
4655         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4656         SMDS_MeshCell::applyInterlace( ind, prevNod );
4657         SMDS_MeshCell::applyInterlace( ind, nextNod );
4658         SMDS_MeshCell::applyInterlace( ind, midlNod );
4659         SMDS_MeshCell::applyInterlace( ind, itNN );
4660         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4661         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4662       }
4663       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4664       vector<int> quantities (nbNodes + 2);
4665       polyedre_nodes.clear();
4666       quantities.clear();
4667
4668       // bottom of prism
4669       for (int inode = 0; inode < nbNodes; inode++)
4670         polyedre_nodes.push_back( prevNod[inode] );
4671       quantities.push_back( nbNodes );
4672
4673       // top of prism
4674       polyedre_nodes.push_back( nextNod[0] );
4675       for (int inode = nbNodes; inode-1; --inode )
4676         polyedre_nodes.push_back( nextNod[inode-1] );
4677       quantities.push_back( nbNodes );
4678
4679       // side faces
4680       for (int iface = 0; iface < nbNodes; iface++)
4681       {
4682         const int prevNbNodes = polyedre_nodes.size();
4683         int inextface = (iface+1) % nbNodes;
4684         polyedre_nodes.push_back( prevNod[inextface] );
4685         polyedre_nodes.push_back( prevNod[iface] );
4686         if ( prevNod[iface] != nextNod[iface] )
4687         {
4688           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4689           polyedre_nodes.push_back( nextNod[iface] );
4690         }
4691         if ( prevNod[inextface] != nextNod[inextface] )
4692         {
4693           polyedre_nodes.push_back( nextNod[inextface] );
4694           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4695         }
4696         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4697         if ( nbFaceNodes > 2 )
4698           quantities.push_back( nbFaceNodes );
4699         else // degenerated face
4700           polyedre_nodes.resize( prevNbNodes );
4701       }
4702       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4703
4704     } //  // try to create a polyherdal prism
4705
4706     if ( aNewElem ) {
4707       newElems.push_back( aNewElem );
4708       myLastCreatedElems.Append(aNewElem);
4709       srcElements.Append( elem );
4710     }
4711
4712     // set new prev nodes
4713     for ( iNode = 0; iNode < nbNodes; iNode++ )
4714       prevNod[ iNode ] = nextNod[ iNode ];
4715
4716   } // loop on steps
4717 }
4718
4719 //=======================================================================
4720 /*!
4721  * \brief Create 1D and 2D elements around swept elements
4722  * \param mapNewNodes - source nodes and ones generated from them
4723  * \param newElemsMap - source elements and ones generated from them
4724  * \param elemNewNodesMap - nodes generated from each node of each element
4725  * \param elemSet - all swept elements
4726  * \param nbSteps - number of sweeping steps
4727  * \param srcElements - to append elem for each generated element
4728  */
4729 //=======================================================================
4730
4731 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4732                                   TTElemOfElemListMap &    newElemsMap,
4733                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4734                                   TIDSortedElemSet&        elemSet,
4735                                   const int                nbSteps,
4736                                   SMESH_SequenceOfElemPtr& srcElements)
4737 {
4738   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4739   SMESHDS_Mesh* aMesh = GetMeshDS();
4740
4741   // Find nodes belonging to only one initial element - sweep them into edges.
4742
4743   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4744   for ( ; nList != mapNewNodes.end(); nList++ )
4745   {
4746     const SMDS_MeshNode* node =
4747       static_cast<const SMDS_MeshNode*>( nList->first );
4748     if ( newElemsMap.count( node ))
4749       continue; // node was extruded into edge
4750     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4751     int nbInitElems = 0;
4752     const SMDS_MeshElement* el = 0;
4753     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4754     while ( eIt->more() && nbInitElems < 2 ) {
4755       el = eIt->next();
4756       SMDSAbs_ElementType type = el->GetType();
4757       if ( type == SMDSAbs_Volume || type < highType ) continue;
4758       if ( type > highType ) {
4759         nbInitElems = 0;
4760         highType = type;
4761       }
4762       nbInitElems += elemSet.count(el);
4763     }
4764     if ( nbInitElems < 2 ) {
4765       bool NotCreateEdge = el && el->IsMediumNode(node);
4766       if(!NotCreateEdge) {
4767         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4768         list<const SMDS_MeshElement*> newEdges;
4769         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4770       }
4771     }
4772   }
4773
4774   // Make a ceiling for each element ie an equal element of last new nodes.
4775   // Find free links of faces - make edges and sweep them into faces.
4776
4777   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
4778   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4779   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4780   {
4781     const SMDS_MeshElement* elem = itElem->first;
4782     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4783
4784     if(itElem->second.size()==0) continue;
4785
4786     const bool isQuadratic = elem->IsQuadratic();
4787
4788     if ( elem->GetType() == SMDSAbs_Edge ) {
4789       // create a ceiling edge
4790       if ( !isQuadratic ) {
4791         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4792                                vecNewNodes[ 1 ]->second.back())) {
4793           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4794                                                    vecNewNodes[ 1 ]->second.back()));
4795           srcElements.Append( elem );
4796         }
4797       }
4798       else {
4799         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4800                                vecNewNodes[ 1 ]->second.back(),
4801                                vecNewNodes[ 2 ]->second.back())) {
4802           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4803                                                    vecNewNodes[ 1 ]->second.back(),
4804                                                    vecNewNodes[ 2 ]->second.back()));
4805           srcElements.Append( elem );
4806         }
4807       }
4808     }
4809     if ( elem->GetType() != SMDSAbs_Face )
4810       continue;
4811
4812     bool hasFreeLinks = false;
4813
4814     TIDSortedElemSet avoidSet;
4815     avoidSet.insert( elem );
4816
4817     set<const SMDS_MeshNode*> aFaceLastNodes;
4818     int iNode, nbNodes = vecNewNodes.size();
4819     if ( !isQuadratic ) {
4820       // loop on the face nodes
4821       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4822         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4823         // look for free links of the face
4824         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4825         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4826         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4827         // check if a link n1-n2 is free
4828         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4829           hasFreeLinks = true;
4830           // make a new edge and a ceiling for a new edge
4831           const SMDS_MeshElement* edge;
4832           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4833             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4834             srcElements.Append( myLastCreatedElems.Last() );
4835           }
4836           n1 = vecNewNodes[ iNode ]->second.back();
4837           n2 = vecNewNodes[ iNext ]->second.back();
4838           if ( !aMesh->FindEdge( n1, n2 )) {
4839             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4840             srcElements.Append( edge );
4841           }
4842         }
4843       }
4844     }
4845     else { // elem is quadratic face
4846       int nbn = nbNodes/2;
4847       for ( iNode = 0; iNode < nbn; iNode++ ) {
4848         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4849         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4850         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4851         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4852         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4853         // check if a link is free
4854         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4855              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4856              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4857           hasFreeLinks = true;
4858           // make an edge and a ceiling for a new edge
4859           // find medium node
4860           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4861             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4862             srcElements.Append( elem );
4863           }
4864           n1 = vecNewNodes[ iNode ]->second.back();
4865           n2 = vecNewNodes[ iNext ]->second.back();
4866           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4867           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4868             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4869             srcElements.Append( elem );
4870           }
4871         }
4872       }
4873       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4874         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4875       }
4876     }
4877
4878     // sweep free links into faces
4879
4880     if ( hasFreeLinks )  {
4881       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4882       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4883
4884       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4885       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4886       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4887         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4888         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4889       }
4890       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
4891         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4892         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4893       }
4894       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4895         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4896         std::advance( v, volNb );
4897         // find indices of free faces of a volume and their source edges
4898         list< int > freeInd;
4899         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4900         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4901         int iF, nbF = vTool.NbFaces();
4902         for ( iF = 0; iF < nbF; iF ++ ) {
4903           if (vTool.IsFreeFace( iF ) &&
4904               vTool.GetFaceNodes( iF, faceNodeSet ) &&
4905               initNodeSet != faceNodeSet) // except an initial face
4906           {
4907             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4908               continue;
4909             if ( faceNodeSet == initNodeSetNoCenter )
4910               continue;
4911             freeInd.push_back( iF );
4912             // find source edge of a free face iF
4913             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4914             commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4915             std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4916                                    initNodeSet.begin(), initNodeSet.end(),
4917                                    commonNodes.begin());
4918             if ( (*v)->IsQuadratic() )
4919               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4920             else
4921               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4922 #ifdef _DEBUG_
4923             if ( !srcEdges.back() )
4924             {
4925               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4926                    << iF << " of volume #" << vTool.ID() << endl;
4927             }
4928 #endif
4929           }
4930         }
4931         if ( freeInd.empty() )
4932           continue;
4933
4934         // create faces for all steps;
4935         // if such a face has been already created by sweep of edge,
4936         // assure that its orientation is OK
4937         for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4938           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4939           vTool.SetExternalNormal();
4940           const int nextShift = vTool.IsForward() ? +1 : -1;
4941           list< int >::iterator ind = freeInd.begin();
4942           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4943           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4944           {
4945             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4946             int nbn = vTool.NbFaceNodes( *ind );
4947             const SMDS_MeshElement * f = 0;
4948             if ( nbn == 3 )              ///// triangle
4949             {
4950               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4951               if ( !f ||
4952                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4953               {
4954                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4955                                                      nodes[ 1 ],
4956                                                      nodes[ 1 + nextShift ] };
4957                 if ( f )
4958                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4959                 else
4960                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4961                                                             newOrder[ 2 ] ));
4962               }
4963             }
4964             else if ( nbn == 4 )       ///// quadrangle
4965             {
4966               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4967               if ( !f ||
4968                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4969               {
4970                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4971                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4972                 if ( f )
4973                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4974                 else
4975                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4976                                                             newOrder[ 2 ], newOrder[ 3 ]));
4977               }
4978             }
4979             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4980             {
4981               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4982               if ( !f ||
4983                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4984               {
4985                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4986                                                      nodes[2],
4987                                                      nodes[2 + 2*nextShift],
4988                                                      nodes[3 - 2*nextShift],
4989                                                      nodes[3],
4990                                                      nodes[3 + 2*nextShift]};
4991                 if ( f )
4992                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4993                 else
4994                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4995                                                             newOrder[ 1 ],
4996                                                             newOrder[ 2 ],
4997                                                             newOrder[ 3 ],
4998                                                             newOrder[ 4 ],
4999                                                             newOrder[ 5 ] ));
5000               }
5001             }
5002             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5003             {
5004               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5005                                    nodes[1], nodes[3], nodes[5], nodes[7] );
5006               if ( !f ||
5007                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5008               {
5009                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5010                                                      nodes[4 - 2*nextShift],
5011                                                      nodes[4],
5012                                                      nodes[4 + 2*nextShift],
5013                                                      nodes[1],
5014                                                      nodes[5 - 2*nextShift],
5015                                                      nodes[5],
5016                                                      nodes[5 + 2*nextShift] };
5017                 if ( f )
5018                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5019                 else
5020                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5021                                                            newOrder[ 2 ], newOrder[ 3 ],
5022                                                            newOrder[ 4 ], newOrder[ 5 ],
5023                                                            newOrder[ 6 ], newOrder[ 7 ]));
5024               }
5025             }
5026             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5027             {
5028               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5029                                       SMDSAbs_Face, /*noMedium=*/false);
5030               if ( !f ||
5031                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5032               {
5033                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5034                                                      nodes[4 - 2*nextShift],
5035                                                      nodes[4],
5036                                                      nodes[4 + 2*nextShift],
5037                                                      nodes[1],
5038                                                      nodes[5 - 2*nextShift],
5039                                                      nodes[5],
5040                                                      nodes[5 + 2*nextShift],
5041                                                      nodes[8] };
5042                 if ( f )
5043                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5044                 else
5045                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5046                                                            newOrder[ 2 ], newOrder[ 3 ],
5047                                                            newOrder[ 4 ], newOrder[ 5 ],
5048                                                            newOrder[ 6 ], newOrder[ 7 ],
5049                                                            newOrder[ 8 ]));
5050               }
5051             }
5052             else  //////// polygon
5053             {
5054               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5055               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5056               if ( !f ||
5057                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5058               {
5059                 if ( !vTool.IsForward() )
5060                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5061                 if ( f )
5062                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5063                 else
5064                   AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
5065               }
5066             }
5067
5068             while ( srcElements.Length() < myLastCreatedElems.Length() )
5069               srcElements.Append( *srcEdge );
5070
5071           }  // loop on free faces
5072
5073           // go to the next volume
5074           iVol = 0;
5075           while ( iVol++ < nbVolumesByStep ) v++;
5076
5077         } // loop on steps
5078       } // loop on volumes of one step
5079     } // sweep free links into faces
5080
5081     // Make a ceiling face with a normal external to a volume
5082
5083     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5084     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5085     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5086
5087     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5088       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5089       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5090     }
5091     if ( iF >= 0 ) {
5092       lastVol.SetExternalNormal();
5093       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5094       int nbn = lastVol.NbFaceNodes( iF );
5095       // we do not use this->AddElement() because nodes are interlaced
5096       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5097       if ( !hasFreeLinks ||
5098            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5099       {
5100         if ( nbn == 3 )
5101           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2] ));
5102
5103         else if ( nbn == 4 )
5104           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2], nodes[3]));
5105
5106         else if ( nbn == 6 && isQuadratic )
5107           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
5108                                                     nodes[1], nodes[3], nodes[5]));
5109         else if ( nbn == 7 && isQuadratic )
5110           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
5111                                                     nodes[1], nodes[3], nodes[5], nodes[6]));
5112         else if ( nbn == 8 && isQuadratic )
5113           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5114                                                     nodes[1], nodes[3], nodes[5], nodes[7]));
5115         else if ( nbn == 9 && isQuadratic )
5116           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5117                                                     nodes[1], nodes[3], nodes[5], nodes[7],
5118                                                     nodes[8]));
5119         else
5120           myLastCreatedElems.Append(aMesh->AddPolygonalFace( nodeVec ));
5121
5122         while ( srcElements.Length() < myLastCreatedElems.Length() )
5123           srcElements.Append( elem );
5124       }
5125     }
5126   } // loop on swept elements
5127 }
5128
5129 //=======================================================================
5130 //function : RotationSweep
5131 //purpose  :
5132 //=======================================================================
5133
5134 SMESH_MeshEditor::PGroupIDs
5135 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5136                                 const gp_Ax1&      theAxis,
5137                                 const double       theAngle,
5138                                 const int          theNbSteps,
5139                                 const double       theTol,
5140                                 const bool         theMakeGroups,
5141                                 const bool         theMakeWalls)
5142 {
5143   myLastCreatedElems.Clear();
5144   myLastCreatedNodes.Clear();
5145
5146   // source elements for each generated one
5147   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5148
5149   MESSAGE( "RotationSweep()");
5150   gp_Trsf aTrsf;
5151   aTrsf.SetRotation( theAxis, theAngle );
5152   gp_Trsf aTrsf2;
5153   aTrsf2.SetRotation( theAxis, theAngle/2. );
5154
5155   gp_Lin aLine( theAxis );
5156   double aSqTol = theTol * theTol;
5157
5158   SMESHDS_Mesh* aMesh = GetMeshDS();
5159
5160   TNodeOfNodeListMap mapNewNodes;
5161   TElemOfVecOfNnlmiMap mapElemNewNodes;
5162   TTElemOfElemListMap newElemsMap;
5163
5164   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5165                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5166                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5167   // loop on theElemSets
5168   setElemsFirst( theElemSets );
5169   TIDSortedElemSet::iterator itElem;
5170   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5171   {
5172     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5173     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5174       const SMDS_MeshElement* elem = *itElem;
5175       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5176         continue;
5177       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5178       newNodesItVec.reserve( elem->NbNodes() );
5179
5180       // loop on elem nodes
5181       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5182       while ( itN->more() )
5183       {
5184         // check if a node has been already sweeped
5185         const SMDS_MeshNode* node = cast2Node( itN->next() );
5186
5187         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5188         double coord[3];
5189         aXYZ.Coord( coord[0], coord[1], coord[2] );
5190         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5191
5192         TNodeOfNodeListMapItr nIt =
5193           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5194         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5195         if ( listNewNodes.empty() )
5196         {
5197           // check if we are to create medium nodes between corner ones
5198           bool needMediumNodes = false;
5199           if ( isQuadraticMesh )
5200           {
5201             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5202             while (it->more() && !needMediumNodes )
5203             {
5204               const SMDS_MeshElement* invElem = it->next();
5205               if ( invElem != elem && !theElems.count( invElem )) continue;
5206               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5207               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5208                 needMediumNodes = true;
5209             }
5210           }
5211
5212           // make new nodes
5213           const SMDS_MeshNode * newNode = node;
5214           for ( int i = 0; i < theNbSteps; i++ ) {
5215             if ( !isOnAxis ) {
5216               if ( needMediumNodes )  // create a medium node
5217               {
5218                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5219                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5220                 myLastCreatedNodes.Append(newNode);
5221                 srcNodes.Append( node );
5222                 listNewNodes.push_back( newNode );
5223                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5224               }
5225               else {
5226                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5227               }
5228               // create a corner node
5229               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5230               myLastCreatedNodes.Append(newNode);
5231               srcNodes.Append( node );
5232               listNewNodes.push_back( newNode );
5233             }
5234             else {
5235               listNewNodes.push_back( newNode );
5236               // if ( needMediumNodes )
5237               //   listNewNodes.push_back( newNode );
5238             }
5239           }
5240         }
5241         newNodesItVec.push_back( nIt );
5242       }
5243       // make new elements
5244       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5245     }
5246   }
5247
5248   if ( theMakeWalls )
5249     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5250
5251   PGroupIDs newGroupIDs;
5252   if ( theMakeGroups )
5253     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5254
5255   return newGroupIDs;
5256 }
5257
5258 //=======================================================================
5259 //function : ExtrusParam
5260 //purpose  : standard construction
5261 //=======================================================================
5262
5263 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&  theStep,
5264                                             const int      theNbSteps,
5265                                             const int      theFlags,
5266                                             const double   theTolerance):
5267   myDir( theStep ),
5268   myFlags( theFlags ),
5269   myTolerance( theTolerance ),
5270   myElemsToUse( NULL )
5271 {
5272   mySteps = new TColStd_HSequenceOfReal;
5273   const double stepSize = theStep.Magnitude();
5274   for (int i=1; i<=theNbSteps; i++ )
5275     mySteps->Append( stepSize );
5276
5277   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5278       ( theTolerance > 0 ))
5279   {
5280     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5281   }
5282   else
5283   {
5284     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5285   }
5286 }
5287
5288 //=======================================================================
5289 //function : ExtrusParam
5290 //purpose  : steps are given explicitly
5291 //=======================================================================
5292
5293 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5294                                             Handle(TColStd_HSequenceOfReal) theSteps,
5295                                             const int                       theFlags,
5296                                             const double                    theTolerance):
5297   myDir( theDir ),
5298   mySteps( theSteps ),
5299   myFlags( theFlags ),
5300   myTolerance( theTolerance ),
5301   myElemsToUse( NULL )
5302 {
5303   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5304       ( theTolerance > 0 ))
5305   {
5306     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5307   }
5308   else
5309   {
5310     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5311   }
5312 }
5313
5314 //=======================================================================
5315 //function : ExtrusParam
5316 //purpose  : for extrusion by normal
5317 //=======================================================================
5318
5319 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5320                                             const int    theNbSteps,
5321                                             const int    theFlags,
5322                                             const int    theDim ):
5323   myDir( 1,0,0 ),
5324   mySteps( new TColStd_HSequenceOfReal ),
5325   myFlags( theFlags ),
5326   myTolerance( 0 ),
5327   myElemsToUse( NULL )
5328 {
5329   for (int i = 0; i < theNbSteps; i++ )
5330     mySteps->Append( theStepSize );
5331
5332   if ( theDim == 1 )
5333   {
5334     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5335   }
5336   else
5337   {
5338     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5339   }
5340 }
5341
5342 //=======================================================================
5343 //function : ExtrusParam::SetElementsToUse
5344 //purpose  : stores elements to use for extrusion by normal, depending on
5345 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag
5346 //=======================================================================
5347
5348 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems )
5349 {
5350   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5351 }
5352
5353 //=======================================================================
5354 //function : ExtrusParam::beginStepIter
5355 //purpose  : prepare iteration on steps
5356 //=======================================================================
5357
5358 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5359 {
5360   myWithMediumNodes = withMediumNodes;
5361   myNextStep = 1;
5362   myCurSteps.clear();
5363 }
5364 //=======================================================================
5365 //function : ExtrusParam::moreSteps
5366 //purpose  : are there more steps?
5367 //=======================================================================
5368
5369 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5370 {
5371   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5372 }
5373 //=======================================================================
5374 //function : ExtrusParam::nextStep
5375 //purpose  : returns the next step
5376 //=======================================================================
5377
5378 double SMESH_MeshEditor::ExtrusParam::nextStep()
5379 {
5380   double res = 0;
5381   if ( !myCurSteps.empty() )
5382   {
5383     res = myCurSteps.back();
5384     myCurSteps.pop_back();
5385   }
5386   else if ( myNextStep <= mySteps->Length() )
5387   {
5388     myCurSteps.push_back( mySteps->Value( myNextStep ));
5389     ++myNextStep;
5390     if ( myWithMediumNodes )
5391     {
5392       myCurSteps.back() /= 2.;
5393       myCurSteps.push_back( myCurSteps.back() );
5394     }
5395     res = nextStep();
5396   }
5397   return res;
5398 }
5399
5400 //=======================================================================
5401 //function : ExtrusParam::makeNodesByDir
5402 //purpose  : create nodes for standard extrusion
5403 //=======================================================================
5404
5405 int SMESH_MeshEditor::ExtrusParam::
5406 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5407                 const SMDS_MeshNode*              srcNode,
5408                 std::list<const SMDS_MeshNode*> & newNodes,
5409                 const bool                        makeMediumNodes)
5410 {
5411   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5412
5413   int nbNodes = 0;
5414   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5415   {
5416     p += myDir.XYZ() * nextStep();
5417     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5418     newNodes.push_back( newNode );
5419   }
5420   return nbNodes;
5421 }
5422
5423 //=======================================================================
5424 //function : ExtrusParam::makeNodesByDirAndSew
5425 //purpose  : create nodes for standard extrusion with sewing
5426 //=======================================================================
5427
5428 int SMESH_MeshEditor::ExtrusParam::
5429 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5430                       const SMDS_MeshNode*              srcNode,
5431                       std::list<const SMDS_MeshNode*> & newNodes,
5432                       const bool                        makeMediumNodes)
5433 {
5434   gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5435
5436   int nbNodes = 0;
5437   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5438   {
5439     P1 += myDir.XYZ() * nextStep();
5440
5441     // try to search in sequence of existing nodes
5442     // if myNodes.Length()>0 we 'nave to use given sequence
5443     // else - use all nodes of mesh
5444     const SMDS_MeshNode * node = 0;
5445     if ( myNodes.Length() > 0 ) {
5446       int i;
5447       for(i=1; i<=myNodes.Length(); i++) {
5448         gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5449         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5450         {
5451           node = myNodes.Value(i);
5452           break;
5453         }
5454       }
5455     }
5456     else {
5457       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5458       while(itn->more()) {
5459         SMESH_TNodeXYZ P2( itn->next() );
5460         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5461         {
5462           node = P2._node;
5463           break;
5464         }
5465       }
5466     }
5467
5468     if ( !node )
5469       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5470
5471     newNodes.push_back( node );
5472
5473   } // loop on steps
5474
5475   return nbNodes;
5476 }
5477
5478 //=======================================================================
5479 //function : ExtrusParam::makeNodesByNormal2D
5480 //purpose  : create nodes for extrusion using normals of faces
5481 //=======================================================================
5482
5483 int SMESH_MeshEditor::ExtrusParam::
5484 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5485                      const SMDS_MeshNode*              srcNode,
5486                      std::list<const SMDS_MeshNode*> & newNodes,
5487                      const bool                        makeMediumNodes)
5488 {
5489   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5490
5491   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5492
5493   // get normals to faces sharing srcNode
5494   vector< gp_XYZ > norms, baryCenters;
5495   gp_XYZ norm, avgNorm( 0,0,0 );
5496   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5497   while ( faceIt->more() )
5498   {
5499     const SMDS_MeshElement* face = faceIt->next();
5500     if ( myElemsToUse && !myElemsToUse->count( face ))
5501       continue;
5502     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5503     {
5504       norms.push_back( norm );
5505       avgNorm += norm;
5506       if ( !alongAvgNorm )
5507       {
5508         gp_XYZ bc(0,0,0);
5509         int nbN = 0;
5510         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5511           bc += SMESH_TNodeXYZ( nIt->next() );
5512         baryCenters.push_back( bc / nbN );
5513       }
5514     }
5515   }
5516
5517   if ( norms.empty() ) return 0;
5518
5519   double normSize = avgNorm.Modulus();
5520   if ( normSize < std::numeric_limits<double>::min() )
5521     return 0;
5522
5523   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5524   {
5525     myDir = avgNorm;
5526     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5527   }
5528
5529   avgNorm /= normSize;
5530
5531   int nbNodes = 0;
5532   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5533   {
5534     gp_XYZ pNew = p;
5535     double stepSize = nextStep();
5536
5537     if ( norms.size() > 1 )
5538     {
5539       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5540       {
5541         // translate plane of a face
5542         baryCenters[ iF ] += norms[ iF ] * stepSize;
5543
5544         // find point of intersection of the face plane located at baryCenters[ iF ]
5545         // and avgNorm located at pNew
5546         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5547         double dot  = ( norms[ iF ] * avgNorm );
5548         if ( dot < std::numeric_limits<double>::min() )
5549           dot = stepSize * 1e-3;
5550         double step = -( norms[ iF ] * pNew + d ) / dot;
5551         pNew += step * avgNorm;
5552       }
5553     }
5554     else
5555     {
5556       pNew += stepSize * avgNorm;
5557     }
5558     p = pNew;
5559
5560     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5561     newNodes.push_back( newNode );
5562   }
5563   return nbNodes;
5564 }
5565
5566 //=======================================================================
5567 //function : ExtrusParam::makeNodesByNormal1D
5568 //purpose  : create nodes for extrusion using normals of edges
5569 //=======================================================================
5570
5571 int SMESH_MeshEditor::ExtrusParam::
5572 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5573                      const SMDS_MeshNode*              srcNode,
5574                      std::list<const SMDS_MeshNode*> & newNodes,
5575                      const bool                        makeMediumNodes)
5576 {
5577   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5578   return 0;
5579 }
5580
5581 //=======================================================================
5582 //function : ExtrusionSweep
5583 //purpose  :
5584 //=======================================================================
5585
5586 SMESH_MeshEditor::PGroupIDs
5587 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5588                                   const gp_Vec&        theStep,
5589                                   const int            theNbSteps,
5590                                   TTElemOfElemListMap& newElemsMap,
5591                                   const int            theFlags,
5592                                   const double         theTolerance)
5593 {
5594   ExtrusParam aParams( theStep, theNbSteps, theFlags, theTolerance );
5595   return ExtrusionSweep( theElems, aParams, newElemsMap );
5596 }
5597
5598
5599 //=======================================================================
5600 //function : ExtrusionSweep
5601 //purpose  :
5602 //=======================================================================
5603
5604 SMESH_MeshEditor::PGroupIDs
5605 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5606                                   ExtrusParam&         theParams,
5607                                   TTElemOfElemListMap& newElemsMap)
5608 {
5609   myLastCreatedElems.Clear();
5610   myLastCreatedNodes.Clear();
5611
5612   // source elements for each generated one
5613   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5614
5615   SMESHDS_Mesh* aMesh = GetMeshDS();
5616
5617   setElemsFirst( theElemSets );
5618   const int nbSteps = theParams.NbSteps();
5619   theParams.SetElementsToUse( theElemSets[0] );
5620
5621   TNodeOfNodeListMap mapNewNodes;
5622   //TNodeOfNodeVecMap mapNewNodes;
5623   TElemOfVecOfNnlmiMap mapElemNewNodes;
5624   //TElemOfVecOfMapNodesMap mapElemNewNodes;
5625
5626   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5627                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5628                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5629   // loop on theElems
5630   TIDSortedElemSet::iterator itElem;
5631   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5632   {
5633     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5634     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5635     {
5636       // check element type
5637       const SMDS_MeshElement* elem = *itElem;
5638       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5639         continue;
5640
5641       const size_t nbNodes = elem->NbNodes();
5642       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5643       newNodesItVec.reserve( nbNodes );
5644
5645       // loop on elem nodes
5646       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5647       while ( itN->more() )
5648       {
5649         // check if a node has been already sweeped
5650         const SMDS_MeshNode* node = cast2Node( itN->next() );
5651         TNodeOfNodeListMap::iterator nIt =
5652           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5653         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5654         if ( listNewNodes.empty() )
5655         {
5656           // make new nodes
5657
5658           // check if we are to create medium nodes between corner ones
5659           bool needMediumNodes = false;
5660           if ( isQuadraticMesh )
5661           {
5662             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5663             while (it->more() && !needMediumNodes )
5664             {
5665               const SMDS_MeshElement* invElem = it->next();
5666               if ( invElem != elem && !theElems.count( invElem )) continue;
5667               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5668               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5669                 needMediumNodes = true;
5670             }
5671           }
5672           // create nodes for all steps
5673           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5674           {
5675             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5676             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5677             {
5678               myLastCreatedNodes.Append( *newNodesIt );
5679               srcNodes.Append( node );
5680             }
5681           }
5682           else
5683           {
5684             break; // newNodesItVec will be shorter than nbNodes
5685           }
5686         }
5687         newNodesItVec.push_back( nIt );
5688       }
5689       // make new elements
5690       if ( newNodesItVec.size() == nbNodes )
5691         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5692     }
5693   }
5694
5695   if ( theParams.ToMakeBoundary() ) {
5696     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5697   }
5698   PGroupIDs newGroupIDs;
5699   if ( theParams.ToMakeGroups() )
5700     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5701
5702   return newGroupIDs;
5703 }
5704
5705 //=======================================================================
5706 //function : ExtrusionAlongTrack
5707 //purpose  :
5708 //=======================================================================
5709 SMESH_MeshEditor::Extrusion_Error
5710 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5711                                        SMESH_subMesh*       theTrack,
5712                                        const SMDS_MeshNode* theN1,
5713                                        const bool           theHasAngles,
5714                                        list<double>&        theAngles,
5715                                        const bool           theLinearVariation,
5716                                        const bool           theHasRefPoint,
5717                                        const gp_Pnt&        theRefPoint,
5718                                        const bool           theMakeGroups)
5719 {
5720   MESSAGE("ExtrusionAlongTrack");
5721   myLastCreatedElems.Clear();
5722   myLastCreatedNodes.Clear();
5723
5724   int aNbE;
5725   std::list<double> aPrms;
5726   TIDSortedElemSet::iterator itElem;
5727
5728   gp_XYZ aGC;
5729   TopoDS_Edge aTrackEdge;
5730   TopoDS_Vertex aV1, aV2;
5731
5732   SMDS_ElemIteratorPtr aItE;
5733   SMDS_NodeIteratorPtr aItN;
5734   SMDSAbs_ElementType aTypeE;
5735
5736   TNodeOfNodeListMap mapNewNodes;
5737
5738   // 1. Check data
5739   aNbE = theElements[0].size() + theElements[1].size();
5740   // nothing to do
5741   if ( !aNbE )
5742     return EXTR_NO_ELEMENTS;
5743
5744   // 1.1 Track Pattern
5745   ASSERT( theTrack );
5746
5747   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5748
5749   aItE = pSubMeshDS->GetElements();
5750   while ( aItE->more() ) {
5751     const SMDS_MeshElement* pE = aItE->next();
5752     aTypeE = pE->GetType();
5753     // Pattern must contain links only
5754     if ( aTypeE != SMDSAbs_Edge )
5755       return EXTR_PATH_NOT_EDGE;
5756   }
5757
5758   list<SMESH_MeshEditor_PathPoint> fullList;
5759
5760   const TopoDS_Shape& aS = theTrack->GetSubShape();
5761   // Sub-shape for the Pattern must be an Edge or Wire
5762   if( aS.ShapeType() == TopAbs_EDGE ) {
5763     aTrackEdge = TopoDS::Edge( aS );
5764     // the Edge must not be degenerated
5765     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5766       return EXTR_BAD_PATH_SHAPE;
5767     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5768     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5769     const SMDS_MeshNode* aN1 = aItN->next();
5770     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5771     const SMDS_MeshNode* aN2 = aItN->next();
5772     // starting node must be aN1 or aN2
5773     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5774       return EXTR_BAD_STARTING_NODE;
5775     aItN = pSubMeshDS->GetNodes();
5776     while ( aItN->more() ) {
5777       const SMDS_MeshNode* pNode = aItN->next();
5778       const SMDS_EdgePosition* pEPos =
5779         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5780       double aT = pEPos->GetUParameter();
5781       aPrms.push_back( aT );
5782     }
5783     //Extrusion_Error err =
5784     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5785   } else if( aS.ShapeType() == TopAbs_WIRE ) {
5786     list< SMESH_subMesh* > LSM;
5787     TopTools_SequenceOfShape Edges;
5788     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5789     while(itSM->more()) {
5790       SMESH_subMesh* SM = itSM->next();
5791       LSM.push_back(SM);
5792       const TopoDS_Shape& aS = SM->GetSubShape();
5793       Edges.Append(aS);
5794     }
5795     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5796     int startNid = theN1->GetID();
5797     TColStd_MapOfInteger UsedNums;
5798
5799     int NbEdges = Edges.Length();
5800     int i = 1;
5801     for(; i<=NbEdges; i++) {
5802       int k = 0;
5803       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5804       for(; itLSM!=LSM.end(); itLSM++) {
5805         k++;
5806         if(UsedNums.Contains(k)) continue;
5807         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5808         SMESH_subMesh* locTrack = *itLSM;
5809         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5810         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5811         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5812         const SMDS_MeshNode* aN1 = aItN->next();
5813         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5814         const SMDS_MeshNode* aN2 = aItN->next();
5815         // starting node must be aN1 or aN2
5816         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5817         // 2. Collect parameters on the track edge
5818         aPrms.clear();
5819         aItN = locMeshDS->GetNodes();
5820         while ( aItN->more() ) {
5821           const SMDS_MeshNode* pNode = aItN->next();
5822           const SMDS_EdgePosition* pEPos =
5823             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5824           double aT = pEPos->GetUParameter();
5825           aPrms.push_back( aT );
5826         }
5827         list<SMESH_MeshEditor_PathPoint> LPP;
5828         //Extrusion_Error err =
5829         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5830         LLPPs.push_back(LPP);
5831         UsedNums.Add(k);
5832         // update startN for search following egde
5833         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5834         else startNid = aN1->GetID();
5835         break;
5836       }
5837     }
5838     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5839     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5840     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5841     for(; itPP!=firstList.end(); itPP++) {
5842       fullList.push_back( *itPP );
5843     }
5844     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5845     fullList.pop_back();
5846     itLLPP++;
5847     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5848       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5849       itPP = currList.begin();
5850       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5851       gp_Dir D1 = PP1.Tangent();
5852       gp_Dir D2 = PP2.Tangent();
5853       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5854                            (D1.Z()+D2.Z())/2 ) );
5855       PP1.SetTangent(Dnew);
5856       fullList.push_back(PP1);
5857       itPP++;
5858       for(; itPP!=firstList.end(); itPP++) {
5859         fullList.push_back( *itPP );
5860       }
5861       PP1 = fullList.back();
5862       fullList.pop_back();
5863     }
5864     // if wire not closed
5865     fullList.push_back(PP1);
5866     // else ???
5867   }
5868   else {
5869     return EXTR_BAD_PATH_SHAPE;
5870   }
5871
5872   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5873                           theHasRefPoint, theRefPoint, theMakeGroups);
5874 }
5875
5876
5877 //=======================================================================
5878 //function : ExtrusionAlongTrack
5879 //purpose  :
5880 //=======================================================================
5881 SMESH_MeshEditor::Extrusion_Error
5882 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5883                                        SMESH_Mesh*          theTrack,
5884                                        const SMDS_MeshNode* theN1,
5885                                        const bool           theHasAngles,
5886                                        list<double>&        theAngles,
5887                                        const bool           theLinearVariation,
5888                                        const bool           theHasRefPoint,
5889                                        const gp_Pnt&        theRefPoint,
5890                                        const bool           theMakeGroups)
5891 {
5892   myLastCreatedElems.Clear();
5893   myLastCreatedNodes.Clear();
5894
5895   int aNbE;
5896   std::list<double> aPrms;
5897   TIDSortedElemSet::iterator itElem;
5898
5899   gp_XYZ aGC;
5900   TopoDS_Edge aTrackEdge;
5901   TopoDS_Vertex aV1, aV2;
5902
5903   SMDS_ElemIteratorPtr aItE;
5904   SMDS_NodeIteratorPtr aItN;
5905   SMDSAbs_ElementType aTypeE;
5906
5907   TNodeOfNodeListMap mapNewNodes;
5908
5909   // 1. Check data
5910   aNbE = theElements[0].size() + theElements[1].size();
5911   // nothing to do
5912   if ( !aNbE )
5913     return EXTR_NO_ELEMENTS;
5914
5915   // 1.1 Track Pattern
5916   ASSERT( theTrack );
5917
5918   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5919
5920   aItE = pMeshDS->elementsIterator();
5921   while ( aItE->more() ) {
5922     const SMDS_MeshElement* pE = aItE->next();
5923     aTypeE = pE->GetType();
5924     // Pattern must contain links only
5925     if ( aTypeE != SMDSAbs_Edge )
5926       return EXTR_PATH_NOT_EDGE;
5927   }
5928
5929   list<SMESH_MeshEditor_PathPoint> fullList;
5930
5931   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5932
5933   if ( !theTrack->HasShapeToMesh() ) {
5934     //Mesh without shape
5935     const SMDS_MeshNode* currentNode = NULL;
5936     const SMDS_MeshNode* prevNode = theN1;
5937     std::vector<const SMDS_MeshNode*> aNodesList;
5938     aNodesList.push_back(theN1);
5939     int nbEdges = 0, conn=0;
5940     const SMDS_MeshElement* prevElem = NULL;
5941     const SMDS_MeshElement* currentElem = NULL;
5942     int totalNbEdges = theTrack->NbEdges();
5943     SMDS_ElemIteratorPtr nIt;
5944
5945     //check start node
5946     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5947       return EXTR_BAD_STARTING_NODE;
5948     }
5949
5950     conn = nbEdgeConnectivity(theN1);
5951     if( conn != 1 )
5952       return EXTR_PATH_NOT_EDGE;
5953
5954     aItE = theN1->GetInverseElementIterator();
5955     prevElem = aItE->next();
5956     currentElem = prevElem;
5957     //Get all nodes
5958     if(totalNbEdges == 1 ) {
5959       nIt = currentElem->nodesIterator();
5960       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5961       if(currentNode == prevNode)
5962         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5963       aNodesList.push_back(currentNode);
5964     } else {
5965       nIt = currentElem->nodesIterator();
5966       while( nIt->more() ) {
5967         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5968         if(currentNode == prevNode)
5969           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5970         aNodesList.push_back(currentNode);
5971
5972         //case of the closed mesh
5973         if(currentNode == theN1) {
5974           nbEdges++;
5975           break;
5976         }
5977
5978         conn = nbEdgeConnectivity(currentNode);
5979         if(conn > 2) {
5980           return EXTR_PATH_NOT_EDGE;
5981         }else if( conn == 1 && nbEdges > 0 ) {
5982           //End of the path
5983           nbEdges++;
5984           break;
5985         }else {
5986           prevNode = currentNode;
5987           aItE = currentNode->GetInverseElementIterator();
5988           currentElem = aItE->next();
5989           if( currentElem  == prevElem)
5990             currentElem = aItE->next();
5991           nIt = currentElem->nodesIterator();
5992           prevElem = currentElem;
5993           nbEdges++;
5994         }
5995       }
5996     }
5997
5998     if(nbEdges != totalNbEdges)
5999       return EXTR_PATH_NOT_EDGE;
6000
6001     TopTools_SequenceOfShape Edges;
6002     double x1,x2,y1,y2,z1,z2;
6003     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6004     int startNid = theN1->GetID();
6005     for(int i = 1; i < aNodesList.size(); i++) {
6006       x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
6007       y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
6008       z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
6009       TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
6010       list<SMESH_MeshEditor_PathPoint> LPP;
6011       aPrms.clear();
6012       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6013       LLPPs.push_back(LPP);
6014       if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
6015       else startNid = aNodesList[i-1]->GetID();
6016
6017     }
6018
6019     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6020     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6021     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6022     for(; itPP!=firstList.end(); itPP++) {
6023       fullList.push_back( *itPP );
6024     }
6025
6026     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6027     SMESH_MeshEditor_PathPoint PP2;
6028     fullList.pop_back();
6029     itLLPP++;
6030     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6031       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6032       itPP = currList.begin();
6033       PP2 = currList.front();
6034       gp_Dir D1 = PP1.Tangent();
6035       gp_Dir D2 = PP2.Tangent();
6036       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6037                            (D1.Z()+D2.Z())/2 ) );
6038       PP1.SetTangent(Dnew);
6039       fullList.push_back(PP1);
6040       itPP++;
6041       for(; itPP!=currList.end(); itPP++) {
6042         fullList.push_back( *itPP );
6043       }
6044       PP1 = fullList.back();
6045       fullList.pop_back();
6046     }
6047     fullList.push_back(PP1);
6048
6049   } // Sub-shape for the Pattern must be an Edge or Wire
6050   else if( aS.ShapeType() == TopAbs_EDGE ) {
6051     aTrackEdge = TopoDS::Edge( aS );
6052     // the Edge must not be degenerated
6053     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6054       return EXTR_BAD_PATH_SHAPE;
6055     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6056     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6057     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6058     // starting node must be aN1 or aN2
6059     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6060       return EXTR_BAD_STARTING_NODE;
6061     aItN = pMeshDS->nodesIterator();
6062     while ( aItN->more() ) {
6063       const SMDS_MeshNode* pNode = aItN->next();
6064       if( pNode==aN1 || pNode==aN2 ) continue;
6065       const SMDS_EdgePosition* pEPos =
6066         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6067       double aT = pEPos->GetUParameter();
6068       aPrms.push_back( aT );
6069     }
6070     //Extrusion_Error err =
6071     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6072   }
6073   else if( aS.ShapeType() == TopAbs_WIRE ) {
6074     list< SMESH_subMesh* > LSM;
6075     TopTools_SequenceOfShape Edges;
6076     TopExp_Explorer eExp(aS, TopAbs_EDGE);
6077     for(; eExp.More(); eExp.Next()) {
6078       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6079       if( SMESH_Algo::isDegenerated(E) ) continue;
6080       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6081       if(SM) {
6082         LSM.push_back(SM);
6083         Edges.Append(E);
6084       }
6085     }
6086     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6087     TopoDS_Vertex aVprev;
6088     TColStd_MapOfInteger UsedNums;
6089     int NbEdges = Edges.Length();
6090     int i = 1;
6091     for(; i<=NbEdges; i++) {
6092       int k = 0;
6093       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6094       for(; itLSM!=LSM.end(); itLSM++) {
6095         k++;
6096         if(UsedNums.Contains(k)) continue;
6097         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6098         SMESH_subMesh* locTrack = *itLSM;
6099         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6100         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6101         bool aN1isOK = false, aN2isOK = false;
6102         if ( aVprev.IsNull() ) {
6103           // if previous vertex is not yet defined, it means that we in the beginning of wire
6104           // and we have to find initial vertex corresponding to starting node theN1
6105           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6106           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6107           // starting node must be aN1 or aN2
6108           aN1isOK = ( aN1 && aN1 == theN1 );
6109           aN2isOK = ( aN2 && aN2 == theN1 );
6110         }
6111         else {
6112           // we have specified ending vertex of the previous edge on the previous iteration
6113           // and we have just to check that it corresponds to any vertex in current segment
6114           aN1isOK = aVprev.IsSame( aV1 );
6115           aN2isOK = aVprev.IsSame( aV2 );
6116         }
6117         if ( !aN1isOK && !aN2isOK ) continue;
6118         // 2. Collect parameters on the track edge
6119         aPrms.clear();
6120         aItN = locMeshDS->GetNodes();
6121         while ( aItN->more() ) {
6122           const SMDS_MeshNode*     pNode = aItN->next();
6123           const SMDS_EdgePosition* pEPos =
6124             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6125           double aT = pEPos->GetUParameter();
6126           aPrms.push_back( aT );
6127         }
6128         list<SMESH_MeshEditor_PathPoint> LPP;
6129         //Extrusion_Error err =
6130         MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6131         LLPPs.push_back(LPP);
6132         UsedNums.Add(k);
6133         // update startN for search following egde
6134         if ( aN1isOK ) aVprev = aV2;
6135         else           aVprev = aV1;
6136         break;
6137       }
6138     }
6139     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6140     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6141     fullList.splice( fullList.end(), firstList );
6142
6143     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6144     fullList.pop_back();
6145     itLLPP++;
6146     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6147       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6148       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6149       gp_Dir D1 = PP1.Tangent();
6150       gp_Dir D2 = PP2.Tangent();
6151       gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
6152       PP1.SetTangent(Dnew);
6153       fullList.push_back(PP1);
6154       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6155       PP1 = fullList.back();
6156       fullList.pop_back();
6157     }
6158     // if wire not closed
6159     fullList.push_back(PP1);
6160     // else ???
6161   }
6162   else {
6163     return EXTR_BAD_PATH_SHAPE;
6164   }
6165
6166   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6167                           theHasRefPoint, theRefPoint, theMakeGroups);
6168 }
6169
6170
6171 //=======================================================================
6172 //function : MakeEdgePathPoints
6173 //purpose  : auxilary for ExtrusionAlongTrack
6174 //=======================================================================
6175 SMESH_MeshEditor::Extrusion_Error
6176 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>&                aPrms,
6177                                      const TopoDS_Edge&                aTrackEdge,
6178                                      bool                              FirstIsStart,
6179                                      list<SMESH_MeshEditor_PathPoint>& LPP)
6180 {
6181   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6182   aTolVec=1.e-7;
6183   aTolVec2=aTolVec*aTolVec;
6184   double aT1, aT2;
6185   TopoDS_Vertex aV1, aV2;
6186   TopExp::Vertices( aTrackEdge, aV1, aV2 );
6187   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6188   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6189   // 2. Collect parameters on the track edge
6190   aPrms.push_front( aT1 );
6191   aPrms.push_back( aT2 );
6192   // sort parameters
6193   aPrms.sort();
6194   if( FirstIsStart ) {
6195     if ( aT1 > aT2 ) {
6196       aPrms.reverse();
6197     }
6198   }
6199   else {
6200     if ( aT2 > aT1 ) {
6201       aPrms.reverse();
6202     }
6203   }
6204   // 3. Path Points
6205   SMESH_MeshEditor_PathPoint aPP;
6206   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6207   std::list<double>::iterator aItD = aPrms.begin();
6208   for(; aItD != aPrms.end(); ++aItD) {
6209     double aT = *aItD;
6210     gp_Pnt aP3D;
6211     gp_Vec aVec;
6212     aC3D->D1( aT, aP3D, aVec );
6213     aL2 = aVec.SquareMagnitude();
6214     if ( aL2 < aTolVec2 )
6215       return EXTR_CANT_GET_TANGENT;
6216     gp_Dir aTgt( aVec );
6217     aPP.SetPnt( aP3D );
6218     aPP.SetTangent( aTgt );
6219     aPP.SetParameter( aT );
6220     LPP.push_back(aPP);
6221   }
6222   return EXTR_OK;
6223 }
6224
6225
6226 //=======================================================================
6227 //function : MakeExtrElements
6228 //purpose  : auxilary for ExtrusionAlongTrack
6229 //=======================================================================
6230 SMESH_MeshEditor::Extrusion_Error
6231 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet                  theElemSets[2],
6232                                    list<SMESH_MeshEditor_PathPoint>& fullList,
6233                                    const bool                        theHasAngles,
6234                                    list<double>&                     theAngles,
6235                                    const bool                        theLinearVariation,
6236                                    const bool                        theHasRefPoint,
6237                                    const gp_Pnt&                     theRefPoint,
6238                                    const bool                        theMakeGroups)
6239 {
6240   const int aNbTP = fullList.size();
6241   // Angles
6242   if( theHasAngles && !theAngles.empty() && theLinearVariation )
6243     LinearAngleVariation(aNbTP-1, theAngles);
6244   // fill vector of path points with angles
6245   vector<SMESH_MeshEditor_PathPoint> aPPs;
6246   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6247   list<double>::iterator                 itAngles = theAngles.begin();
6248   aPPs.push_back( *itPP++ );
6249   for( ; itPP != fullList.end(); itPP++) {
6250     aPPs.push_back( *itPP );
6251     if ( theHasAngles && itAngles != theAngles.end() )
6252       aPPs.back().SetAngle( *itAngles++ );
6253   }
6254
6255   TNodeOfNodeListMap   mapNewNodes;
6256   TElemOfVecOfNnlmiMap mapElemNewNodes;
6257   TTElemOfElemListMap  newElemsMap;
6258   TIDSortedElemSet::iterator itElem;
6259   // source elements for each generated one
6260   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6261
6262   // 3. Center of rotation aV0
6263   gp_Pnt aV0 = theRefPoint;
6264   if ( !theHasRefPoint )
6265   {
6266     gp_XYZ aGC( 0.,0.,0. );
6267     TIDSortedElemSet newNodes;
6268
6269     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6270     {
6271       TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6272       itElem = theElements.begin();
6273       for ( ; itElem != theElements.end(); itElem++ ) {
6274         const SMDS_MeshElement* elem = *itElem;
6275
6276         SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6277         while ( itN->more() ) {
6278           const SMDS_MeshElement* node = itN->next();
6279           if ( newNodes.insert( node ).second )
6280             aGC += SMESH_TNodeXYZ( node );
6281         }
6282       }
6283     }
6284     aGC /= newNodes.size();
6285     aV0.SetXYZ( aGC );
6286   } // if (!theHasRefPoint) {
6287
6288   // 4. Processing the elements
6289   SMESHDS_Mesh* aMesh = GetMeshDS();
6290
6291   setElemsFirst( theElemSets );
6292   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6293   {
6294     TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6295     for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
6296       // check element type
6297       const SMDS_MeshElement* elem = *itElem;
6298       if ( !elem )
6299         continue;
6300       // SMDSAbs_ElementType aTypeE = elem->GetType();
6301       // if ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge )
6302       //   continue;
6303
6304       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6305       newNodesItVec.reserve( elem->NbNodes() );
6306
6307       // loop on elem nodes
6308       int nodeIndex = -1;
6309       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6310       while ( itN->more() )
6311       {
6312         ++nodeIndex;
6313         // check if a node has been already processed
6314         const SMDS_MeshNode* node =
6315           static_cast<const SMDS_MeshNode*>( itN->next() );
6316         TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
6317         if ( nIt == mapNewNodes.end() ) {
6318           nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6319           list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6320
6321           // make new nodes
6322           Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6323           gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6324           gp_Ax1 anAx1, anAxT1T0;
6325           gp_Dir aDT1x, aDT0x, aDT1T0;
6326
6327           aTolAng=1.e-4;
6328
6329           aV0x = aV0;
6330           aPN0 = SMESH_TNodeXYZ( node );
6331
6332           const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6333           aP0x = aPP0.Pnt();
6334           aDT0x= aPP0.Tangent();
6335           //cout<<"j = 0   PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
6336
6337           for ( int j = 1; j < aNbTP; ++j ) {
6338             const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6339             aP1x     = aPP1.Pnt();
6340             aDT1x    = aPP1.Tangent();
6341             aAngle1x = aPP1.Angle();
6342
6343             gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6344             // Translation
6345             gp_Vec aV01x( aP0x, aP1x );
6346             aTrsf.SetTranslation( aV01x );
6347
6348             // traslated point
6349             aV1x = aV0x.Transformed( aTrsf );
6350             aPN1 = aPN0.Transformed( aTrsf );
6351
6352             // rotation 1 [ T1,T0 ]
6353             aAngleT1T0=-aDT1x.Angle( aDT0x );
6354             if (fabs(aAngleT1T0) > aTolAng) {
6355               aDT1T0=aDT1x^aDT0x;
6356               anAxT1T0.SetLocation( aV1x );
6357               anAxT1T0.SetDirection( aDT1T0 );
6358               aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6359
6360               aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6361             }
6362
6363             // rotation 2
6364             if ( theHasAngles ) {
6365               anAx1.SetLocation( aV1x );
6366               anAx1.SetDirection( aDT1x );
6367               aTrsfRot.SetRotation( anAx1, aAngle1x );
6368
6369               aPN1 = aPN1.Transformed( aTrsfRot );
6370             }
6371
6372             // make new node
6373             //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
6374             if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6375               // create additional node
6376               double x = ( aPN1.X() + aPN0.X() )/2.;
6377               double y = ( aPN1.Y() + aPN0.Y() )/2.;
6378               double z = ( aPN1.Z() + aPN0.Z() )/2.;
6379               const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
6380               myLastCreatedNodes.Append(newNode);
6381               srcNodes.Append( node );
6382               listNewNodes.push_back( newNode );
6383             }
6384             const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6385             myLastCreatedNodes.Append(newNode);
6386             srcNodes.Append( node );
6387             listNewNodes.push_back( newNode );
6388
6389             aPN0 = aPN1;
6390             aP0x = aP1x;
6391             aV0x = aV1x;
6392             aDT0x = aDT1x;
6393           }
6394         }
6395
6396         else {
6397           // if current elem is quadratic and current node is not medium
6398           // we have to check - may be it is needed to insert additional nodes
6399           if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6400             list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6401             if(listNewNodes.size()==aNbTP-1) {
6402               vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6403               gp_XYZ P(node->X(), node->Y(), node->Z());
6404               list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6405               int i;
6406               for(i=0; i<aNbTP-1; i++) {
6407                 const SMDS_MeshNode* N = *it;
6408                 double x = ( N->X() + P.X() )/2.;
6409                 double y = ( N->Y() + P.Y() )/2.;
6410                 double z = ( N->Z() + P.Z() )/2.;
6411                 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6412                 srcNodes.Append( node );
6413                 myLastCreatedNodes.Append(newN);
6414                 aNodes[2*i] = newN;
6415                 aNodes[2*i+1] = N;
6416                 P = gp_XYZ(N->X(),N->Y(),N->Z());
6417               }
6418               listNewNodes.clear();
6419               for(i=0; i<2*(aNbTP-1); i++) {
6420                 listNewNodes.push_back(aNodes[i]);
6421               }
6422             }
6423           }
6424         }
6425
6426         newNodesItVec.push_back( nIt );
6427       }
6428       // make new elements
6429       //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6430       //              newNodesItVec[0]->second.size(), myLastCreatedElems );
6431       sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6432     }
6433   }
6434
6435   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6436
6437   if ( theMakeGroups )
6438     generateGroups( srcNodes, srcElems, "extruded");
6439
6440   return EXTR_OK;
6441 }
6442
6443
6444 //=======================================================================
6445 //function : LinearAngleVariation
6446 //purpose  : auxilary for ExtrusionAlongTrack
6447 //=======================================================================
6448 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6449                                             list<double>& Angles)
6450 {
6451   int nbAngles = Angles.size();
6452   if( nbSteps > nbAngles ) {
6453     vector<double> theAngles(nbAngles);
6454     list<double>::iterator it = Angles.begin();
6455     int i = -1;
6456     for(; it!=Angles.end(); it++) {
6457       i++;
6458       theAngles[i] = (*it);
6459     }
6460     list<double> res;
6461     double rAn2St = double( nbAngles ) / double( nbSteps );
6462     double angPrev = 0, angle;
6463     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6464       double angCur = rAn2St * ( iSt+1 );
6465       double angCurFloor  = floor( angCur );
6466       double angPrevFloor = floor( angPrev );
6467       if ( angPrevFloor == angCurFloor )
6468         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6469       else {
6470         int iP = int( angPrevFloor );
6471         double angPrevCeil = ceil(angPrev);
6472         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6473
6474         int iC = int( angCurFloor );
6475         if ( iC < nbAngles )
6476           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6477
6478         iP = int( angPrevCeil );
6479         while ( iC-- > iP )
6480           angle += theAngles[ iC ];
6481       }
6482       res.push_back(angle);
6483       angPrev = angCur;
6484     }
6485     Angles.clear();
6486     it = res.begin();
6487     for(; it!=res.end(); it++)
6488       Angles.push_back( *it );
6489   }
6490 }
6491
6492
6493 //================================================================================
6494 /*!
6495  * \brief Move or copy theElements applying theTrsf to their nodes
6496  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6497  *  \param theTrsf - transformation to apply
6498  *  \param theCopy - if true, create translated copies of theElems
6499  *  \param theMakeGroups - if true and theCopy, create translated groups
6500  *  \param theTargetMesh - mesh to copy translated elements into
6501  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6502  */
6503 //================================================================================
6504
6505 SMESH_MeshEditor::PGroupIDs
6506 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6507                              const gp_Trsf&     theTrsf,
6508                              const bool         theCopy,
6509                              const bool         theMakeGroups,
6510                              SMESH_Mesh*        theTargetMesh)
6511 {
6512   myLastCreatedElems.Clear();
6513   myLastCreatedNodes.Clear();
6514
6515   bool needReverse = false;
6516   string groupPostfix;
6517   switch ( theTrsf.Form() ) {
6518   case gp_PntMirror:
6519     MESSAGE("gp_PntMirror");
6520     needReverse = true;
6521     groupPostfix = "mirrored";
6522     break;
6523   case gp_Ax1Mirror:
6524     MESSAGE("gp_Ax1Mirror");
6525     groupPostfix = "mirrored";
6526     break;
6527   case gp_Ax2Mirror:
6528     MESSAGE("gp_Ax2Mirror");
6529     needReverse = true;
6530     groupPostfix = "mirrored";
6531     break;
6532   case gp_Rotation:
6533     MESSAGE("gp_Rotation");
6534     groupPostfix = "rotated";
6535     break;
6536   case gp_Translation:
6537     MESSAGE("gp_Translation");
6538     groupPostfix = "translated";
6539     break;
6540   case gp_Scale:
6541     MESSAGE("gp_Scale");
6542     groupPostfix = "scaled";
6543     break;
6544   case gp_CompoundTrsf: // different scale by axis
6545     MESSAGE("gp_CompoundTrsf");
6546     groupPostfix = "scaled";
6547     break;
6548   default:
6549     MESSAGE("default");
6550     needReverse = false;
6551     groupPostfix = "transformed";
6552   }
6553
6554   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6555   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6556   SMESHDS_Mesh* aMesh    = GetMeshDS();
6557
6558
6559   // map old node to new one
6560   TNodeNodeMap nodeMap;
6561
6562   // elements sharing moved nodes; those of them which have all
6563   // nodes mirrored but are not in theElems are to be reversed
6564   TIDSortedElemSet inverseElemSet;
6565
6566   // source elements for each generated one
6567   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6568
6569   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6570   TIDSortedElemSet orphanNode;
6571
6572   if ( theElems.empty() ) // transform the whole mesh
6573   {
6574     // add all elements
6575     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6576     while ( eIt->more() ) theElems.insert( eIt->next() );
6577     // add orphan nodes
6578     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6579     while ( nIt->more() )
6580     {
6581       const SMDS_MeshNode* node = nIt->next();
6582       if ( node->NbInverseElements() == 0)
6583         orphanNode.insert( node );
6584     }
6585   }
6586
6587   // loop on elements to transform nodes : first orphan nodes then elems
6588   TIDSortedElemSet::iterator itElem;
6589   TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
6590   for (int i=0; i<2; i++)
6591   for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
6592     const SMDS_MeshElement* elem = *itElem;
6593     if ( !elem )
6594       continue;
6595
6596     // loop on elem nodes
6597     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6598     while ( itN->more() ) {
6599
6600       const SMDS_MeshNode* node = cast2Node( itN->next() );
6601       // check if a node has been already transformed
6602       pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6603         nodeMap.insert( make_pair ( node, node ));
6604       if ( !n2n_isnew.second )
6605         continue;
6606
6607       double coord[3];
6608       coord[0] = node->X();
6609       coord[1] = node->Y();
6610       coord[2] = node->Z();
6611       theTrsf.Transforms( coord[0], coord[1], coord[2] );
6612       if ( theTargetMesh ) {
6613         const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6614         n2n_isnew.first->second = newNode;
6615         myLastCreatedNodes.Append(newNode);
6616         srcNodes.Append( node );
6617       }
6618       else if ( theCopy ) {
6619         const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6620         n2n_isnew.first->second = newNode;
6621         myLastCreatedNodes.Append(newNode);
6622         srcNodes.Append( node );
6623       }
6624       else {
6625         aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6626         // node position on shape becomes invalid
6627         const_cast< SMDS_MeshNode* > ( node )->SetPosition
6628           ( SMDS_SpacePosition::originSpacePosition() );
6629       }
6630
6631       // keep inverse elements
6632       if ( !theCopy && !theTargetMesh && needReverse ) {
6633         SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6634         while ( invElemIt->more() ) {
6635           const SMDS_MeshElement* iel = invElemIt->next();
6636           inverseElemSet.insert( iel );
6637         }
6638       }
6639     }
6640   }
6641
6642   // either create new elements or reverse mirrored ones
6643   if ( !theCopy && !needReverse && !theTargetMesh )
6644     return PGroupIDs();
6645
6646   TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
6647   for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
6648     theElems.insert( *invElemIt );
6649
6650   // Replicate or reverse elements
6651
6652   std::vector<int> iForw;
6653   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6654   {
6655     const SMDS_MeshElement* elem = *itElem;
6656     if ( !elem ) continue;
6657
6658     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6659     int                  nbNodes  = elem->NbNodes();
6660     if ( geomType == SMDSGeom_NONE ) continue; // node
6661
6662     switch ( geomType ) {
6663
6664     case SMDSGeom_POLYGON:  // ---------------------- polygon
6665       {
6666         vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
6667         int iNode = 0;
6668         SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6669         while (itN->more()) {
6670           const SMDS_MeshNode* node =
6671             static_cast<const SMDS_MeshNode*>(itN->next());
6672           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6673           if (nodeMapIt == nodeMap.end())
6674             break; // not all nodes transformed
6675           if (needReverse) {
6676             // reverse mirrored faces and volumes
6677             poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
6678           } else {
6679             poly_nodes[iNode] = (*nodeMapIt).second;
6680           }
6681           iNode++;
6682         }
6683         if ( iNode != nbNodes )
6684           continue; // not all nodes transformed
6685
6686         if ( theTargetMesh ) {
6687           myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
6688           srcElems.Append( elem );
6689         }
6690         else if ( theCopy ) {
6691           myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
6692           srcElems.Append( elem );
6693         }
6694         else {
6695           aMesh->ChangePolygonNodes(elem, poly_nodes);
6696         }
6697       }
6698       break;
6699
6700     case SMDSGeom_POLYHEDRA:  // ------------------ polyhedral volume
6701       {
6702         const SMDS_VtkVolume* aPolyedre =
6703           dynamic_cast<const SMDS_VtkVolume*>( elem );
6704         if (!aPolyedre) {
6705           MESSAGE("Warning: bad volumic element");
6706           continue;
6707         }
6708
6709         vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
6710         vector<int> quantities; quantities.reserve( nbNodes );
6711
6712         bool allTransformed = true;
6713         int nbFaces = aPolyedre->NbFaces();
6714         for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
6715           int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6716           for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
6717             const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6718             TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6719             if (nodeMapIt == nodeMap.end()) {
6720               allTransformed = false; // not all nodes transformed
6721             } else {
6722               poly_nodes.push_back((*nodeMapIt).second);
6723             }
6724             if ( needReverse && allTransformed )
6725               std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
6726           }
6727           quantities.push_back(nbFaceNodes);
6728         }
6729         if ( !allTransformed )
6730           continue; // not all nodes transformed
6731
6732         if ( theTargetMesh ) {
6733           myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
6734           srcElems.Append( elem );
6735         }
6736         else if ( theCopy ) {
6737           myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
6738           srcElems.Append( elem );
6739         }
6740         else {
6741           aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6742         }
6743       }
6744       break;
6745
6746     case SMDSGeom_BALL: // -------------------- Ball
6747       {
6748         if ( !theCopy && !theTargetMesh ) continue;
6749
6750         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
6751         if (nodeMapIt == nodeMap.end())
6752           continue; // not all nodes transformed
6753
6754         double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
6755         if ( theTargetMesh ) {
6756           myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
6757           srcElems.Append( elem );
6758         }
6759         else {
6760           myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
6761           srcElems.Append( elem );
6762         }
6763       }
6764       break;
6765
6766     default: // ----------------------- Regular elements
6767
6768       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6769       const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
6770       const std::vector<int>& i = needReverse ? iRev : iForw;
6771
6772       // find transformed nodes
6773       vector<const SMDS_MeshNode*> nodes(nbNodes);
6774       int iNode = 0;
6775       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6776       while ( itN->more() ) {
6777         const SMDS_MeshNode* node =
6778           static_cast<const SMDS_MeshNode*>( itN->next() );
6779         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6780         if ( nodeMapIt == nodeMap.end() )
6781           break; // not all nodes transformed
6782         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6783       }
6784       if ( iNode != nbNodes )
6785         continue; // not all nodes transformed
6786
6787       if ( theTargetMesh ) {
6788         if ( SMDS_MeshElement* copy =
6789              targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
6790           myLastCreatedElems.Append( copy );
6791           srcElems.Append( elem );
6792         }
6793       }
6794       else if ( theCopy ) {
6795         if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
6796           srcElems.Append( elem );
6797       }
6798       else {
6799         // reverse element as it was reversed by transformation
6800         if ( nbNodes > 2 )
6801           aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6802       }
6803     } // switch ( geomType )
6804
6805   } // loop on elements
6806
6807   PGroupIDs newGroupIDs;
6808
6809   if ( ( theMakeGroups && theCopy ) ||
6810        ( theMakeGroups && theTargetMesh ) )
6811     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6812
6813   return newGroupIDs;
6814 }
6815
6816 //=======================================================================
6817 /*!
6818  * \brief Create groups of elements made during transformation
6819  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6820  *  \param elemGens - elements making corresponding myLastCreatedElems
6821  *  \param postfix - to append to names of new groups
6822  *  \param targetMesh - mesh to create groups in
6823  *  \param topPresent - is there "top" elements that are created by sweeping
6824  */
6825 //=======================================================================
6826
6827 SMESH_MeshEditor::PGroupIDs
6828 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6829                                  const SMESH_SequenceOfElemPtr& elemGens,
6830                                  const std::string&             postfix,
6831                                  SMESH_Mesh*                    targetMesh,
6832                                  const bool                     topPresent)
6833 {
6834   PGroupIDs newGroupIDs( new list<int> );
6835   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6836
6837   // Sort existing groups by types and collect their names
6838
6839   // containers to store an old group and generated new ones;
6840   // 1st new group is for result elems of different type than a source one;
6841   // 2nd new group is for same type result elems ("top" group at extrusion)
6842   using boost::tuple;
6843   using boost::make_tuple;
6844   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6845   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6846   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6847   // group names
6848   set< string > groupNames;
6849
6850   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6851   if ( !groupIt->more() ) return newGroupIDs;
6852
6853   int newGroupID = mesh->GetGroupIds().back()+1;
6854   while ( groupIt->more() )
6855   {
6856     SMESH_Group * group = groupIt->next();
6857     if ( !group ) continue;
6858     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6859     if ( !groupDS || groupDS->IsEmpty() ) continue;
6860     groupNames.insert    ( group->GetName() );
6861     groupDS->SetStoreName( group->GetName() );
6862     const SMDSAbs_ElementType type = groupDS->GetType();
6863     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6864     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6865     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6866     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6867   }
6868
6869   // Loop on nodes and elements to add them in new groups
6870
6871   vector< const SMDS_MeshElement* > resultElems;
6872   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6873   {
6874     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6875     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6876     if ( gens.Length() != elems.Length() )
6877       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6878
6879     // loop on created elements
6880     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6881     {
6882       const SMDS_MeshElement* sourceElem = gens( iElem );
6883       if ( !sourceElem ) {
6884         MESSAGE("generateGroups(): NULL source element");
6885         continue;
6886       }
6887       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6888       if ( groupsOldNew.empty() ) { // no groups of this type at all
6889         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6890           ++iElem; // skip all elements made by sourceElem
6891         continue;
6892       }
6893       // collect all elements made by the iElem-th sourceElem
6894       resultElems.clear();
6895       if ( const SMDS_MeshElement* resElem = elems( iElem ))
6896         if ( resElem != sourceElem )
6897           resultElems.push_back( resElem );
6898       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6899         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6900           if ( resElem != sourceElem )
6901             resultElems.push_back( resElem );
6902
6903       const SMDS_MeshElement* topElem = 0;
6904       if ( isNodes ) // there must be a top element
6905       {
6906         topElem = resultElems.back();
6907         resultElems.pop_back();
6908       }
6909       else
6910       {
6911         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6912         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6913           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6914           {
6915             topElem = *resElemIt;
6916             *resElemIt = 0; // erase *resElemIt
6917             break;
6918           }
6919       }
6920       // add resultElems to groups originted from ones the sourceElem belongs to
6921       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6922       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6923       {
6924         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6925         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6926         {
6927           // fill in a new group
6928           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6929           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6930           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6931             if ( *resElemIt )
6932               newGroup.Add( *resElemIt );
6933
6934           // fill a "top" group
6935           if ( topElem )
6936           {
6937             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6938             newTopGroup.Add( topElem );
6939          }
6940         }
6941       }
6942     } // loop on created elements
6943   }// loop on nodes and elements
6944
6945   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6946
6947   list<int> topGrouIds;
6948   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6949   {
6950     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6951     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6952                                       orderedOldNewGroups[i]->get<2>() };
6953     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6954     {
6955       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6956       if ( newGroupDS->IsEmpty() )
6957       {
6958         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6959       }
6960       else
6961       {
6962         // set group type
6963         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6964
6965         // make a name
6966         const bool isTop = ( topPresent &&
6967                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6968                              is2nd );
6969
6970         string name = oldGroupDS->GetStoreName();
6971         { // remove trailing whitespaces (issue 22599)
6972           size_t size = name.size();
6973           while ( size > 1 && isspace( name[ size-1 ]))
6974             --size;
6975           if ( size != name.size() )
6976           {
6977             name.resize( size );
6978             oldGroupDS->SetStoreName( name.c_str() );
6979           }
6980         }
6981         if ( !targetMesh ) {
6982           string suffix = ( isTop ? "top": postfix.c_str() );
6983           name += "_";
6984           name += suffix;
6985           int nb = 1;
6986           while ( !groupNames.insert( name ).second ) // name exists
6987             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6988         }
6989         else if ( isTop ) {
6990           name += "_top";
6991         }
6992         newGroupDS->SetStoreName( name.c_str() );
6993
6994         // make a SMESH_Groups
6995         mesh->AddGroup( newGroupDS );
6996         if ( isTop )
6997           topGrouIds.push_back( newGroupDS->GetID() );
6998         else
6999           newGroupIDs->push_back( newGroupDS->GetID() );
7000       }
7001     }
7002   }
7003   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7004
7005   return newGroupIDs;
7006 }
7007
7008 //================================================================================
7009 /*!
7010  * \brief Return list of group of nodes close to each other within theTolerance
7011  *        Search among theNodes or in the whole mesh if theNodes is empty using
7012  *        an Octree algorithm
7013  */
7014 //================================================================================
7015
7016 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
7017                                             const double         theTolerance,
7018                                             TListOfListOfNodes & theGroupsOfNodes)
7019 {
7020   myLastCreatedElems.Clear();
7021   myLastCreatedNodes.Clear();
7022
7023   if ( theNodes.empty() )
7024   { // get all nodes in the mesh
7025     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7026     while ( nIt->more() )
7027       theNodes.insert( theNodes.end(),nIt->next());
7028   }
7029
7030   SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
7031 }
7032
7033 //=======================================================================
7034 //function : SimplifyFace
7035 //purpose  :
7036 //=======================================================================
7037
7038 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7039                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7040                                     vector<int>&                         quantities) const
7041 {
7042   int nbNodes = faceNodes.size();
7043
7044   if (nbNodes < 3)
7045     return 0;
7046
7047   set<const SMDS_MeshNode*> nodeSet;
7048
7049   // get simple seq of nodes
7050   //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7051   vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7052   int iSimple = 0, nbUnique = 0;
7053
7054   simpleNodes[iSimple++] = faceNodes[0];
7055   nbUnique++;
7056   for (int iCur = 1; iCur < nbNodes; iCur++) {
7057     if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7058       simpleNodes[iSimple++] = faceNodes[iCur];
7059       if (nodeSet.insert( faceNodes[iCur] ).second)
7060         nbUnique++;
7061     }
7062   }
7063   int nbSimple = iSimple;
7064   if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7065     nbSimple--;
7066     iSimple--;
7067   }
7068
7069   if (nbUnique < 3)
7070     return 0;
7071
7072   // separate loops
7073   int nbNew = 0;
7074   bool foundLoop = (nbSimple > nbUnique);
7075   while (foundLoop) {
7076     foundLoop = false;
7077     set<const SMDS_MeshNode*> loopSet;
7078     for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7079       const SMDS_MeshNode* n = simpleNodes[iSimple];
7080       if (!loopSet.insert( n ).second) {
7081         foundLoop = true;
7082
7083         // separate loop
7084         int iC = 0, curLast = iSimple;
7085         for (; iC < curLast; iC++) {
7086           if (simpleNodes[iC] == n) break;
7087         }
7088         int loopLen = curLast - iC;
7089         if (loopLen > 2) {
7090           // create sub-element
7091           nbNew++;
7092           quantities.push_back(loopLen);
7093           for (; iC < curLast; iC++) {
7094             poly_nodes.push_back(simpleNodes[iC]);
7095           }
7096         }
7097         // shift the rest nodes (place from the first loop position)
7098         for (iC = curLast + 1; iC < nbSimple; iC++) {
7099           simpleNodes[iC - loopLen] = simpleNodes[iC];
7100         }
7101         nbSimple -= loopLen;
7102         iSimple -= loopLen;
7103       }
7104     } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7105   } // while (foundLoop)
7106
7107   if (iSimple > 2) {
7108     nbNew++;
7109     quantities.push_back(iSimple);
7110     for (int i = 0; i < iSimple; i++)
7111       poly_nodes.push_back(simpleNodes[i]);
7112   }
7113
7114   return nbNew;
7115 }
7116
7117 //=======================================================================
7118 //function : MergeNodes
7119 //purpose  : In each group, the cdr of nodes are substituted by the first one
7120 //           in all elements.
7121 //=======================================================================
7122
7123 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7124 {
7125   MESSAGE("MergeNodes");
7126   myLastCreatedElems.Clear();
7127   myLastCreatedNodes.Clear();
7128
7129   SMESHDS_Mesh* aMesh = GetMeshDS();
7130
7131   TNodeNodeMap nodeNodeMap; // node to replace - new node
7132   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7133   list< int > rmElemIds, rmNodeIds;
7134
7135   // Fill nodeNodeMap and elems
7136
7137   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7138   for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7139     list<const SMDS_MeshNode*>& nodes = *grIt;
7140     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7141     const SMDS_MeshNode* nToKeep = *nIt;
7142     //MESSAGE("node to keep " << nToKeep->GetID());
7143     for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7144       const SMDS_MeshNode* nToRemove = *nIt;
7145       nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7146       if ( nToRemove != nToKeep ) {
7147         //MESSAGE("  node to remove " << nToRemove->GetID());
7148         rmNodeIds.push_back( nToRemove->GetID() );
7149         AddToSameGroups( nToKeep, nToRemove, aMesh );
7150         // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7151         // after MergeNodes() w/o creating node in place of merged ones.
7152         const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7153         if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7154           if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7155             sm->SetIsAlwaysComputed( true );
7156       }
7157
7158       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7159       while ( invElemIt->more() ) {
7160         const SMDS_MeshElement* elem = invElemIt->next();
7161         elems.insert(elem);
7162       }
7163     }
7164   }
7165   // Change element nodes or remove an element
7166
7167   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7168   for ( ; eIt != elems.end(); eIt++ ) {
7169     const SMDS_MeshElement* elem = *eIt;
7170     //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7171     int nbNodes = elem->NbNodes();
7172     int aShapeId = FindShape( elem );
7173
7174     set<const SMDS_MeshNode*> nodeSet;
7175     vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7176     int iUnique = 0, iCur = 0, nbRepl = 0;
7177     vector<int> iRepl( nbNodes );
7178
7179     // get new seq of nodes
7180     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7181     while ( itN->more() ) {
7182       const SMDS_MeshNode* n =
7183         static_cast<const SMDS_MeshNode*>( itN->next() );
7184
7185       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7186       if ( nnIt != nodeNodeMap.end() ) { // n sticks
7187         n = (*nnIt).second;
7188         // BUG 0020185: begin
7189         {
7190           bool stopRecur = false;
7191           set<const SMDS_MeshNode*> nodesRecur;
7192           nodesRecur.insert(n);
7193           while (!stopRecur) {
7194             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7195             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7196               n = (*nnIt_i).second;
7197               if (!nodesRecur.insert(n).second) {
7198                 // error: recursive dependancy
7199                 stopRecur = true;
7200               }
7201             }
7202             else
7203               stopRecur = true;
7204           }
7205         }
7206         // BUG 0020185: end
7207       }
7208       curNodes[ iCur ] = n;
7209       bool isUnique = nodeSet.insert( n ).second;
7210       if ( isUnique )
7211         uniqueNodes[ iUnique++ ] = n;
7212       else
7213         iRepl[ nbRepl++ ] = iCur;
7214       iCur++;
7215     }
7216
7217     // Analyse element topology after replacement
7218
7219     bool isOk = true;
7220     int nbUniqueNodes = nodeSet.size();
7221     //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7222     if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7223       // Polygons and Polyhedral volumes
7224       if (elem->IsPoly()) {
7225
7226         if (elem->GetType() == SMDSAbs_Face) {
7227           // Polygon
7228           vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7229           int inode = 0;
7230           for (; inode < nbNodes; inode++) {
7231             face_nodes[inode] = curNodes[inode];
7232           }
7233
7234           vector<const SMDS_MeshNode *> polygons_nodes;
7235           vector<int> quantities;
7236           int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7237           if (nbNew > 0) {
7238             inode = 0;
7239             for (int iface = 0; iface < nbNew; iface++) {
7240               int nbNodes = quantities[iface];
7241               vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7242               for (int ii = 0; ii < nbNodes; ii++, inode++) {
7243                 poly_nodes[ii] = polygons_nodes[inode];
7244               }
7245               SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7246               myLastCreatedElems.Append(newElem);
7247               if (aShapeId)
7248                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7249             }
7250
7251             MESSAGE("ChangeElementNodes MergeNodes Polygon");
7252             //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7253             vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7254             int quid =0;
7255             if (nbNew > 0) quid = nbNew - 1;
7256             vector<int> newquant(quantities.begin()+quid, quantities.end());
7257             const SMDS_MeshElement* newElem = 0;
7258             newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7259             myLastCreatedElems.Append(newElem);
7260             if ( aShapeId && newElem )
7261               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7262             rmElemIds.push_back(elem->GetID());
7263           }
7264           else {
7265             rmElemIds.push_back(elem->GetID());
7266           }
7267
7268         }
7269         else if (elem->GetType() == SMDSAbs_Volume) {
7270           // Polyhedral volume
7271           if (nbUniqueNodes < 4) {
7272             rmElemIds.push_back(elem->GetID());
7273           }
7274           else {
7275             // each face has to be analyzed in order to check volume validity
7276             const SMDS_VtkVolume* aPolyedre =
7277               dynamic_cast<const SMDS_VtkVolume*>( elem );
7278             if (aPolyedre) {
7279               int nbFaces = aPolyedre->NbFaces();
7280
7281               vector<const SMDS_MeshNode *> poly_nodes;
7282               vector<int> quantities;
7283
7284               for (int iface = 1; iface <= nbFaces; iface++) {
7285                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7286                 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7287
7288                 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7289                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7290                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7291                   if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7292                     faceNode = (*nnIt).second;
7293                   }
7294                   faceNodes[inode - 1] = faceNode;
7295                 }
7296
7297                 SimplifyFace(faceNodes, poly_nodes, quantities);
7298               }
7299
7300               if (quantities.size() > 3) {
7301                 // to be done: remove coincident faces
7302               }
7303
7304               if (quantities.size() > 3)
7305                 {
7306                   MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7307                   //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7308                   const SMDS_MeshElement* newElem = 0;
7309                   newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7310                   myLastCreatedElems.Append(newElem);
7311                   if ( aShapeId && newElem )
7312                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
7313                   rmElemIds.push_back(elem->GetID());
7314                 }
7315             }
7316             else {
7317               rmElemIds.push_back(elem->GetID());
7318             }
7319           }
7320         }
7321         else {
7322         }
7323
7324         continue;
7325       } // poly element
7326
7327       // Regular elements
7328       // TODO not all the possible cases are solved. Find something more generic?
7329       switch ( nbNodes ) {
7330       case 2: ///////////////////////////////////// EDGE
7331         isOk = false; break;
7332       case 3: ///////////////////////////////////// TRIANGLE
7333         isOk = false; break;
7334       case 4:
7335         if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7336           isOk = false;
7337         else { //////////////////////////////////// QUADRANGLE
7338           if ( nbUniqueNodes < 3 )
7339             isOk = false;
7340           else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7341             isOk = false; // opposite nodes stick
7342           //MESSAGE("isOk " << isOk);
7343         }
7344         break;
7345       case 6: ///////////////////////////////////// PENTAHEDRON
7346         if ( nbUniqueNodes == 4 ) {
7347           // ---------------------------------> tetrahedron
7348           if (nbRepl == 3 &&
7349               iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7350             // all top nodes stick: reverse a bottom
7351             uniqueNodes[ 0 ] = curNodes [ 1 ];
7352             uniqueNodes[ 1 ] = curNodes [ 0 ];
7353           }
7354           else if (nbRepl == 3 &&
7355                    iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7356             // all bottom nodes stick: set a top before
7357             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7358             uniqueNodes[ 0 ] = curNodes [ 3 ];
7359             uniqueNodes[ 1 ] = curNodes [ 4 ];
7360             uniqueNodes[ 2 ] = curNodes [ 5 ];
7361           }
7362           else if (nbRepl == 4 &&
7363                    iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7364             // a lateral face turns into a line: reverse a bottom
7365             uniqueNodes[ 0 ] = curNodes [ 1 ];
7366             uniqueNodes[ 1 ] = curNodes [ 0 ];
7367           }
7368           else
7369             isOk = false;
7370         }
7371         else if ( nbUniqueNodes == 5 ) {
7372           // PENTAHEDRON --------------------> 2 tetrahedrons
7373           if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7374             // a bottom node sticks with a linked top one
7375             // 1.
7376             SMDS_MeshElement* newElem =
7377               aMesh->AddVolume(curNodes[ 3 ],
7378                                curNodes[ 4 ],
7379                                curNodes[ 5 ],
7380                                curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7381             myLastCreatedElems.Append(newElem);
7382             if ( aShapeId )
7383               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7384             // 2. : reverse a bottom
7385             uniqueNodes[ 0 ] = curNodes [ 1 ];
7386             uniqueNodes[ 1 ] = curNodes [ 0 ];
7387             nbUniqueNodes = 4;
7388           }
7389           else
7390             isOk = false;
7391         }
7392         else
7393           isOk = false;
7394         break;
7395       case 8: {
7396         if(elem->IsQuadratic()) { // Quadratic quadrangle
7397           //   1    5    2
7398           //    +---+---+
7399           //    |       |
7400           //    |       |
7401           //   4+       +6
7402           //    |       |
7403           //    |       |
7404           //    +---+---+
7405           //   0    7    3
7406           isOk = false;
7407           if(nbRepl==2) {
7408             MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7409           }
7410           if(nbRepl==3) {
7411             MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2]);
7412             nbUniqueNodes = 6;
7413             if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7414               uniqueNodes[0] = curNodes[0];
7415               uniqueNodes[1] = curNodes[2];
7416               uniqueNodes[2] = curNodes[3];
7417               uniqueNodes[3] = curNodes[5];
7418               uniqueNodes[4] = curNodes[6];
7419               uniqueNodes[5] = curNodes[7];
7420               isOk = true;
7421             }
7422             if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7423               uniqueNodes[0] = curNodes[0];
7424               uniqueNodes[1] = curNodes[1];
7425               uniqueNodes[2] = curNodes[2];
7426               uniqueNodes[3] = curNodes[4];
7427               uniqueNodes[4] = curNodes[5];
7428               uniqueNodes[5] = curNodes[6];
7429               isOk = true;
7430             }
7431             if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7432               uniqueNodes[0] = curNodes[1];
7433               uniqueNodes[1] = curNodes[2];
7434               uniqueNodes[2] = curNodes[3];
7435               uniqueNodes[3] = curNodes[5];
7436               uniqueNodes[4] = curNodes[6];
7437               uniqueNodes[5] = curNodes[0];
7438               isOk = true;
7439             }
7440             if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7441               uniqueNodes[0] = curNodes[0];
7442               uniqueNodes[1] = curNodes[1];
7443               uniqueNodes[2] = curNodes[3];
7444               uniqueNodes[3] = curNodes[4];
7445               uniqueNodes[4] = curNodes[6];
7446               uniqueNodes[5] = curNodes[7];
7447               isOk = true;
7448             }
7449             if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7450               uniqueNodes[0] = curNodes[0];
7451               uniqueNodes[1] = curNodes[2];
7452               uniqueNodes[2] = curNodes[3];
7453               uniqueNodes[3] = curNodes[1];
7454               uniqueNodes[4] = curNodes[6];
7455               uniqueNodes[5] = curNodes[7];
7456               isOk = true;
7457             }
7458             if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7459               uniqueNodes[0] = curNodes[0];
7460               uniqueNodes[1] = curNodes[1];
7461               uniqueNodes[2] = curNodes[2];
7462               uniqueNodes[3] = curNodes[4];
7463               uniqueNodes[4] = curNodes[5];
7464               uniqueNodes[5] = curNodes[7];
7465               isOk = true;
7466             }
7467             if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7468               uniqueNodes[0] = curNodes[0];
7469               uniqueNodes[1] = curNodes[1];
7470               uniqueNodes[2] = curNodes[3];
7471               uniqueNodes[3] = curNodes[4];
7472               uniqueNodes[4] = curNodes[2];
7473               uniqueNodes[5] = curNodes[7];
7474               isOk = true;
7475             }
7476             if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7477               uniqueNodes[0] = curNodes[0];
7478               uniqueNodes[1] = curNodes[1];
7479               uniqueNodes[2] = curNodes[2];
7480               uniqueNodes[3] = curNodes[4];
7481               uniqueNodes[4] = curNodes[5];
7482               uniqueNodes[5] = curNodes[3];
7483               isOk = true;
7484             }
7485           }
7486           if(nbRepl==4) {
7487             MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3]);
7488           }
7489           if(nbRepl==5) {
7490             MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7491           }
7492           break;
7493         }
7494         //////////////////////////////////// HEXAHEDRON
7495         isOk = false;
7496         SMDS_VolumeTool hexa (elem);
7497         hexa.SetExternalNormal();
7498         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7499           //////////////////////// HEX ---> 1 tetrahedron
7500           for ( int iFace = 0; iFace < 6; iFace++ ) {
7501             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7502             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7503                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7504                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7505               // one face turns into a point ...
7506               int iOppFace = hexa.GetOppFaceIndex( iFace );
7507               ind = hexa.GetFaceNodesIndices( iOppFace );
7508               int nbStick = 0;
7509               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7510                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7511                   nbStick++;
7512               }
7513               if ( nbStick == 1 ) {
7514                 // ... and the opposite one - into a triangle.
7515                 // set a top node
7516                 ind = hexa.GetFaceNodesIndices( iFace );
7517                 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7518                 isOk = true;
7519               }
7520               break;
7521             }
7522           }
7523         }
7524         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7525           //////////////////////// HEX ---> 1 prism
7526           int nbTria = 0, iTria[3];
7527           const int *ind; // indices of face nodes
7528           // look for triangular faces
7529           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7530             ind = hexa.GetFaceNodesIndices( iFace );
7531             TIDSortedNodeSet faceNodes;
7532             for ( iCur = 0; iCur < 4; iCur++ )
7533               faceNodes.insert( curNodes[ind[iCur]] );
7534             if ( faceNodes.size() == 3 )
7535               iTria[ nbTria++ ] = iFace;
7536           }
7537           // check if triangles are opposite
7538           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7539           {
7540             isOk = true;
7541             // set nodes of the bottom triangle
7542             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7543             vector<int> indB;
7544             for ( iCur = 0; iCur < 4; iCur++ )
7545               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7546                 indB.push_back( ind[iCur] );
7547             if ( !hexa.IsForward() )
7548               std::swap( indB[0], indB[2] );
7549             for ( iCur = 0; iCur < 3; iCur++ )
7550               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7551             // set nodes of the top triangle
7552             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7553             for ( iCur = 0; iCur < 3; ++iCur )
7554               for ( int j = 0; j < 4; ++j )
7555                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7556                 {
7557                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7558                   break;
7559                 }
7560           }
7561           break;
7562         }
7563         else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7564           //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7565           for ( int iFace = 0; iFace < 6; iFace++ ) {
7566             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7567             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7568                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7569                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7570               // one face turns into a point ...
7571               int iOppFace = hexa.GetOppFaceIndex( iFace );
7572               ind = hexa.GetFaceNodesIndices( iOppFace );
7573               int nbStick = 0;
7574               iUnique = 2;  // reverse a tetrahedron 1 bottom
7575               for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7576                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7577                   nbStick++;
7578                 else if ( iUnique >= 0 )
7579                   uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7580               }
7581               if ( nbStick == 0 ) {
7582                 // ... and the opposite one is a quadrangle
7583                 // set a top node
7584                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7585                 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7586                 nbUniqueNodes = 4;
7587                 // tetrahedron 2
7588                 SMDS_MeshElement* newElem =
7589                   aMesh->AddVolume(curNodes[ind[ 0 ]],
7590                                    curNodes[ind[ 3 ]],
7591                                    curNodes[ind[ 2 ]],
7592                                    curNodes[indTop[ 0 ]]);
7593                 myLastCreatedElems.Append(newElem);
7594                 if ( aShapeId )
7595                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7596                 isOk = true;
7597               }
7598               break;
7599             }
7600           }
7601         }
7602         else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7603           ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7604           // find indices of quad and tri faces
7605           int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7606           for ( iFace = 0; iFace < 6; iFace++ ) {
7607             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7608             nodeSet.clear();
7609             for ( iCur = 0; iCur < 4; iCur++ )
7610               nodeSet.insert( curNodes[ind[ iCur ]] );
7611             nbUniqueNodes = nodeSet.size();
7612             if ( nbUniqueNodes == 3 )
7613               iTriFace[ nbTri++ ] = iFace;
7614             else if ( nbUniqueNodes == 4 )
7615               iQuadFace[ nbQuad++ ] = iFace;
7616           }
7617           if (nbQuad == 2 && nbTri == 4 &&
7618               hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7619             // 2 opposite quadrangles stuck with a diagonal;
7620             // sample groups of merged indices: (0-4)(2-6)
7621             // --------------------------------------------> 2 tetrahedrons
7622             const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7623             const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7624             int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7625             if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7626                 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7627               // stuck with 0-2 diagonal
7628               i0  = ind1[ 3 ];
7629               i1d = ind1[ 0 ];
7630               i2  = ind1[ 1 ];
7631               i3d = ind1[ 2 ];
7632               i0t = ind2[ 1 ];
7633               i2t = ind2[ 3 ];
7634             }
7635             else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7636                      curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7637               // stuck with 1-3 diagonal
7638               i0  = ind1[ 0 ];
7639               i1d = ind1[ 1 ];
7640               i2  = ind1[ 2 ];
7641               i3d = ind1[ 3 ];
7642               i0t = ind2[ 0 ];
7643               i2t = ind2[ 1 ];
7644             }
7645             else {
7646               ASSERT(0);
7647             }
7648             // tetrahedron 1
7649             uniqueNodes[ 0 ] = curNodes [ i0 ];
7650             uniqueNodes[ 1 ] = curNodes [ i1d ];
7651             uniqueNodes[ 2 ] = curNodes [ i3d ];
7652             uniqueNodes[ 3 ] = curNodes [ i0t ];
7653             nbUniqueNodes = 4;
7654             // tetrahedron 2
7655             SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7656                                                          curNodes[ i2 ],
7657                                                          curNodes[ i3d ],
7658                                                          curNodes[ i2t ]);
7659             myLastCreatedElems.Append(newElem);
7660             if ( aShapeId )
7661               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7662             isOk = true;
7663           }
7664           else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7665                    ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7666             // --------------------------------------------> prism
7667             // find 2 opposite triangles
7668             nbUniqueNodes = 6;
7669             for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7670               if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7671                 // find indices of kept and replaced nodes
7672                 // and fill unique nodes of 2 opposite triangles
7673                 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7674                 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7675                 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7676                 // fill unique nodes
7677                 iUnique = 0;
7678                 isOk = true;
7679                 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7680                   const SMDS_MeshNode* n     = curNodes[ind1[ iCur ]];
7681                   const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7682                   if ( n == nInit ) {
7683                     // iCur of a linked node of the opposite face (make normals co-directed):
7684                     int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7685                     // check that correspondent corners of triangles are linked
7686                     if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7687                       isOk = false;
7688                     else {
7689                       uniqueNodes[ iUnique ] = n;
7690                       uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7691                       iUnique++;
7692                     }
7693                   }
7694                 }
7695                 break;
7696               }
7697             }
7698           }
7699         } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7700         else
7701         {
7702           MESSAGE("MergeNodes() removes hexahedron "<< elem);
7703         }
7704         break;
7705       } // HEXAHEDRON
7706
7707       default:
7708         isOk = false;
7709       } // switch ( nbNodes )
7710
7711     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7712
7713     if ( isOk ) { // the elem remains valid after sticking nodes
7714       if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
7715       {
7716         // Change nodes of polyedre
7717         const SMDS_VtkVolume* aPolyedre =
7718           dynamic_cast<const SMDS_VtkVolume*>( elem );
7719         if (aPolyedre) {
7720           int nbFaces = aPolyedre->NbFaces();
7721
7722           vector<const SMDS_MeshNode *> poly_nodes;
7723           vector<int> quantities (nbFaces);
7724
7725           for (int iface = 1; iface <= nbFaces; iface++) {
7726             int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7727             quantities[iface - 1] = nbFaceNodes;
7728
7729             for (inode = 1; inode <= nbFaceNodes; inode++) {
7730               const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7731
7732               TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7733               if (nnIt != nodeNodeMap.end()) { // curNode sticks
7734                 curNode = (*nnIt).second;
7735               }
7736               poly_nodes.push_back(curNode);
7737             }
7738           }
7739           aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7740         }
7741       }
7742       else // replace non-polyhedron elements
7743       {
7744         const SMDSAbs_ElementType etyp = elem->GetType();
7745         const int elemId               = elem->GetID();
7746         const bool isPoly              = (elem->GetEntityType() == SMDSEntity_Polygon);
7747         uniqueNodes.resize(nbUniqueNodes);
7748
7749         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7750
7751         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7752         SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
7753         if ( sm && newElem )
7754           sm->AddElement( newElem );
7755         if ( elem != newElem )
7756           ReplaceElemInGroups( elem, newElem, aMesh );
7757       }
7758     }
7759     else {
7760       // Remove invalid regular element or invalid polygon
7761       rmElemIds.push_back( elem->GetID() );
7762     }
7763
7764   } // loop on elements
7765
7766   // Remove bad elements, then equal nodes (order important)
7767
7768   Remove( rmElemIds, false );
7769   Remove( rmNodeIds, true );
7770
7771 }
7772
7773
7774 // ========================================================
7775 // class   : SortableElement
7776 // purpose : allow sorting elements basing on their nodes
7777 // ========================================================
7778 class SortableElement : public set <const SMDS_MeshElement*>
7779 {
7780 public:
7781
7782   SortableElement( const SMDS_MeshElement* theElem )
7783   {
7784     myElem = theElem;
7785     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7786     while ( nodeIt->more() )
7787       this->insert( nodeIt->next() );
7788   }
7789
7790   const SMDS_MeshElement* Get() const
7791   { return myElem; }
7792
7793   void Set(const SMDS_MeshElement* e) const
7794   { myElem = e; }
7795
7796
7797 private:
7798   mutable const SMDS_MeshElement* myElem;
7799 };
7800
7801 //=======================================================================
7802 //function : FindEqualElements
7803 //purpose  : Return list of group of elements built on the same nodes.
7804 //           Search among theElements or in the whole mesh if theElements is empty
7805 //=======================================================================
7806
7807 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
7808                                          TListOfListOfElementsID & theGroupsOfElementsID)
7809 {
7810   myLastCreatedElems.Clear();
7811   myLastCreatedNodes.Clear();
7812
7813   typedef map< SortableElement, int > TMapOfNodeSet;
7814   typedef list<int> TGroupOfElems;
7815
7816   if ( theElements.empty() )
7817   { // get all elements in the mesh
7818     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7819     while ( eIt->more() )
7820       theElements.insert( theElements.end(), eIt->next());
7821   }
7822
7823   vector< TGroupOfElems > arrayOfGroups;
7824   TGroupOfElems groupOfElems;
7825   TMapOfNodeSet mapOfNodeSet;
7826
7827   TIDSortedElemSet::iterator elemIt = theElements.begin();
7828   for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
7829     const SMDS_MeshElement* curElem = *elemIt;
7830     SortableElement SE(curElem);
7831     int ind = -1;
7832     // check uniqueness
7833     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7834     if( !(pp.second) ) {
7835       TMapOfNodeSet::iterator& itSE = pp.first;
7836       ind = (*itSE).second;
7837       arrayOfGroups[ind].push_back(curElem->GetID());
7838     }
7839     else {
7840       groupOfElems.clear();
7841       groupOfElems.push_back(curElem->GetID());
7842       arrayOfGroups.push_back(groupOfElems);
7843       i++;
7844     }
7845   }
7846
7847   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7848   for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7849     groupOfElems = *groupIt;
7850     if ( groupOfElems.size() > 1 ) {
7851       groupOfElems.sort();
7852       theGroupsOfElementsID.push_back(groupOfElems);
7853     }
7854   }
7855 }
7856
7857 //=======================================================================
7858 //function : MergeElements
7859 //purpose  : In each given group, substitute all elements by the first one.
7860 //=======================================================================
7861
7862 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7863 {
7864   myLastCreatedElems.Clear();
7865   myLastCreatedNodes.Clear();
7866
7867   typedef list<int> TListOfIDs;
7868   TListOfIDs rmElemIds; // IDs of elems to remove
7869
7870   SMESHDS_Mesh* aMesh = GetMeshDS();
7871
7872   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7873   while ( groupsIt != theGroupsOfElementsID.end() ) {
7874     TListOfIDs& aGroupOfElemID = *groupsIt;
7875     aGroupOfElemID.sort();
7876     int elemIDToKeep = aGroupOfElemID.front();
7877     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7878     aGroupOfElemID.pop_front();
7879     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7880     while ( idIt != aGroupOfElemID.end() ) {
7881       int elemIDToRemove = *idIt;
7882       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7883       // add the kept element in groups of removed one (PAL15188)
7884       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7885       rmElemIds.push_back( elemIDToRemove );
7886       ++idIt;
7887     }
7888     ++groupsIt;
7889   }
7890
7891   Remove( rmElemIds, false );
7892 }
7893
7894 //=======================================================================
7895 //function : MergeEqualElements
7896 //purpose  : Remove all but one of elements built on the same nodes.
7897 //=======================================================================
7898
7899 void SMESH_MeshEditor::MergeEqualElements()
7900 {
7901   TIDSortedElemSet aMeshElements; /* empty input ==
7902                                      to merge equal elements in the whole mesh */
7903   TListOfListOfElementsID aGroupsOfElementsID;
7904   FindEqualElements(aMeshElements, aGroupsOfElementsID);
7905   MergeElements(aGroupsOfElementsID);
7906 }
7907
7908 //=======================================================================
7909 //function : findAdjacentFace
7910 //purpose  :
7911 //=======================================================================
7912
7913 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7914                                                 const SMDS_MeshNode* n2,
7915                                                 const SMDS_MeshElement* elem)
7916 {
7917   TIDSortedElemSet elemSet, avoidSet;
7918   if ( elem )
7919     avoidSet.insert ( elem );
7920   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7921 }
7922
7923 //=======================================================================
7924 //function : FindFreeBorder
7925 //purpose  :
7926 //=======================================================================
7927
7928 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7929
7930 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7931                                        const SMDS_MeshNode*             theSecondNode,
7932                                        const SMDS_MeshNode*             theLastNode,
7933                                        list< const SMDS_MeshNode* > &   theNodes,
7934                                        list< const SMDS_MeshElement* >& theFaces)
7935 {
7936   if ( !theFirstNode || !theSecondNode )
7937     return false;
7938   // find border face between theFirstNode and theSecondNode
7939   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7940   if ( !curElem )
7941     return false;
7942
7943   theFaces.push_back( curElem );
7944   theNodes.push_back( theFirstNode );
7945   theNodes.push_back( theSecondNode );
7946
7947   //vector<const SMDS_MeshNode*> nodes;
7948   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7949   TIDSortedElemSet foundElems;
7950   bool needTheLast = ( theLastNode != 0 );
7951
7952   while ( nStart != theLastNode ) {
7953     if ( nStart == theFirstNode )
7954       return !needTheLast;
7955
7956     // find all free border faces sharing form nStart
7957
7958     list< const SMDS_MeshElement* > curElemList;
7959     list< const SMDS_MeshNode* > nStartList;
7960     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7961     while ( invElemIt->more() ) {
7962       const SMDS_MeshElement* e = invElemIt->next();
7963       if ( e == curElem || foundElems.insert( e ).second ) {
7964         // get nodes
7965         int iNode = 0, nbNodes = e->NbNodes();
7966         //const SMDS_MeshNode* nodes[nbNodes+1];
7967         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7968
7969         if(e->IsQuadratic()) {
7970           const SMDS_VtkFace* F =
7971             dynamic_cast<const SMDS_VtkFace*>(e);
7972           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7973           // use special nodes iterator
7974           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7975           while( anIter->more() ) {
7976             nodes[ iNode++ ] = cast2Node(anIter->next());
7977           }
7978         }
7979         else {
7980           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7981           while ( nIt->more() )
7982             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7983         }
7984         nodes[ iNode ] = nodes[ 0 ];
7985         // check 2 links
7986         for ( iNode = 0; iNode < nbNodes; iNode++ )
7987           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7988                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7989               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7990           {
7991             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7992             curElemList.push_back( e );
7993           }
7994       }
7995     }
7996     // analyse the found
7997
7998     int nbNewBorders = curElemList.size();
7999     if ( nbNewBorders == 0 ) {
8000       // no free border furthermore
8001       return !needTheLast;
8002     }
8003     else if ( nbNewBorders == 1 ) {
8004       // one more element found
8005       nIgnore = nStart;
8006       nStart = nStartList.front();
8007       curElem = curElemList.front();
8008       theFaces.push_back( curElem );
8009       theNodes.push_back( nStart );
8010     }
8011     else {
8012       // several continuations found
8013       list< const SMDS_MeshElement* >::iterator curElemIt;
8014       list< const SMDS_MeshNode* >::iterator nStartIt;
8015       // check if one of them reached the last node
8016       if ( needTheLast ) {
8017         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8018              curElemIt!= curElemList.end();
8019              curElemIt++, nStartIt++ )
8020           if ( *nStartIt == theLastNode ) {
8021             theFaces.push_back( *curElemIt );
8022             theNodes.push_back( *nStartIt );
8023             return true;
8024           }
8025       }
8026       // find the best free border by the continuations
8027       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8028       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8029       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8030            curElemIt!= curElemList.end();
8031            curElemIt++, nStartIt++ )
8032       {
8033         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8034         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8035         // find one more free border
8036         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8037           cNL->clear();
8038           cFL->clear();
8039         }
8040         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8041           // choice: clear a worse one
8042           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8043           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8044           contNodes[ iWorse ].clear();
8045           contFaces[ iWorse ].clear();
8046         }
8047       }
8048       if ( contNodes[0].empty() && contNodes[1].empty() )
8049         return false;
8050
8051       // append the best free border
8052       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8053       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8054       theNodes.pop_back(); // remove nIgnore
8055       theNodes.pop_back(); // remove nStart
8056       theFaces.pop_back(); // remove curElem
8057       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8058       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8059       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8060       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8061       return true;
8062
8063     } // several continuations found
8064   } // while ( nStart != theLastNode )
8065
8066   return true;
8067 }
8068
8069 //=======================================================================
8070 //function : CheckFreeBorderNodes
8071 //purpose  : Return true if the tree nodes are on a free border
8072 //=======================================================================
8073
8074 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8075                                             const SMDS_MeshNode* theNode2,
8076                                             const SMDS_MeshNode* theNode3)
8077 {
8078   list< const SMDS_MeshNode* > nodes;
8079   list< const SMDS_MeshElement* > faces;
8080   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8081 }
8082
8083 //=======================================================================
8084 //function : SewFreeBorder
8085 //purpose  :
8086 //=======================================================================
8087
8088 SMESH_MeshEditor::Sew_Error
8089 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8090                                  const SMDS_MeshNode* theBordSecondNode,
8091                                  const SMDS_MeshNode* theBordLastNode,
8092                                  const SMDS_MeshNode* theSideFirstNode,
8093                                  const SMDS_MeshNode* theSideSecondNode,
8094                                  const SMDS_MeshNode* theSideThirdNode,
8095                                  const bool           theSideIsFreeBorder,
8096                                  const bool           toCreatePolygons,
8097                                  const bool           toCreatePolyedrs)
8098 {
8099   myLastCreatedElems.Clear();
8100   myLastCreatedNodes.Clear();
8101
8102   MESSAGE("::SewFreeBorder()");
8103   Sew_Error aResult = SEW_OK;
8104
8105   // ====================================
8106   //    find side nodes and elements
8107   // ====================================
8108
8109   list< const SMDS_MeshNode* > nSide[ 2 ];
8110   list< const SMDS_MeshElement* > eSide[ 2 ];
8111   list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8112   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8113
8114   // Free border 1
8115   // --------------
8116   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8117                       nSide[0], eSide[0])) {
8118     MESSAGE(" Free Border 1 not found " );
8119     aResult = SEW_BORDER1_NOT_FOUND;
8120   }
8121   if (theSideIsFreeBorder) {
8122     // Free border 2
8123     // --------------
8124     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8125                         nSide[1], eSide[1])) {
8126       MESSAGE(" Free Border 2 not found " );
8127       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8128     }
8129   }
8130   if ( aResult != SEW_OK )
8131     return aResult;
8132
8133   if (!theSideIsFreeBorder) {
8134     // Side 2
8135     // --------------
8136
8137     // -------------------------------------------------------------------------
8138     // Algo:
8139     // 1. If nodes to merge are not coincident, move nodes of the free border
8140     //    from the coord sys defined by the direction from the first to last
8141     //    nodes of the border to the correspondent sys of the side 2
8142     // 2. On the side 2, find the links most co-directed with the correspondent
8143     //    links of the free border
8144     // -------------------------------------------------------------------------
8145
8146     // 1. Since sewing may break if there are volumes to split on the side 2,
8147     //    we wont move nodes but just compute new coordinates for them
8148     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8149     TNodeXYZMap nBordXYZ;
8150     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8151     list< const SMDS_MeshNode* >::iterator nBordIt;
8152
8153     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8154     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8155     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8156     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8157     double tol2 = 1.e-8;
8158     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8159     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8160       // Need node movement.
8161
8162       // find X and Z axes to create trsf
8163       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8164       gp_Vec X = Zs ^ Zb;
8165       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8166         // Zb || Zs
8167         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8168
8169       // coord systems
8170       gp_Ax3 toBordAx( Pb1, Zb, X );
8171       gp_Ax3 fromSideAx( Ps1, Zs, X );
8172       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8173       // set trsf
8174       gp_Trsf toBordSys, fromSide2Sys;
8175       toBordSys.SetTransformation( toBordAx );
8176       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8177       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8178
8179       // move
8180       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8181         const SMDS_MeshNode* n = *nBordIt;
8182         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8183         toBordSys.Transforms( xyz );
8184         fromSide2Sys.Transforms( xyz );
8185         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8186       }
8187     }
8188     else {
8189       // just insert nodes XYZ in the nBordXYZ map
8190       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8191         const SMDS_MeshNode* n = *nBordIt;
8192         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8193       }
8194     }
8195
8196     // 2. On the side 2, find the links most co-directed with the correspondent
8197     //    links of the free border
8198
8199     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8200     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8201     sideNodes.push_back( theSideFirstNode );
8202
8203     bool hasVolumes = false;
8204     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8205     set<long> foundSideLinkIDs, checkedLinkIDs;
8206     SMDS_VolumeTool volume;
8207     //const SMDS_MeshNode* faceNodes[ 4 ];
8208
8209     const SMDS_MeshNode*    sideNode;
8210     const SMDS_MeshElement* sideElem;
8211     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8212     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8213     nBordIt = bordNodes.begin();
8214     nBordIt++;
8215     // border node position and border link direction to compare with
8216     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8217     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8218     // choose next side node by link direction or by closeness to
8219     // the current border node:
8220     bool searchByDir = ( *nBordIt != theBordLastNode );
8221     do {
8222       // find the next node on the Side 2
8223       sideNode = 0;
8224       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8225       long linkID;
8226       checkedLinkIDs.clear();
8227       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8228
8229       // loop on inverse elements of current node (prevSideNode) on the Side 2
8230       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8231       while ( invElemIt->more() )
8232       {
8233         const SMDS_MeshElement* elem = invElemIt->next();
8234         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8235         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8236         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8237         bool isVolume = volume.Set( elem );
8238         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8239         if ( isVolume ) // --volume
8240           hasVolumes = true;
8241         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8242           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8243           if(elem->IsQuadratic()) {
8244             const SMDS_VtkFace* F =
8245               dynamic_cast<const SMDS_VtkFace*>(elem);
8246             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8247             // use special nodes iterator
8248             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8249             while( anIter->more() ) {
8250               nodes[ iNode ] = cast2Node(anIter->next());
8251               if ( nodes[ iNode++ ] == prevSideNode )
8252                 iPrevNode = iNode - 1;
8253             }
8254           }
8255           else {
8256             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8257             while ( nIt->more() ) {
8258               nodes[ iNode ] = cast2Node( nIt->next() );
8259               if ( nodes[ iNode++ ] == prevSideNode )
8260                 iPrevNode = iNode - 1;
8261             }
8262           }
8263           // there are 2 links to check
8264           nbNodes = 2;
8265         }
8266         else // --edge
8267           continue;
8268         // loop on links, to be precise, on the second node of links
8269         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8270           const SMDS_MeshNode* n = nodes[ iNode ];
8271           if ( isVolume ) {
8272             if ( !volume.IsLinked( n, prevSideNode ))
8273               continue;
8274           }
8275           else {
8276             if ( iNode ) // a node before prevSideNode
8277               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8278             else         // a node after prevSideNode
8279               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8280           }
8281           // check if this link was already used
8282           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8283           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8284           if (!isJustChecked &&
8285               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8286           {
8287             // test a link geometrically
8288             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8289             bool linkIsBetter = false;
8290             double dot = 0.0, dist = 0.0;
8291             if ( searchByDir ) { // choose most co-directed link
8292               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8293               linkIsBetter = ( dot > maxDot );
8294             }
8295             else { // choose link with the node closest to bordPos
8296               dist = ( nextXYZ - bordPos ).SquareModulus();
8297               linkIsBetter = ( dist < minDist );
8298             }
8299             if ( linkIsBetter ) {
8300               maxDot = dot;
8301               minDist = dist;
8302               linkID = iLink;
8303               sideNode = n;
8304               sideElem = elem;
8305             }
8306           }
8307         }
8308       } // loop on inverse elements of prevSideNode
8309
8310       if ( !sideNode ) {
8311         MESSAGE(" Cant find path by links of the Side 2 ");
8312         return SEW_BAD_SIDE_NODES;
8313       }
8314       sideNodes.push_back( sideNode );
8315       sideElems.push_back( sideElem );
8316       foundSideLinkIDs.insert ( linkID );
8317       prevSideNode = sideNode;
8318
8319       if ( *nBordIt == theBordLastNode )
8320         searchByDir = false;
8321       else {
8322         // find the next border link to compare with
8323         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8324         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8325         // move to next border node if sideNode is before forward border node (bordPos)
8326         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8327           prevBordNode = *nBordIt;
8328           nBordIt++;
8329           bordPos = nBordXYZ[ *nBordIt ];
8330           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8331           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8332         }
8333       }
8334     }
8335     while ( sideNode != theSideSecondNode );
8336
8337     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8338       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8339       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8340     }
8341   } // end nodes search on the side 2
8342
8343   // ============================
8344   // sew the border to the side 2
8345   // ============================
8346
8347   int nbNodes[]  = { nSide[0].size(), nSide[1].size() };
8348   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8349
8350   TListOfListOfNodes nodeGroupsToMerge;
8351   if ( nbNodes[0] == nbNodes[1] ||
8352        ( theSideIsFreeBorder && !theSideThirdNode)) {
8353
8354     // all nodes are to be merged
8355
8356     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8357          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8358          nIt[0]++, nIt[1]++ )
8359     {
8360       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8361       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8362       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8363     }
8364   }
8365   else {
8366
8367     // insert new nodes into the border and the side to get equal nb of segments
8368
8369     // get normalized parameters of nodes on the borders
8370     //double param[ 2 ][ maxNbNodes ];
8371     double* param[ 2 ];
8372     param[0] = new double [ maxNbNodes ];
8373     param[1] = new double [ maxNbNodes ];
8374     int iNode, iBord;
8375     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8376       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8377       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8378       const SMDS_MeshNode* nPrev = *nIt;
8379       double bordLength = 0;
8380       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8381         const SMDS_MeshNode* nCur = *nIt;
8382         gp_XYZ segment (nCur->X() - nPrev->X(),
8383                         nCur->Y() - nPrev->Y(),
8384                         nCur->Z() - nPrev->Z());
8385         double segmentLen = segment.Modulus();
8386         bordLength += segmentLen;
8387         param[ iBord ][ iNode ] = bordLength;
8388         nPrev = nCur;
8389       }
8390       // normalize within [0,1]
8391       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8392         param[ iBord ][ iNode ] /= bordLength;
8393       }
8394     }
8395
8396     // loop on border segments
8397     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8398     int i[ 2 ] = { 0, 0 };
8399     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8400     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8401
8402     TElemOfNodeListMap insertMap;
8403     TElemOfNodeListMap::iterator insertMapIt;
8404     // insertMap is
8405     // key:   elem to insert nodes into
8406     // value: 2 nodes to insert between + nodes to be inserted
8407     do {
8408       bool next[ 2 ] = { false, false };
8409
8410       // find min adjacent segment length after sewing
8411       double nextParam = 10., prevParam = 0;
8412       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8413         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8414           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8415         if ( i[ iBord ] > 0 )
8416           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8417       }
8418       double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8419       double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8420       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8421
8422       // choose to insert or to merge nodes
8423       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8424       if ( Abs( du ) <= minSegLen * 0.2 ) {
8425         // merge
8426         // ------
8427         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8428         const SMDS_MeshNode* n0 = *nIt[0];
8429         const SMDS_MeshNode* n1 = *nIt[1];
8430         nodeGroupsToMerge.back().push_back( n1 );
8431         nodeGroupsToMerge.back().push_back( n0 );
8432         // position of node of the border changes due to merge
8433         param[ 0 ][ i[0] ] += du;
8434         // move n1 for the sake of elem shape evaluation during insertion.
8435         // n1 will be removed by MergeNodes() anyway
8436         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8437         next[0] = next[1] = true;
8438       }
8439       else {
8440         // insert
8441         // ------
8442         int intoBord = ( du < 0 ) ? 0 : 1;
8443         const SMDS_MeshElement* elem = *eIt[ intoBord ];
8444         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8445         const SMDS_MeshNode*    n2   = *nIt[ intoBord ];
8446         const SMDS_MeshNode*    nIns = *nIt[ 1 - intoBord ];
8447         if ( intoBord == 1 ) {
8448           // move node of the border to be on a link of elem of the side
8449           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8450           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8451           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8452           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8453           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8454         }
8455         insertMapIt = insertMap.find( elem );
8456         bool notFound = ( insertMapIt == insertMap.end() );
8457         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8458         if ( otherLink ) {
8459           // insert into another link of the same element:
8460           // 1. perform insertion into the other link of the elem
8461           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8462           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8463           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8464           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8465           // 2. perform insertion into the link of adjacent faces
8466           while (true) {
8467             const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8468             if ( adjElem )
8469               InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8470             else
8471               break;
8472           }
8473           if (toCreatePolyedrs) {
8474             // perform insertion into the links of adjacent volumes
8475             UpdateVolumes(n12, n22, nodeList);
8476           }
8477           // 3. find an element appeared on n1 and n2 after the insertion
8478           insertMap.erase( elem );
8479           elem = findAdjacentFace( n1, n2, 0 );
8480         }
8481         if ( notFound || otherLink ) {
8482           // add element and nodes of the side into the insertMap
8483           insertMapIt = insertMap.insert
8484             ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8485           (*insertMapIt).second.push_back( n1 );
8486           (*insertMapIt).second.push_back( n2 );
8487         }
8488         // add node to be inserted into elem
8489         (*insertMapIt).second.push_back( nIns );
8490         next[ 1 - intoBord ] = true;
8491       }
8492
8493       // go to the next segment
8494       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8495         if ( next[ iBord ] ) {
8496           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8497             eIt[ iBord ]++;
8498           nPrev[ iBord ] = *nIt[ iBord ];
8499           nIt[ iBord ]++; i[ iBord ]++;
8500         }
8501       }
8502     }
8503     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8504
8505     // perform insertion of nodes into elements
8506
8507     for (insertMapIt = insertMap.begin();
8508          insertMapIt != insertMap.end();
8509          insertMapIt++ )
8510     {
8511       const SMDS_MeshElement* elem = (*insertMapIt).first;
8512       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8513       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8514       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8515
8516       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8517
8518       if ( !theSideIsFreeBorder ) {
8519         // look for and insert nodes into the faces adjacent to elem
8520         while (true) {
8521           const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8522           if ( adjElem )
8523             InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8524           else
8525             break;
8526         }
8527       }
8528       if (toCreatePolyedrs) {
8529         // perform insertion into the links of adjacent volumes
8530         UpdateVolumes(n1, n2, nodeList);
8531       }
8532     }
8533
8534     delete param[0];
8535     delete param[1];
8536   } // end: insert new nodes
8537
8538   MergeNodes ( nodeGroupsToMerge );
8539
8540   return aResult;
8541 }
8542
8543 //=======================================================================
8544 //function : InsertNodesIntoLink
8545 //purpose  : insert theNodesToInsert into theFace between theBetweenNode1
8546 //           and theBetweenNode2 and split theElement
8547 //=======================================================================
8548
8549 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theFace,
8550                                            const SMDS_MeshNode*        theBetweenNode1,
8551                                            const SMDS_MeshNode*        theBetweenNode2,
8552                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8553                                            const bool                  toCreatePoly)
8554 {
8555   if ( theFace->GetType() != SMDSAbs_Face ) return;
8556
8557   // find indices of 2 link nodes and of the rest nodes
8558   int iNode = 0, il1, il2, i3, i4;
8559   il1 = il2 = i3 = i4 = -1;
8560   //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8561   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8562
8563   if(theFace->IsQuadratic()) {
8564     const SMDS_VtkFace* F =
8565       dynamic_cast<const SMDS_VtkFace*>(theFace);
8566     if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8567     // use special nodes iterator
8568     SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8569     while( anIter->more() ) {
8570       const SMDS_MeshNode* n = cast2Node(anIter->next());
8571       if ( n == theBetweenNode1 )
8572         il1 = iNode;
8573       else if ( n == theBetweenNode2 )
8574         il2 = iNode;
8575       else if ( i3 < 0 )
8576         i3 = iNode;
8577       else
8578         i4 = iNode;
8579       nodes[ iNode++ ] = n;
8580     }
8581   }
8582   else {
8583     SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8584     while ( nodeIt->more() ) {
8585       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8586       if ( n == theBetweenNode1 )
8587         il1 = iNode;
8588       else if ( n == theBetweenNode2 )
8589         il2 = iNode;
8590       else if ( i3 < 0 )
8591         i3 = iNode;
8592       else
8593         i4 = iNode;
8594       nodes[ iNode++ ] = n;
8595     }
8596   }
8597   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8598     return ;
8599
8600   // arrange link nodes to go one after another regarding the face orientation
8601   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8602   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8603   if ( reverse ) {
8604     iNode = il1;
8605     il1 = il2;
8606     il2 = iNode;
8607     aNodesToInsert.reverse();
8608   }
8609   // check that not link nodes of a quadrangles are in good order
8610   int nbFaceNodes = theFace->NbNodes();
8611   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8612     iNode = i3;
8613     i3 = i4;
8614     i4 = iNode;
8615   }
8616
8617   if (toCreatePoly || theFace->IsPoly()) {
8618
8619     iNode = 0;
8620     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8621
8622     // add nodes of face up to first node of link
8623     bool isFLN = false;
8624
8625     if(theFace->IsQuadratic()) {
8626       const SMDS_VtkFace* F =
8627         dynamic_cast<const SMDS_VtkFace*>(theFace);
8628       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8629       // use special nodes iterator
8630       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8631       while( anIter->more()  && !isFLN ) {
8632         const SMDS_MeshNode* n = cast2Node(anIter->next());
8633         poly_nodes[iNode++] = n;
8634         if (n == nodes[il1]) {
8635           isFLN = true;
8636         }
8637       }
8638       // add nodes to insert
8639       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8640       for (; nIt != aNodesToInsert.end(); nIt++) {
8641         poly_nodes[iNode++] = *nIt;
8642       }
8643       // add nodes of face starting from last node of link
8644       while ( anIter->more() ) {
8645         poly_nodes[iNode++] = cast2Node(anIter->next());
8646       }
8647     }
8648     else {
8649       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8650       while ( nodeIt->more() && !isFLN ) {
8651         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8652         poly_nodes[iNode++] = n;
8653         if (n == nodes[il1]) {
8654           isFLN = true;
8655         }
8656       }
8657       // add nodes to insert
8658       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8659       for (; nIt != aNodesToInsert.end(); nIt++) {
8660         poly_nodes[iNode++] = *nIt;
8661       }
8662       // add nodes of face starting from last node of link
8663       while ( nodeIt->more() ) {
8664         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8665         poly_nodes[iNode++] = n;
8666       }
8667     }
8668
8669     // edit or replace the face
8670     SMESHDS_Mesh *aMesh = GetMeshDS();
8671
8672     if (theFace->IsPoly()) {
8673       aMesh->ChangePolygonNodes(theFace, poly_nodes);
8674     }
8675     else {
8676       int aShapeId = FindShape( theFace );
8677
8678       SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8679       myLastCreatedElems.Append(newElem);
8680       if ( aShapeId && newElem )
8681         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8682
8683       aMesh->RemoveElement(theFace);
8684     }
8685     return;
8686   }
8687
8688   SMESHDS_Mesh *aMesh = GetMeshDS();
8689   if( !theFace->IsQuadratic() ) {
8690
8691     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8692     int nbLinkNodes = 2 + aNodesToInsert.size();
8693     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8694     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8695     linkNodes[ 0 ] = nodes[ il1 ];
8696     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8697     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8698     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8699       linkNodes[ iNode++ ] = *nIt;
8700     }
8701     // decide how to split a quadrangle: compare possible variants
8702     // and choose which of splits to be a quadrangle
8703     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8704     if ( nbFaceNodes == 3 ) {
8705       iBestQuad = nbSplits;
8706       i4 = i3;
8707     }
8708     else if ( nbFaceNodes == 4 ) {
8709       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8710       double aBestRate = DBL_MAX;
8711       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8712         i1 = 0; i2 = 1;
8713         double aBadRate = 0;
8714         // evaluate elements quality
8715         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8716           if ( iSplit == iQuad ) {
8717             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8718                                    linkNodes[ i2++ ],
8719                                    nodes[ i3 ],
8720                                    nodes[ i4 ]);
8721             aBadRate += getBadRate( &quad, aCrit );
8722           }
8723           else {
8724             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8725                                    linkNodes[ i2++ ],
8726                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8727             aBadRate += getBadRate( &tria, aCrit );
8728           }
8729         }
8730         // choice
8731         if ( aBadRate < aBestRate ) {
8732           iBestQuad = iQuad;
8733           aBestRate = aBadRate;
8734         }
8735       }
8736     }
8737
8738     // create new elements
8739     int aShapeId = FindShape( theFace );
8740
8741     i1 = 0; i2 = 1;
8742     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8743       SMDS_MeshElement* newElem = 0;
8744       if ( iSplit == iBestQuad )
8745         newElem = aMesh->AddFace (linkNodes[ i1++ ],
8746                                   linkNodes[ i2++ ],
8747                                   nodes[ i3 ],
8748                                   nodes[ i4 ]);
8749       else
8750         newElem = aMesh->AddFace (linkNodes[ i1++ ],
8751                                   linkNodes[ i2++ ],
8752                                   nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8753       myLastCreatedElems.Append(newElem);
8754       if ( aShapeId && newElem )
8755         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8756     }
8757
8758     // change nodes of theFace
8759     const SMDS_MeshNode* newNodes[ 4 ];
8760     newNodes[ 0 ] = linkNodes[ i1 ];
8761     newNodes[ 1 ] = linkNodes[ i2 ];
8762     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8763     newNodes[ 3 ] = nodes[ i4 ];
8764     //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8765     const SMDS_MeshElement* newElem = 0;
8766     if (iSplit == iBestQuad)
8767       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8768     else
8769       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8770     myLastCreatedElems.Append(newElem);
8771     if ( aShapeId && newElem )
8772       aMesh->SetMeshElementOnShape( newElem, aShapeId );
8773 } // end if(!theFace->IsQuadratic())
8774   else { // theFace is quadratic
8775     // we have to split theFace on simple triangles and one simple quadrangle
8776     int tmp = il1/2;
8777     int nbshift = tmp*2;
8778     // shift nodes in nodes[] by nbshift
8779     int i,j;
8780     for(i=0; i<nbshift; i++) {
8781       const SMDS_MeshNode* n = nodes[0];
8782       for(j=0; j<nbFaceNodes-1; j++) {
8783         nodes[j] = nodes[j+1];
8784       }
8785       nodes[nbFaceNodes-1] = n;
8786     }
8787     il1 = il1 - nbshift;
8788     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8789     //   n0      n1     n2    n0      n1     n2
8790     //     +-----+-----+        +-----+-----+
8791     //      \         /         |           |
8792     //       \       /          |           |
8793     //      n5+     +n3       n7+           +n3
8794     //         \   /            |           |
8795     //          \ /             |           |
8796     //           +              +-----+-----+
8797     //           n4           n6      n5     n4
8798
8799     // create new elements
8800     int aShapeId = FindShape( theFace );
8801
8802     int n1,n2,n3;
8803     if(nbFaceNodes==6) { // quadratic triangle
8804       SMDS_MeshElement* newElem =
8805         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8806       myLastCreatedElems.Append(newElem);
8807       if ( aShapeId && newElem )
8808         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8809       if(theFace->IsMediumNode(nodes[il1])) {
8810         // create quadrangle
8811         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8812         myLastCreatedElems.Append(newElem);
8813         if ( aShapeId && newElem )
8814           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8815         n1 = 1;
8816         n2 = 2;
8817         n3 = 3;
8818       }
8819       else {
8820         // create quadrangle
8821         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8822         myLastCreatedElems.Append(newElem);
8823         if ( aShapeId && newElem )
8824           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8825         n1 = 0;
8826         n2 = 1;
8827         n3 = 5;
8828       }
8829     }
8830     else { // nbFaceNodes==8 - quadratic quadrangle
8831       SMDS_MeshElement* newElem =
8832         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8833       myLastCreatedElems.Append(newElem);
8834       if ( aShapeId && newElem )
8835         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8836       newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8837       myLastCreatedElems.Append(newElem);
8838       if ( aShapeId && newElem )
8839         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8840       newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8841       myLastCreatedElems.Append(newElem);
8842       if ( aShapeId && newElem )
8843         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8844       if(theFace->IsMediumNode(nodes[il1])) {
8845         // create quadrangle
8846         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8847         myLastCreatedElems.Append(newElem);
8848         if ( aShapeId && newElem )
8849           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8850         n1 = 1;
8851         n2 = 2;
8852         n3 = 3;
8853       }
8854       else {
8855         // create quadrangle
8856         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8857         myLastCreatedElems.Append(newElem);
8858         if ( aShapeId && newElem )
8859           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8860         n1 = 0;
8861         n2 = 1;
8862         n3 = 7;
8863       }
8864     }
8865     // create needed triangles using n1,n2,n3 and inserted nodes
8866     int nbn = 2 + aNodesToInsert.size();
8867     //const SMDS_MeshNode* aNodes[nbn];
8868     vector<const SMDS_MeshNode*> aNodes(nbn);
8869     aNodes[0] = nodes[n1];
8870     aNodes[nbn-1] = nodes[n2];
8871     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8872     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8873       aNodes[iNode++] = *nIt;
8874     }
8875     for(i=1; i<nbn; i++) {
8876       SMDS_MeshElement* newElem =
8877         aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8878       myLastCreatedElems.Append(newElem);
8879       if ( aShapeId && newElem )
8880         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8881     }
8882   }
8883   // remove old face
8884   aMesh->RemoveElement(theFace);
8885 }
8886
8887 //=======================================================================
8888 //function : UpdateVolumes
8889 //purpose  :
8890 //=======================================================================
8891 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8892                                       const SMDS_MeshNode*        theBetweenNode2,
8893                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8894 {
8895   myLastCreatedElems.Clear();
8896   myLastCreatedNodes.Clear();
8897
8898   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8899   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8900     const SMDS_MeshElement* elem = invElemIt->next();
8901
8902     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8903     SMDS_VolumeTool aVolume (elem);
8904     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8905       continue;
8906
8907     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8908     int iface, nbFaces = aVolume.NbFaces();
8909     vector<const SMDS_MeshNode *> poly_nodes;
8910     vector<int> quantities (nbFaces);
8911
8912     for (iface = 0; iface < nbFaces; iface++) {
8913       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8914       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8915       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8916
8917       for (int inode = 0; inode < nbFaceNodes; inode++) {
8918         poly_nodes.push_back(faceNodes[inode]);
8919
8920         if (nbInserted == 0) {
8921           if (faceNodes[inode] == theBetweenNode1) {
8922             if (faceNodes[inode + 1] == theBetweenNode2) {
8923               nbInserted = theNodesToInsert.size();
8924
8925               // add nodes to insert
8926               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8927               for (; nIt != theNodesToInsert.end(); nIt++) {
8928                 poly_nodes.push_back(*nIt);
8929               }
8930             }
8931           }
8932           else if (faceNodes[inode] == theBetweenNode2) {
8933             if (faceNodes[inode + 1] == theBetweenNode1) {
8934               nbInserted = theNodesToInsert.size();
8935
8936               // add nodes to insert in reversed order
8937               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8938               nIt--;
8939               for (; nIt != theNodesToInsert.begin(); nIt--) {
8940                 poly_nodes.push_back(*nIt);
8941               }
8942               poly_nodes.push_back(*nIt);
8943             }
8944           }
8945           else {
8946           }
8947         }
8948       }
8949       quantities[iface] = nbFaceNodes + nbInserted;
8950     }
8951
8952     // Replace or update the volume
8953     SMESHDS_Mesh *aMesh = GetMeshDS();
8954
8955     if (elem->IsPoly()) {
8956       aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8957
8958     }
8959     else {
8960       int aShapeId = FindShape( elem );
8961
8962       SMDS_MeshElement* newElem =
8963         aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8964       myLastCreatedElems.Append(newElem);
8965       if (aShapeId && newElem)
8966         aMesh->SetMeshElementOnShape(newElem, aShapeId);
8967
8968       aMesh->RemoveElement(elem);
8969     }
8970   }
8971 }
8972
8973 namespace
8974 {
8975   //================================================================================
8976   /*!
8977    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8978    */
8979   //================================================================================
8980
8981   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8982                            vector<const SMDS_MeshNode *> & nodes,
8983                            vector<int> &                   nbNodeInFaces )
8984   {
8985     nodes.clear();
8986     nbNodeInFaces.clear();
8987     SMDS_VolumeTool vTool ( elem );
8988     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8989     {
8990       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8991       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8992       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8993     }
8994   }
8995 }
8996
8997 //=======================================================================
8998 /*!
8999  * \brief Convert elements contained in a submesh to quadratic
9000  * \return int - nb of checked elements
9001  */
9002 //=======================================================================
9003
9004 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9005                                              SMESH_MesherHelper& theHelper,
9006                                              const bool          theForce3d)
9007 {
9008   int nbElem = 0;
9009   if( !theSm ) return nbElem;
9010
9011   vector<int> nbNodeInFaces;
9012   vector<const SMDS_MeshNode *> nodes;
9013   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9014   while(ElemItr->more())
9015   {
9016     nbElem++;
9017     const SMDS_MeshElement* elem = ElemItr->next();
9018     if( !elem ) continue;
9019
9020     // analyse a necessity of conversion
9021     const SMDSAbs_ElementType aType = elem->GetType();
9022     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9023       continue;
9024     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9025     bool hasCentralNodes = false;
9026     if ( elem->IsQuadratic() )
9027     {
9028       bool alreadyOK;
9029       switch ( aGeomType ) {
9030       case SMDSEntity_Quad_Triangle:
9031       case SMDSEntity_Quad_Quadrangle:
9032       case SMDSEntity_Quad_Hexa:
9033         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9034
9035       case SMDSEntity_BiQuad_Triangle:
9036       case SMDSEntity_BiQuad_Quadrangle:
9037       case SMDSEntity_TriQuad_Hexa:
9038         alreadyOK = theHelper.GetIsBiQuadratic();
9039         hasCentralNodes = true;
9040         break;
9041       default:
9042         alreadyOK = true;
9043       }
9044       // take into account already present modium nodes
9045       switch ( aType ) {
9046       case SMDSAbs_Volume:
9047         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9048       case SMDSAbs_Face:
9049         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9050       case SMDSAbs_Edge:
9051         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9052       default:;
9053       }
9054       if ( alreadyOK )
9055         continue;
9056     }
9057     // get elem data needed to re-create it
9058     //
9059     const int id      = elem->GetID();
9060     const int nbNodes = elem->NbCornerNodes();
9061     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9062     if ( aGeomType == SMDSEntity_Polyhedra )
9063       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9064     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9065       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9066
9067     // remove a linear element
9068     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9069
9070     // remove central nodes of biquadratic elements (biquad->quad convertion)
9071     if ( hasCentralNodes )
9072       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9073         if ( nodes[i]->NbInverseElements() == 0 )
9074           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9075
9076     const SMDS_MeshElement* NewElem = 0;
9077
9078     switch( aType )
9079     {
9080     case SMDSAbs_Edge :
9081       {
9082         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9083         break;
9084       }
9085     case SMDSAbs_Face :
9086       {
9087         switch(nbNodes)
9088         {
9089         case 3:
9090           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9091           break;
9092         case 4:
9093           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9094           break;
9095         default:
9096           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9097         }
9098         break;
9099       }
9100     case SMDSAbs_Volume :
9101       {
9102         switch( aGeomType )
9103         {
9104         case SMDSEntity_Tetra:
9105           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9106           break;
9107         case SMDSEntity_Pyramid:
9108           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9109           break;
9110         case SMDSEntity_Penta:
9111           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9112           break;
9113         case SMDSEntity_Hexa:
9114         case SMDSEntity_Quad_Hexa:
9115         case SMDSEntity_TriQuad_Hexa:
9116           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9117                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9118           break;
9119         case SMDSEntity_Hexagonal_Prism:
9120         default:
9121           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9122         }
9123         break;
9124       }
9125     default :
9126       continue;
9127     }
9128     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9129     if( NewElem && NewElem->getshapeId() < 1 )
9130       theSm->AddElement( NewElem );
9131   }
9132   return nbElem;
9133 }
9134 //=======================================================================
9135 //function : ConvertToQuadratic
9136 //purpose  :
9137 //=======================================================================
9138
9139 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9140 {
9141   SMESHDS_Mesh* meshDS = GetMeshDS();
9142
9143   SMESH_MesherHelper aHelper(*myMesh);
9144
9145   aHelper.SetIsQuadratic( true );
9146   aHelper.SetIsBiQuadratic( theToBiQuad );
9147   aHelper.SetElementsOnShape(true);
9148   aHelper.ToFixNodeParameters( true );
9149
9150   // convert elements assigned to sub-meshes
9151   int nbCheckedElems = 0;
9152   if ( myMesh->HasShapeToMesh() )
9153   {
9154     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9155     {
9156       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9157       while ( smIt->more() ) {
9158         SMESH_subMesh* sm = smIt->next();
9159         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9160           aHelper.SetSubShape( sm->GetSubShape() );
9161           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9162         }
9163       }
9164     }
9165   }
9166
9167   // convert elements NOT assigned to sub-meshes
9168   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9169   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9170   {
9171     aHelper.SetElementsOnShape(false);
9172     SMESHDS_SubMesh *smDS = 0;
9173
9174     // convert edges
9175     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9176     while( aEdgeItr->more() )
9177     {
9178       const SMDS_MeshEdge* edge = aEdgeItr->next();
9179       if ( !edge->IsQuadratic() )
9180       {
9181         int                  id = edge->GetID();
9182         const SMDS_MeshNode* n1 = edge->GetNode(0);
9183         const SMDS_MeshNode* n2 = edge->GetNode(1);
9184
9185         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9186
9187         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9188         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9189       }
9190       else
9191       {
9192         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9193       }
9194     }
9195
9196     // convert faces
9197     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9198     while( aFaceItr->more() )
9199     {
9200       const SMDS_MeshFace* face = aFaceItr->next();
9201       if ( !face ) continue;
9202       
9203       const SMDSAbs_EntityType type = face->GetEntityType();
9204       bool alreadyOK;
9205       switch( type )
9206       {
9207       case SMDSEntity_Quad_Triangle:
9208       case SMDSEntity_Quad_Quadrangle:
9209         alreadyOK = !theToBiQuad;
9210         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9211         break;
9212       case SMDSEntity_BiQuad_Triangle:
9213       case SMDSEntity_BiQuad_Quadrangle:
9214         alreadyOK = theToBiQuad;
9215         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9216         break;
9217       default: alreadyOK = false;
9218       }
9219       if ( alreadyOK )
9220         continue;
9221
9222       const int id = face->GetID();
9223       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9224
9225       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9226
9227       SMDS_MeshFace * NewFace = 0;
9228       switch( type )
9229       {
9230       case SMDSEntity_Triangle:
9231       case SMDSEntity_Quad_Triangle:
9232       case SMDSEntity_BiQuad_Triangle:
9233         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9234         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9235           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9236         break;
9237
9238       case SMDSEntity_Quadrangle:
9239       case SMDSEntity_Quad_Quadrangle:
9240       case SMDSEntity_BiQuad_Quadrangle:
9241         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9242         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9243           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9244         break;
9245
9246       default:;
9247         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9248       }
9249       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9250     }
9251
9252     // convert volumes
9253     vector<int> nbNodeInFaces;
9254     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9255     while(aVolumeItr->more())
9256     {
9257       const SMDS_MeshVolume* volume = aVolumeItr->next();
9258       if ( !volume ) continue;
9259
9260       const SMDSAbs_EntityType type = volume->GetEntityType();
9261       if ( volume->IsQuadratic() )
9262       {
9263         bool alreadyOK;
9264         switch ( type )
9265         {
9266         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9267         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9268         default:                      alreadyOK = true;
9269         }
9270         if ( alreadyOK )
9271         {
9272           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9273           continue;
9274         }
9275       }
9276       const int id = volume->GetID();
9277       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9278       if ( type == SMDSEntity_Polyhedra )
9279         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9280       else if ( type == SMDSEntity_Hexagonal_Prism )
9281         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9282
9283       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9284
9285       SMDS_MeshVolume * NewVolume = 0;
9286       switch ( type )
9287       {
9288       case SMDSEntity_Tetra:
9289         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9290         break;
9291       case SMDSEntity_Hexa:
9292       case SMDSEntity_Quad_Hexa:
9293       case SMDSEntity_TriQuad_Hexa:
9294         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9295                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9296         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9297           if ( nodes[i]->NbInverseElements() == 0 )
9298             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9299         break;
9300       case SMDSEntity_Pyramid:
9301         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9302                                       nodes[3], nodes[4], id, theForce3d);
9303         break;
9304       case SMDSEntity_Penta:
9305         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9306                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9307         break;
9308       case SMDSEntity_Hexagonal_Prism:
9309       default:
9310         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9311       }
9312       ReplaceElemInGroups(volume, NewVolume, meshDS);
9313     }
9314   }
9315
9316   if ( !theForce3d )
9317   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9318     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9319     // aHelper.FixQuadraticElements(myError);
9320     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9321   }
9322 }
9323
9324 //================================================================================
9325 /*!
9326  * \brief Makes given elements quadratic
9327  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9328  *  \param theElements - elements to make quadratic
9329  */
9330 //================================================================================
9331
9332 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9333                                           TIDSortedElemSet& theElements,
9334                                           const bool        theToBiQuad)
9335 {
9336   if ( theElements.empty() ) return;
9337
9338   // we believe that all theElements are of the same type
9339   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9340
9341   // get all nodes shared by theElements
9342   TIDSortedNodeSet allNodes;
9343   TIDSortedElemSet::iterator eIt = theElements.begin();
9344   for ( ; eIt != theElements.end(); ++eIt )
9345     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9346
9347   // complete theElements with elements of lower dim whose all nodes are in allNodes
9348
9349   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9350   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9351   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9352   for ( ; nIt != allNodes.end(); ++nIt )
9353   {
9354     const SMDS_MeshNode* n = *nIt;
9355     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9356     while ( invIt->more() )
9357     {
9358       const SMDS_MeshElement*      e = invIt->next();
9359       const SMDSAbs_ElementType type = e->GetType();
9360       if ( e->IsQuadratic() )
9361       {
9362         quadAdjacentElems[ type ].insert( e );
9363
9364         bool alreadyOK;
9365         switch ( e->GetEntityType() ) {
9366         case SMDSEntity_Quad_Triangle:
9367         case SMDSEntity_Quad_Quadrangle:
9368         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9369         case SMDSEntity_BiQuad_Triangle:
9370         case SMDSEntity_BiQuad_Quadrangle:
9371         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9372         default:                           alreadyOK = true;
9373         }
9374         if ( alreadyOK )
9375           continue;
9376       }
9377       if ( type >= elemType )
9378         continue; // same type or more complex linear element
9379
9380       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9381         continue; // e is already checked
9382
9383       // check nodes
9384       bool allIn = true;
9385       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9386       while ( nodeIt->more() && allIn )
9387         allIn = allNodes.count( nodeIt->next() );
9388       if ( allIn )
9389         theElements.insert(e );
9390     }
9391   }
9392
9393   SMESH_MesherHelper helper(*myMesh);
9394   helper.SetIsQuadratic( true );
9395   helper.SetIsBiQuadratic( theToBiQuad );
9396
9397   // add links of quadratic adjacent elements to the helper
9398
9399   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9400     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9401           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9402     {
9403       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9404     }
9405   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9406     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9407           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9408     {
9409       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9410     }
9411   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9412     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9413           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9414     {
9415       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9416     }
9417
9418   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9419
9420   SMESHDS_Mesh*  meshDS = GetMeshDS();
9421   SMESHDS_SubMesh* smDS = 0;
9422   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9423   {
9424     const SMDS_MeshElement* elem = *eIt;
9425
9426     bool alreadyOK;
9427     int nbCentralNodes = 0;
9428     switch ( elem->GetEntityType() ) {
9429       // linear convertible
9430     case SMDSEntity_Edge:
9431     case SMDSEntity_Triangle:
9432     case SMDSEntity_Quadrangle:
9433     case SMDSEntity_Tetra:
9434     case SMDSEntity_Pyramid:
9435     case SMDSEntity_Hexa:
9436     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9437       // quadratic that can become bi-quadratic
9438     case SMDSEntity_Quad_Triangle:
9439     case SMDSEntity_Quad_Quadrangle:
9440     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9441       // bi-quadratic
9442     case SMDSEntity_BiQuad_Triangle:
9443     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9444     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9445       // the rest
9446     default:                           alreadyOK = true;
9447     }
9448     if ( alreadyOK ) continue;
9449
9450     const SMDSAbs_ElementType type = elem->GetType();
9451     const int                   id = elem->GetID();
9452     const int              nbNodes = elem->NbCornerNodes();
9453     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9454
9455     helper.SetSubShape( elem->getshapeId() );
9456
9457     if ( !smDS || !smDS->Contains( elem ))
9458       smDS = meshDS->MeshElements( elem->getshapeId() );
9459     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9460
9461     SMDS_MeshElement * newElem = 0;
9462     switch( nbNodes )
9463     {
9464     case 4: // cases for most frequently used element types go first (for optimization)
9465       if ( type == SMDSAbs_Volume )
9466         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9467       else
9468         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9469       break;
9470     case 8:
9471       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9472                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9473       break;
9474     case 3:
9475       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9476       break;
9477     case 2:
9478       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9479       break;
9480     case 5:
9481       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9482                                  nodes[4], id, theForce3d);
9483       break;
9484     case 6:
9485       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9486                                  nodes[4], nodes[5], id, theForce3d);
9487       break;
9488     default:;
9489     }
9490     ReplaceElemInGroups( elem, newElem, meshDS);
9491     if( newElem && smDS )
9492       smDS->AddElement( newElem );
9493
9494      // remove central nodes
9495     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9496       if ( nodes[i]->NbInverseElements() == 0 )
9497         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9498
9499   } // loop on theElements
9500
9501   if ( !theForce3d )
9502   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9503     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9504     // helper.FixQuadraticElements( myError );
9505     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9506   }
9507 }
9508
9509 //=======================================================================
9510 /*!
9511  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9512  * \return int - nb of checked elements
9513  */
9514 //=======================================================================
9515
9516 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9517                                      SMDS_ElemIteratorPtr theItr,
9518                                      const int            theShapeID)
9519 {
9520   int nbElem = 0;
9521   SMESHDS_Mesh* meshDS = GetMeshDS();
9522
9523   while( theItr->more() )
9524   {
9525     const SMDS_MeshElement* elem = theItr->next();
9526     nbElem++;
9527     if( elem && elem->IsQuadratic())
9528     {
9529       int id                    = elem->GetID();
9530       int nbCornerNodes         = elem->NbCornerNodes();
9531       SMDSAbs_ElementType aType = elem->GetType();
9532
9533       vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9534
9535       //remove a quadratic element
9536       if ( !theSm || !theSm->Contains( elem ))
9537         theSm = meshDS->MeshElements( elem->getshapeId() );
9538       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9539
9540       // remove medium nodes
9541       for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9542         if ( nodes[i]->NbInverseElements() == 0 )
9543           meshDS->RemoveFreeNode( nodes[i], theSm );
9544
9545       // add a linear element
9546       nodes.resize( nbCornerNodes );
9547       SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9548       ReplaceElemInGroups(elem, newElem, meshDS);
9549       if( theSm && newElem )
9550         theSm->AddElement( newElem );
9551     }
9552   }
9553   return nbElem;
9554 }
9555
9556 //=======================================================================
9557 //function : ConvertFromQuadratic
9558 //purpose  :
9559 //=======================================================================
9560
9561 bool SMESH_MeshEditor::ConvertFromQuadratic()
9562 {
9563   int nbCheckedElems = 0;
9564   if ( myMesh->HasShapeToMesh() )
9565   {
9566     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9567     {
9568       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9569       while ( smIt->more() ) {
9570         SMESH_subMesh* sm = smIt->next();
9571         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9572           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9573       }
9574     }
9575   }
9576
9577   int totalNbElems =
9578     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9579   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9580   {
9581     SMESHDS_SubMesh *aSM = 0;
9582     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9583   }
9584
9585   return true;
9586 }
9587
9588 namespace
9589 {
9590   //================================================================================
9591   /*!
9592    * \brief Return true if all medium nodes of the element are in the node set
9593    */
9594   //================================================================================
9595
9596   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9597   {
9598     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9599       if ( !nodeSet.count( elem->GetNode(i) ))
9600         return false;
9601     return true;
9602   }
9603 }
9604
9605 //================================================================================
9606 /*!
9607  * \brief Makes given elements linear
9608  */
9609 //================================================================================
9610
9611 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9612 {
9613   if ( theElements.empty() ) return;
9614
9615   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9616   set<int> mediumNodeIDs;
9617   TIDSortedElemSet::iterator eIt = theElements.begin();
9618   for ( ; eIt != theElements.end(); ++eIt )
9619   {
9620     const SMDS_MeshElement* e = *eIt;
9621     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9622       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9623   }
9624
9625   // replace given elements by linear ones
9626   SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9627   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9628
9629   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9630   // except those elements sharing medium nodes of quadratic element whose medium nodes
9631   // are not all in mediumNodeIDs
9632
9633   // get remaining medium nodes
9634   TIDSortedNodeSet mediumNodes;
9635   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9636   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9637     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9638       mediumNodes.insert( mediumNodes.end(), n );
9639
9640   // find more quadratic elements to convert
9641   TIDSortedElemSet moreElemsToConvert;
9642   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9643   for ( ; nIt != mediumNodes.end(); ++nIt )
9644   {
9645     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9646     while ( invIt->more() )
9647     {
9648       const SMDS_MeshElement* e = invIt->next();
9649       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9650       {
9651         // find a more complex element including e and
9652         // whose medium nodes are not in mediumNodes
9653         bool complexFound = false;
9654         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9655         {
9656           SMDS_ElemIteratorPtr invIt2 =
9657             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9658           while ( invIt2->more() )
9659           {
9660             const SMDS_MeshElement* eComplex = invIt2->next();
9661             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9662             {
9663               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9664               if ( nbCommonNodes == e->NbNodes())
9665               {
9666                 complexFound = true;
9667                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9668                 break;
9669               }
9670             }
9671           }
9672         }
9673         if ( !complexFound )
9674           moreElemsToConvert.insert( e );
9675       }
9676     }
9677   }
9678   elemIt = elemSetIterator( moreElemsToConvert );
9679   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9680 }
9681
9682 //=======================================================================
9683 //function : SewSideElements
9684 //purpose  :
9685 //=======================================================================
9686
9687 SMESH_MeshEditor::Sew_Error
9688 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9689                                    TIDSortedElemSet&    theSide2,
9690                                    const SMDS_MeshNode* theFirstNode1,
9691                                    const SMDS_MeshNode* theFirstNode2,
9692                                    const SMDS_MeshNode* theSecondNode1,
9693                                    const SMDS_MeshNode* theSecondNode2)
9694 {
9695   myLastCreatedElems.Clear();
9696   myLastCreatedNodes.Clear();
9697
9698   MESSAGE ("::::SewSideElements()");
9699   if ( theSide1.size() != theSide2.size() )
9700     return SEW_DIFF_NB_OF_ELEMENTS;
9701
9702   Sew_Error aResult = SEW_OK;
9703   // Algo:
9704   // 1. Build set of faces representing each side
9705   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9706   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9707
9708   // =======================================================================
9709   // 1. Build set of faces representing each side:
9710   // =======================================================================
9711   // a. build set of nodes belonging to faces
9712   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9713   // c. create temporary faces representing side of volumes if correspondent
9714   //    face does not exist
9715
9716   SMESHDS_Mesh* aMesh = GetMeshDS();
9717   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9718   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9719   TIDSortedElemSet             faceSet1, faceSet2;
9720   set<const SMDS_MeshElement*> volSet1,  volSet2;
9721   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9722   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9723   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9724   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9725   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9726   int iSide, iFace, iNode;
9727
9728   list<const SMDS_MeshElement* > tempFaceList;
9729   for ( iSide = 0; iSide < 2; iSide++ ) {
9730     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9731     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9732     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9733     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9734     set<const SMDS_MeshElement*>::iterator vIt;
9735     TIDSortedElemSet::iterator eIt;
9736     set<const SMDS_MeshNode*>::iterator    nIt;
9737
9738     // check that given nodes belong to given elements
9739     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9740     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9741     int firstIndex = -1, secondIndex = -1;
9742     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9743       const SMDS_MeshElement* elem = *eIt;
9744       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9745       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9746       if ( firstIndex > -1 && secondIndex > -1 ) break;
9747     }
9748     if ( firstIndex < 0 || secondIndex < 0 ) {
9749       // we can simply return until temporary faces created
9750       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9751     }
9752
9753     // -----------------------------------------------------------
9754     // 1a. Collect nodes of existing faces
9755     //     and build set of face nodes in order to detect missing
9756     //     faces corresponding to sides of volumes
9757     // -----------------------------------------------------------
9758
9759     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9760
9761     // loop on the given element of a side
9762     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9763       //const SMDS_MeshElement* elem = *eIt;
9764       const SMDS_MeshElement* elem = *eIt;
9765       if ( elem->GetType() == SMDSAbs_Face ) {
9766         faceSet->insert( elem );
9767         set <const SMDS_MeshNode*> faceNodeSet;
9768         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9769         while ( nodeIt->more() ) {
9770           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9771           nodeSet->insert( n );
9772           faceNodeSet.insert( n );
9773         }
9774         setOfFaceNodeSet.insert( faceNodeSet );
9775       }
9776       else if ( elem->GetType() == SMDSAbs_Volume )
9777         volSet->insert( elem );
9778     }
9779     // ------------------------------------------------------------------------------
9780     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9781     // ------------------------------------------------------------------------------
9782
9783     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9784       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9785       while ( fIt->more() ) { // loop on faces sharing a node
9786         const SMDS_MeshElement* f = fIt->next();
9787         if ( faceSet->find( f ) == faceSet->end() ) {
9788           // check if all nodes are in nodeSet and
9789           // complete setOfFaceNodeSet if they are
9790           set <const SMDS_MeshNode*> faceNodeSet;
9791           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9792           bool allInSet = true;
9793           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9794             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9795             if ( nodeSet->find( n ) == nodeSet->end() )
9796               allInSet = false;
9797             else
9798               faceNodeSet.insert( n );
9799           }
9800           if ( allInSet ) {
9801             faceSet->insert( f );
9802             setOfFaceNodeSet.insert( faceNodeSet );
9803           }
9804         }
9805       }
9806     }
9807
9808     // -------------------------------------------------------------------------
9809     // 1c. Create temporary faces representing sides of volumes if correspondent
9810     //     face does not exist
9811     // -------------------------------------------------------------------------
9812
9813     if ( !volSet->empty() ) {
9814       //int nodeSetSize = nodeSet->size();
9815
9816       // loop on given volumes
9817       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9818         SMDS_VolumeTool vol (*vIt);
9819         // loop on volume faces: find free faces
9820         // --------------------------------------
9821         list<const SMDS_MeshElement* > freeFaceList;
9822         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9823           if ( !vol.IsFreeFace( iFace ))
9824             continue;
9825           // check if there is already a face with same nodes in a face set
9826           const SMDS_MeshElement* aFreeFace = 0;
9827           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9828           int nbNodes = vol.NbFaceNodes( iFace );
9829           set <const SMDS_MeshNode*> faceNodeSet;
9830           vol.GetFaceNodes( iFace, faceNodeSet );
9831           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9832           if ( isNewFace ) {
9833             // no such a face is given but it still can exist, check it
9834             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9835             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9836           }
9837           if ( !aFreeFace ) {
9838             // create a temporary face
9839             if ( nbNodes == 3 ) {
9840               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9841               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9842             }
9843             else if ( nbNodes == 4 ) {
9844               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9845               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9846             }
9847             else {
9848               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9849               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9850               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9851             }
9852             if ( aFreeFace )
9853               tempFaceList.push_back( aFreeFace );
9854           }
9855
9856           if ( aFreeFace )
9857             freeFaceList.push_back( aFreeFace );
9858
9859         } // loop on faces of a volume
9860
9861         // choose one of several free faces of a volume
9862         // --------------------------------------------
9863         if ( freeFaceList.size() > 1 ) {
9864           // choose a face having max nb of nodes shared by other elems of a side
9865           int maxNbNodes = -1;
9866           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9867           while ( fIt != freeFaceList.end() ) { // loop on free faces
9868             int nbSharedNodes = 0;
9869             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9870             while ( nodeIt->more() ) { // loop on free face nodes
9871               const SMDS_MeshNode* n =
9872                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9873               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9874               while ( invElemIt->more() ) {
9875                 const SMDS_MeshElement* e = invElemIt->next();
9876                 nbSharedNodes += faceSet->count( e );
9877                 nbSharedNodes += elemSet->count( e );
9878               }
9879             }
9880             if ( nbSharedNodes > maxNbNodes ) {
9881               maxNbNodes = nbSharedNodes;
9882               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9883             }
9884             else if ( nbSharedNodes == maxNbNodes ) {
9885               fIt++;
9886             }
9887             else {
9888               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9889             }
9890           }
9891           if ( freeFaceList.size() > 1 )
9892           {
9893             // could not choose one face, use another way
9894             // choose a face most close to the bary center of the opposite side
9895             gp_XYZ aBC( 0., 0., 0. );
9896             set <const SMDS_MeshNode*> addedNodes;
9897             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9898             eIt = elemSet2->begin();
9899             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9900               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9901               while ( nodeIt->more() ) { // loop on free face nodes
9902                 const SMDS_MeshNode* n =
9903                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9904                 if ( addedNodes.insert( n ).second )
9905                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9906               }
9907             }
9908             aBC /= addedNodes.size();
9909             double minDist = DBL_MAX;
9910             fIt = freeFaceList.begin();
9911             while ( fIt != freeFaceList.end() ) { // loop on free faces
9912               double dist = 0;
9913               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9914               while ( nodeIt->more() ) { // loop on free face nodes
9915                 const SMDS_MeshNode* n =
9916                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9917                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9918                 dist += ( aBC - p ).SquareModulus();
9919               }
9920               if ( dist < minDist ) {
9921                 minDist = dist;
9922                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9923               }
9924               else
9925                 fIt = freeFaceList.erase( fIt++ );
9926             }
9927           }
9928         } // choose one of several free faces of a volume
9929
9930         if ( freeFaceList.size() == 1 ) {
9931           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9932           faceSet->insert( aFreeFace );
9933           // complete a node set with nodes of a found free face
9934           //           for ( iNode = 0; iNode < ; iNode++ )
9935           //             nodeSet->insert( fNodes[ iNode ] );
9936         }
9937
9938       } // loop on volumes of a side
9939
9940       //       // complete a set of faces if new nodes in a nodeSet appeared
9941       //       // ----------------------------------------------------------
9942       //       if ( nodeSetSize != nodeSet->size() ) {
9943       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9944       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9945       //           while ( fIt->more() ) { // loop on faces sharing a node
9946       //             const SMDS_MeshElement* f = fIt->next();
9947       //             if ( faceSet->find( f ) == faceSet->end() ) {
9948       //               // check if all nodes are in nodeSet and
9949       //               // complete setOfFaceNodeSet if they are
9950       //               set <const SMDS_MeshNode*> faceNodeSet;
9951       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9952       //               bool allInSet = true;
9953       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9954       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9955       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9956       //                   allInSet = false;
9957       //                 else
9958       //                   faceNodeSet.insert( n );
9959       //               }
9960       //               if ( allInSet ) {
9961       //                 faceSet->insert( f );
9962       //                 setOfFaceNodeSet.insert( faceNodeSet );
9963       //               }
9964       //             }
9965       //           }
9966       //         }
9967       //       }
9968     } // Create temporary faces, if there are volumes given
9969   } // loop on sides
9970
9971   if ( faceSet1.size() != faceSet2.size() ) {
9972     // delete temporary faces: they are in reverseElements of actual nodes
9973 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9974 //    while ( tmpFaceIt->more() )
9975 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9976 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9977 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9978 //      aMesh->RemoveElement(*tmpFaceIt);
9979     MESSAGE("Diff nb of faces");
9980     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9981   }
9982
9983   // ============================================================
9984   // 2. Find nodes to merge:
9985   //              bind a node to remove to a node to put instead
9986   // ============================================================
9987
9988   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9989   if ( theFirstNode1 != theFirstNode2 )
9990     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9991   if ( theSecondNode1 != theSecondNode2 )
9992     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9993
9994   LinkID_Gen aLinkID_Gen( GetMeshDS() );
9995   set< long > linkIdSet; // links to process
9996   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9997
9998   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9999   list< NLink > linkList[2];
10000   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10001   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10002   // loop on links in linkList; find faces by links and append links
10003   // of the found faces to linkList
10004   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10005   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10006   {
10007     NLink link[] = { *linkIt[0], *linkIt[1] };
10008     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10009     if ( !linkIdSet.count( linkID ) )
10010       continue;
10011
10012     // by links, find faces in the face sets,
10013     // and find indices of link nodes in the found faces;
10014     // in a face set, there is only one or no face sharing a link
10015     // ---------------------------------------------------------------
10016
10017     const SMDS_MeshElement* face[] = { 0, 0 };
10018     vector<const SMDS_MeshNode*> fnodes[2];
10019     int iLinkNode[2][2];
10020     TIDSortedElemSet avoidSet;
10021     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10022       const SMDS_MeshNode* n1 = link[iSide].first;
10023       const SMDS_MeshNode* n2 = link[iSide].second;
10024       //cout << "Side " << iSide << " ";
10025       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10026       // find a face by two link nodes
10027       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10028                                                       *faceSetPtr[ iSide ], avoidSet,
10029                                                       &iLinkNode[iSide][0],
10030                                                       &iLinkNode[iSide][1] );
10031       if ( face[ iSide ])
10032       {
10033         //cout << " F " << face[ iSide]->GetID() <<endl;
10034         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10035         // put face nodes to fnodes
10036         if ( face[ iSide ]->IsQuadratic() )
10037         {
10038           // use interlaced nodes iterator
10039           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10040           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10041           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10042           while ( nIter->more() )
10043             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10044         }
10045         else
10046         {
10047           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10048                                   face[ iSide ]->end_nodes() );
10049         }
10050         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10051       }
10052     }
10053
10054     // check similarity of elements of the sides
10055     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10056       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10057       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10058         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10059       }
10060       else {
10061         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10062       }
10063       break; // do not return because it's necessary to remove tmp faces
10064     }
10065
10066     // set nodes to merge
10067     // -------------------
10068
10069     if ( face[0] && face[1] )  {
10070       const int nbNodes = face[0]->NbNodes();
10071       if ( nbNodes != face[1]->NbNodes() ) {
10072         MESSAGE("Diff nb of face nodes");
10073         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10074         break; // do not return because it s necessary to remove tmp faces
10075       }
10076       bool reverse[] = { false, false }; // order of nodes in the link
10077       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10078         // analyse link orientation in faces
10079         int i1 = iLinkNode[ iSide ][ 0 ];
10080         int i2 = iLinkNode[ iSide ][ 1 ];
10081         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10082       }
10083       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10084       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10085       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10086       {
10087         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10088                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10089       }
10090
10091       // add other links of the faces to linkList
10092       // -----------------------------------------
10093
10094       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10095         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10096         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10097         if ( !iter_isnew.second ) { // already in a set: no need to process
10098           linkIdSet.erase( iter_isnew.first );
10099         }
10100         else // new in set == encountered for the first time: add
10101         {
10102           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10103           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10104           linkList[0].push_back ( NLink( n1, n2 ));
10105           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10106         }
10107       }
10108     } // 2 faces found
10109
10110     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10111       break;
10112
10113   } // loop on link lists
10114
10115   if ( aResult == SEW_OK &&
10116        ( //linkIt[0] != linkList[0].end() ||
10117          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10118     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10119              " " << (faceSetPtr[1]->empty()));
10120     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10121   }
10122
10123   // ====================================================================
10124   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10125   // ====================================================================
10126
10127   // delete temporary faces
10128 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10129 //  while ( tmpFaceIt->more() )
10130 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10131   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10132   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10133     aMesh->RemoveElement(*tmpFaceIt);
10134
10135   if ( aResult != SEW_OK)
10136     return aResult;
10137
10138   list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10139   // loop on nodes replacement map
10140   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10141   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10142     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10143       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10144       nodeIDsToRemove.push_back( nToRemove->GetID() );
10145       // loop on elements sharing nToRemove
10146       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10147       while ( invElemIt->more() ) {
10148         const SMDS_MeshElement* e = invElemIt->next();
10149         // get a new suite of nodes: make replacement
10150         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10151         vector< const SMDS_MeshNode*> nodes( nbNodes );
10152         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10153         while ( nIt->more() ) {
10154           const SMDS_MeshNode* n =
10155             static_cast<const SMDS_MeshNode*>( nIt->next() );
10156           nnIt = nReplaceMap.find( n );
10157           if ( nnIt != nReplaceMap.end() ) {
10158             nbReplaced++;
10159             n = (*nnIt).second;
10160           }
10161           nodes[ i++ ] = n;
10162         }
10163         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10164         //         elemIDsToRemove.push_back( e->GetID() );
10165         //       else
10166         if ( nbReplaced )
10167           {
10168             SMDSAbs_ElementType etyp = e->GetType();
10169             SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10170             if (newElem)
10171               {
10172                 myLastCreatedElems.Append(newElem);
10173                 AddToSameGroups(newElem, e, aMesh);
10174                 int aShapeId = e->getshapeId();
10175                 if ( aShapeId )
10176                   {
10177                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
10178                   }
10179               }
10180             aMesh->RemoveElement(e);
10181           }
10182       }
10183     }
10184
10185   Remove( nodeIDsToRemove, true );
10186
10187   return aResult;
10188 }
10189
10190 //================================================================================
10191 /*!
10192  * \brief Find corresponding nodes in two sets of faces
10193  * \param theSide1 - first face set
10194  * \param theSide2 - second first face
10195  * \param theFirstNode1 - a boundary node of set 1
10196  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10197  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10198  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10199  * \param nReplaceMap - output map of corresponding nodes
10200  * \return bool  - is a success or not
10201  */
10202 //================================================================================
10203
10204 #ifdef _DEBUG_
10205 //#define DEBUG_MATCHING_NODES
10206 #endif
10207
10208 SMESH_MeshEditor::Sew_Error
10209 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10210                                     set<const SMDS_MeshElement*>& theSide2,
10211                                     const SMDS_MeshNode*          theFirstNode1,
10212                                     const SMDS_MeshNode*          theFirstNode2,
10213                                     const SMDS_MeshNode*          theSecondNode1,
10214                                     const SMDS_MeshNode*          theSecondNode2,
10215                                     TNodeNodeMap &                nReplaceMap)
10216 {
10217   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10218
10219   nReplaceMap.clear();
10220   if ( theFirstNode1 != theFirstNode2 )
10221     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10222   if ( theSecondNode1 != theSecondNode2 )
10223     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10224
10225   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10226   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10227
10228   list< NLink > linkList[2];
10229   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10230   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10231
10232   // loop on links in linkList; find faces by links and append links
10233   // of the found faces to linkList
10234   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10235   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10236     NLink link[] = { *linkIt[0], *linkIt[1] };
10237     if ( linkSet.find( link[0] ) == linkSet.end() )
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     list<const SMDS_MeshNode*> notLinkNodes[2];
10247     //bool reverse[] = { false, false }; // order of notLinkNodes
10248     int nbNodes[2];
10249     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10250     {
10251       const SMDS_MeshNode* n1 = link[iSide].first;
10252       const SMDS_MeshNode* n2 = link[iSide].second;
10253       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10254       set< const SMDS_MeshElement* > facesOfNode1;
10255       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10256       {
10257         // during a loop of the first node, we find all faces around n1,
10258         // during a loop of the second node, we find one face sharing both n1 and n2
10259         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10260         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10261         while ( fIt->more() ) { // loop on faces sharing a node
10262           const SMDS_MeshElement* f = fIt->next();
10263           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10264               ! facesOfNode1.insert( f ).second ) // f encounters twice
10265           {
10266             if ( face[ iSide ] ) {
10267               MESSAGE( "2 faces per link " );
10268               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10269             }
10270             face[ iSide ] = f;
10271             faceSet->erase( f );
10272
10273             // get not link nodes
10274             int nbN = f->NbNodes();
10275             if ( f->IsQuadratic() )
10276               nbN /= 2;
10277             nbNodes[ iSide ] = nbN;
10278             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10279             int i1 = f->GetNodeIndex( n1 );
10280             int i2 = f->GetNodeIndex( n2 );
10281             int iEnd = nbN, iBeg = -1, iDelta = 1;
10282             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10283             if ( reverse ) {
10284               std::swap( iEnd, iBeg ); iDelta = -1;
10285             }
10286             int i = i2;
10287             while ( true ) {
10288               i += iDelta;
10289               if ( i == iEnd ) i = iBeg + iDelta;
10290               if ( i == i1 ) break;
10291               nodes.push_back ( f->GetNode( i ) );
10292             }
10293           }
10294         }
10295       }
10296     }
10297     // check similarity of elements of the sides
10298     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10299       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10300       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10301         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10302       }
10303       else {
10304         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10305       }
10306     }
10307
10308     // set nodes to merge
10309     // -------------------
10310
10311     if ( face[0] && face[1] )  {
10312       if ( nbNodes[0] != nbNodes[1] ) {
10313         MESSAGE("Diff nb of face nodes");
10314         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10315       }
10316 #ifdef DEBUG_MATCHING_NODES
10317       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10318                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10319                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10320 #endif
10321       int nbN = nbNodes[0];
10322       {
10323         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10324         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10325         for ( int i = 0 ; i < nbN - 2; ++i ) {
10326 #ifdef DEBUG_MATCHING_NODES
10327           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10328 #endif
10329           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10330         }
10331       }
10332
10333       // add other links of the face 1 to linkList
10334       // -----------------------------------------
10335
10336       const SMDS_MeshElement* f0 = face[0];
10337       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10338       for ( int i = 0; i < nbN; i++ )
10339       {
10340         const SMDS_MeshNode* n2 = f0->GetNode( i );
10341         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10342           linkSet.insert( SMESH_TLink( n1, n2 ));
10343         if ( !iter_isnew.second ) { // already in a set: no need to process
10344           linkSet.erase( iter_isnew.first );
10345         }
10346         else // new in set == encountered for the first time: add
10347         {
10348 #ifdef DEBUG_MATCHING_NODES
10349           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10350                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10351 #endif
10352           linkList[0].push_back ( NLink( n1, n2 ));
10353           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10354         }
10355         n1 = n2;
10356       }
10357     } // 2 faces found
10358   } // loop on link lists
10359
10360   return SEW_OK;
10361 }
10362
10363 //================================================================================
10364 /*!
10365  * \brief Create elements equal (on same nodes) to given ones
10366  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10367  *              elements of the uppest dimension are duplicated.
10368  */
10369 //================================================================================
10370
10371 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10372 {
10373   ClearLastCreated();
10374   SMESHDS_Mesh* mesh = GetMeshDS();
10375
10376   // get an element type and an iterator over elements
10377
10378   SMDSAbs_ElementType type;
10379   SMDS_ElemIteratorPtr elemIt;
10380   vector< const SMDS_MeshElement* > allElems;
10381   if ( theElements.empty() )
10382   {
10383     if ( mesh->NbNodes() == 0 )
10384       return;
10385     // get most complex type
10386     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10387       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10388       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10389     };
10390     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10391       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10392       {
10393         type = types[i];
10394         break;
10395       }
10396     // put all elements in the vector <allElems>
10397     allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10398     elemIt = mesh->elementsIterator( type );
10399     while ( elemIt->more() )
10400       allElems.push_back( elemIt->next());
10401     elemIt = elemSetIterator( allElems );
10402   }
10403   else
10404   {
10405     type = (*theElements.begin())->GetType();
10406     elemIt = elemSetIterator( theElements );
10407   }
10408
10409   // duplicate elements
10410
10411   if ( type == SMDSAbs_Ball )
10412   {
10413     SMDS_UnstructuredGrid* vtkGrid = mesh->getGrid();
10414     while ( elemIt->more() )
10415     {
10416       const SMDS_MeshElement* elem = elemIt->next();
10417       if ( elem->GetType() != SMDSAbs_Ball )
10418         continue;
10419       if (( elem = mesh->AddBall( elem->GetNode(0),
10420                                   vtkGrid->GetBallDiameter( elem->getVtkId() ))))
10421         myLastCreatedElems.Append( elem );
10422     }
10423   }
10424   else
10425   {
10426     vector< const SMDS_MeshNode* > nodes;
10427     while ( elemIt->more() )
10428     {
10429       const SMDS_MeshElement* elem = elemIt->next();
10430       if ( elem->GetType() != type )
10431         continue;
10432
10433       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10434
10435       if ( type == SMDSAbs_Volume  && elem->GetVtkType() == VTK_POLYHEDRON )
10436       {
10437         std::vector<int> quantities =
10438           static_cast< const SMDS_VtkVolume* >( elem )->GetQuantities();
10439         elem = mesh->AddPolyhedralVolume( nodes, quantities );
10440       }
10441       else
10442       {
10443         AddElement( nodes, type, elem->IsPoly() );
10444         elem = 0; // myLastCreatedElems is already filled
10445       }
10446       if ( elem )
10447         myLastCreatedElems.Append( elem );
10448     }
10449   }
10450 }
10451
10452 //================================================================================
10453 /*!
10454   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10455   \param theElems - the list of elements (edges or faces) to be replicated
10456   The nodes for duplication could be found from these elements
10457   \param theNodesNot - list of nodes to NOT replicate
10458   \param theAffectedElems - the list of elements (cells and edges) to which the
10459   replicated nodes should be associated to.
10460   \return TRUE if operation has been completed successfully, FALSE otherwise
10461 */
10462 //================================================================================
10463
10464 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10465                                     const TIDSortedElemSet& theNodesNot,
10466                                     const TIDSortedElemSet& theAffectedElems )
10467 {
10468   myLastCreatedElems.Clear();
10469   myLastCreatedNodes.Clear();
10470
10471   if ( theElems.size() == 0 )
10472     return false;
10473
10474   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10475   if ( !aMeshDS )
10476     return false;
10477
10478   bool res = false;
10479   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10480   // duplicate elements and nodes
10481   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10482   // replce nodes by duplications
10483   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10484   return res;
10485 }
10486
10487 //================================================================================
10488 /*!
10489   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10490   \param theMeshDS - mesh instance
10491   \param theElems - the elements replicated or modified (nodes should be changed)
10492   \param theNodesNot - nodes to NOT replicate
10493   \param theNodeNodeMap - relation of old node to new created node
10494   \param theIsDoubleElem - flag os to replicate element or modify
10495   \return TRUE if operation has been completed successfully, FALSE otherwise
10496 */
10497 //================================================================================
10498
10499 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh*     theMeshDS,
10500                                     const TIDSortedElemSet& theElems,
10501                                     const TIDSortedElemSet& theNodesNot,
10502                                     std::map< const SMDS_MeshNode*,
10503                                     const SMDS_MeshNode* >& theNodeNodeMap,
10504                                     const bool theIsDoubleElem )
10505 {
10506   MESSAGE("doubleNodes");
10507   // iterate on through element and duplicate them (by nodes duplication)
10508   bool res = false;
10509   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10510   for ( ;  elemItr != theElems.end(); ++elemItr )
10511   {
10512     const SMDS_MeshElement* anElem = *elemItr;
10513     if (!anElem)
10514       continue;
10515
10516     bool isDuplicate = false;
10517     // duplicate nodes to duplicate element
10518     std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10519     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10520     int ind = 0;
10521     while ( anIter->more() )
10522     {
10523
10524       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10525       SMDS_MeshNode* aNewNode = aCurrNode;
10526       if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10527         aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10528       else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10529       {
10530         // duplicate node
10531         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10532         copyPosition( aCurrNode, aNewNode );
10533         theNodeNodeMap[ aCurrNode ] = aNewNode;
10534         myLastCreatedNodes.Append( aNewNode );
10535       }
10536       isDuplicate |= (aCurrNode != aNewNode);
10537       newNodes[ ind++ ] = aNewNode;
10538     }
10539     if ( !isDuplicate )
10540       continue;
10541
10542     if ( theIsDoubleElem )
10543       AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10544     else
10545       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10546
10547     res = true;
10548   }
10549   return res;
10550 }
10551
10552 //================================================================================
10553 /*!
10554   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10555   \param theNodes - identifiers of nodes to be doubled
10556   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10557   nodes. If list of element identifiers is empty then nodes are doubled but
10558   they not assigned to elements
10559   \return TRUE if operation has been completed successfully, FALSE otherwise
10560 */
10561 //================================================================================
10562
10563 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10564                                     const std::list< int >& theListOfModifiedElems )
10565 {
10566   MESSAGE("DoubleNodes");
10567   myLastCreatedElems.Clear();
10568   myLastCreatedNodes.Clear();
10569
10570   if ( theListOfNodes.size() == 0 )
10571     return false;
10572
10573   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10574   if ( !aMeshDS )
10575     return false;
10576
10577   // iterate through nodes and duplicate them
10578
10579   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10580
10581   std::list< int >::const_iterator aNodeIter;
10582   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10583   {
10584     int aCurr = *aNodeIter;
10585     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10586     if ( !aNode )
10587       continue;
10588
10589     // duplicate node
10590
10591     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10592     if ( aNewNode )
10593     {
10594       copyPosition( aNode, aNewNode );
10595       anOldNodeToNewNode[ aNode ] = aNewNode;
10596       myLastCreatedNodes.Append( aNewNode );
10597     }
10598   }
10599
10600   // Create map of new nodes for modified elements
10601
10602   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10603
10604   std::list< int >::const_iterator anElemIter;
10605   for ( anElemIter = theListOfModifiedElems.begin();
10606         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10607   {
10608     int aCurr = *anElemIter;
10609     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10610     if ( !anElem )
10611       continue;
10612
10613     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10614
10615     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10616     int ind = 0;
10617     while ( anIter->more() )
10618     {
10619       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10620       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10621       {
10622         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10623         aNodeArr[ ind++ ] = aNewNode;
10624       }
10625       else
10626         aNodeArr[ ind++ ] = aCurrNode;
10627     }
10628     anElemToNodes[ anElem ] = aNodeArr;
10629   }
10630
10631   // Change nodes of elements
10632
10633   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10634     anElemToNodesIter = anElemToNodes.begin();
10635   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10636   {
10637     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10638     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10639     if ( anElem )
10640       {
10641       MESSAGE("ChangeElementNodes");
10642       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10643       }
10644   }
10645
10646   return true;
10647 }
10648
10649 namespace {
10650
10651   //================================================================================
10652   /*!
10653   \brief Check if element located inside shape
10654   \return TRUE if IN or ON shape, FALSE otherwise
10655   */
10656   //================================================================================
10657
10658   template<class Classifier>
10659   bool isInside(const SMDS_MeshElement* theElem,
10660                 Classifier&             theClassifier,
10661                 const double            theTol)
10662   {
10663     gp_XYZ centerXYZ (0, 0, 0);
10664     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10665     while (aNodeItr->more())
10666       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10667
10668     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10669     theClassifier.Perform(aPnt, theTol);
10670     TopAbs_State aState = theClassifier.State();
10671     return (aState == TopAbs_IN || aState == TopAbs_ON );
10672   }
10673
10674   //================================================================================
10675   /*!
10676    * \brief Classifier of the 3D point on the TopoDS_Face
10677    *        with interaface suitable for isInside()
10678    */
10679   //================================================================================
10680
10681   struct _FaceClassifier
10682   {
10683     Extrema_ExtPS       _extremum;
10684     BRepAdaptor_Surface _surface;
10685     TopAbs_State        _state;
10686
10687     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10688     {
10689       _extremum.Initialize( _surface,
10690                             _surface.FirstUParameter(), _surface.LastUParameter(),
10691                             _surface.FirstVParameter(), _surface.LastVParameter(),
10692                             _surface.Tolerance(), _surface.Tolerance() );
10693     }
10694     void Perform(const gp_Pnt& aPnt, double theTol)
10695     {
10696       theTol *= theTol;
10697       _state = TopAbs_OUT;
10698       _extremum.Perform(aPnt);
10699       if ( _extremum.IsDone() )
10700         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10701           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10702     }
10703     TopAbs_State State() const
10704     {
10705       return _state;
10706     }
10707   };
10708 }
10709
10710 //================================================================================
10711 /*!
10712   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10713   This method is the first step of DoubleNodeElemGroupsInRegion.
10714   \param theElems - list of groups of elements (edges or faces) to be replicated
10715   \param theNodesNot - list of groups of nodes not to replicated
10716   \param theShape - shape to detect affected elements (element which geometric center
10717          located on or inside shape). If the shape is null, detection is done on faces orientations
10718          (select elements with a gravity center on the side given by faces normals).
10719          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10720          The replicated nodes should be associated to affected elements.
10721   \return groups of affected elements
10722   \sa DoubleNodeElemGroupsInRegion()
10723  */
10724 //================================================================================
10725
10726 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10727                                                    const TIDSortedElemSet& theNodesNot,
10728                                                    const TopoDS_Shape&     theShape,
10729                                                    TIDSortedElemSet&       theAffectedElems)
10730 {
10731   if ( theShape.IsNull() )
10732   {
10733     std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10734     std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10735     std::set<const SMDS_MeshElement*> edgesToCheck;
10736     alreadyCheckedNodes.clear();
10737     alreadyCheckedElems.clear();
10738     edgesToCheck.clear();
10739
10740     // --- iterates on elements to be replicated and get elements by back references from their nodes
10741
10742     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10743     int ielem;
10744     for ( ielem=1;  elemItr != theElems.end(); ++elemItr )
10745     {
10746       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10747       if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10748         continue;
10749       gp_XYZ normal;
10750       SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10751       MESSAGE("element " << ielem++ <<  " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10752       std::set<const SMDS_MeshNode*> nodesElem;
10753       nodesElem.clear();
10754       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10755       while ( nodeItr->more() )
10756       {
10757         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10758         nodesElem.insert(aNode);
10759       }
10760       std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10761       for (; nodit != nodesElem.end(); nodit++)
10762       {
10763         MESSAGE("  noeud ");
10764         const SMDS_MeshNode* aNode = *nodit;
10765         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10766           continue;
10767         if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10768           continue;
10769         alreadyCheckedNodes.insert(aNode);
10770         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10771         while ( backElemItr->more() )
10772         {
10773           MESSAGE("    backelem ");
10774           const SMDS_MeshElement* curElem = backElemItr->next();
10775           if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10776             continue;
10777           if (theElems.find(curElem) != theElems.end())
10778             continue;
10779           alreadyCheckedElems.insert(curElem);
10780           double x=0, y=0, z=0;
10781           int nb = 0;
10782           SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10783           while ( nodeItr2->more() )
10784           {
10785             const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10786             x += anotherNode->X();
10787             y += anotherNode->Y();
10788             z += anotherNode->Z();
10789             nb++;
10790           }
10791           gp_XYZ p;
10792           p.SetCoord( x/nb -aNode->X(),
10793                       y/nb -aNode->Y(),
10794                       z/nb -aNode->Z() );
10795           MESSAGE("      check " << p.X() << " " << p.Y() << " " << p.Z());
10796           if (normal*p > 0)
10797           {
10798             MESSAGE("    --- inserted")
10799             theAffectedElems.insert( curElem );
10800           }
10801           else if (curElem->GetType() == SMDSAbs_Edge)
10802             edgesToCheck.insert(curElem);
10803         }
10804       }
10805     }
10806     // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10807     std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10808     for( ; eit != edgesToCheck.end(); eit++)
10809     {
10810       bool onside = true;
10811       const SMDS_MeshElement* anEdge = *eit;
10812       SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10813       while ( nodeItr->more() )
10814       {
10815         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10816         if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10817         {
10818           onside = false;
10819           break;
10820         }
10821       }
10822       if (onside)
10823       {
10824         MESSAGE("    --- edge onside inserted")
10825         theAffectedElems.insert(anEdge);
10826       }
10827     }
10828   }
10829   else
10830   {
10831     const double aTol = Precision::Confusion();
10832     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10833     auto_ptr<_FaceClassifier>              aFaceClassifier;
10834     if ( theShape.ShapeType() == TopAbs_SOLID )
10835     {
10836       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10837       bsc3d->PerformInfinitePoint(aTol);
10838     }
10839     else if (theShape.ShapeType() == TopAbs_FACE )
10840     {
10841       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10842     }
10843
10844     // iterates on indicated elements and get elements by back references from their nodes
10845     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10846     int ielem;
10847     for ( ielem = 1;  elemItr != theElems.end(); ++elemItr )
10848     {
10849       MESSAGE("element " << ielem++);
10850       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10851       if (!anElem)
10852         continue;
10853       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10854       while ( nodeItr->more() )
10855       {
10856         MESSAGE("  noeud ");
10857         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10858         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10859           continue;
10860         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10861         while ( backElemItr->more() )
10862         {
10863           MESSAGE("    backelem ");
10864           const SMDS_MeshElement* curElem = backElemItr->next();
10865           if ( curElem && theElems.find(curElem) == theElems.end() &&
10866               ( bsc3d.get() ?
10867                 isInside( curElem, *bsc3d, aTol ) :
10868                 isInside( curElem, *aFaceClassifier, aTol )))
10869             theAffectedElems.insert( curElem );
10870         }
10871       }
10872     }
10873   }
10874   return true;
10875 }
10876
10877 //================================================================================
10878 /*!
10879   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10880   \param theElems - group of of elements (edges or faces) to be replicated
10881   \param theNodesNot - group of nodes not to replicate
10882   \param theShape - shape to detect affected elements (element which geometric center
10883   located on or inside shape).
10884   The replicated nodes should be associated to affected elements.
10885   \return TRUE if operation has been completed successfully, FALSE otherwise
10886 */
10887 //================================================================================
10888
10889 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10890                                             const TIDSortedElemSet& theNodesNot,
10891                                             const TopoDS_Shape&     theShape )
10892 {
10893   if ( theShape.IsNull() )
10894     return false;
10895
10896   const double aTol = Precision::Confusion();
10897   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10898   auto_ptr<_FaceClassifier>              aFaceClassifier;
10899   if ( theShape.ShapeType() == TopAbs_SOLID )
10900   {
10901     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10902     bsc3d->PerformInfinitePoint(aTol);
10903   }
10904   else if (theShape.ShapeType() == TopAbs_FACE )
10905   {
10906     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10907   }
10908
10909   // iterates on indicated elements and get elements by back references from their nodes
10910   TIDSortedElemSet anAffected;
10911   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10912   for ( ;  elemItr != theElems.end(); ++elemItr )
10913   {
10914     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10915     if (!anElem)
10916       continue;
10917
10918     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10919     while ( nodeItr->more() )
10920     {
10921       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10922       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10923         continue;
10924       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10925       while ( backElemItr->more() )
10926       {
10927         const SMDS_MeshElement* curElem = backElemItr->next();
10928         if ( curElem && theElems.find(curElem) == theElems.end() &&
10929              ( bsc3d.get() ?
10930                isInside( curElem, *bsc3d, aTol ) :
10931                isInside( curElem, *aFaceClassifier, aTol )))
10932           anAffected.insert( curElem );
10933       }
10934     }
10935   }
10936   return DoubleNodes( theElems, theNodesNot, anAffected );
10937 }
10938
10939 /*!
10940  *  \brief compute an oriented angle between two planes defined by four points.
10941  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10942  *  @param p0 base of the rotation axe
10943  *  @param p1 extremity of the rotation axe
10944  *  @param g1 belongs to the first plane
10945  *  @param g2 belongs to the second plane
10946  */
10947 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10948 {
10949 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10950 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10951 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10952 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10953   gp_Vec vref(p0, p1);
10954   gp_Vec v1(p0, g1);
10955   gp_Vec v2(p0, g2);
10956   gp_Vec n1 = vref.Crossed(v1);
10957   gp_Vec n2 = vref.Crossed(v2);
10958   try {
10959     return n2.AngleWithRef(n1, vref);
10960   }
10961   catch ( Standard_Failure ) {
10962   }
10963   return Max( v1.Magnitude(), v2.Magnitude() );
10964 }
10965
10966 /*!
10967  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10968  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10969  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10970  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10971  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10972  * 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.
10973  * 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.
10974  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10975  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10976  * \param theElems - list of groups of volumes, where a group of volume is a set of
10977  *        SMDS_MeshElements sorted by Id.
10978  * \param createJointElems - if TRUE, create the elements
10979  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10980  *        the boundary between \a theDomains and the rest mesh
10981  * \return TRUE if operation has been completed successfully, FALSE otherwise
10982  */
10983 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10984                                                      bool                                 createJointElems,
10985                                                      bool                                 onAllBoundaries)
10986 {
10987   MESSAGE("----------------------------------------------");
10988   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10989   MESSAGE("----------------------------------------------");
10990
10991   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10992   meshDS->BuildDownWardConnectivity(true);
10993   CHRONO(50);
10994   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10995
10996   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10997   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10998   //     build the list of nodes shared by 2 or more domains, with their domain indexes
10999
11000   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11001   std::map<int,int>celldom; // cell vtkId --> domain
11002   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11003   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11004   faceDomains.clear();
11005   celldom.clear();
11006   cellDomains.clear();
11007   nodeDomains.clear();
11008   std::map<int,int> emptyMap;
11009   std::set<int> emptySet;
11010   emptyMap.clear();
11011
11012   MESSAGE(".. Number of domains :"<<theElems.size());
11013
11014   TIDSortedElemSet theRestDomElems;
11015   const int iRestDom  = -1;
11016   const int idom0     = onAllBoundaries ? iRestDom : 0;
11017   const int nbDomains = theElems.size();
11018
11019   // Check if the domains do not share an element
11020   for (int idom = 0; idom < nbDomains-1; idom++)
11021     {
11022 //       MESSAGE("... Check of domain #" << idom);
11023       const TIDSortedElemSet& domain = theElems[idom];
11024       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11025       for (; elemItr != domain.end(); ++elemItr)
11026         {
11027           const SMDS_MeshElement* anElem = *elemItr;
11028           int idombisdeb = idom + 1 ;
11029           for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
11030           {
11031             const TIDSortedElemSet& domainbis = theElems[idombis];
11032             if ( domainbis.count(anElem) )
11033             {
11034               MESSAGE(".... Domain #" << idom);
11035               MESSAGE(".... Domain #" << idombis);
11036               throw SALOME_Exception("The domains are not disjoint.");
11037               return false ;
11038             }
11039           }
11040         }
11041     }
11042
11043   for (int idom = 0; idom < nbDomains; idom++)
11044     {
11045
11046       // --- build a map (face to duplicate --> volume to modify)
11047       //     with all the faces shared by 2 domains (group of elements)
11048       //     and corresponding volume of this domain, for each shared face.
11049       //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11050
11051       MESSAGE("... Neighbors of domain #" << idom);
11052       const TIDSortedElemSet& domain = theElems[idom];
11053       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11054       for (; elemItr != domain.end(); ++elemItr)
11055         {
11056           const SMDS_MeshElement* anElem = *elemItr;
11057           if (!anElem)
11058             continue;
11059           int vtkId = anElem->getVtkId();
11060           //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11061           int neighborsVtkIds[NBMAXNEIGHBORS];
11062           int downIds[NBMAXNEIGHBORS];
11063           unsigned char downTypes[NBMAXNEIGHBORS];
11064           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11065           for (int n = 0; n < nbNeighbors; n++)
11066             {
11067               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11068               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11069               if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11070                 {
11071                   bool ok = false ;
11072                   for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11073                   {
11074                     // MESSAGE("Domain " << idombis);
11075                     const TIDSortedElemSet& domainbis = theElems[idombis];
11076                     if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11077                   }
11078                   if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11079                   {
11080                     DownIdType face(downIds[n], downTypes[n]);
11081                     if (!faceDomains[face].count(idom))
11082                       {
11083                         faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11084                         celldom[vtkId] = idom;
11085                         //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11086                       }
11087                     if ( !ok )
11088                     {
11089                       theRestDomElems.insert( elem );
11090                       faceDomains[face][iRestDom] = neighborsVtkIds[n];
11091                       celldom[neighborsVtkIds[n]] = iRestDom;
11092                     }
11093                   }
11094                 }
11095             }
11096         }
11097     }
11098
11099   //MESSAGE("Number of shared faces " << faceDomains.size());
11100   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11101
11102   // --- explore the shared faces domain by domain,
11103   //     explore the nodes of the face and see if they belong to a cell in the domain,
11104   //     which has only a node or an edge on the border (not a shared face)
11105
11106   for (int idomain = idom0; idomain < nbDomains; idomain++)
11107     {
11108       //MESSAGE("Domain " << idomain);
11109       const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11110       itface = faceDomains.begin();
11111       for (; itface != faceDomains.end(); ++itface)
11112         {
11113           const std::map<int, int>& domvol = itface->second;
11114           if (!domvol.count(idomain))
11115             continue;
11116           DownIdType face = itface->first;
11117           //MESSAGE(" --- face " << face.cellId);
11118           std::set<int> oldNodes;
11119           oldNodes.clear();
11120           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11121           std::set<int>::iterator itn = oldNodes.begin();
11122           for (; itn != oldNodes.end(); ++itn)
11123             {
11124               int oldId = *itn;
11125               //MESSAGE("     node " << oldId);
11126               vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11127               for (int i=0; i<l.ncells; i++)
11128                 {
11129                   int vtkId = l.cells[i];
11130                   const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11131                   if (!domain.count(anElem))
11132                     continue;
11133                   int vtkType = grid->GetCellType(vtkId);
11134                   int downId = grid->CellIdToDownId(vtkId);
11135                   if (downId < 0)
11136                     {
11137                       MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11138                       continue; // not OK at this stage of the algorithm:
11139                                 //no cells created after BuildDownWardConnectivity
11140                     }
11141                   DownIdType aCell(downId, vtkType);
11142                   cellDomains[aCell][idomain] = vtkId;
11143                   celldom[vtkId] = idomain;
11144                   //MESSAGE("       cell " << vtkId << " domain " << idomain);
11145                 }
11146             }
11147         }
11148     }
11149
11150   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11151   //     for each shared face, get the nodes
11152   //     for each node, for each domain of the face, create a clone of the node
11153
11154   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11155   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11156   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11157
11158   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11159   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11160   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11161
11162   MESSAGE(".. Duplication of the nodes");
11163   for (int idomain = idom0; idomain < nbDomains; idomain++)
11164     {
11165       itface = faceDomains.begin();
11166       for (; itface != faceDomains.end(); ++itface)
11167         {
11168           const std::map<int, int>& domvol = itface->second;
11169           if (!domvol.count(idomain))
11170             continue;
11171           DownIdType face = itface->first;
11172           //MESSAGE(" --- face " << face.cellId);
11173           std::set<int> oldNodes;
11174           oldNodes.clear();
11175           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11176           std::set<int>::iterator itn = oldNodes.begin();
11177           for (; itn != oldNodes.end(); ++itn)
11178             {
11179               int oldId = *itn;
11180               if (nodeDomains[oldId].empty())
11181                 {
11182                   nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11183                   //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11184                 }
11185               std::map<int, int>::const_iterator itdom = domvol.begin();
11186               for (; itdom != domvol.end(); ++itdom)
11187                 {
11188                   int idom = itdom->first;
11189                   //MESSAGE("         domain " << idom);
11190                   if (!nodeDomains[oldId].count(idom)) // --- node to clone
11191                     {
11192                       if (nodeDomains[oldId].size() >= 2) // a multiple node
11193                         {
11194                           vector<int> orderedDoms;
11195                           //MESSAGE("multiple node " << oldId);
11196                           if (mutipleNodes.count(oldId))
11197                             orderedDoms = mutipleNodes[oldId];
11198                           else
11199                             {
11200                               map<int,int>::iterator it = nodeDomains[oldId].begin();
11201                               for (; it != nodeDomains[oldId].end(); ++it)
11202                                 orderedDoms.push_back(it->first);
11203                             }
11204                           orderedDoms.push_back(idom); // TODO order ==> push_front or back
11205                           //stringstream txt;
11206                           //for (int i=0; i<orderedDoms.size(); i++)
11207                           //  txt << orderedDoms[i] << " ";
11208                           //MESSAGE("orderedDoms " << txt.str());
11209                           mutipleNodes[oldId] = orderedDoms;
11210                         }
11211                       double *coords = grid->GetPoint(oldId);
11212                       SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11213                       copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11214                       int newId = newNode->getVtkId();
11215                       nodeDomains[oldId][idom] = newId; // cloned node for other domains
11216                       //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11217                     }
11218                 }
11219             }
11220         }
11221     }
11222
11223   MESSAGE(".. Creation of elements");
11224   for (int idomain = idom0; idomain < nbDomains; idomain++)
11225     {
11226       itface = faceDomains.begin();
11227       for (; itface != faceDomains.end(); ++itface)
11228         {
11229           std::map<int, int> domvol = itface->second;
11230           if (!domvol.count(idomain))
11231             continue;
11232           DownIdType face = itface->first;
11233           //MESSAGE(" --- face " << face.cellId);
11234           std::set<int> oldNodes;
11235           oldNodes.clear();
11236           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11237           int nbMultipleNodes = 0;
11238           std::set<int>::iterator itn = oldNodes.begin();
11239           for (; itn != oldNodes.end(); ++itn)
11240             {
11241               int oldId = *itn;
11242               if (mutipleNodes.count(oldId))
11243                 nbMultipleNodes++;
11244             }
11245           if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11246             {
11247               //MESSAGE("multiple Nodes detected on a shared face");
11248               int downId = itface->first.cellId;
11249               unsigned char cellType = itface->first.cellType;
11250               // --- shared edge or shared face ?
11251               if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11252                 {
11253                   int nodes[3];
11254                   int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11255                   for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11256                     if (mutipleNodes.count(nodes[i]))
11257                       if (!mutipleNodesToFace.count(nodes[i]))
11258                         mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11259                 }
11260               else // shared face (between two volumes)
11261                 {
11262                   int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11263                   const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11264                   const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11265                   for (int ie =0; ie < nbEdges; ie++)
11266                     {
11267                       int nodes[3];
11268                       int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11269                       if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11270                         {
11271                           vector<int> vn0 = mutipleNodes[nodes[0]];
11272                           vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11273                           vector<int> doms;
11274                           for (int i0 = 0; i0 < vn0.size(); i0++)
11275                             for (int i1 = 0; i1 < vn1.size(); i1++)
11276                               if (vn0[i0] == vn1[i1])
11277                                 doms.push_back(vn0[i0]);
11278                           if (doms.size() >2)
11279                             {
11280                               //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11281                               double *coords = grid->GetPoint(nodes[0]);
11282                               gp_Pnt p0(coords[0], coords[1], coords[2]);
11283                               coords = grid->GetPoint(nodes[nbNodes - 1]);
11284                               gp_Pnt p1(coords[0], coords[1], coords[2]);
11285                               gp_Pnt gref;
11286                               int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11287                               map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11288                               map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11289                               int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11290                               for (int id=0; id < doms.size(); id++)
11291                                 {
11292                                   int idom = doms[id];
11293                                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11294                                   for (int ivol=0; ivol<nbvol; ivol++)
11295                                     {
11296                                       int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11297                                       SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11298                                       if (domain.count(elem))
11299                                         {
11300                                           SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11301                                           domvol[idom] = svol;
11302                                           //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11303                                           double values[3];
11304                                           vtkIdType npts = 0;
11305                                           vtkIdType* pts = 0;
11306                                           grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11307                                           SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11308                                           if (id ==0)
11309                                             {
11310                                               gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11311                                               angleDom[idom] = 0;
11312                                             }
11313                                           else
11314                                             {
11315                                               gp_Pnt g(values[0], values[1], values[2]);
11316                                               angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11317                                               //MESSAGE("  angle=" << angleDom[idom]);
11318                                             }
11319                                           break;
11320                                         }
11321                                     }
11322                                 }
11323                               map<double, int> sortedDom; // sort domains by angle
11324                               for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11325                                 sortedDom[ia->second] = ia->first;
11326                               vector<int> vnodes;
11327                               vector<int> vdom;
11328                               for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11329                                 {
11330                                   vdom.push_back(ib->second);
11331                                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11332                                 }
11333                               for (int ino = 0; ino < nbNodes; ino++)
11334                                 vnodes.push_back(nodes[ino]);
11335                               edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11336                             }
11337                         }
11338                     }
11339                 }
11340             }
11341         }
11342     }
11343
11344   // --- iterate on shared faces (volumes to modify, face to extrude)
11345   //     get node id's of the face (id SMDS = id VTK)
11346   //     create flat element with old and new nodes if requested
11347
11348   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11349   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11350
11351   std::map<int, std::map<long,int> > nodeQuadDomains;
11352   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11353
11354   MESSAGE(".. Creation of elements: simple junction");
11355   if (createJointElems)
11356     {
11357       int idg;
11358       string joints2DName = "joints2D";
11359       mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11360       SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11361       string joints3DName = "joints3D";
11362       mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11363       SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11364
11365       itface = faceDomains.begin();
11366       for (; itface != faceDomains.end(); ++itface)
11367         {
11368           DownIdType face = itface->first;
11369           std::set<int> oldNodes;
11370           std::set<int>::iterator itn;
11371           oldNodes.clear();
11372           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11373
11374           std::map<int, int> domvol = itface->second;
11375           std::map<int, int>::iterator itdom = domvol.begin();
11376           int dom1 = itdom->first;
11377           int vtkVolId = itdom->second;
11378           itdom++;
11379           int dom2 = itdom->first;
11380           SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11381                                                              nodeQuadDomains);
11382           stringstream grpname;
11383           grpname << "j_";
11384           if (dom1 < dom2)
11385             grpname << dom1 << "_" << dom2;
11386           else
11387             grpname << dom2 << "_" << dom1;
11388           string namegrp = grpname.str();
11389           if (!mapOfJunctionGroups.count(namegrp))
11390             mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11391           SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11392           if (sgrp)
11393             sgrp->Add(vol->GetID());
11394           if (vol->GetType() == SMDSAbs_Volume)
11395             joints3DGrp->Add(vol->GetID());
11396           else if (vol->GetType() == SMDSAbs_Face)
11397             joints2DGrp->Add(vol->GetID());
11398         }
11399     }
11400
11401   // --- create volumes on multiple domain intersection if requested
11402   //     iterate on mutipleNodesToFace
11403   //     iterate on edgesMultiDomains
11404
11405   MESSAGE(".. Creation of elements: multiple junction");
11406   if (createJointElems)
11407     {
11408       // --- iterate on mutipleNodesToFace
11409
11410       std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11411       for (; itn != mutipleNodesToFace.end(); ++itn)
11412         {
11413           int node = itn->first;
11414           vector<int> orderDom = itn->second;
11415           vector<vtkIdType> orderedNodes;
11416           for (int idom = 0; idom <orderDom.size(); idom++)
11417             orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11418             SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11419
11420             stringstream grpname;
11421             grpname << "m2j_";
11422             grpname << 0 << "_" << 0;
11423             int idg;
11424             string namegrp = grpname.str();
11425             if (!mapOfJunctionGroups.count(namegrp))
11426               mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11427             SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11428             if (sgrp)
11429               sgrp->Add(face->GetID());
11430         }
11431
11432       // --- iterate on edgesMultiDomains
11433
11434       std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11435       for (; ite != edgesMultiDomains.end(); ++ite)
11436         {
11437           vector<int> nodes = ite->first;
11438           vector<int> orderDom = ite->second;
11439           vector<vtkIdType> orderedNodes;
11440           if (nodes.size() == 2)
11441             {
11442               //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11443               for (int ino=0; ino < nodes.size(); ino++)
11444                 if (orderDom.size() == 3)
11445                   for (int idom = 0; idom <orderDom.size(); idom++)
11446                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11447                 else
11448                   for (int idom = orderDom.size()-1; idom >=0; idom--)
11449                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11450               SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11451
11452               int idg;
11453               string namegrp = "jointsMultiples";
11454               if (!mapOfJunctionGroups.count(namegrp))
11455                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11456               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11457               if (sgrp)
11458                 sgrp->Add(vol->GetID());
11459             }
11460           else
11461             {
11462               INFOS("Quadratic multiple joints not implemented");
11463               // TODO quadratic nodes
11464             }
11465         }
11466     }
11467
11468   // --- list the explicit faces and edges of the mesh that need to be modified,
11469   //     i.e. faces and edges built with one or more duplicated nodes.
11470   //     associate these faces or edges to their corresponding domain.
11471   //     only the first domain found is kept when a face or edge is shared
11472
11473   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11474   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11475   faceOrEdgeDom.clear();
11476   feDom.clear();
11477
11478   MESSAGE(".. Modification of elements");
11479   for (int idomain = idom0; idomain < nbDomains; idomain++)
11480     {
11481       std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11482       for (; itnod != nodeDomains.end(); ++itnod)
11483         {
11484           int oldId = itnod->first;
11485           //MESSAGE("     node " << oldId);
11486           vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11487           for (int i = 0; i < l.ncells; i++)
11488             {
11489               int vtkId = l.cells[i];
11490               int vtkType = grid->GetCellType(vtkId);
11491               int downId = grid->CellIdToDownId(vtkId);
11492               if (downId < 0)
11493                 continue; // new cells: not to be modified
11494               DownIdType aCell(downId, vtkType);
11495               int volParents[1000];
11496               int nbvol = grid->GetParentVolumes(volParents, vtkId);
11497               for (int j = 0; j < nbvol; j++)
11498                 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11499                   if (!feDom.count(vtkId))
11500                     {
11501                       feDom[vtkId] = idomain;
11502                       faceOrEdgeDom[aCell] = emptyMap;
11503                       faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11504                       //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11505                       //        << " type " << vtkType << " downId " << downId);
11506                     }
11507             }
11508         }
11509     }
11510
11511   // --- iterate on shared faces (volumes to modify, face to extrude)
11512   //     get node id's of the face
11513   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11514
11515   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11516   for (int m=0; m<3; m++)
11517     {
11518       std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11519       itface = (*amap).begin();
11520       for (; itface != (*amap).end(); ++itface)
11521         {
11522           DownIdType face = itface->first;
11523           std::set<int> oldNodes;
11524           std::set<int>::iterator itn;
11525           oldNodes.clear();
11526           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11527           //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11528           std::map<int, int> localClonedNodeIds;
11529
11530           std::map<int, int> domvol = itface->second;
11531           std::map<int, int>::iterator itdom = domvol.begin();
11532           for (; itdom != domvol.end(); ++itdom)
11533             {
11534               int idom = itdom->first;
11535               int vtkVolId = itdom->second;
11536               //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11537               localClonedNodeIds.clear();
11538               for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11539                 {
11540                   int oldId = *itn;
11541                   if (nodeDomains[oldId].count(idom))
11542                     {
11543                       localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11544                       //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11545                     }
11546                 }
11547               meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11548             }
11549         }
11550     }
11551
11552   // Remove empty groups (issue 0022812)
11553   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11554   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11555   {
11556     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11557       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11558   }
11559
11560   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11561   grid->BuildLinks();
11562
11563   CHRONOSTOP(50);
11564   counters::stats();
11565   return true;
11566 }
11567
11568 /*!
11569  * \brief Double nodes on some external faces and create flat elements.
11570  * Flat elements are mainly used by some types of mechanic calculations.
11571  *
11572  * Each group of the list must be constituted of faces.
11573  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11574  * @param theElems - list of groups of faces, where a group of faces is a set of
11575  * SMDS_MeshElements sorted by Id.
11576  * @return TRUE if operation has been completed successfully, FALSE otherwise
11577  */
11578 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11579 {
11580   MESSAGE("-------------------------------------------------");
11581   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11582   MESSAGE("-------------------------------------------------");
11583
11584   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11585
11586   // --- For each group of faces
11587   //     duplicate the nodes, create a flat element based on the face
11588   //     replace the nodes of the faces by their clones
11589
11590   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11591   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11592   clonedNodes.clear();
11593   intermediateNodes.clear();
11594   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11595   mapOfJunctionGroups.clear();
11596
11597   for (int idom = 0; idom < theElems.size(); idom++)
11598     {
11599       const TIDSortedElemSet& domain = theElems[idom];
11600       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11601       for (; elemItr != domain.end(); ++elemItr)
11602         {
11603           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11604           SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11605           if (!aFace)
11606             continue;
11607           // MESSAGE("aFace=" << aFace->GetID());
11608           bool isQuad = aFace->IsQuadratic();
11609           vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11610
11611           // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11612
11613           SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11614           while (nodeIt->more())
11615             {
11616               const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11617               bool isMedium = isQuad && (aFace->IsMediumNode(node));
11618               if (isMedium)
11619                 ln2.push_back(node);
11620               else
11621                 ln0.push_back(node);
11622
11623               const SMDS_MeshNode* clone = 0;
11624               if (!clonedNodes.count(node))
11625                 {
11626                   clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11627                   copyPosition( node, clone );
11628                   clonedNodes[node] = clone;
11629                 }
11630               else
11631                 clone = clonedNodes[node];
11632
11633               if (isMedium)
11634                 ln3.push_back(clone);
11635               else
11636                 ln1.push_back(clone);
11637
11638               const SMDS_MeshNode* inter = 0;
11639               if (isQuad && (!isMedium))
11640                 {
11641                   if (!intermediateNodes.count(node))
11642                     {
11643                       inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11644                       copyPosition( node, inter );
11645                       intermediateNodes[node] = inter;
11646                     }
11647                   else
11648                     inter = intermediateNodes[node];
11649                   ln4.push_back(inter);
11650                 }
11651             }
11652
11653           // --- extrude the face
11654
11655           vector<const SMDS_MeshNode*> ln;
11656           SMDS_MeshVolume* vol = 0;
11657           vtkIdType aType = aFace->GetVtkType();
11658           switch (aType)
11659           {
11660             case VTK_TRIANGLE:
11661               vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11662               // MESSAGE("vol prism " << vol->GetID());
11663               ln.push_back(ln1[0]);
11664               ln.push_back(ln1[1]);
11665               ln.push_back(ln1[2]);
11666               break;
11667             case VTK_QUAD:
11668               vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11669               // MESSAGE("vol hexa " << vol->GetID());
11670               ln.push_back(ln1[0]);
11671               ln.push_back(ln1[1]);
11672               ln.push_back(ln1[2]);
11673               ln.push_back(ln1[3]);
11674               break;
11675             case VTK_QUADRATIC_TRIANGLE:
11676               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11677                                       ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11678               // MESSAGE("vol quad prism " << vol->GetID());
11679               ln.push_back(ln1[0]);
11680               ln.push_back(ln1[1]);
11681               ln.push_back(ln1[2]);
11682               ln.push_back(ln3[0]);
11683               ln.push_back(ln3[1]);
11684               ln.push_back(ln3[2]);
11685               break;
11686             case VTK_QUADRATIC_QUAD:
11687 //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11688 //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11689 //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11690               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11691                                       ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11692                                       ln4[0], ln4[1], ln4[2], ln4[3]);
11693               // MESSAGE("vol quad hexa " << vol->GetID());
11694               ln.push_back(ln1[0]);
11695               ln.push_back(ln1[1]);
11696               ln.push_back(ln1[2]);
11697               ln.push_back(ln1[3]);
11698               ln.push_back(ln3[0]);
11699               ln.push_back(ln3[1]);
11700               ln.push_back(ln3[2]);
11701               ln.push_back(ln3[3]);
11702               break;
11703             case VTK_POLYGON:
11704               break;
11705             default:
11706               break;
11707           }
11708
11709           if (vol)
11710             {
11711               stringstream grpname;
11712               grpname << "jf_";
11713               grpname << idom;
11714               int idg;
11715               string namegrp = grpname.str();
11716               if (!mapOfJunctionGroups.count(namegrp))
11717                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11718               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11719               if (sgrp)
11720                 sgrp->Add(vol->GetID());
11721             }
11722
11723           // --- modify the face
11724
11725           aFace->ChangeNodes(&ln[0], ln.size());
11726         }
11727     }
11728   return true;
11729 }
11730
11731 /*!
11732  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11733  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11734  *  groups of faces to remove inside the object, (idem edges).
11735  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11736  */
11737 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11738                                       const TopoDS_Shape& theShape,
11739                                       SMESH_NodeSearcher* theNodeSearcher,
11740                                       const char* groupName,
11741                                       std::vector<double>&   nodesCoords,
11742                                       std::vector<std::vector<int> >& listOfListOfNodes)
11743 {
11744   MESSAGE("--------------------------------");
11745   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11746   MESSAGE("--------------------------------");
11747
11748   // --- zone of volumes to remove is given :
11749   //     1 either by a geom shape (one or more vertices) and a radius,
11750   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11751   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11752   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11753   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11754   //     defined by it's name.
11755
11756   SMESHDS_GroupBase* groupDS = 0;
11757   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11758   while ( groupIt->more() )
11759     {
11760       groupDS = 0;
11761       SMESH_Group * group = groupIt->next();
11762       if ( !group ) continue;
11763       groupDS = group->GetGroupDS();
11764       if ( !groupDS || groupDS->IsEmpty() ) continue;
11765       std::string grpName = group->GetName();
11766       //MESSAGE("grpName=" << grpName);
11767       if (grpName == groupName)
11768         break;
11769       else
11770         groupDS = 0;
11771     }
11772
11773   bool isNodeGroup = false;
11774   bool isNodeCoords = false;
11775   if (groupDS)
11776     {
11777       if (groupDS->GetType() != SMDSAbs_Node)
11778         return;
11779       isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11780     }
11781
11782   if (nodesCoords.size() > 0)
11783     isNodeCoords = true; // a list o nodes given by their coordinates
11784   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11785
11786   // --- define groups to build
11787
11788   int idg; // --- group of SMDS volumes
11789   string grpvName = groupName;
11790   grpvName += "_vol";
11791   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11792   if (!grp)
11793     {
11794       MESSAGE("group not created " << grpvName);
11795       return;
11796     }
11797   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11798
11799   int idgs; // --- group of SMDS faces on the skin
11800   string grpsName = groupName;
11801   grpsName += "_skin";
11802   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11803   if (!grps)
11804     {
11805       MESSAGE("group not created " << grpsName);
11806       return;
11807     }
11808   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11809
11810   int idgi; // --- group of SMDS faces internal (several shapes)
11811   string grpiName = groupName;
11812   grpiName += "_internalFaces";
11813   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11814   if (!grpi)
11815     {
11816       MESSAGE("group not created " << grpiName);
11817       return;
11818     }
11819   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11820
11821   int idgei; // --- group of SMDS faces internal (several shapes)
11822   string grpeiName = groupName;
11823   grpeiName += "_internalEdges";
11824   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11825   if (!grpei)
11826     {
11827       MESSAGE("group not created " << grpeiName);
11828       return;
11829     }
11830   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11831
11832   // --- build downward connectivity
11833
11834   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11835   meshDS->BuildDownWardConnectivity(true);
11836   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11837
11838   // --- set of volumes detected inside
11839
11840   std::set<int> setOfInsideVol;
11841   std::set<int> setOfVolToCheck;
11842
11843   std::vector<gp_Pnt> gpnts;
11844   gpnts.clear();
11845
11846   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11847     {
11848       MESSAGE("group of nodes provided");
11849       SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11850       while ( elemIt->more() )
11851         {
11852           const SMDS_MeshElement* elem = elemIt->next();
11853           if (!elem)
11854             continue;
11855           const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11856           if (!node)
11857             continue;
11858           SMDS_MeshElement* vol = 0;
11859           SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11860           while (volItr->more())
11861             {
11862               vol = (SMDS_MeshElement*)volItr->next();
11863               setOfInsideVol.insert(vol->getVtkId());
11864               sgrp->Add(vol->GetID());
11865             }
11866         }
11867     }
11868   else if (isNodeCoords)
11869     {
11870       MESSAGE("list of nodes coordinates provided");
11871       int i = 0;
11872       int k = 0;
11873       while (i < nodesCoords.size()-2)
11874         {
11875           double x = nodesCoords[i++];
11876           double y = nodesCoords[i++];
11877           double z = nodesCoords[i++];
11878           gp_Pnt p = gp_Pnt(x, y ,z);
11879           gpnts.push_back(p);
11880           MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11881           k++;
11882         }
11883     }
11884   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11885     {
11886       MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11887       TopTools_IndexedMapOfShape vertexMap;
11888       TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11889       gp_Pnt p = gp_Pnt(0,0,0);
11890       if (vertexMap.Extent() < 1)
11891         return;
11892
11893       for ( int i = 1; i <= vertexMap.Extent(); ++i )
11894         {
11895           const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11896           p = BRep_Tool::Pnt(vertex);
11897           gpnts.push_back(p);
11898           MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11899         }
11900     }
11901
11902   if (gpnts.size() > 0)
11903     {
11904       int nodeId = 0;
11905       const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11906       if (startNode)
11907         nodeId = startNode->GetID();
11908       MESSAGE("nodeId " << nodeId);
11909
11910       double radius2 = radius*radius;
11911       MESSAGE("radius2 " << radius2);
11912
11913       // --- volumes on start node
11914
11915       setOfVolToCheck.clear();
11916       SMDS_MeshElement* startVol = 0;
11917       SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11918       while (volItr->more())
11919         {
11920           startVol = (SMDS_MeshElement*)volItr->next();
11921           setOfVolToCheck.insert(startVol->getVtkId());
11922         }
11923       if (setOfVolToCheck.empty())
11924         {
11925           MESSAGE("No volumes found");
11926           return;
11927         }
11928
11929       // --- starting with central volumes then their neighbors, check if they are inside
11930       //     or outside the domain, until no more new neighbor volume is inside.
11931       //     Fill the group of inside volumes
11932
11933       std::map<int, double> mapOfNodeDistance2;
11934       mapOfNodeDistance2.clear();
11935       std::set<int> setOfOutsideVol;
11936       while (!setOfVolToCheck.empty())
11937         {
11938           std::set<int>::iterator it = setOfVolToCheck.begin();
11939           int vtkId = *it;
11940           MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11941           bool volInside = false;
11942           vtkIdType npts = 0;
11943           vtkIdType* pts = 0;
11944           grid->GetCellPoints(vtkId, npts, pts);
11945           for (int i=0; i<npts; i++)
11946             {
11947               double distance2 = 0;
11948               if (mapOfNodeDistance2.count(pts[i]))
11949                 {
11950                   distance2 = mapOfNodeDistance2[pts[i]];
11951                   MESSAGE("point " << pts[i] << " distance2 " << distance2);
11952                 }
11953               else
11954                 {
11955                   double *coords = grid->GetPoint(pts[i]);
11956                   gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11957                   distance2 = 1.E40;
11958                   for (int j=0; j<gpnts.size(); j++)
11959                     {
11960                       double d2 = aPoint.SquareDistance(gpnts[j]);
11961                       if (d2 < distance2)
11962                         {
11963                           distance2 = d2;
11964                           if (distance2 < radius2)
11965                             break;
11966                         }
11967                     }
11968                   mapOfNodeDistance2[pts[i]] = distance2;
11969                   MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
11970                 }
11971               if (distance2 < radius2)
11972                 {
11973                   volInside = true; // one or more nodes inside the domain
11974                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11975                   break;
11976                 }
11977             }
11978           if (volInside)
11979             {
11980               setOfInsideVol.insert(vtkId);
11981               MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11982               int neighborsVtkIds[NBMAXNEIGHBORS];
11983               int downIds[NBMAXNEIGHBORS];
11984               unsigned char downTypes[NBMAXNEIGHBORS];
11985               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11986               for (int n = 0; n < nbNeighbors; n++)
11987                 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11988                   setOfVolToCheck.insert(neighborsVtkIds[n]);
11989             }
11990           else
11991             {
11992               setOfOutsideVol.insert(vtkId);
11993               MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11994             }
11995           setOfVolToCheck.erase(vtkId);
11996         }
11997     }
11998
11999   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12000   //     If yes, add the volume to the inside set
12001
12002   bool addedInside = true;
12003   std::set<int> setOfVolToReCheck;
12004   while (addedInside)
12005     {
12006       MESSAGE(" --------------------------- re check");
12007       addedInside = false;
12008       std::set<int>::iterator itv = setOfInsideVol.begin();
12009       for (; itv != setOfInsideVol.end(); ++itv)
12010         {
12011           int vtkId = *itv;
12012           int neighborsVtkIds[NBMAXNEIGHBORS];
12013           int downIds[NBMAXNEIGHBORS];
12014           unsigned char downTypes[NBMAXNEIGHBORS];
12015           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12016           for (int n = 0; n < nbNeighbors; n++)
12017             if (!setOfInsideVol.count(neighborsVtkIds[n]))
12018               setOfVolToReCheck.insert(neighborsVtkIds[n]);
12019         }
12020       setOfVolToCheck = setOfVolToReCheck;
12021       setOfVolToReCheck.clear();
12022       while  (!setOfVolToCheck.empty())
12023         {
12024           std::set<int>::iterator it = setOfVolToCheck.begin();
12025           int vtkId = *it;
12026           if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12027             {
12028               MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12029               int countInside = 0;
12030               int neighborsVtkIds[NBMAXNEIGHBORS];
12031               int downIds[NBMAXNEIGHBORS];
12032               unsigned char downTypes[NBMAXNEIGHBORS];
12033               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12034               for (int n = 0; n < nbNeighbors; n++)
12035                 if (setOfInsideVol.count(neighborsVtkIds[n]))
12036                   countInside++;
12037               MESSAGE("countInside " << countInside);
12038               if (countInside > 1)
12039                 {
12040                   MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12041                   setOfInsideVol.insert(vtkId);
12042                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12043                   addedInside = true;
12044                 }
12045               else
12046                 setOfVolToReCheck.insert(vtkId);
12047             }
12048           setOfVolToCheck.erase(vtkId);
12049         }
12050     }
12051
12052   // --- map of Downward faces at the boundary, inside the global volume
12053   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12054   //     fill group of SMDS faces inside the volume (when several volume shapes)
12055   //     fill group of SMDS faces on the skin of the global volume (if skin)
12056
12057   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12058   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12059   std::set<int>::iterator it = setOfInsideVol.begin();
12060   for (; it != setOfInsideVol.end(); ++it)
12061     {
12062       int vtkId = *it;
12063       //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12064       int neighborsVtkIds[NBMAXNEIGHBORS];
12065       int downIds[NBMAXNEIGHBORS];
12066       unsigned char downTypes[NBMAXNEIGHBORS];
12067       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12068       for (int n = 0; n < nbNeighbors; n++)
12069         {
12070           int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12071           if (neighborDim == 3)
12072             {
12073               if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12074                 {
12075                   DownIdType face(downIds[n], downTypes[n]);
12076                   boundaryFaces[face] = vtkId;
12077                 }
12078               // if the face between to volumes is in the mesh, get it (internal face between shapes)
12079               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12080               if (vtkFaceId >= 0)
12081                 {
12082                   sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12083                   // find also the smds edges on this face
12084                   int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12085                   const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12086                   const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12087                   for (int i = 0; i < nbEdges; i++)
12088                     {
12089                       int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12090                       if (vtkEdgeId >= 0)
12091                         sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12092                     }
12093                 }
12094             }
12095           else if (neighborDim == 2) // skin of the volume
12096             {
12097               DownIdType face(downIds[n], downTypes[n]);
12098               skinFaces[face] = vtkId;
12099               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12100               if (vtkFaceId >= 0)
12101                 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12102             }
12103         }
12104     }
12105
12106   // --- identify the edges constituting the wire of each subshape on the skin
12107   //     define polylines with the nodes of edges, equivalent to wires
12108   //     project polylines on subshapes, and partition, to get geom faces
12109
12110   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12111   std::set<int> emptySet;
12112   emptySet.clear();
12113   std::set<int> shapeIds;
12114
12115   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12116   while (itelem->more())
12117     {
12118       const SMDS_MeshElement *elem = itelem->next();
12119       int shapeId = elem->getshapeId();
12120       int vtkId = elem->getVtkId();
12121       if (!shapeIdToVtkIdSet.count(shapeId))
12122         {
12123           shapeIdToVtkIdSet[shapeId] = emptySet;
12124           shapeIds.insert(shapeId);
12125         }
12126       shapeIdToVtkIdSet[shapeId].insert(vtkId);
12127     }
12128
12129   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12130   std::set<DownIdType, DownIdCompare> emptyEdges;
12131   emptyEdges.clear();
12132
12133   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12134   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12135     {
12136       int shapeId = itShape->first;
12137       MESSAGE(" --- Shape ID --- "<< shapeId);
12138       shapeIdToEdges[shapeId] = emptyEdges;
12139
12140       std::vector<int> nodesEdges;
12141
12142       std::set<int>::iterator its = itShape->second.begin();
12143       for (; its != itShape->second.end(); ++its)
12144         {
12145           int vtkId = *its;
12146           MESSAGE("     " << vtkId);
12147           int neighborsVtkIds[NBMAXNEIGHBORS];
12148           int downIds[NBMAXNEIGHBORS];
12149           unsigned char downTypes[NBMAXNEIGHBORS];
12150           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12151           for (int n = 0; n < nbNeighbors; n++)
12152             {
12153               if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12154                 continue;
12155               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12156               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12157               if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12158                 {
12159                   DownIdType edge(downIds[n], downTypes[n]);
12160                   if (!shapeIdToEdges[shapeId].count(edge))
12161                     {
12162                       shapeIdToEdges[shapeId].insert(edge);
12163                       int vtkNodeId[3];
12164                       int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12165                       nodesEdges.push_back(vtkNodeId[0]);
12166                       nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12167                       MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12168                     }
12169                 }
12170             }
12171         }
12172
12173       std::list<int> order;
12174       order.clear();
12175       if (nodesEdges.size() > 0)
12176         {
12177           order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12178           nodesEdges[0] = -1;
12179           order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
12180           nodesEdges[1] = -1; // do not reuse this edge
12181           bool found = true;
12182           while (found)
12183             {
12184               int nodeTofind = order.back(); // try first to push back
12185               int i = 0;
12186               for (i = 0; i<nodesEdges.size(); i++)
12187                 if (nodesEdges[i] == nodeTofind)
12188                   break;
12189               if (i == nodesEdges.size())
12190                 found = false; // no follower found on back
12191               else
12192                 {
12193                   if (i%2) // odd ==> use the previous one
12194                     if (nodesEdges[i-1] < 0)
12195                       found = false;
12196                     else
12197                       {
12198                         order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
12199                         nodesEdges[i-1] = -1;
12200                       }
12201                   else // even ==> use the next one
12202                     if (nodesEdges[i+1] < 0)
12203                       found = false;
12204                     else
12205                       {
12206                         order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
12207                         nodesEdges[i+1] = -1;
12208                       }
12209                 }
12210               if (found)
12211                 continue;
12212               // try to push front
12213               found = true;
12214               nodeTofind = order.front(); // try to push front
12215               for (i = 0; i<nodesEdges.size(); i++)
12216                 if (nodesEdges[i] == nodeTofind)
12217                   break;
12218               if (i == nodesEdges.size())
12219                 {
12220                   found = false; // no predecessor found on front
12221                   continue;
12222                 }
12223               if (i%2) // odd ==> use the previous one
12224                 if (nodesEdges[i-1] < 0)
12225                   found = false;
12226                 else
12227                   {
12228                     order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
12229                     nodesEdges[i-1] = -1;
12230                   }
12231               else // even ==> use the next one
12232                 if (nodesEdges[i+1] < 0)
12233                   found = false;
12234                 else
12235                   {
12236                     order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
12237                     nodesEdges[i+1] = -1;
12238                   }
12239             }
12240         }
12241
12242
12243       std::vector<int> nodes;
12244       nodes.push_back(shapeId);
12245       std::list<int>::iterator itl = order.begin();
12246       for (; itl != order.end(); itl++)
12247         {
12248           nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12249           MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12250         }
12251       listOfListOfNodes.push_back(nodes);
12252     }
12253
12254   //     partition geom faces with blocFissure
12255   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12256   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12257
12258   return;
12259 }
12260
12261
12262 //================================================================================
12263 /*!
12264  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12265  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12266  * \return TRUE if operation has been completed successfully, FALSE otherwise
12267  */
12268 //================================================================================
12269
12270 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12271 {
12272   // iterates on volume elements and detect all free faces on them
12273   SMESHDS_Mesh* aMesh = GetMeshDS();
12274   if (!aMesh)
12275     return false;
12276   //bool res = false;
12277   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12278   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12279   while(vIt->more())
12280   {
12281     const SMDS_MeshVolume* volume = vIt->next();
12282     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12283     vTool.SetExternalNormal();
12284     //const bool isPoly = volume->IsPoly();
12285     const int iQuad = volume->IsQuadratic();
12286     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12287     {
12288       if (!vTool.IsFreeFace(iface))
12289         continue;
12290       nbFree++;
12291       vector<const SMDS_MeshNode *> nodes;
12292       int nbFaceNodes = vTool.NbFaceNodes(iface);
12293       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12294       int inode = 0;
12295       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12296         nodes.push_back(faceNodes[inode]);
12297       if (iQuad) { // add medium nodes
12298         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12299           nodes.push_back(faceNodes[inode]);
12300         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12301           nodes.push_back(faceNodes[8]);
12302       }
12303       // add new face based on volume nodes
12304       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12305         nbExisted++;
12306         continue; // face already exsist
12307       }
12308       AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12309       nbCreated++;
12310     }
12311   }
12312   return ( nbFree==(nbExisted+nbCreated) );
12313 }
12314
12315 namespace
12316 {
12317   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12318   {
12319     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12320       return n;
12321     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12322   }
12323 }
12324 //================================================================================
12325 /*!
12326  * \brief Creates missing boundary elements
12327  *  \param elements - elements whose boundary is to be checked
12328  *  \param dimension - defines type of boundary elements to create
12329  *  \param group - a group to store created boundary elements in
12330  *  \param targetMesh - a mesh to store created boundary elements in
12331  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12332  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12333  *                                boundary elements will be copied into the targetMesh
12334  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12335  *                                boundary elements will be added into the new group
12336  *  \param aroundElements - if true, elements will be created on boundary of given
12337  *                          elements else, on boundary of the whole mesh.
12338  * \return nb of added boundary elements
12339  */
12340 //================================================================================
12341
12342 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12343                                        Bnd_Dimension           dimension,
12344                                        SMESH_Group*            group/*=0*/,
12345                                        SMESH_Mesh*             targetMesh/*=0*/,
12346                                        bool                    toCopyElements/*=false*/,
12347                                        bool                    toCopyExistingBoundary/*=false*/,
12348                                        bool                    toAddExistingBondary/*= false*/,
12349                                        bool                    aroundElements/*= false*/)
12350 {
12351   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12352   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12353   // hope that all elements are of the same type, do not check them all
12354   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12355     throw SALOME_Exception(LOCALIZED("wrong element type"));
12356
12357   if ( !targetMesh )
12358     toCopyElements = toCopyExistingBoundary = false;
12359
12360   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12361   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12362   int nbAddedBnd = 0;
12363
12364   // editor adding present bnd elements and optionally holding elements to add to the group
12365   SMESH_MeshEditor* presentEditor;
12366   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12367   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12368
12369   SMESH_MesherHelper helper( *myMesh );
12370   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12371   SMDS_VolumeTool vTool;
12372   TIDSortedElemSet avoidSet;
12373   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12374   int inode;
12375
12376   typedef vector<const SMDS_MeshNode*> TConnectivity;
12377
12378   SMDS_ElemIteratorPtr eIt;
12379   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12380   else                  eIt = elemSetIterator( elements );
12381
12382   while (eIt->more())
12383   {
12384     const SMDS_MeshElement* elem = eIt->next();
12385     const int              iQuad = elem->IsQuadratic();
12386
12387     // ------------------------------------------------------------------------------------
12388     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12389     // ------------------------------------------------------------------------------------
12390     vector<const SMDS_MeshElement*> presentBndElems;
12391     vector<TConnectivity>           missingBndElems;
12392     TConnectivity nodes, elemNodes;
12393     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12394     {
12395       vTool.SetExternalNormal();
12396       const SMDS_MeshElement* otherVol = 0;
12397       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12398       {
12399         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12400              ( !aroundElements || elements.count( otherVol )))
12401           continue;
12402         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12403         const int    nbFaceNodes = vTool.NbFaceNodes (iface);
12404         if ( missType == SMDSAbs_Edge ) // boundary edges
12405         {
12406           nodes.resize( 2+iQuad );
12407           for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12408           {
12409             for ( int j = 0; j < nodes.size(); ++j )
12410               nodes[j] =nn[i+j];
12411             if ( const SMDS_MeshElement* edge =
12412                  aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12413               presentBndElems.push_back( edge );
12414             else
12415               missingBndElems.push_back( nodes );
12416           }
12417         }
12418         else // boundary face
12419         {
12420           nodes.clear();
12421           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12422             nodes.push_back( nn[inode] ); // add corner nodes
12423           if (iQuad)
12424             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12425               nodes.push_back( nn[inode] ); // add medium nodes
12426           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12427           if ( iCenter > 0 )
12428             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12429
12430           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12431                                                                SMDSAbs_Face, /*noMedium=*/false ))
12432             presentBndElems.push_back( f );
12433           else
12434             missingBndElems.push_back( nodes );
12435
12436           if ( targetMesh != myMesh )
12437           {
12438             // add 1D elements on face boundary to be added to a new mesh
12439             const SMDS_MeshElement* edge;
12440             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12441             {
12442               if ( iQuad )
12443                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12444               else
12445                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12446               if ( edge && avoidSet.insert( edge ).second )
12447                 presentBndElems.push_back( edge );
12448             }
12449           }
12450         }
12451       }
12452     }
12453     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12454     {
12455       avoidSet.clear(), avoidSet.insert( elem );
12456       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12457                         SMDS_MeshElement::iterator() );
12458       elemNodes.push_back( elemNodes[0] );
12459       nodes.resize( 2 + iQuad );
12460       const int nbLinks = elem->NbCornerNodes();
12461       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12462       {
12463         nodes[0] = elemNodes[iN];
12464         nodes[1] = elemNodes[iN+1+iQuad];
12465         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12466           continue; // not free link
12467
12468         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12469         if ( const SMDS_MeshElement* edge =
12470              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12471           presentBndElems.push_back( edge );
12472         else
12473           missingBndElems.push_back( nodes );
12474       }
12475     }
12476
12477     // ---------------------------------
12478     // 2. Add missing boundary elements
12479     // ---------------------------------
12480     if ( targetMesh != myMesh )
12481       // instead of making a map of nodes in this mesh and targetMesh,
12482       // we create nodes with same IDs.
12483       for ( int i = 0; i < missingBndElems.size(); ++i )
12484       {
12485         TConnectivity& srcNodes = missingBndElems[i];
12486         TConnectivity  nodes( srcNodes.size() );
12487         for ( inode = 0; inode < nodes.size(); ++inode )
12488           nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12489         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12490                                                                    missType,
12491                                                                    /*noMedium=*/false))
12492           continue;
12493         tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12494         ++nbAddedBnd;
12495       }
12496     else
12497       for ( int i = 0; i < missingBndElems.size(); ++i )
12498       {
12499         TConnectivity& nodes = missingBndElems[i];
12500         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12501                                                                    missType,
12502                                                                    /*noMedium=*/false))
12503           continue;
12504         SMDS_MeshElement* elem =
12505           tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12506         ++nbAddedBnd;
12507
12508         // try to set a new element to a shape
12509         if ( myMesh->HasShapeToMesh() )
12510         {
12511           bool ok = true;
12512           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12513           const int nbN = nodes.size() / (iQuad+1 );
12514           for ( inode = 0; inode < nbN && ok; ++inode )
12515           {
12516             pair<int, TopAbs_ShapeEnum> i_stype =
12517               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12518             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12519               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12520           }
12521           if ( ok && mediumShapes.size() > 1 )
12522           {
12523             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12524             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12525             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12526             {
12527               if (( ok = ( stype_i->first != stype_i_0.first )))
12528                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12529                                         aMesh->IndexToShape( stype_i_0.second ));
12530             }
12531           }
12532           if ( ok && mediumShapes.begin()->first == missShapeType )
12533             aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12534         }
12535       }
12536
12537     // ----------------------------------
12538     // 3. Copy present boundary elements
12539     // ----------------------------------
12540     if ( toCopyExistingBoundary )
12541       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12542       {
12543         const SMDS_MeshElement* e = presentBndElems[i];
12544         TConnectivity nodes( e->NbNodes() );
12545         for ( inode = 0; inode < nodes.size(); ++inode )
12546           nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12547         presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12548       }
12549     else // store present elements to add them to a group
12550       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12551       {
12552         presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12553       }
12554
12555   } // loop on given elements
12556
12557   // ---------------------------------------------
12558   // 4. Fill group with boundary elements
12559   // ---------------------------------------------
12560   if ( group )
12561   {
12562     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12563       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12564         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12565   }
12566   tgtEditor.myLastCreatedElems.Clear();
12567   tgtEditor2.myLastCreatedElems.Clear();
12568
12569   // -----------------------
12570   // 5. Copy given elements
12571   // -----------------------
12572   if ( toCopyElements && targetMesh != myMesh )
12573   {
12574     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12575     else                  eIt = elemSetIterator( elements );
12576     while (eIt->more())
12577     {
12578       const SMDS_MeshElement* elem = eIt->next();
12579       TConnectivity nodes( elem->NbNodes() );
12580       for ( inode = 0; inode < nodes.size(); ++inode )
12581         nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12582       tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12583
12584       tgtEditor.myLastCreatedElems.Clear();
12585     }
12586   }
12587   return nbAddedBnd;
12588 }
12589
12590 //================================================================================
12591 /*!
12592  * \brief Copy node position and set \a to node on the same geometry
12593  */
12594 //================================================================================
12595
12596 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12597                                      const SMDS_MeshNode* to )
12598 {
12599   if ( !from || !to ) return;
12600
12601   SMDS_PositionPtr pos = from->GetPosition();
12602   if ( !pos || from->getshapeId() < 1 ) return;
12603
12604   switch ( pos->GetTypeOfPosition() )
12605   {
12606   case SMDS_TOP_3DSPACE: break;
12607
12608   case SMDS_TOP_FACE:
12609   {
12610     const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12611     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12612                                 fPos->GetUParameter(), fPos->GetVParameter() );
12613     break;
12614   }
12615   case SMDS_TOP_EDGE:
12616   {
12617     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12618     const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12619     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12620     break;
12621   }
12622   case SMDS_TOP_VERTEX:
12623   {
12624     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12625     break;
12626   }
12627   case SMDS_TOP_UNSPEC:
12628   default:;
12629   }
12630 }