Salome HOME
Regression of XSMESH_TEST/SMESHCOMMON/SMESH_TEST/Grids/smesh/bugs12/M6
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2013  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.
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_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
48
49 #include <Basics_OCCTVersion.hxx>
50
51 #include "utilities.h"
52
53 #include <BRepAdaptor_Surface.hxx>
54 #include <BRepBuilderAPI_MakeEdge.hxx>
55 #include <BRepClass3d_SolidClassifier.hxx>
56 #include <BRep_Tool.hxx>
57 #include <ElCLib.hxx>
58 #include <Extrema_GenExtPS.hxx>
59 #include <Extrema_POnCurv.hxx>
60 #include <Extrema_POnSurf.hxx>
61 #include <GC_MakeSegment.hxx>
62 #include <Geom2d_Curve.hxx>
63 #include <GeomAPI_ExtremaCurveCurve.hxx>
64 #include <GeomAdaptor_Surface.hxx>
65 #include <Geom_Curve.hxx>
66 #include <Geom_Line.hxx>
67 #include <Geom_Surface.hxx>
68 #include <IntAna_IntConicQuad.hxx>
69 #include <IntAna_Quadric.hxx>
70 #include <Precision.hxx>
71 #include <TColStd_ListOfInteger.hxx>
72 #include <TopAbs_State.hxx>
73 #include <TopExp.hxx>
74 #include <TopExp_Explorer.hxx>
75 #include <TopTools_ListIteratorOfListOfShape.hxx>
76 #include <TopTools_ListOfShape.hxx>
77 #include <TopTools_SequenceOfShape.hxx>
78 #include <TopoDS.hxx>
79 #include <TopoDS_Face.hxx>
80 #include <TopoDS_Solid.hxx>
81 #include <gp.hxx>
82 #include <gp_Ax1.hxx>
83 #include <gp_Dir.hxx>
84 #include <gp_Lin.hxx>
85 #include <gp_Pln.hxx>
86 #include <gp_Trsf.hxx>
87 #include <gp_Vec.hxx>
88 #include <gp_XY.hxx>
89 #include <gp_XYZ.hxx>
90
91 #include <cmath>
92
93 #include <map>
94 #include <set>
95 #include <numeric>
96 #include <limits>
97 #include <algorithm>
98 #include <sstream>
99
100 #include <boost/tuple/tuple.hpp>
101
102 #include <Standard_Failure.hxx>
103 #include <Standard_ErrorHandler.hxx>
104
105 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
106
107 using namespace std;
108 using namespace SMESH::Controls;
109
110 typedef SMDS_SetIterator< SMDS_pElement, TIDSortedElemSet::const_iterator> TSetIterator;
111
112 //=======================================================================
113 //function : SMESH_MeshEditor
114 //purpose  :
115 //=======================================================================
116
117 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
118   :myMesh( theMesh ) // theMesh may be NULL
119 {
120 }
121
122 //================================================================================
123 /*!
124  * \brief Clears myLastCreatedNodes and myLastCreatedElems
125  */
126 //================================================================================
127
128 void SMESH_MeshEditor::CrearLastCreated()
129 {
130   myLastCreatedNodes.Clear();
131   myLastCreatedElems.Clear();
132 }
133
134
135 //=======================================================================
136 /*!
137  * \brief Add element
138  */
139 //=======================================================================
140
141 SMDS_MeshElement*
142 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
143                              const SMDSAbs_ElementType            type,
144                              const bool                           isPoly,
145                              const int                            ID,
146                              const double                         ballDiameter)
147 {
148   //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
149   SMDS_MeshElement* e = 0;
150   int nbnode = node.size();
151   SMESHDS_Mesh* mesh = GetMeshDS();
152   switch ( type ) {
153   case SMDSAbs_Face:
154     if ( !isPoly ) {
155       if      (nbnode == 3) {
156         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
157         else           e = mesh->AddFace      (node[0], node[1], node[2] );
158       }
159       else if (nbnode == 4) {
160         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
161         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
162       }
163       else if (nbnode == 6) {
164         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
165                                                node[4], node[5], ID);
166         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
167                                                node[4], node[5] );
168       }
169       else if (nbnode == 8) {
170         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
171                                                node[4], node[5], node[6], node[7], ID);
172         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
173                                                node[4], node[5], node[6], node[7] );
174       }
175       else if (nbnode == 9) {
176         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
177                                                node[4], node[5], node[6], node[7], node[8], ID);
178         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
179                                                node[4], node[5], node[6], node[7], node[8] );
180       }
181     } else {
182       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
183       else           e = mesh->AddPolygonalFace      (node    );
184     }
185     break;
186
187   case SMDSAbs_Volume:
188     if ( !isPoly ) {
189       if      (nbnode == 4) {
190         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
191         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
192       }
193       else if (nbnode == 5) {
194         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
195                                                  node[4], ID);
196         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
197                                                  node[4] );
198       }
199       else if (nbnode == 6) {
200         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
201                                                  node[4], node[5], ID);
202         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
203                                                  node[4], node[5] );
204       }
205       else if (nbnode == 8) {
206         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
207                                                  node[4], node[5], node[6], node[7], ID);
208         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
209                                                  node[4], node[5], node[6], node[7] );
210       }
211       else if (nbnode == 10) {
212         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
213                                                  node[4], node[5], node[6], node[7],
214                                                  node[8], node[9], ID);
215         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
216                                                  node[4], node[5], node[6], node[7],
217                                                  node[8], node[9] );
218       }
219       else if (nbnode == 12) {
220         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
221                                                  node[4], node[5], node[6], node[7],
222                                                  node[8], node[9], node[10], node[11], ID);
223         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
224                                                  node[4], node[5], node[6], node[7],
225                                                  node[8], node[9], node[10], node[11] );
226       }
227       else if (nbnode == 13) {
228         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
229                                                  node[4], node[5], node[6], node[7],
230                                                  node[8], node[9], node[10],node[11],
231                                                  node[12],ID);
232         else           e = mesh->AddVolume      (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],
235                                                  node[12] );
236       }
237       else if (nbnode == 15) {
238         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
239                                                  node[4], node[5], node[6], node[7],
240                                                  node[8], node[9], node[10],node[11],
241                                                  node[12],node[13],node[14],ID);
242         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
243                                                  node[4], node[5], node[6], node[7],
244                                                  node[8], node[9], node[10],node[11],
245                                                  node[12],node[13],node[14] );
246       }
247       else if (nbnode == 20) {
248         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
249                                                  node[4], node[5], node[6], node[7],
250                                                  node[8], node[9], node[10],node[11],
251                                                  node[12],node[13],node[14],node[15],
252                                                  node[16],node[17],node[18],node[19],ID);
253         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
254                                                  node[4], node[5], node[6], node[7],
255                                                  node[8], node[9], node[10],node[11],
256                                                  node[12],node[13],node[14],node[15],
257                                                  node[16],node[17],node[18],node[19] );
258       }
259       else if (nbnode == 27) {
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],
265                                                  node[20],node[21],node[22],node[23],
266                                                  node[24],node[25],node[26], ID);
267         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
268                                                  node[4], node[5], node[6], node[7],
269                                                  node[8], node[9], node[10],node[11],
270                                                  node[12],node[13],node[14],node[15],
271                                                  node[16],node[17],node[18],node[19],
272                                                  node[20],node[21],node[22],node[23],
273                                                  node[24],node[25],node[26] );
274       }
275     }
276     break;
277
278   case SMDSAbs_Edge:
279     if ( nbnode == 2 ) {
280       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
281       else           e = mesh->AddEdge      (node[0], node[1] );
282     }
283     else if ( nbnode == 3 ) {
284       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
285       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
286     }
287     break;
288
289   case SMDSAbs_0DElement:
290     if ( nbnode == 1 ) {
291       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
292       else           e = mesh->Add0DElement      (node[0] );
293     }
294     break;
295
296   case SMDSAbs_Node:
297     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
298     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z());
299     break;
300
301   case SMDSAbs_Ball:
302     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
303     else           e = mesh->AddBall      (node[0], ballDiameter);
304     break;
305
306   default:;
307   }
308   if ( e ) myLastCreatedElems.Append( e );
309   return e;
310 }
311
312 //=======================================================================
313 /*!
314  * \brief Add element
315  */
316 //=======================================================================
317
318 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> &       nodeIDs,
319                                                const SMDSAbs_ElementType type,
320                                                const bool                isPoly,
321                                                const int                 ID)
322 {
323   vector<const SMDS_MeshNode*> nodes;
324   nodes.reserve( nodeIDs.size() );
325   vector<int>::const_iterator id = nodeIDs.begin();
326   while ( id != nodeIDs.end() ) {
327     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
328       nodes.push_back( node );
329     else
330       return 0;
331   }
332   return AddElement( nodes, type, isPoly, ID );
333 }
334
335 //=======================================================================
336 //function : Remove
337 //purpose  : Remove a node or an element.
338 //           Modify a compute state of sub-meshes which become empty
339 //=======================================================================
340
341 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
342                               const bool         isNodes )
343 {
344   myLastCreatedElems.Clear();
345   myLastCreatedNodes.Clear();
346
347   SMESHDS_Mesh* aMesh = GetMeshDS();
348   set< SMESH_subMesh *> smmap;
349
350   int removed = 0;
351   list<int>::const_iterator it = theIDs.begin();
352   for ( ; it != theIDs.end(); it++ ) {
353     const SMDS_MeshElement * elem;
354     if ( isNodes )
355       elem = aMesh->FindNode( *it );
356     else
357       elem = aMesh->FindElement( *it );
358     if ( !elem )
359       continue;
360
361     // Notify VERTEX sub-meshes about modification
362     if ( isNodes ) {
363       const SMDS_MeshNode* node = cast2Node( elem );
364       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
365         if ( int aShapeID = node->getshapeId() )
366           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
367             smmap.insert( sm );
368     }
369     // Find sub-meshes to notify about modification
370     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
371     //     while ( nodeIt->more() ) {
372     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
373     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
374     //       if ( aPosition.get() ) {
375     //         if ( int aShapeID = aPosition->GetShapeId() ) {
376     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
377     //             smmap.insert( sm );
378     //         }
379     //       }
380     //     }
381
382     // Do remove
383     if ( isNodes )
384       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
385     else
386       aMesh->RemoveElement( elem );
387     removed++;
388   }
389
390   // Notify sub-meshes about modification
391   if ( !smmap.empty() ) {
392     set< SMESH_subMesh *>::iterator smIt;
393     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
394       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
395   }
396
397   //   // Check if the whole mesh becomes empty
398   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
399   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
400
401   return removed;
402 }
403
404 //================================================================================
405 /*!
406  * \brief Create 0D elements on all nodes of the given object except those
407  *        nodes on which a 0D element already exists.
408  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
409  *                    the all mesh is treated
410  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
411  */
412 //================================================================================
413
414 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
415                                                    TIDSortedElemSet&       all0DElems )
416 {
417   typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::const_iterator> TSetIterator;
418   SMDS_ElemIteratorPtr elemIt;
419   if ( elements.empty() )
420     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
421   else
422     elemIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
423
424   while ( elemIt->more() )
425   {
426     const SMDS_MeshElement* e = elemIt->next();
427     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
428     while ( nodeIt->more() )
429     {
430       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
431       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
432       if ( it0D->more() )
433         all0DElems.insert( it0D->next() );
434       else {
435         myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
436         all0DElems.insert( myLastCreatedElems.Last() );
437       }
438     }
439   }
440 }
441
442 //=======================================================================
443 //function : FindShape
444 //purpose  : Return an index of the shape theElem is on
445 //           or zero if a shape not found
446 //=======================================================================
447
448 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
449 {
450   myLastCreatedElems.Clear();
451   myLastCreatedNodes.Clear();
452
453   SMESHDS_Mesh * aMesh = GetMeshDS();
454   if ( aMesh->ShapeToMesh().IsNull() )
455     return 0;
456
457   int aShapeID = theElem->getshapeId();
458   if ( aShapeID < 1 )
459     return 0;
460
461   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
462     if ( sm->Contains( theElem ))
463       return aShapeID;
464
465   if ( theElem->GetType() == SMDSAbs_Node ) {
466     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
467   }
468   else {
469     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
470   }
471
472   TopoDS_Shape aShape; // the shape a node of theElem is on
473   if ( theElem->GetType() != SMDSAbs_Node )
474   {
475     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
476     while ( nodeIt->more() ) {
477       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
478       if ((aShapeID = node->getshapeId()) > 0) {
479         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
480           if ( sm->Contains( theElem ))
481             return aShapeID;
482           if ( aShape.IsNull() )
483             aShape = aMesh->IndexToShape( aShapeID );
484         }
485       }
486     }
487   }
488
489   // None of nodes is on a proper shape,
490   // find the shape among ancestors of aShape on which a node is
491   if ( !aShape.IsNull() ) {
492     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
493     for ( ; ancIt.More(); ancIt.Next() ) {
494       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
495       if ( sm && sm->Contains( theElem ))
496         return aMesh->ShapeToIndex( ancIt.Value() );
497     }
498   }
499   else
500   {
501     const map<int,SMESHDS_SubMesh*>& id2sm = GetMeshDS()->SubMeshes();
502     map<int,SMESHDS_SubMesh*>::const_iterator id_sm = id2sm.begin();
503     for ( ; id_sm != id2sm.end(); ++id_sm )
504       if ( id_sm->second->Contains( theElem ))
505         return id_sm->first;
506   }
507
508   //MESSAGE ("::FindShape() - SHAPE NOT FOUND")
509   return 0;
510 }
511
512 //=======================================================================
513 //function : IsMedium
514 //purpose  :
515 //=======================================================================
516
517 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
518                                 const SMDSAbs_ElementType typeToCheck)
519 {
520   bool isMedium = false;
521   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
522   while (it->more() && !isMedium ) {
523     const SMDS_MeshElement* elem = it->next();
524     isMedium = elem->IsMediumNode(node);
525   }
526   return isMedium;
527 }
528
529 //=======================================================================
530 //function : ShiftNodesQuadTria
531 //purpose  : auxilary
532 //           Shift nodes in the array corresponded to quadratic triangle
533 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
534 //=======================================================================
535 static void ShiftNodesQuadTria(const SMDS_MeshNode* aNodes[])
536 {
537   const SMDS_MeshNode* nd1 = aNodes[0];
538   aNodes[0] = aNodes[1];
539   aNodes[1] = aNodes[2];
540   aNodes[2] = nd1;
541   const SMDS_MeshNode* nd2 = aNodes[3];
542   aNodes[3] = aNodes[4];
543   aNodes[4] = aNodes[5];
544   aNodes[5] = nd2;
545 }
546
547 //=======================================================================
548 //function : edgeConnectivity
549 //purpose  : auxilary
550 //           return number of the edges connected with the theNode.
551 //           if theEdges has connections with the other type of the
552 //           elements, return -1
553 //=======================================================================
554 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
555 {
556   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
557   int nb=0;
558   while(elemIt->more()) {
559     elemIt->next();
560     nb++;
561   }
562   return nb;
563 }
564
565
566 //=======================================================================
567 //function : GetNodesFromTwoTria
568 //purpose  : auxilary
569 //           Shift nodes in the array corresponded to quadratic triangle
570 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
571 //=======================================================================
572 static bool GetNodesFromTwoTria(const SMDS_MeshElement * theTria1,
573                                 const SMDS_MeshElement * theTria2,
574                                 const SMDS_MeshNode* N1[],
575                                 const SMDS_MeshNode* N2[])
576 {
577   SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
578   int i=0;
579   while(i<6) {
580     N1[i] = static_cast<const SMDS_MeshNode*>( it->next() );
581     i++;
582   }
583   if(it->more()) return false;
584   it = theTria2->nodesIterator();
585   i=0;
586   while(i<6) {
587     N2[i] = static_cast<const SMDS_MeshNode*>( it->next() );
588     i++;
589   }
590   if(it->more()) return false;
591
592   int sames[3] = {-1,-1,-1};
593   int nbsames = 0;
594   int j;
595   for(i=0; i<3; i++) {
596     for(j=0; j<3; j++) {
597       if(N1[i]==N2[j]) {
598         sames[i] = j;
599         nbsames++;
600         break;
601       }
602     }
603   }
604   if(nbsames!=2) return false;
605   if(sames[0]>-1) {
606     ShiftNodesQuadTria(N1);
607     if(sames[1]>-1) {
608       ShiftNodesQuadTria(N1);
609     }
610   }
611   i = sames[0] + sames[1] + sames[2];
612   for(; i<2; i++) {
613     ShiftNodesQuadTria(N2);
614   }
615   // now we receive following N1 and N2 (using numeration as above image)
616   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
617   // i.e. first nodes from both arrays determ new diagonal
618   return true;
619 }
620
621 //=======================================================================
622 //function : InverseDiag
623 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
624 //           but having other common link.
625 //           Return False if args are improper
626 //=======================================================================
627
628 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
629                                     const SMDS_MeshElement * theTria2 )
630 {
631   MESSAGE("InverseDiag");
632   myLastCreatedElems.Clear();
633   myLastCreatedNodes.Clear();
634
635   if (!theTria1 || !theTria2)
636     return false;
637
638   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
639   if (!F1) return false;
640   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
641   if (!F2) return false;
642   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
643       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
644
645     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
646     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
647     //    |/ |                                         | \|
648     //  B +--+ 2                                     B +--+ 2
649
650     // put nodes in array and find out indices of the same ones
651     const SMDS_MeshNode* aNodes [6];
652     int sameInd [] = { 0, 0, 0, 0, 0, 0 };
653     int i = 0;
654     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
655     while ( it->more() ) {
656       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
657
658       if ( i > 2 ) // theTria2
659         // find same node of theTria1
660         for ( int j = 0; j < 3; j++ )
661           if ( aNodes[ i ] == aNodes[ j ]) {
662             sameInd[ j ] = i;
663             sameInd[ i ] = j;
664             break;
665           }
666       // next
667       i++;
668       if ( i == 3 ) {
669         if ( it->more() )
670           return false; // theTria1 is not a triangle
671         it = theTria2->nodesIterator();
672       }
673       if ( i == 6 && it->more() )
674         return false; // theTria2 is not a triangle
675     }
676
677     // find indices of 1,2 and of A,B in theTria1
678     int iA = 0, iB = 0, i1 = 0, i2 = 0;
679     for ( i = 0; i < 6; i++ ) {
680       if ( sameInd [ i ] == 0 ) {
681         if ( i < 3 ) i1 = i;
682         else         i2 = i;
683       }
684       else if (i < 3) {
685         if ( iA ) iB = i;
686         else      iA = i;
687       }
688     }
689     // nodes 1 and 2 should not be the same
690     if ( aNodes[ i1 ] == aNodes[ i2 ] )
691       return false;
692
693     // theTria1: A->2
694     aNodes[ iA ] = aNodes[ i2 ];
695     // theTria2: B->1
696     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
697
698     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
699     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
700
701     return true;
702
703   } // end if(F1 && F2)
704
705   // check case of quadratic faces
706   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle)
707     return false;
708   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle)
709     return false;
710
711   //       5
712   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
713   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
714   //    |   / |
715   //  7 +  +  + 6
716   //    | /9  |
717   //    |/    |
718   //  4 +--+--+ 3
719   //       8
720
721   const SMDS_MeshNode* N1 [6];
722   const SMDS_MeshNode* N2 [6];
723   if(!GetNodesFromTwoTria(theTria1,theTria2,N1,N2))
724     return false;
725   // now we receive following N1 and N2 (using numeration as above image)
726   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
727   // i.e. first nodes from both arrays determ new diagonal
728
729   const SMDS_MeshNode* N1new [6];
730   const SMDS_MeshNode* N2new [6];
731   N1new[0] = N1[0];
732   N1new[1] = N2[0];
733   N1new[2] = N2[1];
734   N1new[3] = N1[4];
735   N1new[4] = N2[3];
736   N1new[5] = N1[5];
737   N2new[0] = N1[0];
738   N2new[1] = N1[1];
739   N2new[2] = N2[0];
740   N2new[3] = N1[3];
741   N2new[4] = N2[5];
742   N2new[5] = N1[4];
743   // replaces nodes in faces
744   GetMeshDS()->ChangeElementNodes( theTria1, N1new, 6 );
745   GetMeshDS()->ChangeElementNodes( theTria2, N2new, 6 );
746
747   return true;
748 }
749
750 //=======================================================================
751 //function : findTriangles
752 //purpose  : find triangles sharing theNode1-theNode2 link
753 //=======================================================================
754
755 static bool findTriangles(const SMDS_MeshNode *    theNode1,
756                           const SMDS_MeshNode *    theNode2,
757                           const SMDS_MeshElement*& theTria1,
758                           const SMDS_MeshElement*& theTria2)
759 {
760   if ( !theNode1 || !theNode2 ) return false;
761
762   theTria1 = theTria2 = 0;
763
764   set< const SMDS_MeshElement* > emap;
765   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
766   while (it->more()) {
767     const SMDS_MeshElement* elem = it->next();
768     if ( elem->NbNodes() == 3 )
769       emap.insert( elem );
770   }
771   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
772   while (it->more()) {
773     const SMDS_MeshElement* elem = it->next();
774     if ( emap.find( elem ) != emap.end() ) {
775       if ( theTria1 ) {
776         // theTria1 must be element with minimum ID
777         if( theTria1->GetID() < elem->GetID() ) {
778           theTria2 = elem;
779         }
780         else {
781           theTria2 = theTria1;
782           theTria1 = elem;
783         }
784         break;
785       }
786       else {
787         theTria1 = elem;
788       }
789     }
790   }
791   return ( theTria1 && theTria2 );
792 }
793
794 //=======================================================================
795 //function : InverseDiag
796 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
797 //           with ones built on the same 4 nodes but having other common link.
798 //           Return false if proper faces not found
799 //=======================================================================
800
801 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
802                                     const SMDS_MeshNode * theNode2)
803 {
804   myLastCreatedElems.Clear();
805   myLastCreatedNodes.Clear();
806
807   MESSAGE( "::InverseDiag()" );
808
809   const SMDS_MeshElement *tr1, *tr2;
810   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
811     return false;
812
813   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
814   if (!F1) return false;
815   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
816   if (!F2) return false;
817   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
818       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
819
820     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
821     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
822     //    |/ |                                    | \|
823     //  B +--+ 2                                B +--+ 2
824
825     // put nodes in array
826     // and find indices of 1,2 and of A in tr1 and of B in tr2
827     int i, iA1 = 0, i1 = 0;
828     const SMDS_MeshNode* aNodes1 [3];
829     SMDS_ElemIteratorPtr it;
830     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
831       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
832       if ( aNodes1[ i ] == theNode1 )
833         iA1 = i; // node A in tr1
834       else if ( aNodes1[ i ] != theNode2 )
835         i1 = i;  // node 1
836     }
837     int iB2 = 0, i2 = 0;
838     const SMDS_MeshNode* aNodes2 [3];
839     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
840       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
841       if ( aNodes2[ i ] == theNode2 )
842         iB2 = i; // node B in tr2
843       else if ( aNodes2[ i ] != theNode1 )
844         i2 = i;  // node 2
845     }
846
847     // nodes 1 and 2 should not be the same
848     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
849       return false;
850
851     // tr1: A->2
852     aNodes1[ iA1 ] = aNodes2[ i2 ];
853     // tr2: B->1
854     aNodes2[ iB2 ] = aNodes1[ i1 ];
855
856     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
857     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
858
859     return true;
860   }
861
862   // check case of quadratic faces
863   return InverseDiag(tr1,tr2);
864 }
865
866 //=======================================================================
867 //function : getQuadrangleNodes
868 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
869 //           fusion of triangles tr1 and tr2 having shared link on
870 //           theNode1 and theNode2
871 //=======================================================================
872
873 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
874                         const SMDS_MeshNode *    theNode1,
875                         const SMDS_MeshNode *    theNode2,
876                         const SMDS_MeshElement * tr1,
877                         const SMDS_MeshElement * tr2 )
878 {
879   if( tr1->NbNodes() != tr2->NbNodes() )
880     return false;
881   // find the 4-th node to insert into tr1
882   const SMDS_MeshNode* n4 = 0;
883   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
884   int i=0;
885   while ( !n4 && i<3 ) {
886     const SMDS_MeshNode * n = cast2Node( it->next() );
887     i++;
888     bool isDiag = ( n == theNode1 || n == theNode2 );
889     if ( !isDiag )
890       n4 = n;
891   }
892   // Make an array of nodes to be in a quadrangle
893   int iNode = 0, iFirstDiag = -1;
894   it = tr1->nodesIterator();
895   i=0;
896   while ( i<3 ) {
897     const SMDS_MeshNode * n = cast2Node( it->next() );
898     i++;
899     bool isDiag = ( n == theNode1 || n == theNode2 );
900     if ( isDiag ) {
901       if ( iFirstDiag < 0 )
902         iFirstDiag = iNode;
903       else if ( iNode - iFirstDiag == 1 )
904         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
905     }
906     else if ( n == n4 ) {
907       return false; // tr1 and tr2 should not have all the same nodes
908     }
909     theQuadNodes[ iNode++ ] = n;
910   }
911   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
912     theQuadNodes[ iNode ] = n4;
913
914   return true;
915 }
916
917 //=======================================================================
918 //function : DeleteDiag
919 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
920 //           with a quadrangle built on the same 4 nodes.
921 //           Return false if proper faces not found
922 //=======================================================================
923
924 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
925                                    const SMDS_MeshNode * theNode2)
926 {
927   myLastCreatedElems.Clear();
928   myLastCreatedNodes.Clear();
929
930   MESSAGE( "::DeleteDiag()" );
931
932   const SMDS_MeshElement *tr1, *tr2;
933   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
934     return false;
935
936   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
937   if (!F1) return false;
938   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
939   if (!F2) return false;
940   SMESHDS_Mesh * aMesh = GetMeshDS();
941
942   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
943       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
944
945     const SMDS_MeshNode* aNodes [ 4 ];
946     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
947       return false;
948
949     const SMDS_MeshElement* newElem = 0;
950     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
951     myLastCreatedElems.Append(newElem);
952     AddToSameGroups( newElem, tr1, aMesh );
953     int aShapeId = tr1->getshapeId();
954     if ( aShapeId )
955       {
956         aMesh->SetMeshElementOnShape( newElem, aShapeId );
957       }
958     aMesh->RemoveElement( tr1 );
959     aMesh->RemoveElement( tr2 );
960
961     return true;
962   }
963
964   // check case of quadratic faces
965   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
966     return false;
967   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
968     return false;
969
970   //       5
971   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
972   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
973   //    |   / |
974   //  7 +  +  + 6
975   //    | /9  |
976   //    |/    |
977   //  4 +--+--+ 3
978   //       8
979
980   const SMDS_MeshNode* N1 [6];
981   const SMDS_MeshNode* N2 [6];
982   if(!GetNodesFromTwoTria(tr1,tr2,N1,N2))
983     return false;
984   // now we receive following N1 and N2 (using numeration as above image)
985   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
986   // i.e. first nodes from both arrays determ new diagonal
987
988   const SMDS_MeshNode* aNodes[8];
989   aNodes[0] = N1[0];
990   aNodes[1] = N1[1];
991   aNodes[2] = N2[0];
992   aNodes[3] = N2[1];
993   aNodes[4] = N1[3];
994   aNodes[5] = N2[5];
995   aNodes[6] = N2[3];
996   aNodes[7] = N1[5];
997
998   const SMDS_MeshElement* newElem = 0;
999   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1000                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1001   myLastCreatedElems.Append(newElem);
1002   AddToSameGroups( newElem, tr1, aMesh );
1003   int aShapeId = tr1->getshapeId();
1004   if ( aShapeId )
1005     {
1006       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1007     }
1008   aMesh->RemoveElement( tr1 );
1009   aMesh->RemoveElement( tr2 );
1010
1011   // remove middle node (9)
1012   GetMeshDS()->RemoveNode( N1[4] );
1013
1014   return true;
1015 }
1016
1017 //=======================================================================
1018 //function : Reorient
1019 //purpose  : Reverse theElement orientation
1020 //=======================================================================
1021
1022 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1023 {
1024   MESSAGE("Reorient");
1025   myLastCreatedElems.Clear();
1026   myLastCreatedNodes.Clear();
1027
1028   if (!theElem)
1029     return false;
1030   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1031   if ( !it || !it->more() )
1032     return false;
1033
1034   switch ( theElem->GetType() ) {
1035
1036   case SMDSAbs_Edge:
1037   case SMDSAbs_Face: {
1038     if(!theElem->IsQuadratic()) {
1039       int i = theElem->NbNodes();
1040       vector<const SMDS_MeshNode*> aNodes( i );
1041       while ( it->more() )
1042         aNodes[ --i ]= static_cast<const SMDS_MeshNode*>( it->next() );
1043       return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], theElem->NbNodes() );
1044     }
1045     else {
1046       // quadratic elements
1047       if(theElem->GetType()==SMDSAbs_Edge) {
1048         vector<const SMDS_MeshNode*> aNodes(3);
1049         aNodes[1]= static_cast<const SMDS_MeshNode*>( it->next() );
1050         aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1051         aNodes[2]= static_cast<const SMDS_MeshNode*>( it->next() );
1052         return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], 3 );
1053       }
1054       else {
1055         int nbn = theElem->NbNodes();
1056         vector<const SMDS_MeshNode*> aNodes(nbn);
1057         aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1058         int i=1;
1059         for(; i<nbn/2; i++) {
1060           aNodes[nbn/2-i]= static_cast<const SMDS_MeshNode*>( it->next() );
1061         }
1062         for(i=0; i<nbn/2; i++) {
1063           aNodes[nbn-i-1]= static_cast<const SMDS_MeshNode*>( it->next() );
1064         }
1065         return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], nbn );
1066       }
1067     }
1068   }
1069   case SMDSAbs_Volume: {
1070     if (theElem->IsPoly()) {
1071       // TODO reorient vtk polyhedron
1072       MESSAGE("reorient vtk polyhedron ?");
1073       const SMDS_VtkVolume* aPolyedre =
1074         dynamic_cast<const SMDS_VtkVolume*>( theElem );
1075       if (!aPolyedre) {
1076         MESSAGE("Warning: bad volumic element");
1077         return false;
1078       }
1079
1080       int nbFaces = aPolyedre->NbFaces();
1081       vector<const SMDS_MeshNode *> poly_nodes;
1082       vector<int> quantities (nbFaces);
1083
1084       // reverse each face of the polyedre
1085       for (int iface = 1; iface <= nbFaces; iface++) {
1086         int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1087         quantities[iface - 1] = nbFaceNodes;
1088
1089         for (inode = nbFaceNodes; inode >= 1; inode--) {
1090           const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1091           poly_nodes.push_back(curNode);
1092         }
1093       }
1094
1095       return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1096
1097     }
1098     else {
1099       SMDS_VolumeTool vTool;
1100       if ( !vTool.Set( theElem ))
1101         return false;
1102       vTool.Inverse();
1103       MESSAGE("ChangeElementNodes reorient: check vTool.Inverse");
1104       return GetMeshDS()->ChangeElementNodes( theElem, vTool.GetNodes(), vTool.NbNodes() );
1105     }
1106   }
1107   default:;
1108   }
1109
1110   return false;
1111 }
1112
1113 //================================================================================
1114 /*!
1115  * \brief Reorient faces.
1116  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1117  * \param theDirection - desired direction of normal of \a theFace
1118  * \param theFace - one of \a theFaces that sould be oriented according to
1119  *        \a theDirection and whose orientation defines orientation of other faces
1120  * \return number of reoriented faces.
1121  */
1122 //================================================================================
1123
1124 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1125                                   const gp_Dir&            theDirection,
1126                                   const SMDS_MeshElement * theFace)
1127 {
1128   int nbReori = 0;
1129   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1130
1131   if ( theFaces.empty() )
1132   {
1133     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1134     while ( fIt->more() )
1135       theFaces.insert( theFaces.end(), fIt->next() );
1136   }
1137
1138   // orient theFace according to theDirection
1139   gp_XYZ normal;
1140   SMESH_Algo::FaceNormal( theFace, normal, /*normalized=*/false );
1141   if ( normal * theDirection.XYZ() < 0 )
1142     nbReori += Reorient( theFace );
1143
1144   // Orient other faces
1145
1146   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1147   TIDSortedElemSet avoidSet;
1148   set< SMESH_TLink > checkedLinks;
1149   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1150
1151   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1152     theFaces.erase( theFace );
1153   startFaces.insert( theFace );
1154
1155   int nodeInd1, nodeInd2;
1156   const SMDS_MeshElement*           otherFace;
1157   vector< const SMDS_MeshElement* > facesNearLink;
1158   vector< std::pair< int, int > >   nodeIndsOfFace;
1159
1160   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1161   while ( !startFaces.empty() )
1162   {
1163     startFace = startFaces.begin();
1164     theFace = *startFace;
1165     startFaces.erase( startFace );
1166     if ( !visitedFaces.insert( theFace ).second )
1167       continue;
1168
1169     avoidSet.clear();
1170     avoidSet.insert(theFace);
1171
1172     NLink link( theFace->GetNode( 0 ), 0 );
1173
1174     const int nbNodes = theFace->NbCornerNodes();
1175     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1176     {
1177       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1178       linkIt_isNew = checkedLinks.insert( link );
1179       if ( !linkIt_isNew.second )
1180       {
1181         // link has already been checked and won't be encountered more
1182         // if the group (theFaces) is manifold
1183         //checkedLinks.erase( linkIt_isNew.first );
1184       }
1185       else
1186       {
1187         facesNearLink.clear();
1188         nodeIndsOfFace.clear();
1189         while (( otherFace = FindFaceInSet( link.first, link.second,
1190                                             theFaces, avoidSet, &nodeInd1, &nodeInd2 )))
1191           if ( otherFace != theFace)
1192           {
1193             facesNearLink.push_back( otherFace );
1194             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1195             avoidSet.insert( otherFace );
1196           }
1197         if ( facesNearLink.size() > 1 )
1198         {
1199           // NON-MANIFOLD mesh shell !
1200           // select a face most co-directed with theFace,
1201           // other faces won't be visited this time
1202           gp_XYZ NF, NOF;
1203           SMESH_Algo::FaceNormal( theFace, NF, /*normalized=*/false );
1204           double proj, maxProj = -1;
1205           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1206             SMESH_Algo::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1207             if (( proj = Abs( NF * NOF )) > maxProj ) {
1208               maxProj = proj;
1209               otherFace = facesNearLink[i];
1210               nodeInd1  = nodeIndsOfFace[i].first;
1211               nodeInd2  = nodeIndsOfFace[i].second;
1212             }
1213           }
1214           // not to visit rejected faces
1215           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1216             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1217               visitedFaces.insert( facesNearLink[i] );
1218         }
1219         else if ( facesNearLink.size() == 1 )
1220         {
1221           otherFace = facesNearLink[0];
1222           nodeInd1  = nodeIndsOfFace.back().first;
1223           nodeInd2  = nodeIndsOfFace.back().second;
1224         }
1225         if ( otherFace && otherFace != theFace)
1226         {
1227           // link must be reverse in otherFace if orientation ot otherFace
1228           // is same as that of theFace
1229           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1230           {
1231             nbReori += Reorient( otherFace );
1232           }
1233           startFaces.insert( otherFace );
1234         }
1235       }
1236       std::swap( link.first, link.second ); // reverse the link
1237     }
1238   }
1239   return nbReori;
1240 }
1241
1242 //=======================================================================
1243 //function : getBadRate
1244 //purpose  :
1245 //=======================================================================
1246
1247 static double getBadRate (const SMDS_MeshElement*               theElem,
1248                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1249 {
1250   SMESH::Controls::TSequenceOfXYZ P;
1251   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1252     return 1e100;
1253   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1254   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1255 }
1256
1257 //=======================================================================
1258 //function : QuadToTri
1259 //purpose  : Cut quadrangles into triangles.
1260 //           theCrit is used to select a diagonal to cut
1261 //=======================================================================
1262
1263 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1264                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1265 {
1266   myLastCreatedElems.Clear();
1267   myLastCreatedNodes.Clear();
1268
1269   MESSAGE( "::QuadToTri()" );
1270
1271   if ( !theCrit.get() )
1272     return false;
1273
1274   SMESHDS_Mesh * aMesh = GetMeshDS();
1275
1276   Handle(Geom_Surface) surface;
1277   SMESH_MesherHelper   helper( *GetMesh() );
1278
1279   TIDSortedElemSet::iterator itElem;
1280   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1281     const SMDS_MeshElement* elem = *itElem;
1282     if ( !elem || elem->GetType() != SMDSAbs_Face )
1283       continue;
1284     if ( elem->NbCornerNodes() != 4 )
1285       continue;
1286
1287     // retrieve element nodes
1288     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1289
1290     // compare two sets of possible triangles
1291     double aBadRate1, aBadRate2; // to what extent a set is bad
1292     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1293     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1294     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1295
1296     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1297     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1298     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1299
1300     int aShapeId = FindShape( elem );
1301     const SMDS_MeshElement* newElem1 = 0;
1302     const SMDS_MeshElement* newElem2 = 0;
1303
1304     if( !elem->IsQuadratic() ) {
1305
1306       // split liner quadrangle
1307       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1308       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1309       if ( aBadRate1 <= aBadRate2 ) {
1310         // tr1 + tr2 is better
1311         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1312         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1313       }
1314       else {
1315         // tr3 + tr4 is better
1316         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1317         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1318       }
1319     }
1320     else {
1321
1322       // split quadratic quadrangle
1323
1324       // get surface elem is on
1325       if ( aShapeId != helper.GetSubShapeID() ) {
1326         surface.Nullify();
1327         TopoDS_Shape shape;
1328         if ( aShapeId > 0 )
1329           shape = aMesh->IndexToShape( aShapeId );
1330         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1331           TopoDS_Face face = TopoDS::Face( shape );
1332           surface = BRep_Tool::Surface( face );
1333           if ( !surface.IsNull() )
1334             helper.SetSubShape( shape );
1335         }
1336       }
1337       // find middle point for (0,1,2,3)
1338       // and create a node in this point;
1339       const SMDS_MeshNode* newN = 0;
1340       if ( aNodes.size() == 9 )
1341       {
1342         // SMDSEntity_BiQuad_Quadrangle
1343         newN = aNodes.back();
1344       }
1345       else
1346       {
1347         gp_XYZ p( 0,0,0 );
1348         if ( surface.IsNull() )
1349         {
1350           for ( int i = 0; i < 4; i++ )
1351             p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1352           p /= 4;
1353         }
1354         else
1355         {
1356           const SMDS_MeshNode* inFaceNode = 0;
1357           if ( helper.GetNodeUVneedInFaceNode() )
1358             for ( size_t i = 0; i < aNodes.size() && !inFaceNode; ++i )
1359               if ( aNodes[ i ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1360                 inFaceNode = aNodes[ i ];
1361
1362           TopoDS_Face face = TopoDS::Face( helper.GetSubShape() );
1363           gp_XY uv( 0,0 );
1364           for ( int i = 0; i < 4; i++ )
1365             uv += helper.GetNodeUV( face, aNodes[i], inFaceNode );
1366           uv /= 4.;
1367           p = surface->Value( uv.X(), uv.Y() ).XYZ();
1368         }
1369         newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1370         myLastCreatedNodes.Append(newN);
1371       }
1372       // create a new element
1373       if ( aBadRate1 <= aBadRate2 ) {
1374         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1375                                   aNodes[6], aNodes[7], newN );
1376         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
1377                                   newN,      aNodes[4], aNodes[5] );
1378       }
1379       else {
1380         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1381                                   aNodes[7], aNodes[4], newN );
1382         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
1383                                   newN,      aNodes[5], aNodes[6] );
1384       }
1385     } // quadratic case
1386
1387     // care of a new element
1388
1389     myLastCreatedElems.Append(newElem1);
1390     myLastCreatedElems.Append(newElem2);
1391     AddToSameGroups( newElem1, elem, aMesh );
1392     AddToSameGroups( newElem2, elem, aMesh );
1393
1394     // put a new triangle on the same shape
1395     if ( aShapeId )
1396       {
1397         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1398         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1399       }
1400     aMesh->RemoveElement( elem );
1401   }
1402   return true;
1403 }
1404
1405 //=======================================================================
1406 //function : BestSplit
1407 //purpose  : Find better diagonal for cutting.
1408 //=======================================================================
1409
1410 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1411                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1412 {
1413   myLastCreatedElems.Clear();
1414   myLastCreatedNodes.Clear();
1415
1416   if (!theCrit.get())
1417     return -1;
1418
1419   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1420     return -1;
1421
1422   if( theQuad->NbNodes()==4 ||
1423       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1424
1425     // retrieve element nodes
1426     const SMDS_MeshNode* aNodes [4];
1427     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1428     int i = 0;
1429     //while (itN->more())
1430     while (i<4) {
1431       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1432     }
1433     // compare two sets of possible triangles
1434     double aBadRate1, aBadRate2; // to what extent a set is bad
1435     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1436     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1437     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1438
1439     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1440     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1441     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1442     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1443     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1444     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1445       return 1; // diagonal 1-3
1446
1447     return 2; // diagonal 2-4
1448   }
1449   return -1;
1450 }
1451
1452 namespace
1453 {
1454   // Methods of splitting volumes into tetra
1455
1456   const int theHexTo5_1[5*4+1] =
1457     {
1458       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1459     };
1460   const int theHexTo5_2[5*4+1] =
1461     {
1462       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1463     };
1464   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1465
1466   const int theHexTo6_1[6*4+1] =
1467     {
1468       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
1469     };
1470   const int theHexTo6_2[6*4+1] =
1471     {
1472       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
1473     };
1474   const int theHexTo6_3[6*4+1] =
1475     {
1476       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
1477     };
1478   const int theHexTo6_4[6*4+1] =
1479     {
1480       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
1481     };
1482   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1483
1484   const int thePyraTo2_1[2*4+1] =
1485     {
1486       0, 1, 2, 4,    0, 2, 3, 4,   -1
1487     };
1488   const int thePyraTo2_2[2*4+1] =
1489     {
1490       1, 2, 3, 4,    1, 3, 0, 4,   -1
1491     };
1492   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1493
1494   const int thePentaTo3_1[3*4+1] =
1495     {
1496       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1497     };
1498   const int thePentaTo3_2[3*4+1] =
1499     {
1500       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1501     };
1502   const int thePentaTo3_3[3*4+1] =
1503     {
1504       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1505     };
1506   const int thePentaTo3_4[3*4+1] =
1507     {
1508       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1509     };
1510   const int thePentaTo3_5[3*4+1] =
1511     {
1512       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1513     };
1514   const int thePentaTo3_6[3*4+1] =
1515     {
1516       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1517     };
1518   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1519                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1520
1521   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1522   {
1523     int _n1, _n2, _n3;
1524     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1525     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1526     bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1527   };
1528   struct TSplitMethod
1529   {
1530     int        _nbTetra;
1531     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1532     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1533     bool       _ownConn;      //!< to delete _connectivity in destructor
1534     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1535
1536     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1537       : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1538     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1539     bool hasFacet( const TTriangleFacet& facet ) const
1540     {
1541       const int* tetConn = _connectivity;
1542       for ( ; tetConn[0] >= 0; tetConn += 4 )
1543         if (( facet.contains( tetConn[0] ) +
1544               facet.contains( tetConn[1] ) +
1545               facet.contains( tetConn[2] ) +
1546               facet.contains( tetConn[3] )) == 3 )
1547           return true;
1548       return false;
1549     }
1550   };
1551
1552   //=======================================================================
1553   /*!
1554    * \brief return TSplitMethod for the given element
1555    */
1556   //=======================================================================
1557
1558   TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1559   {
1560     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1561
1562     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1563     // an edge and a face barycenter; tertaherdons are based on triangles and
1564     // a volume barycenter
1565     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1566
1567     // Find out how adjacent volumes are split
1568
1569     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1570     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1571     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1572     {
1573       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1574       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1575       if ( nbNodes < 4 ) continue;
1576
1577       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1578       const int* nInd = vol.GetFaceNodesIndices( iF );
1579       if ( nbNodes == 4 )
1580       {
1581         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1582         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1583         if      ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1584         else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1585       }
1586       else
1587       {
1588         int iCom = 0; // common node of triangle faces to split into
1589         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1590         {
1591           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1592                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1593                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1594           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1595                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1596                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1597           if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1598           {
1599             triaSplits.push_back( t012 );
1600             triaSplits.push_back( t023 );
1601             break;
1602           }
1603         }
1604       }
1605       if ( !triaSplits.empty() )
1606         hasAdjacentSplits = true;
1607     }
1608
1609     // Among variants of split method select one compliant with adjacent volumes
1610
1611     TSplitMethod method;
1612     if ( !vol.Element()->IsPoly() && !is24TetMode )
1613     {
1614       int nbVariants = 2, nbTet = 0;
1615       const int** connVariants = 0;
1616       switch ( vol.Element()->GetEntityType() )
1617       {
1618       case SMDSEntity_Hexa:
1619       case SMDSEntity_Quad_Hexa:
1620       case SMDSEntity_TriQuad_Hexa:
1621         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1622           connVariants = theHexTo5, nbTet = 5;
1623         else
1624           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1625         break;
1626       case SMDSEntity_Pyramid:
1627       case SMDSEntity_Quad_Pyramid:
1628         connVariants = thePyraTo2;  nbTet = 2;
1629         break;
1630       case SMDSEntity_Penta:
1631       case SMDSEntity_Quad_Penta:
1632         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1633         break;
1634       default:
1635         nbVariants = 0;
1636       }
1637       for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1638       {
1639         // check method compliancy with adjacent tetras,
1640         // all found splits must be among facets of tetras described by this method
1641         method = TSplitMethod( nbTet, connVariants[variant] );
1642         if ( hasAdjacentSplits && method._nbTetra > 0 )
1643         {
1644           bool facetCreated = true;
1645           for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1646           {
1647             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1648             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1649               facetCreated = method.hasFacet( *facet );
1650           }
1651           if ( !facetCreated )
1652             method = TSplitMethod(0); // incompatible method
1653         }
1654       }
1655     }
1656     if ( method._nbTetra < 1 )
1657     {
1658       // No standard method is applicable, use a generic solution:
1659       // each facet of a volume is split into triangles and
1660       // each of triangles and a volume barycenter form a tetrahedron.
1661
1662       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1663
1664       int* connectivity = new int[ maxTetConnSize + 1 ];
1665       method._connectivity = connectivity;
1666       method._ownConn = true;
1667       method._baryNode = !isHex27; // to create central node or not
1668
1669       int connSize = 0;
1670       int baryCenInd = vol.NbNodes() - int( isHex27 );
1671       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1672       {
1673         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1674         const int*   nInd = vol.GetFaceNodesIndices( iF );
1675         // find common node of triangle facets of tetra to create
1676         int iCommon = 0; // index in linear numeration
1677         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1678         if ( !triaSplits.empty() )
1679         {
1680           // by found facets
1681           const TTriangleFacet* facet = &triaSplits.front();
1682           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1683             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1684                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1685               break;
1686         }
1687         else if ( nbNodes > 3 && !is24TetMode )
1688         {
1689           // find the best method of splitting into triangles by aspect ratio
1690           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1691           map< double, int > badness2iCommon;
1692           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1693           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1694           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1695           {
1696             double badness = 0;
1697             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1698             {
1699               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1700                                       nodes[ iQ*((iLast-1)%nbNodes)],
1701                                       nodes[ iQ*((iLast  )%nbNodes)]);
1702               badness += getBadRate( &tria, aspectRatio );
1703             }
1704             badness2iCommon.insert( make_pair( badness, iCommon ));
1705           }
1706           // use iCommon with lowest badness
1707           iCommon = badness2iCommon.begin()->second;
1708         }
1709         if ( iCommon >= nbNodes )
1710           iCommon = 0; // something wrong
1711
1712         // fill connectivity of tetrahedra based on a current face
1713         int nbTet = nbNodes - 2;
1714         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1715         {
1716           int faceBaryCenInd;
1717           if ( isHex27 )
1718           {
1719             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1720             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1721           }
1722           else
1723           {
1724             method._faceBaryNode[ iF ] = 0;
1725             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1726           }
1727           nbTet = nbNodes;
1728           for ( int i = 0; i < nbTet; ++i )
1729           {
1730             int i1 = i, i2 = (i+1) % nbNodes;
1731             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1732             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1733             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1734             connectivity[ connSize++ ] = faceBaryCenInd;
1735             connectivity[ connSize++ ] = baryCenInd;
1736           }
1737         }
1738         else
1739         {
1740           for ( int i = 0; i < nbTet; ++i )
1741           {
1742             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1743             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1744             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1745             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1746             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1747             connectivity[ connSize++ ] = baryCenInd;
1748           }
1749         }
1750         method._nbTetra += nbTet;
1751
1752       } // loop on volume faces
1753
1754       connectivity[ connSize++ ] = -1;
1755
1756     } // end of generic solution
1757
1758     return method;
1759   }
1760   //================================================================================
1761   /*!
1762    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1763    */
1764   //================================================================================
1765
1766   bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1767   {
1768     // find the tetrahedron including the three nodes of facet
1769     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1770     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1771     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1772     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1773     while ( volIt1->more() )
1774     {
1775       const SMDS_MeshElement* v = volIt1->next();
1776       SMDSAbs_EntityType type = v->GetEntityType();
1777       if ( type != SMDSEntity_Tetra && type != SMDSEntity_Quad_Tetra )
1778         continue;
1779       if ( type == SMDSEntity_Quad_Tetra && v->GetNodeIndex( n1 ) > 3 )
1780         continue; // medium node not allowed
1781       const int ind2 = v->GetNodeIndex( n2 );
1782       if ( ind2 < 0 || 3 < ind2 )
1783         continue;
1784       const int ind3 = v->GetNodeIndex( n3 );
1785       if ( ind3 < 0 || 3 < ind3 )
1786         continue;
1787       return true;
1788     }
1789     return false;
1790   }
1791
1792   //=======================================================================
1793   /*!
1794    * \brief A key of a face of volume
1795    */
1796   //=======================================================================
1797
1798   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
1799   {
1800     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1801     {
1802       TIDSortedNodeSet sortedNodes;
1803       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1804       int nbNodes = vol.NbFaceNodes( iF );
1805       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1806       for ( int i = 0; i < nbNodes; i += iQ )
1807         sortedNodes.insert( fNodes[i] );
1808       TIDSortedNodeSet::iterator n = sortedNodes.begin();
1809       first.first   = (*(n++))->GetID();
1810       first.second  = (*(n++))->GetID();
1811       second.first  = (*(n++))->GetID();
1812       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
1813     }
1814   };
1815 } // namespace
1816
1817 //=======================================================================
1818 //function : SplitVolumesIntoTetra
1819 //purpose  : Split volume elements into tetrahedra.
1820 //=======================================================================
1821
1822 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1823                                               const int                theMethodFlags)
1824 {
1825   // std-like iterator on coordinates of nodes of mesh element
1826   typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1827   NXyzIterator xyzEnd;
1828
1829   SMDS_VolumeTool    volTool;
1830   SMESH_MesherHelper helper( *GetMesh());
1831
1832   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1833   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1834
1835   SMESH_SequenceOfElemPtr newNodes, newElems;
1836
1837   // map face of volume to it's baricenrtic node
1838   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1839   double bc[3];
1840
1841   TIDSortedElemSet::const_iterator elem = theElems.begin();
1842   for ( ; elem != theElems.end(); ++elem )
1843   {
1844     if ( (*elem)->GetType() != SMDSAbs_Volume )
1845       continue;
1846     SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1847     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
1848       continue;
1849
1850     if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange...
1851
1852     TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1853     if ( splitMethod._nbTetra < 1 ) continue;
1854
1855     // find submesh to add new tetras to
1856     if ( !subMesh || !subMesh->Contains( *elem ))
1857     {
1858       int shapeID = FindShape( *elem );
1859       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1860       subMesh = GetMeshDS()->MeshElements( shapeID );
1861     }
1862     int iQ;
1863     if ( (*elem)->IsQuadratic() )
1864     {
1865       iQ = 2;
1866       // add quadratic links to the helper
1867       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1868       {
1869         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1870         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
1871         for ( int iN = 0; iN < nbN; iN += iQ )
1872           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
1873       }
1874       helper.SetIsQuadratic( true );
1875     }
1876     else
1877     {
1878       iQ = 1;
1879       helper.SetIsQuadratic( false );
1880     }
1881     vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1882     helper.SetElementsOnShape( true );
1883     if ( splitMethod._baryNode )
1884     {
1885       // make a node at barycenter
1886       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1887       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1888       nodes.push_back( gcNode );
1889       newNodes.Append( gcNode );
1890     }
1891     if ( !splitMethod._faceBaryNode.empty() )
1892     {
1893       // make or find baricentric nodes of faces
1894       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1895       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1896       {
1897         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1898           volFace2BaryNode.insert
1899           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
1900         if ( !f_n->second )
1901         {
1902           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1903           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1904         }
1905         nodes.push_back( iF_n->second = f_n->second );
1906       }
1907     }
1908
1909     // make tetras
1910     vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1911     const int* tetConn = splitMethod._connectivity;
1912     for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1913       newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1914                                                        nodes[ tetConn[1] ],
1915                                                        nodes[ tetConn[2] ],
1916                                                        nodes[ tetConn[3] ]));
1917
1918     ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
1919
1920     // Split faces on sides of the split volume
1921
1922     const SMDS_MeshNode** volNodes = volTool.GetNodes();
1923     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1924     {
1925       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
1926       if ( nbNodes < 4 ) continue;
1927
1928       // find an existing face
1929       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
1930                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
1931       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
1932                                                                        /*noMedium=*/false))
1933       {
1934         // make triangles
1935         helper.SetElementsOnShape( false );
1936         vector< const SMDS_MeshElement* > triangles;
1937
1938         // find submesh to add new triangles in
1939         if ( !fSubMesh || !fSubMesh->Contains( face ))
1940         {
1941           int shapeID = FindShape( face );
1942           fSubMesh = GetMeshDS()->MeshElements( shapeID );
1943         }
1944         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
1945         if ( iF_n != splitMethod._faceBaryNode.end() )
1946         {
1947           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
1948           {
1949             const SMDS_MeshNode* n1 = fNodes[iN];
1950             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
1951             const SMDS_MeshNode *n3 = iF_n->second;
1952             if ( !volTool.IsFaceExternal( iF ))
1953               swap( n2, n3 );
1954             triangles.push_back( helper.AddFace( n1,n2,n3 ));
1955
1956             if ( fSubMesh && n3->getshapeId() < 1 )
1957               fSubMesh->AddNode( n3 );
1958           }
1959         }
1960         else
1961         {
1962           // among possible triangles create ones discribed by split method
1963           const int* nInd = volTool.GetFaceNodesIndices( iF );
1964           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1965           int iCom = 0; // common node of triangle faces to split into
1966           list< TTriangleFacet > facets;
1967           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
1968           {
1969             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
1970                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
1971                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
1972             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
1973                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
1974                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
1975             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
1976             {
1977               facets.push_back( t012 );
1978               facets.push_back( t023 );
1979               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
1980                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
1981                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
1982                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
1983               break;
1984             }
1985           }
1986           list< TTriangleFacet >::iterator facet = facets.begin();
1987           for ( ; facet != facets.end(); ++facet )
1988           {
1989             if ( !volTool.IsFaceExternal( iF ))
1990               swap( facet->_n2, facet->_n3 );
1991             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
1992                                                  volNodes[ facet->_n2 ],
1993                                                  volNodes[ facet->_n3 ]));
1994           }
1995         }
1996         for ( int i = 0; i < triangles.size(); ++i )
1997         {
1998           if ( !triangles[i] ) continue;
1999           if ( fSubMesh )
2000             fSubMesh->AddElement( triangles[i]);
2001           newElems.Append( triangles[i] );
2002         }
2003         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2004         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2005       }
2006
2007     } // loop on volume faces to split them into triangles
2008
2009     GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
2010
2011     if ( geomType == SMDSEntity_TriQuad_Hexa )
2012     {
2013       // remove medium nodes that could become free
2014       for ( int i = 20; i < volTool.NbNodes(); ++i )
2015         if ( volNodes[i]->NbInverseElements() == 0 )
2016           GetMeshDS()->RemoveNode( volNodes[i] );
2017     }
2018   } // loop on volumes to split
2019
2020   myLastCreatedNodes = newNodes;
2021   myLastCreatedElems = newElems;
2022 }
2023
2024 //=======================================================================
2025 //function : AddToSameGroups
2026 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2027 //=======================================================================
2028
2029 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2030                                         const SMDS_MeshElement* elemInGroups,
2031                                         SMESHDS_Mesh *          aMesh)
2032 {
2033   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2034   if (!groups.empty()) {
2035     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2036     for ( ; grIt != groups.end(); grIt++ ) {
2037       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2038       if ( group && group->Contains( elemInGroups ))
2039         group->SMDSGroup().Add( elemToAdd );
2040     }
2041   }
2042 }
2043
2044
2045 //=======================================================================
2046 //function : RemoveElemFromGroups
2047 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2048 //=======================================================================
2049 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2050                                              SMESHDS_Mesh *          aMesh)
2051 {
2052   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2053   if (!groups.empty())
2054   {
2055     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2056     for (; GrIt != groups.end(); GrIt++)
2057     {
2058       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2059       if (!grp || grp->IsEmpty()) continue;
2060       grp->SMDSGroup().Remove(removeelem);
2061     }
2062   }
2063 }
2064
2065 //================================================================================
2066 /*!
2067  * \brief Replace elemToRm by elemToAdd in the all groups
2068  */
2069 //================================================================================
2070
2071 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2072                                             const SMDS_MeshElement* elemToAdd,
2073                                             SMESHDS_Mesh *          aMesh)
2074 {
2075   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2076   if (!groups.empty()) {
2077     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2078     for ( ; grIt != groups.end(); grIt++ ) {
2079       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2080       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2081         group->SMDSGroup().Add( elemToAdd );
2082     }
2083   }
2084 }
2085
2086 //================================================================================
2087 /*!
2088  * \brief Replace elemToRm by elemToAdd in the all groups
2089  */
2090 //================================================================================
2091
2092 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2093                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2094                                             SMESHDS_Mesh *                         aMesh)
2095 {
2096   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2097   if (!groups.empty())
2098   {
2099     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2100     for ( ; grIt != groups.end(); grIt++ ) {
2101       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2102       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2103         for ( int i = 0; i < elemToAdd.size(); ++i )
2104           group->SMDSGroup().Add( elemToAdd[ i ] );
2105     }
2106   }
2107 }
2108
2109 //=======================================================================
2110 //function : QuadToTri
2111 //purpose  : Cut quadrangles into triangles.
2112 //           theCrit is used to select a diagonal to cut
2113 //=======================================================================
2114
2115 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2116                                   const bool         the13Diag)
2117 {
2118   myLastCreatedElems.Clear();
2119   myLastCreatedNodes.Clear();
2120
2121   MESSAGE( "::QuadToTri()" );
2122
2123   SMESHDS_Mesh * aMesh = GetMeshDS();
2124
2125   Handle(Geom_Surface) surface;
2126   SMESH_MesherHelper   helper( *GetMesh() );
2127
2128   TIDSortedElemSet::iterator itElem;
2129   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2130     const SMDS_MeshElement* elem = *itElem;
2131     if ( !elem || elem->GetType() != SMDSAbs_Face )
2132       continue;
2133     bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2134     if(!isquad) continue;
2135
2136     if(elem->NbNodes()==4) {
2137       // retrieve element nodes
2138       const SMDS_MeshNode* aNodes [4];
2139       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2140       int i = 0;
2141       while ( itN->more() )
2142         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2143
2144       int aShapeId = FindShape( elem );
2145       const SMDS_MeshElement* newElem1 = 0;
2146       const SMDS_MeshElement* newElem2 = 0;
2147       if ( the13Diag ) {
2148         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2149         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2150       }
2151       else {
2152         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2153         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2154       }
2155       myLastCreatedElems.Append(newElem1);
2156       myLastCreatedElems.Append(newElem2);
2157       // put a new triangle on the same shape and add to the same groups
2158       if ( aShapeId )
2159         {
2160           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2161           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2162         }
2163       AddToSameGroups( newElem1, elem, aMesh );
2164       AddToSameGroups( newElem2, elem, aMesh );
2165       //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2166       aMesh->RemoveElement( elem );
2167     }
2168
2169     // Quadratic quadrangle
2170
2171     if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2172
2173       // get surface elem is on
2174       int aShapeId = FindShape( elem );
2175       if ( aShapeId != helper.GetSubShapeID() ) {
2176         surface.Nullify();
2177         TopoDS_Shape shape;
2178         if ( aShapeId > 0 )
2179           shape = aMesh->IndexToShape( aShapeId );
2180         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2181           TopoDS_Face face = TopoDS::Face( shape );
2182           surface = BRep_Tool::Surface( face );
2183           if ( !surface.IsNull() )
2184             helper.SetSubShape( shape );
2185         }
2186       }
2187
2188       const SMDS_MeshNode* aNodes [8];
2189       const SMDS_MeshNode* inFaceNode = 0;
2190       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2191       int i = 0;
2192       while ( itN->more() ) {
2193         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2194         if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2195              aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2196         {
2197           inFaceNode = aNodes[ i-1 ];
2198         }
2199       }
2200
2201       // find middle point for (0,1,2,3)
2202       // and create a node in this point;
2203       gp_XYZ p( 0,0,0 );
2204       if ( surface.IsNull() ) {
2205         for(i=0; i<4; i++)
2206           p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2207         p /= 4;
2208       }
2209       else {
2210         TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2211         gp_XY uv( 0,0 );
2212         for(i=0; i<4; i++)
2213           uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2214         uv /= 4.;
2215         p = surface->Value( uv.X(), uv.Y() ).XYZ();
2216       }
2217       const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2218       myLastCreatedNodes.Append(newN);
2219
2220       // create a new element
2221       const SMDS_MeshElement* newElem1 = 0;
2222       const SMDS_MeshElement* newElem2 = 0;
2223       if ( the13Diag ) {
2224         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2225                                   aNodes[6], aNodes[7], newN );
2226         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2227                                   newN,      aNodes[4], aNodes[5] );
2228       }
2229       else {
2230         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2231                                   aNodes[7], aNodes[4], newN );
2232         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2233                                   newN,      aNodes[5], aNodes[6] );
2234       }
2235       myLastCreatedElems.Append(newElem1);
2236       myLastCreatedElems.Append(newElem2);
2237       // put a new triangle on the same shape and add to the same groups
2238       if ( aShapeId )
2239         {
2240           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2241           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2242         }
2243       AddToSameGroups( newElem1, elem, aMesh );
2244       AddToSameGroups( newElem2, elem, aMesh );
2245       aMesh->RemoveElement( elem );
2246     }
2247   }
2248
2249   return true;
2250 }
2251
2252 //=======================================================================
2253 //function : getAngle
2254 //purpose  :
2255 //=======================================================================
2256
2257 double getAngle(const SMDS_MeshElement * tr1,
2258                 const SMDS_MeshElement * tr2,
2259                 const SMDS_MeshNode *    n1,
2260                 const SMDS_MeshNode *    n2)
2261 {
2262   double angle = 2. * M_PI; // bad angle
2263
2264   // get normals
2265   SMESH::Controls::TSequenceOfXYZ P1, P2;
2266   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2267        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2268     return angle;
2269   gp_Vec N1,N2;
2270   if(!tr1->IsQuadratic())
2271     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2272   else
2273     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2274   if ( N1.SquareMagnitude() <= gp::Resolution() )
2275     return angle;
2276   if(!tr2->IsQuadratic())
2277     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2278   else
2279     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2280   if ( N2.SquareMagnitude() <= gp::Resolution() )
2281     return angle;
2282
2283   // find the first diagonal node n1 in the triangles:
2284   // take in account a diagonal link orientation
2285   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2286   for ( int t = 0; t < 2; t++ ) {
2287     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2288     int i = 0, iDiag = -1;
2289     while ( it->more()) {
2290       const SMDS_MeshElement *n = it->next();
2291       if ( n == n1 || n == n2 ) {
2292         if ( iDiag < 0)
2293           iDiag = i;
2294         else {
2295           if ( i - iDiag == 1 )
2296             nFirst[ t ] = ( n == n1 ? n2 : n1 );
2297           else
2298             nFirst[ t ] = n;
2299           break;
2300         }
2301       }
2302       i++;
2303     }
2304   }
2305   if ( nFirst[ 0 ] == nFirst[ 1 ] )
2306     N2.Reverse();
2307
2308   angle = N1.Angle( N2 );
2309   //SCRUTE( angle );
2310   return angle;
2311 }
2312
2313 // =================================================
2314 // class generating a unique ID for a pair of nodes
2315 // and able to return nodes by that ID
2316 // =================================================
2317 class LinkID_Gen {
2318 public:
2319
2320   LinkID_Gen( const SMESHDS_Mesh* theMesh )
2321     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2322   {}
2323
2324   long GetLinkID (const SMDS_MeshNode * n1,
2325                   const SMDS_MeshNode * n2) const
2326   {
2327     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2328   }
2329
2330   bool GetNodes (const long             theLinkID,
2331                  const SMDS_MeshNode* & theNode1,
2332                  const SMDS_MeshNode* & theNode2) const
2333   {
2334     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2335     if ( !theNode1 ) return false;
2336     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2337     if ( !theNode2 ) return false;
2338     return true;
2339   }
2340
2341 private:
2342   LinkID_Gen();
2343   const SMESHDS_Mesh* myMesh;
2344   long                myMaxID;
2345 };
2346
2347
2348 //=======================================================================
2349 //function : TriToQuad
2350 //purpose  : Fuse neighbour triangles into quadrangles.
2351 //           theCrit is used to select a neighbour to fuse with.
2352 //           theMaxAngle is a max angle between element normals at which
2353 //           fusion is still performed.
2354 //=======================================================================
2355
2356 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
2357                                   SMESH::Controls::NumericalFunctorPtr theCrit,
2358                                   const double                         theMaxAngle)
2359 {
2360   myLastCreatedElems.Clear();
2361   myLastCreatedNodes.Clear();
2362
2363   MESSAGE( "::TriToQuad()" );
2364
2365   if ( !theCrit.get() )
2366     return false;
2367
2368   SMESHDS_Mesh * aMesh = GetMeshDS();
2369
2370   // Prepare data for algo: build
2371   // 1. map of elements with their linkIDs
2372   // 2. map of linkIDs with their elements
2373
2374   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2375   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2376   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
2377   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2378
2379   TIDSortedElemSet::iterator itElem;
2380   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2381     const SMDS_MeshElement* elem = *itElem;
2382     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2383     bool IsTria = elem->NbNodes()==3 || (elem->NbNodes()==6 && elem->IsQuadratic());
2384     if(!IsTria) continue;
2385
2386     // retrieve element nodes
2387     const SMDS_MeshNode* aNodes [4];
2388     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2389     int i = 0;
2390     while ( i<3 )
2391       aNodes[ i++ ] = cast2Node( itN->next() );
2392     aNodes[ 3 ] = aNodes[ 0 ];
2393
2394     // fill maps
2395     for ( i = 0; i < 3; i++ ) {
2396       SMESH_TLink link( aNodes[i], aNodes[i+1] );
2397       // check if elements sharing a link can be fused
2398       itLE = mapLi_listEl.find( link );
2399       if ( itLE != mapLi_listEl.end() ) {
2400         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2401           continue;
2402         const SMDS_MeshElement* elem2 = (*itLE).second.front();
2403         //if ( FindShape( elem ) != FindShape( elem2 ))
2404         //  continue; // do not fuse triangles laying on different shapes
2405         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2406           continue; // avoid making badly shaped quads
2407         (*itLE).second.push_back( elem );
2408       }
2409       else {
2410         mapLi_listEl[ link ].push_back( elem );
2411       }
2412       mapEl_setLi [ elem ].insert( link );
2413     }
2414   }
2415   // Clean the maps from the links shared by a sole element, ie
2416   // links to which only one element is bound in mapLi_listEl
2417
2418   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2419     int nbElems = (*itLE).second.size();
2420     if ( nbElems < 2  ) {
2421       const SMDS_MeshElement* elem = (*itLE).second.front();
2422       SMESH_TLink link = (*itLE).first;
2423       mapEl_setLi[ elem ].erase( link );
2424       if ( mapEl_setLi[ elem ].empty() )
2425         mapEl_setLi.erase( elem );
2426     }
2427   }
2428
2429   // Algo: fuse triangles into quadrangles
2430
2431   while ( ! mapEl_setLi.empty() ) {
2432     // Look for the start element:
2433     // the element having the least nb of shared links
2434     const SMDS_MeshElement* startElem = 0;
2435     int minNbLinks = 4;
2436     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2437       int nbLinks = (*itEL).second.size();
2438       if ( nbLinks < minNbLinks ) {
2439         startElem = (*itEL).first;
2440         minNbLinks = nbLinks;
2441         if ( minNbLinks == 1 )
2442           break;
2443       }
2444     }
2445
2446     // search elements to fuse starting from startElem or links of elements
2447     // fused earlyer - startLinks
2448     list< SMESH_TLink > startLinks;
2449     while ( startElem || !startLinks.empty() ) {
2450       while ( !startElem && !startLinks.empty() ) {
2451         // Get an element to start, by a link
2452         SMESH_TLink linkId = startLinks.front();
2453         startLinks.pop_front();
2454         itLE = mapLi_listEl.find( linkId );
2455         if ( itLE != mapLi_listEl.end() ) {
2456           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2457           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2458           for ( ; itE != listElem.end() ; itE++ )
2459             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2460               startElem = (*itE);
2461           mapLi_listEl.erase( itLE );
2462         }
2463       }
2464
2465       if ( startElem ) {
2466         // Get candidates to be fused
2467         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2468         const SMESH_TLink *link12, *link13;
2469         startElem = 0;
2470         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2471         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2472         ASSERT( !setLi.empty() );
2473         set< SMESH_TLink >::iterator itLi;
2474         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2475         {
2476           const SMESH_TLink & link = (*itLi);
2477           itLE = mapLi_listEl.find( link );
2478           if ( itLE == mapLi_listEl.end() )
2479             continue;
2480
2481           const SMDS_MeshElement* elem = (*itLE).second.front();
2482           if ( elem == tr1 )
2483             elem = (*itLE).second.back();
2484           mapLi_listEl.erase( itLE );
2485           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2486             continue;
2487           if ( tr2 ) {
2488             tr3 = elem;
2489             link13 = &link;
2490           }
2491           else {
2492             tr2 = elem;
2493             link12 = &link;
2494           }
2495
2496           // add other links of elem to list of links to re-start from
2497           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2498           set< SMESH_TLink >::iterator it;
2499           for ( it = links.begin(); it != links.end(); it++ ) {
2500             const SMESH_TLink& link2 = (*it);
2501             if ( link2 != link )
2502               startLinks.push_back( link2 );
2503           }
2504         }
2505
2506         // Get nodes of possible quadrangles
2507         const SMDS_MeshNode *n12 [4], *n13 [4];
2508         bool Ok12 = false, Ok13 = false;
2509         const SMDS_MeshNode *linkNode1, *linkNode2;
2510         if(tr2) {
2511           linkNode1 = link12->first;
2512           linkNode2 = link12->second;
2513           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2514             Ok12 = true;
2515         }
2516         if(tr3) {
2517           linkNode1 = link13->first;
2518           linkNode2 = link13->second;
2519           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2520             Ok13 = true;
2521         }
2522
2523         // Choose a pair to fuse
2524         if ( Ok12 && Ok13 ) {
2525           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2526           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2527           double aBadRate12 = getBadRate( &quad12, theCrit );
2528           double aBadRate13 = getBadRate( &quad13, theCrit );
2529           if (  aBadRate13 < aBadRate12 )
2530             Ok12 = false;
2531           else
2532             Ok13 = false;
2533         }
2534
2535         // Make quadrangles
2536         // and remove fused elems and removed links from the maps
2537         mapEl_setLi.erase( tr1 );
2538         if ( Ok12 ) {
2539           mapEl_setLi.erase( tr2 );
2540           mapLi_listEl.erase( *link12 );
2541           if(tr1->NbNodes()==3) {
2542             const SMDS_MeshElement* newElem = 0;
2543             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2544             myLastCreatedElems.Append(newElem);
2545             AddToSameGroups( newElem, tr1, aMesh );
2546             int aShapeId = tr1->getshapeId();
2547             if ( aShapeId )
2548               {
2549                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2550               }
2551             aMesh->RemoveElement( tr1 );
2552             aMesh->RemoveElement( tr2 );
2553           }
2554           else {
2555             const SMDS_MeshNode* N1 [6];
2556             const SMDS_MeshNode* N2 [6];
2557             GetNodesFromTwoTria(tr1,tr2,N1,N2);
2558             // now we receive following N1 and N2 (using numeration as above image)
2559             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
2560             // i.e. first nodes from both arrays determ new diagonal
2561             const SMDS_MeshNode* aNodes[8];
2562             aNodes[0] = N1[0];
2563             aNodes[1] = N1[1];
2564             aNodes[2] = N2[0];
2565             aNodes[3] = N2[1];
2566             aNodes[4] = N1[3];
2567             aNodes[5] = N2[5];
2568             aNodes[6] = N2[3];
2569             aNodes[7] = N1[5];
2570             const SMDS_MeshElement* newElem = 0;
2571             newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2572                                      aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2573             myLastCreatedElems.Append(newElem);
2574             AddToSameGroups( newElem, tr1, aMesh );
2575             int aShapeId = tr1->getshapeId();
2576             if ( aShapeId )
2577               {
2578                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2579               }
2580             aMesh->RemoveElement( tr1 );
2581             aMesh->RemoveElement( tr2 );
2582             // remove middle node (9)
2583             GetMeshDS()->RemoveNode( N1[4] );
2584           }
2585         }
2586         else if ( Ok13 ) {
2587           mapEl_setLi.erase( tr3 );
2588           mapLi_listEl.erase( *link13 );
2589           if(tr1->NbNodes()==3) {
2590             const SMDS_MeshElement* newElem = 0;
2591             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2592             myLastCreatedElems.Append(newElem);
2593             AddToSameGroups( newElem, tr1, aMesh );
2594             int aShapeId = tr1->getshapeId();
2595             if ( aShapeId )
2596               {
2597                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2598               }
2599             aMesh->RemoveElement( tr1 );
2600             aMesh->RemoveElement( tr3 );
2601           }
2602           else {
2603             const SMDS_MeshNode* N1 [6];
2604             const SMDS_MeshNode* N2 [6];
2605             GetNodesFromTwoTria(tr1,tr3,N1,N2);
2606             // now we receive following N1 and N2 (using numeration as above image)
2607             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
2608             // i.e. first nodes from both arrays determ new diagonal
2609             const SMDS_MeshNode* aNodes[8];
2610             aNodes[0] = N1[0];
2611             aNodes[1] = N1[1];
2612             aNodes[2] = N2[0];
2613             aNodes[3] = N2[1];
2614             aNodes[4] = N1[3];
2615             aNodes[5] = N2[5];
2616             aNodes[6] = N2[3];
2617             aNodes[7] = N1[5];
2618             const SMDS_MeshElement* newElem = 0;
2619             newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2620                                      aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2621             myLastCreatedElems.Append(newElem);
2622             AddToSameGroups( newElem, tr1, aMesh );
2623             int aShapeId = tr1->getshapeId();
2624             if ( aShapeId )
2625               {
2626                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2627               }
2628             aMesh->RemoveElement( tr1 );
2629             aMesh->RemoveElement( tr3 );
2630             // remove middle node (9)
2631             GetMeshDS()->RemoveNode( N1[4] );
2632           }
2633         }
2634
2635         // Next element to fuse: the rejected one
2636         if ( tr3 )
2637           startElem = Ok12 ? tr3 : tr2;
2638
2639       } // if ( startElem )
2640     } // while ( startElem || !startLinks.empty() )
2641   } // while ( ! mapEl_setLi.empty() )
2642
2643   return true;
2644 }
2645
2646
2647 /*#define DUMPSO(txt) \
2648 //  cout << txt << endl;
2649 //=============================================================================
2650 //
2651 //
2652 //
2653 //=============================================================================
2654 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2655 {
2656 if ( i1 == i2 )
2657 return;
2658 int tmp = idNodes[ i1 ];
2659 idNodes[ i1 ] = idNodes[ i2 ];
2660 idNodes[ i2 ] = tmp;
2661 gp_Pnt Ptmp = P[ i1 ];
2662 P[ i1 ] = P[ i2 ];
2663 P[ i2 ] = Ptmp;
2664 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2665 }
2666
2667 //=======================================================================
2668 //function : SortQuadNodes
2669 //purpose  : Set 4 nodes of a quadrangle face in a good order.
2670 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
2671 //           1 or 2 else 0.
2672 //=======================================================================
2673
2674 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2675 int               idNodes[] )
2676 {
2677   gp_Pnt P[4];
2678   int i;
2679   for ( i = 0; i < 4; i++ ) {
2680     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2681     if ( !n ) return 0;
2682     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2683   }
2684
2685   gp_Vec V1(P[0], P[1]);
2686   gp_Vec V2(P[0], P[2]);
2687   gp_Vec V3(P[0], P[3]);
2688
2689   gp_Vec Cross1 = V1 ^ V2;
2690   gp_Vec Cross2 = V2 ^ V3;
2691
2692   i = 0;
2693   if (Cross1.Dot(Cross2) < 0)
2694   {
2695     Cross1 = V2 ^ V1;
2696     Cross2 = V1 ^ V3;
2697
2698     if (Cross1.Dot(Cross2) < 0)
2699       i = 2;
2700     else
2701       i = 1;
2702     swap ( i, i + 1, idNodes, P );
2703
2704     //     for ( int ii = 0; ii < 4; ii++ ) {
2705     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2706     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2707     //     }
2708   }
2709   return i;
2710 }
2711
2712 //=======================================================================
2713 //function : SortHexaNodes
2714 //purpose  : Set 8 nodes of a hexahedron in a good order.
2715 //           Return success status
2716 //=======================================================================
2717
2718 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2719                                       int               idNodes[] )
2720 {
2721   gp_Pnt P[8];
2722   int i;
2723   DUMPSO( "INPUT: ========================================");
2724   for ( i = 0; i < 8; i++ ) {
2725     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2726     if ( !n ) return false;
2727     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2728     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2729   }
2730   DUMPSO( "========================================");
2731
2732
2733   set<int> faceNodes;  // ids of bottom face nodes, to be found
2734   set<int> checkedId1; // ids of tried 2-nd nodes
2735   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2736   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
2737   int iMin, iLoop1 = 0;
2738
2739   // Loop to try the 2-nd nodes
2740
2741   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2742   {
2743     // Find not checked 2-nd node
2744     for ( i = 1; i < 8; i++ )
2745       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2746         int id1 = idNodes[i];
2747         swap ( 1, i, idNodes, P );
2748         checkedId1.insert ( id1 );
2749         break;
2750       }
2751
2752     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2753     // ie that all but meybe one (id3 which is on the same face) nodes
2754     // lay on the same side from the triangle plane.
2755
2756     bool manyInPlane = false; // more than 4 nodes lay in plane
2757     int iLoop2 = 0;
2758     while ( ++iLoop2 < 6 ) {
2759
2760       // get 1-2-3 plane coeffs
2761       Standard_Real A, B, C, D;
2762       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2763       if ( N.SquareMagnitude() > gp::Resolution() )
2764       {
2765         gp_Pln pln ( P[0], N );
2766         pln.Coefficients( A, B, C, D );
2767
2768         // find the node (iMin) closest to pln
2769         Standard_Real dist[ 8 ], minDist = DBL_MAX;
2770         set<int> idInPln;
2771         for ( i = 3; i < 8; i++ ) {
2772           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2773           if ( fabs( dist[i] ) < minDist ) {
2774             minDist = fabs( dist[i] );
2775             iMin = i;
2776           }
2777           if ( fabs( dist[i] ) <= tol )
2778             idInPln.insert( idNodes[i] );
2779         }
2780
2781         // there should not be more than 4 nodes in bottom plane
2782         if ( idInPln.size() > 1 )
2783         {
2784           DUMPSO( "### idInPln.size() = " << idInPln.size());
2785           // idInPlane does not contain the first 3 nodes
2786           if ( manyInPlane || idInPln.size() == 5)
2787             return false; // all nodes in one plane
2788           manyInPlane = true;
2789
2790           // set the 1-st node to be not in plane
2791           for ( i = 3; i < 8; i++ ) {
2792             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2793               DUMPSO( "### Reset 0-th node");
2794               swap( 0, i, idNodes, P );
2795               break;
2796             }
2797           }
2798
2799           // reset to re-check second nodes
2800           leastDist = DBL_MAX;
2801           faceNodes.clear();
2802           checkedId1.clear();
2803           iLoop1 = 0;
2804           break; // from iLoop2;
2805         }
2806
2807         // check that the other 4 nodes are on the same side
2808         bool sameSide = true;
2809         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2810         for ( i = 3; sameSide && i < 8; i++ ) {
2811           if ( i != iMin )
2812             sameSide = ( isNeg == dist[i] <= 0.);
2813         }
2814
2815         // keep best solution
2816         if ( sameSide && minDist < leastDist ) {
2817           leastDist = minDist;
2818           faceNodes.clear();
2819           faceNodes.insert( idNodes[ 1 ] );
2820           faceNodes.insert( idNodes[ 2 ] );
2821           faceNodes.insert( idNodes[ iMin ] );
2822           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2823                   << " leastDist = " << leastDist);
2824           if ( leastDist <= DBL_MIN )
2825             break;
2826         }
2827       }
2828
2829       // set next 3-d node to check
2830       int iNext = 2 + iLoop2;
2831       if ( iNext < 8 ) {
2832         DUMPSO( "Try 2-nd");
2833         swap ( 2, iNext, idNodes, P );
2834       }
2835     } // while ( iLoop2 < 6 )
2836   } // iLoop1
2837
2838   if ( faceNodes.empty() ) return false;
2839
2840   // Put the faceNodes in proper places
2841   for ( i = 4; i < 8; i++ ) {
2842     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2843       // find a place to put
2844       int iTo = 1;
2845       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2846         iTo++;
2847       DUMPSO( "Set faceNodes");
2848       swap ( iTo, i, idNodes, P );
2849     }
2850   }
2851
2852
2853   // Set nodes of the found bottom face in good order
2854   DUMPSO( " Found bottom face: ");
2855   i = SortQuadNodes( theMesh, idNodes );
2856   if ( i ) {
2857     gp_Pnt Ptmp = P[ i ];
2858     P[ i ] = P[ i+1 ];
2859     P[ i+1 ] = Ptmp;
2860   }
2861   //   else
2862   //     for ( int ii = 0; ii < 4; ii++ ) {
2863   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2864   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2865   //    }
2866
2867   // Gravity center of the top and bottom faces
2868   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2869   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2870
2871   // Get direction from the bottom to the top face
2872   gp_Vec upDir ( aGCb, aGCt );
2873   Standard_Real upDirSize = upDir.Magnitude();
2874   if ( upDirSize <= gp::Resolution() ) return false;
2875   upDir / upDirSize;
2876
2877   // Assure that the bottom face normal points up
2878   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2879   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2880   if ( Nb.Dot( upDir ) < 0 ) {
2881     DUMPSO( "Reverse bottom face");
2882     swap( 1, 3, idNodes, P );
2883   }
2884
2885   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2886   Standard_Real minDist = DBL_MAX;
2887   for ( i = 4; i < 8; i++ ) {
2888     // projection of P[i] to the plane defined by P[0] and upDir
2889     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2890     Standard_Real sqDist = P[0].SquareDistance( Pp );
2891     if ( sqDist < minDist ) {
2892       minDist = sqDist;
2893       iMin = i;
2894     }
2895   }
2896   DUMPSO( "Set 4-th");
2897   swap ( 4, iMin, idNodes, P );
2898
2899   // Set nodes of the top face in good order
2900   DUMPSO( "Sort top face");
2901   i = SortQuadNodes( theMesh, &idNodes[4] );
2902   if ( i ) {
2903     i += 4;
2904     gp_Pnt Ptmp = P[ i ];
2905     P[ i ] = P[ i+1 ];
2906     P[ i+1 ] = Ptmp;
2907   }
2908
2909   // Assure that direction of the top face normal is from the bottom face
2910   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
2911   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
2912   if ( Nt.Dot( upDir ) < 0 ) {
2913     DUMPSO( "Reverse top face");
2914     swap( 5, 7, idNodes, P );
2915   }
2916
2917   //   DUMPSO( "OUTPUT: ========================================");
2918   //   for ( i = 0; i < 8; i++ ) {
2919   //     float *p = ugrid->GetPoint(idNodes[i]);
2920   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
2921   //   }
2922
2923   return true;
2924 }*/
2925
2926 //================================================================================
2927 /*!
2928  * \brief Return nodes linked to the given one
2929  * \param theNode - the node
2930  * \param linkedNodes - the found nodes
2931  * \param type - the type of elements to check
2932  *
2933  * Medium nodes are ignored
2934  */
2935 //================================================================================
2936
2937 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
2938                                        TIDSortedElemSet &   linkedNodes,
2939                                        SMDSAbs_ElementType  type )
2940 {
2941   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
2942   while ( elemIt->more() )
2943   {
2944     const SMDS_MeshElement* elem = elemIt->next();
2945     if(elem->GetType() == SMDSAbs_0DElement)
2946       continue;
2947
2948     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
2949     if ( elem->GetType() == SMDSAbs_Volume )
2950     {
2951       SMDS_VolumeTool vol( elem );
2952       while ( nodeIt->more() ) {
2953         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2954         if ( theNode != n && vol.IsLinked( theNode, n ))
2955           linkedNodes.insert( n );
2956       }
2957     }
2958     else
2959     {
2960       for ( int i = 0; nodeIt->more(); ++i ) {
2961         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2962         if ( n == theNode ) {
2963           int iBefore = i - 1;
2964           int iAfter  = i + 1;
2965           if ( elem->IsQuadratic() ) {
2966             int nb = elem->NbNodes() / 2;
2967             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
2968             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
2969           }
2970           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
2971           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
2972         }
2973       }
2974     }
2975   }
2976 }
2977
2978 //=======================================================================
2979 //function : laplacianSmooth
2980 //purpose  : pulls theNode toward the center of surrounding nodes directly
2981 //           connected to that node along an element edge
2982 //=======================================================================
2983
2984 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
2985                      const Handle(Geom_Surface)&          theSurface,
2986                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2987 {
2988   // find surrounding nodes
2989
2990   TIDSortedElemSet nodeSet;
2991   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
2992
2993   // compute new coodrs
2994
2995   double coord[] = { 0., 0., 0. };
2996   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
2997   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
2998     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
2999     if ( theSurface.IsNull() ) { // smooth in 3D
3000       coord[0] += node->X();
3001       coord[1] += node->Y();
3002       coord[2] += node->Z();
3003     }
3004     else { // smooth in 2D
3005       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3006       gp_XY* uv = theUVMap[ node ];
3007       coord[0] += uv->X();
3008       coord[1] += uv->Y();
3009     }
3010   }
3011   int nbNodes = nodeSet.size();
3012   if ( !nbNodes )
3013     return;
3014   coord[0] /= nbNodes;
3015   coord[1] /= nbNodes;
3016
3017   if ( !theSurface.IsNull() ) {
3018     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3019     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3020     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3021     coord[0] = p3d.X();
3022     coord[1] = p3d.Y();
3023     coord[2] = p3d.Z();
3024   }
3025   else
3026     coord[2] /= nbNodes;
3027
3028   // move node
3029
3030   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3031 }
3032
3033 //=======================================================================
3034 //function : centroidalSmooth
3035 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3036 //           surrounding elements
3037 //=======================================================================
3038
3039 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3040                       const Handle(Geom_Surface)&          theSurface,
3041                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3042 {
3043   gp_XYZ aNewXYZ(0.,0.,0.);
3044   SMESH::Controls::Area anAreaFunc;
3045   double totalArea = 0.;
3046   int nbElems = 0;
3047
3048   // compute new XYZ
3049
3050   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3051   while ( elemIt->more() )
3052   {
3053     const SMDS_MeshElement* elem = elemIt->next();
3054     nbElems++;
3055
3056     gp_XYZ elemCenter(0.,0.,0.);
3057     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3058     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3059     int nn = elem->NbNodes();
3060     if(elem->IsQuadratic()) nn = nn/2;
3061     int i=0;
3062     //while ( itN->more() ) {
3063     while ( i<nn ) {
3064       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3065       i++;
3066       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3067       aNodePoints.push_back( aP );
3068       if ( !theSurface.IsNull() ) { // smooth in 2D
3069         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3070         gp_XY* uv = theUVMap[ aNode ];
3071         aP.SetCoord( uv->X(), uv->Y(), 0. );
3072       }
3073       elemCenter += aP;
3074     }
3075     double elemArea = anAreaFunc.GetValue( aNodePoints );
3076     totalArea += elemArea;
3077     elemCenter /= nn;
3078     aNewXYZ += elemCenter * elemArea;
3079   }
3080   aNewXYZ /= totalArea;
3081   if ( !theSurface.IsNull() ) {
3082     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3083     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3084   }
3085
3086   // move node
3087
3088   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3089 }
3090
3091 //=======================================================================
3092 //function : getClosestUV
3093 //purpose  : return UV of closest projection
3094 //=======================================================================
3095
3096 static bool getClosestUV (Extrema_GenExtPS& projector,
3097                           const gp_Pnt&     point,
3098                           gp_XY &           result)
3099 {
3100   projector.Perform( point );
3101   if ( projector.IsDone() ) {
3102     double u, v, minVal = DBL_MAX;
3103     for ( int i = projector.NbExt(); i > 0; i-- )
3104 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3105       if ( projector.SquareDistance( i ) < minVal ) {
3106         minVal = projector.SquareDistance( i );
3107 #else
3108       if ( projector.Value( i ) < minVal ) {
3109         minVal = projector.Value( i );
3110 #endif
3111         projector.Point( i ).Parameter( u, v );
3112       }
3113     result.SetCoord( u, v );
3114     return true;
3115   }
3116   return false;
3117 }
3118
3119 //=======================================================================
3120 //function : Smooth
3121 //purpose  : Smooth theElements during theNbIterations or until a worst
3122 //           element has aspect ratio <= theTgtAspectRatio.
3123 //           Aspect Ratio varies in range [1.0, inf].
3124 //           If theElements is empty, the whole mesh is smoothed.
3125 //           theFixedNodes contains additionally fixed nodes. Nodes built
3126 //           on edges and boundary nodes are always fixed.
3127 //=======================================================================
3128
3129 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3130                                set<const SMDS_MeshNode*> & theFixedNodes,
3131                                const SmoothMethod          theSmoothMethod,
3132                                const int                   theNbIterations,
3133                                double                      theTgtAspectRatio,
3134                                const bool                  the2D)
3135 {
3136   myLastCreatedElems.Clear();
3137   myLastCreatedNodes.Clear();
3138
3139   MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3140
3141   if ( theTgtAspectRatio < 1.0 )
3142     theTgtAspectRatio = 1.0;
3143
3144   const double disttol = 1.e-16;
3145
3146   SMESH::Controls::AspectRatio aQualityFunc;
3147
3148   SMESHDS_Mesh* aMesh = GetMeshDS();
3149
3150   if ( theElems.empty() ) {
3151     // add all faces to theElems
3152     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3153     while ( fIt->more() ) {
3154       const SMDS_MeshElement* face = fIt->next();
3155       theElems.insert( theElems.end(), face );
3156     }
3157   }
3158   // get all face ids theElems are on
3159   set< int > faceIdSet;
3160   TIDSortedElemSet::iterator itElem;
3161   if ( the2D )
3162     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3163       int fId = FindShape( *itElem );
3164       // check that corresponding submesh exists and a shape is face
3165       if (fId &&
3166           faceIdSet.find( fId ) == faceIdSet.end() &&
3167           aMesh->MeshElements( fId )) {
3168         TopoDS_Shape F = aMesh->IndexToShape( fId );
3169         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3170           faceIdSet.insert( fId );
3171       }
3172     }
3173   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3174
3175   // ===============================================
3176   // smooth elements on each TopoDS_Face separately
3177   // ===============================================
3178
3179   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3180   for ( ; fId != faceIdSet.rend(); ++fId ) {
3181     // get face surface and submesh
3182     Handle(Geom_Surface) surface;
3183     SMESHDS_SubMesh* faceSubMesh = 0;
3184     TopoDS_Face face;
3185     double fToler2 = 0, f,l;
3186     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3187     bool isUPeriodic = false, isVPeriodic = false;
3188     if ( *fId ) {
3189       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3190       surface = BRep_Tool::Surface( face );
3191       faceSubMesh = aMesh->MeshElements( *fId );
3192       fToler2 = BRep_Tool::Tolerance( face );
3193       fToler2 *= fToler2 * 10.;
3194       isUPeriodic = surface->IsUPeriodic();
3195       if ( isUPeriodic )
3196         surface->UPeriod();
3197       isVPeriodic = surface->IsVPeriodic();
3198       if ( isVPeriodic )
3199         surface->VPeriod();
3200       surface->Bounds( u1, u2, v1, v2 );
3201     }
3202     // ---------------------------------------------------------
3203     // for elements on a face, find movable and fixed nodes and
3204     // compute UV for them
3205     // ---------------------------------------------------------
3206     bool checkBoundaryNodes = false;
3207     bool isQuadratic = false;
3208     set<const SMDS_MeshNode*> setMovableNodes;
3209     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3210     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3211     list< const SMDS_MeshElement* > elemsOnFace;
3212
3213     Extrema_GenExtPS projector;
3214     GeomAdaptor_Surface surfAdaptor;
3215     if ( !surface.IsNull() ) {
3216       surfAdaptor.Load( surface );
3217       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3218     }
3219     int nbElemOnFace = 0;
3220     itElem = theElems.begin();
3221     // loop on not yet smoothed elements: look for elems on a face
3222     while ( itElem != theElems.end() ) {
3223       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3224         break; // all elements found
3225
3226       const SMDS_MeshElement* elem = *itElem;
3227       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3228            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3229         ++itElem;
3230         continue;
3231       }
3232       elemsOnFace.push_back( elem );
3233       theElems.erase( itElem++ );
3234       nbElemOnFace++;
3235
3236       if ( !isQuadratic )
3237         isQuadratic = elem->IsQuadratic();
3238
3239       // get movable nodes of elem
3240       const SMDS_MeshNode* node;
3241       SMDS_TypeOfPosition posType;
3242       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3243       int nn = 0, nbn =  elem->NbNodes();
3244       if(elem->IsQuadratic())
3245         nbn = nbn/2;
3246       while ( nn++ < nbn ) {
3247         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3248         const SMDS_PositionPtr& pos = node->GetPosition();
3249         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3250         if (posType != SMDS_TOP_EDGE &&
3251             posType != SMDS_TOP_VERTEX &&
3252             theFixedNodes.find( node ) == theFixedNodes.end())
3253         {
3254           // check if all faces around the node are on faceSubMesh
3255           // because a node on edge may be bound to face
3256           SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3257           bool all = true;
3258           if ( faceSubMesh ) {
3259             while ( eIt->more() && all ) {
3260               const SMDS_MeshElement* e = eIt->next();
3261               all = faceSubMesh->Contains( e );
3262             }
3263           }
3264           if ( all )
3265             setMovableNodes.insert( node );
3266           else
3267             checkBoundaryNodes = true;
3268         }
3269         if ( posType == SMDS_TOP_3DSPACE )
3270           checkBoundaryNodes = true;
3271       }
3272
3273       if ( surface.IsNull() )
3274         continue;
3275
3276       // get nodes to check UV
3277       list< const SMDS_MeshNode* > uvCheckNodes;
3278       itN = elem->nodesIterator();
3279       nn = 0; nbn =  elem->NbNodes();
3280       if(elem->IsQuadratic())
3281         nbn = nbn/2;
3282       while ( nn++ < nbn ) {
3283         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3284         if ( uvMap.find( node ) == uvMap.end() )
3285           uvCheckNodes.push_back( node );
3286         // add nodes of elems sharing node
3287         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3288         //         while ( eIt->more() ) {
3289         //           const SMDS_MeshElement* e = eIt->next();
3290         //           if ( e != elem ) {
3291         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3292         //             while ( nIt->more() ) {
3293         //               const SMDS_MeshNode* n =
3294         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3295         //               if ( uvMap.find( n ) == uvMap.end() )
3296         //                 uvCheckNodes.push_back( n );
3297         //             }
3298         //           }
3299         //         }
3300       }
3301       // check UV on face
3302       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3303       for ( ; n != uvCheckNodes.end(); ++n ) {
3304         node = *n;
3305         gp_XY uv( 0, 0 );
3306         const SMDS_PositionPtr& pos = node->GetPosition();
3307         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3308         // get existing UV
3309         switch ( posType ) {
3310         case SMDS_TOP_FACE: {
3311           SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3312           uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3313           break;
3314         }
3315         case SMDS_TOP_EDGE: {
3316           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3317           Handle(Geom2d_Curve) pcurve;
3318           if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3319             pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3320           if ( !pcurve.IsNull() ) {
3321             double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3322             uv = pcurve->Value( u ).XY();
3323           }
3324           break;
3325         }
3326         case SMDS_TOP_VERTEX: {
3327           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3328           if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3329             uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3330           break;
3331         }
3332         default:;
3333         }
3334         // check existing UV
3335         bool project = true;
3336         gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3337         double dist1 = DBL_MAX, dist2 = 0;
3338         if ( posType != SMDS_TOP_3DSPACE ) {
3339           dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3340           project = dist1 > fToler2;
3341         }
3342         if ( project ) { // compute new UV
3343           gp_XY newUV;
3344           if ( !getClosestUV( projector, pNode, newUV )) {
3345             MESSAGE("Node Projection Failed " << node);
3346           }
3347           else {
3348             if ( isUPeriodic )
3349               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3350             if ( isVPeriodic )
3351               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3352             // check new UV
3353             if ( posType != SMDS_TOP_3DSPACE )
3354               dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3355             if ( dist2 < dist1 )
3356               uv = newUV;
3357           }
3358         }
3359         // store UV in the map
3360         listUV.push_back( uv );
3361         uvMap.insert( make_pair( node, &listUV.back() ));
3362       }
3363     } // loop on not yet smoothed elements
3364
3365     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3366       checkBoundaryNodes = true;
3367
3368     // fix nodes on mesh boundary
3369
3370     if ( checkBoundaryNodes ) {
3371       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3372       map< SMESH_TLink, int >::iterator link_nb;
3373       // put all elements links to linkNbMap
3374       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3375       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3376         const SMDS_MeshElement* elem = (*elemIt);
3377         int nbn =  elem->NbCornerNodes();
3378         // loop on elem links: insert them in linkNbMap
3379         for ( int iN = 0; iN < nbn; ++iN ) {
3380           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3381           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3382           SMESH_TLink link( n1, n2 );
3383           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3384           link_nb->second++;
3385         }
3386       }
3387       // remove nodes that are in links encountered only once from setMovableNodes
3388       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3389         if ( link_nb->second == 1 ) {
3390           setMovableNodes.erase( link_nb->first.node1() );
3391           setMovableNodes.erase( link_nb->first.node2() );
3392         }
3393       }
3394     }
3395
3396     // -----------------------------------------------------
3397     // for nodes on seam edge, compute one more UV ( uvMap2 );
3398     // find movable nodes linked to nodes on seam and which
3399     // are to be smoothed using the second UV ( uvMap2 )
3400     // -----------------------------------------------------
3401
3402     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3403     if ( !surface.IsNull() ) {
3404       TopExp_Explorer eExp( face, TopAbs_EDGE );
3405       for ( ; eExp.More(); eExp.Next() ) {
3406         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3407         if ( !BRep_Tool::IsClosed( edge, face ))
3408           continue;
3409         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3410         if ( !sm ) continue;
3411         // find out which parameter varies for a node on seam
3412         double f,l;
3413         gp_Pnt2d uv1, uv2;
3414         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3415         if ( pcurve.IsNull() ) continue;
3416         uv1 = pcurve->Value( f );
3417         edge.Reverse();
3418         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3419         if ( pcurve.IsNull() ) continue;
3420         uv2 = pcurve->Value( f );
3421         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3422         // assure uv1 < uv2
3423         if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3424           gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3425         }
3426         // get nodes on seam and its vertices
3427         list< const SMDS_MeshNode* > seamNodes;
3428         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3429         while ( nSeamIt->more() ) {
3430           const SMDS_MeshNode* node = nSeamIt->next();
3431           if ( !isQuadratic || !IsMedium( node ))
3432             seamNodes.push_back( node );
3433         }
3434         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3435         for ( ; vExp.More(); vExp.Next() ) {
3436           sm = aMesh->MeshElements( vExp.Current() );
3437           if ( sm ) {
3438             nSeamIt = sm->GetNodes();
3439             while ( nSeamIt->more() )
3440               seamNodes.push_back( nSeamIt->next() );
3441           }
3442         }
3443         // loop on nodes on seam
3444         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3445         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3446           const SMDS_MeshNode* nSeam = *noSeIt;
3447           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3448           if ( n_uv == uvMap.end() )
3449             continue;
3450           // set the first UV
3451           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3452           // set the second UV
3453           listUV.push_back( *n_uv->second );
3454           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3455           if ( uvMap2.empty() )
3456             uvMap2 = uvMap; // copy the uvMap contents
3457           uvMap2[ nSeam ] = &listUV.back();
3458
3459           // collect movable nodes linked to ones on seam in nodesNearSeam
3460           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3461           while ( eIt->more() ) {
3462             const SMDS_MeshElement* e = eIt->next();
3463             int nbUseMap1 = 0, nbUseMap2 = 0;
3464             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3465             int nn = 0, nbn =  e->NbNodes();
3466             if(e->IsQuadratic()) nbn = nbn/2;
3467             while ( nn++ < nbn )
3468             {
3469               const SMDS_MeshNode* n =
3470                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3471               if (n == nSeam ||
3472                   setMovableNodes.find( n ) == setMovableNodes.end() )
3473                 continue;
3474               // add only nodes being closer to uv2 than to uv1
3475               gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3476                            0.5 * ( n->Y() + nSeam->Y() ),
3477                            0.5 * ( n->Z() + nSeam->Z() ));
3478               gp_XY uv;
3479               getClosestUV( projector, pMid, uv );
3480               if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3481                 nodesNearSeam.insert( n );
3482                 nbUseMap2++;
3483               }
3484               else
3485                 nbUseMap1++;
3486             }
3487             // for centroidalSmooth all element nodes must
3488             // be on one side of a seam
3489             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3490               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3491               nn = 0;
3492               while ( nn++ < nbn ) {
3493                 const SMDS_MeshNode* n =
3494                   static_cast<const SMDS_MeshNode*>( nIt->next() );
3495                 setMovableNodes.erase( n );
3496               }
3497             }
3498           }
3499         } // loop on nodes on seam
3500       } // loop on edge of a face
3501     } // if ( !face.IsNull() )
3502
3503     if ( setMovableNodes.empty() ) {
3504       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3505       continue; // goto next face
3506     }
3507
3508     // -------------
3509     // SMOOTHING //
3510     // -------------
3511
3512     int it = -1;
3513     double maxRatio = -1., maxDisplacement = -1.;
3514     set<const SMDS_MeshNode*>::iterator nodeToMove;
3515     for ( it = 0; it < theNbIterations; it++ ) {
3516       maxDisplacement = 0.;
3517       nodeToMove = setMovableNodes.begin();
3518       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3519         const SMDS_MeshNode* node = (*nodeToMove);
3520         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3521
3522         // smooth
3523         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3524         if ( theSmoothMethod == LAPLACIAN )
3525           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3526         else
3527           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3528
3529         // node displacement
3530         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3531         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3532         if ( aDispl > maxDisplacement )
3533           maxDisplacement = aDispl;
3534       }
3535       // no node movement => exit
3536       //if ( maxDisplacement < 1.e-16 ) {
3537       if ( maxDisplacement < disttol ) {
3538         MESSAGE("-- no node movement --");
3539         break;
3540       }
3541
3542       // check elements quality
3543       maxRatio  = 0;
3544       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3545       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3546         const SMDS_MeshElement* elem = (*elemIt);
3547         if ( !elem || elem->GetType() != SMDSAbs_Face )
3548           continue;
3549         SMESH::Controls::TSequenceOfXYZ aPoints;
3550         if ( aQualityFunc.GetPoints( elem, aPoints )) {
3551           double aValue = aQualityFunc.GetValue( aPoints );
3552           if ( aValue > maxRatio )
3553             maxRatio = aValue;
3554         }
3555       }
3556       if ( maxRatio <= theTgtAspectRatio ) {
3557         MESSAGE("-- quality achived --");
3558         break;
3559       }
3560       if (it+1 == theNbIterations) {
3561         MESSAGE("-- Iteration limit exceeded --");
3562       }
3563     } // smoothing iterations
3564
3565     MESSAGE(" Face id: " << *fId <<
3566             " Nb iterstions: " << it <<
3567             " Displacement: " << maxDisplacement <<
3568             " Aspect Ratio " << maxRatio);
3569
3570     // ---------------------------------------
3571     // new nodes positions are computed,
3572     // record movement in DS and set new UV
3573     // ---------------------------------------
3574     nodeToMove = setMovableNodes.begin();
3575     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3576       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3577       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3578       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3579       if ( node_uv != uvMap.end() ) {
3580         gp_XY* uv = node_uv->second;
3581         node->SetPosition
3582           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3583       }
3584     }
3585
3586     // move medium nodes of quadratic elements
3587     if ( isQuadratic )
3588     {
3589       SMESH_MesherHelper helper( *GetMesh() );
3590       if ( !face.IsNull() )
3591         helper.SetSubShape( face );
3592       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3593       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3594         const SMDS_VtkFace* QF =
3595           dynamic_cast<const SMDS_VtkFace*> (*elemIt);
3596         if(QF && QF->IsQuadratic()) {
3597           vector<const SMDS_MeshNode*> Ns;
3598           Ns.reserve(QF->NbNodes()+1);
3599           SMDS_ElemIteratorPtr anIter = QF->interlacedNodesElemIterator();
3600           while ( anIter->more() )
3601             Ns.push_back( cast2Node(anIter->next()) );
3602           Ns.push_back( Ns[0] );
3603           double x, y, z;
3604           for(int i=0; i<QF->NbNodes(); i=i+2) {
3605             if ( !surface.IsNull() ) {
3606               gp_XY uv1 = helper.GetNodeUV( face, Ns[i], Ns[i+2] );
3607               gp_XY uv2 = helper.GetNodeUV( face, Ns[i+2], Ns[i] );
3608               gp_XY uv = ( uv1 + uv2 ) / 2.;
3609               gp_Pnt xyz = surface->Value( uv.X(), uv.Y() );
3610               x = xyz.X(); y = xyz.Y(); z = xyz.Z();
3611             }
3612             else {
3613               x = (Ns[i]->X() + Ns[i+2]->X())/2;
3614               y = (Ns[i]->Y() + Ns[i+2]->Y())/2;
3615               z = (Ns[i]->Z() + Ns[i+2]->Z())/2;
3616             }
3617             if( fabs( Ns[i+1]->X() - x ) > disttol ||
3618                 fabs( Ns[i+1]->Y() - y ) > disttol ||
3619                 fabs( Ns[i+1]->Z() - z ) > disttol ) {
3620               // we have to move i+1 node
3621               aMesh->MoveNode( Ns[i+1], x, y, z );
3622             }
3623           }
3624         }
3625       }
3626     }
3627
3628   } // loop on face ids
3629
3630 }
3631
3632 //=======================================================================
3633 //function : isReverse
3634 //purpose  : Return true if normal of prevNodes is not co-directied with
3635 //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3636 //           iNotSame is where prevNodes and nextNodes are different.
3637 //           If result is true then future volume orientation is OK
3638 //=======================================================================
3639
3640 static bool isReverse(const SMDS_MeshElement*             face,
3641                       const vector<const SMDS_MeshNode*>& prevNodes,
3642                       const vector<const SMDS_MeshNode*>& nextNodes,
3643                       const int                           iNotSame)
3644 {
3645
3646   SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
3647   SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
3648   gp_XYZ extrDir( pN - pP ), faceNorm;
3649   SMESH_Algo::FaceNormal( face, faceNorm, /*normalized=*/false );
3650
3651   return faceNorm * extrDir < 0.0;
3652 }
3653
3654 //=======================================================================
3655 /*!
3656  * \brief Create elements by sweeping an element
3657  * \param elem - element to sweep
3658  * \param newNodesItVec - nodes generated from each node of the element
3659  * \param newElems - generated elements
3660  * \param nbSteps - number of sweeping steps
3661  * \param srcElements - to append elem for each generated element
3662  */
3663 //=======================================================================
3664
3665 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
3666                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3667                                     list<const SMDS_MeshElement*>&        newElems,
3668                                     const int                             nbSteps,
3669                                     SMESH_SequenceOfElemPtr&              srcElements)
3670 {
3671   //MESSAGE("sweepElement " << nbSteps);
3672   SMESHDS_Mesh* aMesh = GetMeshDS();
3673
3674   const int           nbNodes = elem->NbNodes();
3675   const int         nbCorners = elem->NbCornerNodes();
3676   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
3677                                                           polyhedron creation !!! */
3678   // Loop on elem nodes:
3679   // find new nodes and detect same nodes indices
3680   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3681   vector<const SMDS_MeshNode*> prevNod( nbNodes );
3682   vector<const SMDS_MeshNode*> nextNod( nbNodes );
3683   vector<const SMDS_MeshNode*> midlNod( nbNodes );
3684
3685   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
3686   vector<int> sames(nbNodes);
3687   vector<bool> isSingleNode(nbNodes);
3688
3689   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3690     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
3691     const SMDS_MeshNode*                         node = nnIt->first;
3692     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3693     if ( listNewNodes.empty() )
3694       return;
3695
3696     itNN   [ iNode ] = listNewNodes.begin();
3697     prevNod[ iNode ] = node;
3698     nextNod[ iNode ] = listNewNodes.front();
3699
3700     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
3701                                                              corner node of linear */
3702     if ( prevNod[ iNode ] != nextNod [ iNode ])
3703       nbDouble += !isSingleNode[iNode];
3704
3705     if( iNode < nbCorners ) { // check corners only
3706       if ( prevNod[ iNode ] == nextNod [ iNode ])
3707         sames[nbSame++] = iNode;
3708       else
3709         iNotSameNode = iNode;
3710     }
3711   }
3712
3713   if ( nbSame == nbNodes || nbSame > 2) {
3714     MESSAGE( " Too many same nodes of element " << elem->GetID() );
3715     return;
3716   }
3717
3718   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
3719   {
3720     // fix nodes order to have bottom normal external
3721     if ( baseType == SMDSEntity_Polygon )
3722     {
3723       std::reverse( itNN.begin(), itNN.end() );
3724       std::reverse( prevNod.begin(), prevNod.end() );
3725       std::reverse( midlNod.begin(), midlNod.end() );
3726       std::reverse( nextNod.begin(), nextNod.end() );
3727       std::reverse( isSingleNode.begin(), isSingleNode.end() );
3728     }
3729     else
3730     {
3731       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
3732       SMDS_MeshCell::applyInterlace( ind, itNN );
3733       SMDS_MeshCell::applyInterlace( ind, prevNod );
3734       SMDS_MeshCell::applyInterlace( ind, nextNod );
3735       SMDS_MeshCell::applyInterlace( ind, midlNod );
3736       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3737       if ( nbSame > 0 )
3738       {
3739         sames[nbSame] = iNotSameNode;
3740         for ( int j = 0; j <= nbSame; ++j )
3741           for ( size_t i = 0; i < ind.size(); ++i )
3742             if ( ind[i] == sames[j] )
3743             {
3744               sames[j] = i;
3745               break;
3746             }
3747         iNotSameNode = sames[nbSame];
3748       }
3749     }
3750   }
3751
3752   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3753   if ( nbSame > 0 ) {
3754     iSameNode    = sames[ nbSame-1 ];
3755     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
3756     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
3757     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
3758   }
3759
3760   // make new elements
3761   for (int iStep = 0; iStep < nbSteps; iStep++ )
3762   {
3763     // get next nodes
3764     for ( iNode = 0; iNode < nbNodes; iNode++ )
3765     {
3766       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
3767       nextNod[ iNode ] = *itNN[ iNode ]++;
3768     }
3769
3770     SMDS_MeshElement* aNewElem = 0;
3771     /*if(!elem->IsPoly())*/ {
3772       switch ( baseType ) {
3773       case SMDSEntity_0D:
3774       case SMDSEntity_Node: { // sweep NODE
3775         if ( nbSame == 0 ) {
3776           if ( isSingleNode[0] )
3777             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3778           else
3779             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3780         }
3781         else
3782           return;
3783         break;
3784       }
3785       case SMDSEntity_Edge: { // sweep EDGE
3786         if ( nbDouble == 0 )
3787         {
3788           if ( nbSame == 0 ) // ---> quadrangle
3789             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3790                                       nextNod[ 1 ], nextNod[ 0 ] );
3791           else               // ---> triangle
3792             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3793                                       nextNod[ iNotSameNode ] );
3794         }
3795         else                 // ---> polygon
3796         {
3797           vector<const SMDS_MeshNode*> poly_nodes;
3798           poly_nodes.push_back( prevNod[0] );
3799           poly_nodes.push_back( prevNod[1] );
3800           if ( prevNod[1] != nextNod[1] )
3801           {
3802             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
3803             poly_nodes.push_back( nextNod[1] );
3804           }
3805           if ( prevNod[0] != nextNod[0] )
3806           {
3807             poly_nodes.push_back( nextNod[0] );
3808             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
3809           }
3810           switch ( poly_nodes.size() ) {
3811           case 3:
3812             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
3813             break;
3814           case 4:
3815             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
3816                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
3817             break;
3818           default:
3819             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
3820           }
3821         }
3822         break;
3823       }
3824       case SMDSEntity_Triangle: // TRIANGLE --->
3825         {
3826           if ( nbDouble > 0 ) break;
3827           if ( nbSame == 0 )       // ---> pentahedron
3828             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3829                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
3830
3831           else if ( nbSame == 1 )  // ---> pyramid
3832             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3833                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
3834                                          nextNod[ iSameNode ]);
3835
3836           else // 2 same nodes:       ---> tetrahedron
3837             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3838                                          nextNod[ iNotSameNode ]);
3839           break;
3840         }
3841       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
3842         {
3843           if ( nbSame == 2 )
3844             return;
3845           if ( nbDouble+nbSame == 2 )
3846           {
3847             if(nbSame==0) {      // ---> quadratic quadrangle
3848               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3849                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
3850             }
3851             else { //(nbSame==1) // ---> quadratic triangle
3852               if(sames[0]==2) {
3853                 return; // medium node on axis
3854               }
3855               else if(sames[0]==0)
3856                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3857                                           nextNod[2], midlNod[1], prevNod[2]);
3858               else // sames[0]==1
3859                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3860                                           midlNod[0], nextNod[2], prevNod[2]);
3861             }
3862           }
3863           else if ( nbDouble == 3 )
3864           {
3865             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
3866               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3867                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
3868             }
3869           }
3870           else
3871             return;
3872           break;
3873         }
3874       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
3875         if ( nbDouble > 0 ) break;
3876
3877         if ( nbSame == 0 )       // ---> hexahedron
3878           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
3879                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
3880
3881         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
3882           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3883                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
3884                                        nextNod[ iSameNode ]);
3885           newElems.push_back( aNewElem );
3886           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
3887                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3888                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
3889         }
3890         else if ( nbSame == 2 ) { // ---> pentahedron
3891           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3892             // iBeforeSame is same too
3893             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3894                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
3895                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
3896           else
3897             // iAfterSame is same too
3898             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
3899                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3900                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
3901         }
3902         break;
3903       }
3904       case SMDSEntity_Quad_Triangle: { // sweep Quadratic TRIANGLE --->
3905         if ( nbDouble+nbSame != 3 ) break;
3906         if(nbSame==0) {
3907           // --->  pentahedron with 15 nodes
3908           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3909                                        nextNod[0], nextNod[1], nextNod[2],
3910                                        prevNod[3], prevNod[4], prevNod[5],
3911                                        nextNod[3], nextNod[4], nextNod[5],
3912                                        midlNod[0], midlNod[1], midlNod[2]);
3913         }
3914         else if(nbSame==1) {
3915           // --->  2d order pyramid of 13 nodes
3916           int apex = iSameNode;
3917           int i0 = ( apex + 1 ) % nbCorners;
3918           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
3919           int i0a = apex + 3;
3920           int i1a = i1 + 3;
3921           int i01 = i0 + 3;
3922           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
3923                                       nextNod[i0], nextNod[i1], prevNod[apex],
3924                                       prevNod[i01], midlNod[i0],
3925                                       nextNod[i01], midlNod[i1],
3926                                       prevNod[i1a], prevNod[i0a],
3927                                       nextNod[i0a], nextNod[i1a]);
3928         }
3929         else if(nbSame==2) {
3930           // --->  2d order tetrahedron of 10 nodes
3931           int n1 = iNotSameNode;
3932           int n2 = ( n1 + 1             ) % nbCorners;
3933           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
3934           int n12 = n1 + 3;
3935           int n23 = n2 + 3;
3936           int n31 = n3 + 3;
3937           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
3938                                        prevNod[n12], prevNod[n23], prevNod[n31],
3939                                        midlNod[n1], nextNod[n12], nextNod[n31]);
3940         }
3941         break;
3942       }
3943       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
3944         if( nbSame == 0 ) {
3945           if ( nbDouble != 4 ) break;
3946           // --->  hexahedron with 20 nodes
3947           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3948                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3949                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3950                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3951                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
3952         }
3953         else if(nbSame==1) {
3954           // ---> pyramid + pentahedron - can not be created since it is needed
3955           // additional middle node at the center of face
3956           INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
3957           return;
3958         }
3959         else if( nbSame == 2 ) {
3960           if ( nbDouble != 2 ) break;
3961           // --->  2d order Pentahedron with 15 nodes
3962           int n1,n2,n4,n5;
3963           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
3964             // iBeforeSame is same too
3965             n1 = iBeforeSame;
3966             n2 = iOpposSame;
3967             n4 = iSameNode;
3968             n5 = iAfterSame;
3969           }
3970           else {
3971             // iAfterSame is same too
3972             n1 = iSameNode;
3973             n2 = iBeforeSame;
3974             n4 = iAfterSame;
3975             n5 = iOpposSame;
3976           }
3977           int n12 = n2 + 4;
3978           int n45 = n4 + 4;
3979           int n14 = n1 + 4;
3980           int n25 = n5 + 4;
3981           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
3982                                        prevNod[n4], prevNod[n5], nextNod[n5],
3983                                        prevNod[n12], midlNod[n2], nextNod[n12],
3984                                        prevNod[n45], midlNod[n5], nextNod[n45],
3985                                        prevNod[n14], prevNod[n25], nextNod[n25]);
3986         }
3987         break;
3988       }
3989       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
3990
3991         if( nbSame == 0 && nbDouble == 9 ) {
3992           // --->  tri-quadratic hexahedron with 27 nodes
3993           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3994                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3995                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3996                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3997                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
3998                                        prevNod[8], // bottom center
3999                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4000                                        nextNod[8], // top center
4001                                        midlNod[8]);// elem center
4002         }
4003         else
4004         {
4005           return;
4006         }
4007         break;
4008       }
4009       case SMDSEntity_Polygon: { // sweep POLYGON
4010
4011         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4012           // --->  hexagonal prism
4013           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4014                                        prevNod[3], prevNod[4], prevNod[5],
4015                                        nextNod[0], nextNod[1], nextNod[2],
4016                                        nextNod[3], nextNod[4], nextNod[5]);
4017         }
4018         break;
4019       }
4020       case SMDSEntity_Ball:
4021         return;
4022
4023       default:
4024         break;
4025       }
4026     }
4027
4028     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4029     {
4030       if ( baseType != SMDSEntity_Polygon )
4031       {
4032         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4033         SMDS_MeshCell::applyInterlace( ind, prevNod );
4034         SMDS_MeshCell::applyInterlace( ind, nextNod );
4035         SMDS_MeshCell::applyInterlace( ind, midlNod );
4036         SMDS_MeshCell::applyInterlace( ind, itNN );
4037         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4038         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4039       }
4040       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4041       vector<int> quantities (nbNodes + 2);
4042       polyedre_nodes.clear();
4043       quantities.clear();
4044
4045       // bottom of prism
4046       for (int inode = 0; inode < nbNodes; inode++)
4047         polyedre_nodes.push_back( prevNod[inode] );
4048       quantities.push_back( nbNodes );
4049
4050       // top of prism
4051       polyedre_nodes.push_back( nextNod[0] );
4052       for (int inode = nbNodes; inode-1; --inode )
4053         polyedre_nodes.push_back( nextNod[inode-1] );
4054       quantities.push_back( nbNodes );
4055
4056       // side faces
4057       for (int iface = 0; iface < nbNodes; iface++)
4058       {
4059         const int prevNbNodes = polyedre_nodes.size();
4060         int inextface = (iface+1) % nbNodes;
4061         polyedre_nodes.push_back( prevNod[inextface] );
4062         polyedre_nodes.push_back( prevNod[iface] );
4063         if ( prevNod[iface] != nextNod[iface] )
4064         {
4065           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4066           polyedre_nodes.push_back( nextNod[iface] );
4067         }
4068         if ( prevNod[inextface] != nextNod[inextface] )
4069         {
4070           polyedre_nodes.push_back( nextNod[inextface] );
4071           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4072         }
4073         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4074         if ( nbFaceNodes > 2 )
4075           quantities.push_back( nbFaceNodes );
4076         else // degenerated face
4077           polyedre_nodes.resize( prevNbNodes );
4078       }
4079       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4080     }
4081
4082     if ( aNewElem ) {
4083       newElems.push_back( aNewElem );
4084       myLastCreatedElems.Append(aNewElem);
4085       srcElements.Append( elem );
4086     }
4087
4088     // set new prev nodes
4089     for ( iNode = 0; iNode < nbNodes; iNode++ )
4090       prevNod[ iNode ] = nextNod[ iNode ];
4091
4092   } // for steps
4093 }
4094
4095 //=======================================================================
4096 /*!
4097  * \brief Create 1D and 2D elements around swept elements
4098  * \param mapNewNodes - source nodes and ones generated from them
4099  * \param newElemsMap - source elements and ones generated from them
4100  * \param elemNewNodesMap - nodes generated from each node of each element
4101  * \param elemSet - all swept elements
4102  * \param nbSteps - number of sweeping steps
4103  * \param srcElements - to append elem for each generated element
4104  */
4105 //=======================================================================
4106
4107 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4108                                   TElemOfElemListMap &     newElemsMap,
4109                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4110                                   TIDSortedElemSet&        elemSet,
4111                                   const int                nbSteps,
4112                                   SMESH_SequenceOfElemPtr& srcElements)
4113 {
4114   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4115   SMESHDS_Mesh* aMesh = GetMeshDS();
4116
4117   // Find nodes belonging to only one initial element - sweep them to get edges.
4118
4119   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4120   for ( ; nList != mapNewNodes.end(); nList++ )
4121   {
4122     const SMDS_MeshNode* node =
4123       static_cast<const SMDS_MeshNode*>( nList->first );
4124     if ( newElemsMap.count( node ))
4125       continue; // node was extruded into edge
4126     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4127     int nbInitElems = 0;
4128     const SMDS_MeshElement* el = 0;
4129     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4130     while ( eIt->more() && nbInitElems < 2 ) {
4131       el = eIt->next();
4132       SMDSAbs_ElementType type = el->GetType();
4133       if ( type == SMDSAbs_Volume || type < highType ) continue;
4134       if ( type > highType ) {
4135         nbInitElems = 0;
4136         highType = type;
4137       }
4138       nbInitElems += elemSet.count(el);
4139     }
4140     if ( nbInitElems < 2 ) {
4141       bool NotCreateEdge = el && el->IsMediumNode(node);
4142       if(!NotCreateEdge) {
4143         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4144         list<const SMDS_MeshElement*> newEdges;
4145         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4146       }
4147     }
4148   }
4149
4150   // Make a ceiling for each element ie an equal element of last new nodes.
4151   // Find free links of faces - make edges and sweep them into faces.
4152
4153   TElemOfElemListMap::iterator   itElem      = newElemsMap.begin();
4154   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4155   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4156   {
4157     const SMDS_MeshElement* elem = itElem->first;
4158     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4159
4160     if(itElem->second.size()==0) continue;
4161
4162     const bool isQuadratic = elem->IsQuadratic();
4163
4164     if ( elem->GetType() == SMDSAbs_Edge ) {
4165       // create a ceiling edge
4166       if ( !isQuadratic ) {
4167         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4168                                vecNewNodes[ 1 ]->second.back())) {
4169           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4170                                                    vecNewNodes[ 1 ]->second.back()));
4171           srcElements.Append( elem );
4172         }
4173       }
4174       else {
4175         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4176                                vecNewNodes[ 1 ]->second.back(),
4177                                vecNewNodes[ 2 ]->second.back())) {
4178           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4179                                                    vecNewNodes[ 1 ]->second.back(),
4180                                                    vecNewNodes[ 2 ]->second.back()));
4181           srcElements.Append( elem );
4182         }
4183       }
4184     }
4185     if ( elem->GetType() != SMDSAbs_Face )
4186       continue;
4187
4188     bool hasFreeLinks = false;
4189
4190     TIDSortedElemSet avoidSet;
4191     avoidSet.insert( elem );
4192
4193     set<const SMDS_MeshNode*> aFaceLastNodes;
4194     int iNode, nbNodes = vecNewNodes.size();
4195     if ( !isQuadratic ) {
4196       // loop on the face nodes
4197       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4198         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4199         // look for free links of the face
4200         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4201         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4202         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4203         // check if a link n1-n2 is free
4204         if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4205           hasFreeLinks = true;
4206           // make a new edge and a ceiling for a new edge
4207           const SMDS_MeshElement* edge;
4208           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4209             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4210             srcElements.Append( myLastCreatedElems.Last() );
4211           }
4212           n1 = vecNewNodes[ iNode ]->second.back();
4213           n2 = vecNewNodes[ iNext ]->second.back();
4214           if ( !aMesh->FindEdge( n1, n2 )) {
4215             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4216             srcElements.Append( edge );
4217           }
4218         }
4219       }
4220     }
4221     else { // elem is quadratic face
4222       int nbn = nbNodes/2;
4223       for ( iNode = 0; iNode < nbn; iNode++ ) {
4224         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4225         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4226         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4227         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4228         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4229         // check if a link is free
4230         if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4231              ! SMESH_MeshEditor::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4232              ! SMESH_MeshEditor::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4233           hasFreeLinks = true;
4234           // make an edge and a ceiling for a new edge
4235           // find medium node
4236           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4237             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4238             srcElements.Append( elem );
4239           }
4240           n1 = vecNewNodes[ iNode ]->second.back();
4241           n2 = vecNewNodes[ iNext ]->second.back();
4242           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4243           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4244             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4245             srcElements.Append( elem );
4246           }
4247         }
4248       }
4249       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4250         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4251       }
4252     }
4253
4254     // sweep free links into faces
4255
4256     if ( hasFreeLinks )  {
4257       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4258       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4259
4260       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4261       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4262         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4263         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4264       }
4265       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4266         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4267         std::advance( v, volNb );
4268         // find indices of free faces of a volume and their source edges
4269         list< int > freeInd;
4270         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4271         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4272         int iF, nbF = vTool.NbFaces();
4273         for ( iF = 0; iF < nbF; iF ++ ) {
4274           if (vTool.IsFreeFace( iF ) &&
4275               vTool.GetFaceNodes( iF, faceNodeSet ) &&
4276               initNodeSet != faceNodeSet) // except an initial face
4277           {
4278             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4279               continue;
4280             freeInd.push_back( iF );
4281             // find source edge of a free face iF
4282             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4283             commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4284             std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4285                                    initNodeSet.begin(), initNodeSet.end(),
4286                                    commonNodes.begin());
4287             if ( (*v)->IsQuadratic() )
4288               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4289             else
4290               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4291 #ifdef _DEBUG_
4292             if ( !srcEdges.back() )
4293             {
4294               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4295                    << iF << " of volume #" << vTool.ID() << endl;
4296             }
4297 #endif
4298           }
4299         }
4300         if ( freeInd.empty() )
4301           continue;
4302
4303         // create faces for all steps;
4304         // if such a face has been already created by sweep of edge,
4305         // assure that its orientation is OK
4306         for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4307           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4308           vTool.SetExternalNormal();
4309           const int nextShift = vTool.IsForward() ? +1 : -1;
4310           list< int >::iterator ind = freeInd.begin();
4311           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4312           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4313           {
4314             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4315             int nbn = vTool.NbFaceNodes( *ind );
4316             const SMDS_MeshElement * f = 0;
4317             if ( nbn == 3 )              ///// triangle
4318             {
4319               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4320               if ( !f ||
4321                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4322               {
4323                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4324                                                      nodes[ 1 ],
4325                                                      nodes[ 1 + nextShift ] };
4326                 if ( f )
4327                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4328                 else
4329                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4330                                                             newOrder[ 2 ] ));
4331               }
4332             }
4333             else if ( nbn == 4 )       ///// quadrangle
4334             {
4335               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4336               if ( !f ||
4337                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4338               {
4339                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4340                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4341                 if ( f )
4342                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4343                 else
4344                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4345                                                             newOrder[ 2 ], newOrder[ 3 ]));
4346               }
4347             }
4348             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4349             {
4350               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4351               if ( !f ||
4352                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4353               {
4354                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4355                                                      nodes[2],
4356                                                      nodes[2 + 2*nextShift],
4357                                                      nodes[3 - 2*nextShift],
4358                                                      nodes[3],
4359                                                      nodes[3 + 2*nextShift]};
4360                 if ( f )
4361                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4362                 else
4363                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4364                                                             newOrder[ 1 ],
4365                                                             newOrder[ 2 ],
4366                                                             newOrder[ 3 ],
4367                                                             newOrder[ 4 ],
4368                                                             newOrder[ 5 ] ));
4369               }
4370             }
4371             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4372             {
4373               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4374                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4375               if ( !f ||
4376                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4377               {
4378                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4379                                                      nodes[4 - 2*nextShift],
4380                                                      nodes[4],
4381                                                      nodes[4 + 2*nextShift],
4382                                                      nodes[1],
4383                                                      nodes[5 - 2*nextShift],
4384                                                      nodes[5],
4385                                                      nodes[5 + 2*nextShift] };
4386                 if ( f )
4387                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4388                 else
4389                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4390                                                            newOrder[ 2 ], newOrder[ 3 ],
4391                                                            newOrder[ 4 ], newOrder[ 5 ],
4392                                                            newOrder[ 6 ], newOrder[ 7 ]));
4393               }
4394             }
4395             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4396             {
4397               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4398                                       SMDSAbs_Face, /*noMedium=*/false);
4399               if ( !f ||
4400                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4401               {
4402                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4403                                                      nodes[4 - 2*nextShift],
4404                                                      nodes[4],
4405                                                      nodes[4 + 2*nextShift],
4406                                                      nodes[1],
4407                                                      nodes[5 - 2*nextShift],
4408                                                      nodes[5],
4409                                                      nodes[5 + 2*nextShift],
4410                                                      nodes[8] };
4411                 if ( f )
4412                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4413                 else
4414                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4415                                                            newOrder[ 2 ], newOrder[ 3 ],
4416                                                            newOrder[ 4 ], newOrder[ 5 ],
4417                                                            newOrder[ 6 ], newOrder[ 7 ],
4418                                                            newOrder[ 8 ]));
4419               }
4420             }
4421             else  //////// polygon
4422             {
4423               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4424               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4425               if ( !f ||
4426                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4427               {
4428                 if ( !vTool.IsForward() )
4429                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4430                 if ( f )
4431                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4432                 else
4433                   AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4434               }
4435             }
4436
4437             while ( srcElements.Length() < myLastCreatedElems.Length() )
4438               srcElements.Append( *srcEdge );
4439
4440           }  // loop on free faces
4441
4442           // go to the next volume
4443           iVol = 0;
4444           while ( iVol++ < nbVolumesByStep ) v++;
4445
4446         } // loop on steps
4447       } // loop on volumes of one step
4448     } // sweep free links into faces
4449
4450     // Make a ceiling face with a normal external to a volume
4451
4452     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4453
4454     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4455     if ( iF >= 0 ) {
4456       lastVol.SetExternalNormal();
4457       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4458       int nbn = lastVol.NbFaceNodes( iF );
4459       if ( nbn == 3 ) {
4460         if (!hasFreeLinks ||
4461             !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
4462           myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
4463       }
4464       else if ( nbn == 4 )
4465       {
4466         if (!hasFreeLinks ||
4467             !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
4468           myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]));
4469       }
4470       else if ( nbn == 6 && isQuadratic )
4471       {
4472         if (!hasFreeLinks ||
4473             !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5]) )
4474           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
4475                                                    nodes[1], nodes[3], nodes[5]));
4476       }
4477       else if ( nbn == 8 && isQuadratic )
4478       {
4479         if (!hasFreeLinks ||
4480             !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
4481                              nodes[1], nodes[3], nodes[5], nodes[7]) )
4482           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4483                                                    nodes[1], nodes[3], nodes[5], nodes[7]));
4484       }
4485       else if ( nbn == 9 && isQuadratic )
4486       {
4487         if (!hasFreeLinks ||
4488             !aMesh->FindElement(vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4489                                 SMDSAbs_Face, /*noMedium=*/false) )
4490           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4491                                                    nodes[1], nodes[3], nodes[5], nodes[7],
4492                                                    nodes[8]));
4493       }
4494       else {
4495         vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes + nbn );
4496         if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
4497           myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4498       }
4499
4500       while ( srcElements.Length() < myLastCreatedElems.Length() )
4501         srcElements.Append( elem );
4502     }
4503   } // loop on swept elements
4504 }
4505
4506 //=======================================================================
4507 //function : RotationSweep
4508 //purpose  :
4509 //=======================================================================
4510
4511 SMESH_MeshEditor::PGroupIDs
4512 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4513                                 const gp_Ax1&      theAxis,
4514                                 const double       theAngle,
4515                                 const int          theNbSteps,
4516                                 const double       theTol,
4517                                 const bool         theMakeGroups,
4518                                 const bool         theMakeWalls)
4519 {
4520   myLastCreatedElems.Clear();
4521   myLastCreatedNodes.Clear();
4522
4523   // source elements for each generated one
4524   SMESH_SequenceOfElemPtr srcElems, srcNodes;
4525
4526   MESSAGE( "RotationSweep()");
4527   gp_Trsf aTrsf;
4528   aTrsf.SetRotation( theAxis, theAngle );
4529   gp_Trsf aTrsf2;
4530   aTrsf2.SetRotation( theAxis, theAngle/2. );
4531
4532   gp_Lin aLine( theAxis );
4533   double aSqTol = theTol * theTol;
4534
4535   SMESHDS_Mesh* aMesh = GetMeshDS();
4536
4537   TNodeOfNodeListMap mapNewNodes;
4538   TElemOfVecOfNnlmiMap mapElemNewNodes;
4539   TElemOfElemListMap newElemsMap;
4540
4541   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4542                                      myMesh->NbFaces(ORDER_QUADRATIC) +
4543                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
4544   // loop on theElems
4545   TIDSortedElemSet::iterator itElem;
4546   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4547     const SMDS_MeshElement* elem = *itElem;
4548     if ( !elem || elem->GetType() == SMDSAbs_Volume )
4549       continue;
4550     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4551     newNodesItVec.reserve( elem->NbNodes() );
4552
4553     // loop on elem nodes
4554     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4555     while ( itN->more() )
4556     {
4557       // check if a node has been already sweeped
4558       const SMDS_MeshNode* node = cast2Node( itN->next() );
4559
4560       gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4561       double coord[3];
4562       aXYZ.Coord( coord[0], coord[1], coord[2] );
4563       bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4564
4565       TNodeOfNodeListMapItr nIt =
4566         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4567       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4568       if ( listNewNodes.empty() )
4569       {
4570         // check if we are to create medium nodes between corner ones
4571         bool needMediumNodes = false;
4572         if ( isQuadraticMesh )
4573         {
4574           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4575           while (it->more() && !needMediumNodes )
4576           {
4577             const SMDS_MeshElement* invElem = it->next();
4578             if ( invElem != elem && !theElems.count( invElem )) continue;
4579             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4580             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4581               needMediumNodes = true;
4582           }
4583         }
4584
4585         // make new nodes
4586         const SMDS_MeshNode * newNode = node;
4587         for ( int i = 0; i < theNbSteps; i++ ) {
4588           if ( !isOnAxis ) {
4589             if ( needMediumNodes )  // create a medium node
4590             {
4591               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4592               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4593               myLastCreatedNodes.Append(newNode);
4594               srcNodes.Append( node );
4595               listNewNodes.push_back( newNode );
4596               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4597             }
4598             else {
4599               aTrsf.Transforms( coord[0], coord[1], coord[2] );
4600             }
4601             // create a corner node
4602             newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4603             myLastCreatedNodes.Append(newNode);
4604             srcNodes.Append( node );
4605             listNewNodes.push_back( newNode );
4606           }
4607           else {
4608             listNewNodes.push_back( newNode );
4609             // if ( needMediumNodes )
4610             //   listNewNodes.push_back( newNode );
4611           }
4612         }
4613       }
4614       newNodesItVec.push_back( nIt );
4615     }
4616     // make new elements
4617     sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4618   }
4619
4620   if ( theMakeWalls )
4621     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4622
4623   PGroupIDs newGroupIDs;
4624   if ( theMakeGroups )
4625     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4626
4627   return newGroupIDs;
4628 }
4629
4630
4631 //=======================================================================
4632 //function : CreateNode
4633 //purpose  :
4634 //=======================================================================
4635 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4636                                                   const double y,
4637                                                   const double z,
4638                                                   const double tolnode,
4639                                                   SMESH_SequenceOfNode& aNodes)
4640 {
4641   // myLastCreatedElems.Clear();
4642   // myLastCreatedNodes.Clear();
4643
4644   gp_Pnt P1(x,y,z);
4645   SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4646
4647   // try to search in sequence of existing nodes
4648   // if aNodes.Length()>0 we 'nave to use given sequence
4649   // else - use all nodes of mesh
4650   if(aNodes.Length()>0) {
4651     int i;
4652     for(i=1; i<=aNodes.Length(); i++) {
4653       gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4654       if(P1.Distance(P2)<tolnode)
4655         return aNodes.Value(i);
4656     }
4657   }
4658   else {
4659     SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4660     while(itn->more()) {
4661       const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4662       gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4663       if(P1.Distance(P2)<tolnode)
4664         return aN;
4665     }
4666   }
4667
4668   // create new node and return it
4669   const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4670   //myLastCreatedNodes.Append(NewNode);
4671   return NewNode;
4672 }
4673
4674
4675 //=======================================================================
4676 //function : ExtrusionSweep
4677 //purpose  :
4678 //=======================================================================
4679
4680 SMESH_MeshEditor::PGroupIDs
4681 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &  theElems,
4682                                   const gp_Vec&       theStep,
4683                                   const int           theNbSteps,
4684                                   TElemOfElemListMap& newElemsMap,
4685                                   const bool          theMakeGroups,
4686                                   const int           theFlags,
4687                                   const double        theTolerance)
4688 {
4689   ExtrusParam aParams;
4690   aParams.myDir = gp_Dir(theStep);
4691   aParams.myNodes.Clear();
4692   aParams.mySteps = new TColStd_HSequenceOfReal;
4693   int i;
4694   for(i=1; i<=theNbSteps; i++)
4695     aParams.mySteps->Append(theStep.Magnitude());
4696
4697   return
4698     ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4699 }
4700
4701
4702 //=======================================================================
4703 //function : ExtrusionSweep
4704 //purpose  :
4705 //=======================================================================
4706
4707 SMESH_MeshEditor::PGroupIDs
4708 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &  theElems,
4709                                   ExtrusParam&        theParams,
4710                                   TElemOfElemListMap& newElemsMap,
4711                                   const bool          theMakeGroups,
4712                                   const int           theFlags,
4713                                   const double        theTolerance)
4714 {
4715   myLastCreatedElems.Clear();
4716   myLastCreatedNodes.Clear();
4717
4718   // source elements for each generated one
4719   SMESH_SequenceOfElemPtr srcElems, srcNodes;
4720
4721   SMESHDS_Mesh* aMesh = GetMeshDS();
4722
4723   int nbsteps = theParams.mySteps->Length();
4724
4725   TNodeOfNodeListMap mapNewNodes;
4726   //TNodeOfNodeVecMap mapNewNodes;
4727   TElemOfVecOfNnlmiMap mapElemNewNodes;
4728   //TElemOfVecOfMapNodesMap mapElemNewNodes;
4729
4730   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4731                                      myMesh->NbFaces(ORDER_QUADRATIC) +
4732                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
4733   // loop on theElems
4734   TIDSortedElemSet::iterator itElem;
4735   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4736     // check element type
4737     const SMDS_MeshElement* elem = *itElem;
4738     if ( !elem  || elem->GetType() == SMDSAbs_Volume )
4739       continue;
4740
4741     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4742     newNodesItVec.reserve( elem->NbNodes() );
4743
4744     // loop on elem nodes
4745     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4746     while ( itN->more() )
4747     {
4748       // check if a node has been already sweeped
4749       const SMDS_MeshNode* node = cast2Node( itN->next() );
4750       TNodeOfNodeListMap::iterator nIt =
4751         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4752       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4753       if ( listNewNodes.empty() )
4754       {
4755         // make new nodes
4756
4757         // check if we are to create medium nodes between corner ones
4758         bool needMediumNodes = false;
4759         if ( isQuadraticMesh )
4760         {
4761           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4762           while (it->more() && !needMediumNodes )
4763           {
4764             const SMDS_MeshElement* invElem = it->next();
4765             if ( invElem != elem && !theElems.count( invElem )) continue;
4766             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4767             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4768               needMediumNodes = true;
4769           }
4770         }
4771
4772         double coord[] = { node->X(), node->Y(), node->Z() };
4773         for ( int i = 0; i < nbsteps; i++ )
4774         {
4775           if ( needMediumNodes ) // create a medium node
4776           {
4777             double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4778             double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4779             double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4780             if( theFlags & EXTRUSION_FLAG_SEW ) {
4781               const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4782                                                          theTolerance, theParams.myNodes);
4783               listNewNodes.push_back( newNode );
4784             }
4785             else {
4786               const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4787               myLastCreatedNodes.Append(newNode);
4788               srcNodes.Append( node );
4789               listNewNodes.push_back( newNode );
4790             }
4791           }
4792           // create a corner node
4793           coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4794           coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4795           coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4796           if( theFlags & EXTRUSION_FLAG_SEW ) {
4797             const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4798                                                        theTolerance, theParams.myNodes);
4799             listNewNodes.push_back( newNode );
4800           }
4801           else {
4802             const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4803             myLastCreatedNodes.Append(newNode);
4804             srcNodes.Append( node );
4805             listNewNodes.push_back( newNode );
4806           }
4807         }
4808       }
4809       newNodesItVec.push_back( nIt );
4810     }
4811     // make new elements
4812     sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4813   }
4814
4815   if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4816     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4817   }
4818   PGroupIDs newGroupIDs;
4819   if ( theMakeGroups )
4820     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4821
4822   return newGroupIDs;
4823 }
4824
4825 //=======================================================================
4826 //function : ExtrusionAlongTrack
4827 //purpose  :
4828 //=======================================================================
4829 SMESH_MeshEditor::Extrusion_Error
4830 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
4831                                        SMESH_subMesh*       theTrack,
4832                                        const SMDS_MeshNode* theN1,
4833                                        const bool           theHasAngles,
4834                                        list<double>&        theAngles,
4835                                        const bool           theLinearVariation,
4836                                        const bool           theHasRefPoint,
4837                                        const gp_Pnt&        theRefPoint,
4838                                        const bool           theMakeGroups)
4839 {
4840   MESSAGE("ExtrusionAlongTrack");
4841   myLastCreatedElems.Clear();
4842   myLastCreatedNodes.Clear();
4843
4844   int aNbE;
4845   std::list<double> aPrms;
4846   TIDSortedElemSet::iterator itElem;
4847
4848   gp_XYZ aGC;
4849   TopoDS_Edge aTrackEdge;
4850   TopoDS_Vertex aV1, aV2;
4851
4852   SMDS_ElemIteratorPtr aItE;
4853   SMDS_NodeIteratorPtr aItN;
4854   SMDSAbs_ElementType aTypeE;
4855
4856   TNodeOfNodeListMap mapNewNodes;
4857
4858   // 1. Check data
4859   aNbE = theElements.size();
4860   // nothing to do
4861   if ( !aNbE )
4862     return EXTR_NO_ELEMENTS;
4863
4864   // 1.1 Track Pattern
4865   ASSERT( theTrack );
4866
4867   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4868
4869   aItE = pSubMeshDS->GetElements();
4870   while ( aItE->more() ) {
4871     const SMDS_MeshElement* pE = aItE->next();
4872     aTypeE = pE->GetType();
4873     // Pattern must contain links only
4874     if ( aTypeE != SMDSAbs_Edge )
4875       return EXTR_PATH_NOT_EDGE;
4876   }
4877
4878   list<SMESH_MeshEditor_PathPoint> fullList;
4879
4880   const TopoDS_Shape& aS = theTrack->GetSubShape();
4881   // Sub-shape for the Pattern must be an Edge or Wire
4882   if( aS.ShapeType() == TopAbs_EDGE ) {
4883     aTrackEdge = TopoDS::Edge( aS );
4884     // the Edge must not be degenerated
4885     if ( BRep_Tool::Degenerated( aTrackEdge ) )
4886       return EXTR_BAD_PATH_SHAPE;
4887     TopExp::Vertices( aTrackEdge, aV1, aV2 );
4888     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4889     const SMDS_MeshNode* aN1 = aItN->next();
4890     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4891     const SMDS_MeshNode* aN2 = aItN->next();
4892     // starting node must be aN1 or aN2
4893     if ( !( aN1 == theN1 || aN2 == theN1 ) )
4894       return EXTR_BAD_STARTING_NODE;
4895     aItN = pSubMeshDS->GetNodes();
4896     while ( aItN->more() ) {
4897       const SMDS_MeshNode* pNode = aItN->next();
4898       const SMDS_EdgePosition* pEPos =
4899         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4900       double aT = pEPos->GetUParameter();
4901       aPrms.push_back( aT );
4902     }
4903     //Extrusion_Error err =
4904     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4905   } else if( aS.ShapeType() == TopAbs_WIRE ) {
4906     list< SMESH_subMesh* > LSM;
4907     TopTools_SequenceOfShape Edges;
4908     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4909     while(itSM->more()) {
4910       SMESH_subMesh* SM = itSM->next();
4911       LSM.push_back(SM);
4912       const TopoDS_Shape& aS = SM->GetSubShape();
4913       Edges.Append(aS);
4914     }
4915     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4916     int startNid = theN1->GetID();
4917     TColStd_MapOfInteger UsedNums;
4918
4919     int NbEdges = Edges.Length();
4920     int i = 1;
4921     for(; i<=NbEdges; i++) {
4922       int k = 0;
4923       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4924       for(; itLSM!=LSM.end(); itLSM++) {
4925         k++;
4926         if(UsedNums.Contains(k)) continue;
4927         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4928         SMESH_subMesh* locTrack = *itLSM;
4929         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4930         TopExp::Vertices( aTrackEdge, aV1, aV2 );
4931         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4932         const SMDS_MeshNode* aN1 = aItN->next();
4933         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4934         const SMDS_MeshNode* aN2 = aItN->next();
4935         // starting node must be aN1 or aN2
4936         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4937         // 2. Collect parameters on the track edge
4938         aPrms.clear();
4939         aItN = locMeshDS->GetNodes();
4940         while ( aItN->more() ) {
4941           const SMDS_MeshNode* pNode = aItN->next();
4942           const SMDS_EdgePosition* pEPos =
4943             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4944           double aT = pEPos->GetUParameter();
4945           aPrms.push_back( aT );
4946         }
4947         list<SMESH_MeshEditor_PathPoint> LPP;
4948         //Extrusion_Error err =
4949         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4950         LLPPs.push_back(LPP);
4951         UsedNums.Add(k);
4952         // update startN for search following egde
4953         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4954         else startNid = aN1->GetID();
4955         break;
4956       }
4957     }
4958     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4959     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4960     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4961     for(; itPP!=firstList.end(); itPP++) {
4962       fullList.push_back( *itPP );
4963     }
4964     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4965     fullList.pop_back();
4966     itLLPP++;
4967     for(; itLLPP!=LLPPs.end(); itLLPP++) {
4968       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4969       itPP = currList.begin();
4970       SMESH_MeshEditor_PathPoint PP2 = currList.front();
4971       gp_Dir D1 = PP1.Tangent();
4972       gp_Dir D2 = PP2.Tangent();
4973       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4974                            (D1.Z()+D2.Z())/2 ) );
4975       PP1.SetTangent(Dnew);
4976       fullList.push_back(PP1);
4977       itPP++;
4978       for(; itPP!=firstList.end(); itPP++) {
4979         fullList.push_back( *itPP );
4980       }
4981       PP1 = fullList.back();
4982       fullList.pop_back();
4983     }
4984     // if wire not closed
4985     fullList.push_back(PP1);
4986     // else ???
4987   }
4988   else {
4989     return EXTR_BAD_PATH_SHAPE;
4990   }
4991
4992   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4993                           theHasRefPoint, theRefPoint, theMakeGroups);
4994 }
4995
4996
4997 //=======================================================================
4998 //function : ExtrusionAlongTrack
4999 //purpose  :
5000 //=======================================================================
5001 SMESH_MeshEditor::Extrusion_Error
5002 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
5003                                        SMESH_Mesh*          theTrack,
5004                                        const SMDS_MeshNode* theN1,
5005                                        const bool           theHasAngles,
5006                                        list<double>&        theAngles,
5007                                        const bool           theLinearVariation,
5008                                        const bool           theHasRefPoint,
5009                                        const gp_Pnt&        theRefPoint,
5010                                        const bool           theMakeGroups)
5011 {
5012   myLastCreatedElems.Clear();
5013   myLastCreatedNodes.Clear();
5014
5015   int aNbE;
5016   std::list<double> aPrms;
5017   TIDSortedElemSet::iterator itElem;
5018
5019   gp_XYZ aGC;
5020   TopoDS_Edge aTrackEdge;
5021   TopoDS_Vertex aV1, aV2;
5022
5023   SMDS_ElemIteratorPtr aItE;
5024   SMDS_NodeIteratorPtr aItN;
5025   SMDSAbs_ElementType aTypeE;
5026
5027   TNodeOfNodeListMap mapNewNodes;
5028
5029   // 1. Check data
5030   aNbE = theElements.size();
5031   // nothing to do
5032   if ( !aNbE )
5033     return EXTR_NO_ELEMENTS;
5034
5035   // 1.1 Track Pattern
5036   ASSERT( theTrack );
5037
5038   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5039
5040   aItE = pMeshDS->elementsIterator();
5041   while ( aItE->more() ) {
5042     const SMDS_MeshElement* pE = aItE->next();
5043     aTypeE = pE->GetType();
5044     // Pattern must contain links only
5045     if ( aTypeE != SMDSAbs_Edge )
5046       return EXTR_PATH_NOT_EDGE;
5047   }
5048
5049   list<SMESH_MeshEditor_PathPoint> fullList;
5050
5051   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5052
5053   if( aS == SMESH_Mesh::PseudoShape() ) {
5054     //Mesh without shape
5055     const SMDS_MeshNode* currentNode = NULL;
5056     const SMDS_MeshNode* prevNode = theN1;
5057     std::vector<const SMDS_MeshNode*> aNodesList;
5058     aNodesList.push_back(theN1);
5059     int nbEdges = 0, conn=0;
5060     const SMDS_MeshElement* prevElem = NULL;
5061     const SMDS_MeshElement* currentElem = NULL;
5062     int totalNbEdges = theTrack->NbEdges();
5063     SMDS_ElemIteratorPtr nIt;
5064
5065     //check start node
5066     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5067       return EXTR_BAD_STARTING_NODE;
5068     }
5069
5070     conn = nbEdgeConnectivity(theN1);
5071     if(conn > 2)
5072       return EXTR_PATH_NOT_EDGE;
5073
5074     aItE = theN1->GetInverseElementIterator();
5075     prevElem = aItE->next();
5076     currentElem = prevElem;
5077     //Get all nodes
5078     if(totalNbEdges == 1 ) {
5079       nIt = currentElem->nodesIterator();
5080       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5081       if(currentNode == prevNode)
5082         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5083       aNodesList.push_back(currentNode);
5084     } else {
5085       nIt = currentElem->nodesIterator();
5086       while( nIt->more() ) {
5087         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5088         if(currentNode == prevNode)
5089           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5090         aNodesList.push_back(currentNode);
5091
5092         //case of the closed mesh
5093         if(currentNode == theN1) {
5094           nbEdges++;
5095           break;
5096         }
5097
5098         conn = nbEdgeConnectivity(currentNode);
5099         if(conn > 2) {
5100           return EXTR_PATH_NOT_EDGE;
5101         }else if( conn == 1 && nbEdges > 0 ) {
5102           //End of the path
5103           nbEdges++;
5104           break;
5105         }else {
5106           prevNode = currentNode;
5107           aItE = currentNode->GetInverseElementIterator();
5108           currentElem = aItE->next();
5109           if( currentElem  == prevElem)
5110             currentElem = aItE->next();
5111           nIt = currentElem->nodesIterator();
5112           prevElem = currentElem;
5113           nbEdges++;
5114         }
5115       }
5116     }
5117
5118     if(nbEdges != totalNbEdges)
5119       return EXTR_PATH_NOT_EDGE;
5120
5121     TopTools_SequenceOfShape Edges;
5122     double x1,x2,y1,y2,z1,z2;
5123     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5124     int startNid = theN1->GetID();
5125     for(int i = 1; i < aNodesList.size(); i++) {
5126       x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5127       y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5128       z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5129       TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5130       list<SMESH_MeshEditor_PathPoint> LPP;
5131       aPrms.clear();
5132       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5133       LLPPs.push_back(LPP);
5134       if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5135       else startNid = aNodesList[i-1]->GetID();
5136
5137     }
5138
5139     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5140     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5141     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5142     for(; itPP!=firstList.end(); itPP++) {
5143       fullList.push_back( *itPP );
5144     }
5145
5146     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5147     SMESH_MeshEditor_PathPoint PP2;
5148     fullList.pop_back();
5149     itLLPP++;
5150     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5151       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5152       itPP = currList.begin();
5153       PP2 = currList.front();
5154       gp_Dir D1 = PP1.Tangent();
5155       gp_Dir D2 = PP2.Tangent();
5156       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5157                            (D1.Z()+D2.Z())/2 ) );
5158       PP1.SetTangent(Dnew);
5159       fullList.push_back(PP1);
5160       itPP++;
5161       for(; itPP!=currList.end(); itPP++) {
5162         fullList.push_back( *itPP );
5163       }
5164       PP1 = fullList.back();
5165       fullList.pop_back();
5166     }
5167     fullList.push_back(PP1);
5168
5169   } // Sub-shape for the Pattern must be an Edge or Wire
5170   else if( aS.ShapeType() == TopAbs_EDGE ) {
5171     aTrackEdge = TopoDS::Edge( aS );
5172     // the Edge must not be degenerated
5173     if ( BRep_Tool::Degenerated( aTrackEdge ) )
5174       return EXTR_BAD_PATH_SHAPE;
5175     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5176     const SMDS_MeshNode* aN1 = 0;
5177     const SMDS_MeshNode* aN2 = 0;
5178     if ( theTrack->GetSubMesh( aV1 ) && theTrack->GetSubMesh( aV1 )->GetSubMeshDS() ) {
5179       aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5180       aN1 = aItN->next();
5181     }
5182     if ( theTrack->GetSubMesh( aV2 ) && theTrack->GetSubMesh( aV2 )->GetSubMeshDS() ) {
5183       aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5184       aN2 = aItN->next();
5185     }
5186     // starting node must be aN1 or aN2
5187     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5188       return EXTR_BAD_STARTING_NODE;
5189     aItN = pMeshDS->nodesIterator();
5190     while ( aItN->more() ) {
5191       const SMDS_MeshNode* pNode = aItN->next();
5192       if( pNode==aN1 || pNode==aN2 ) continue;
5193       const SMDS_EdgePosition* pEPos =
5194         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5195       double aT = pEPos->GetUParameter();
5196       aPrms.push_back( aT );
5197     }
5198     //Extrusion_Error err =
5199     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5200   }
5201   else if( aS.ShapeType() == TopAbs_WIRE ) {
5202     list< SMESH_subMesh* > LSM;
5203     TopTools_SequenceOfShape Edges;
5204     TopExp_Explorer eExp(aS, TopAbs_EDGE);
5205     for(; eExp.More(); eExp.Next()) {
5206       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5207       if( BRep_Tool::Degenerated(E) ) continue;
5208       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5209       if(SM) {
5210         LSM.push_back(SM);
5211         Edges.Append(E);
5212       }
5213     }
5214     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5215     TopoDS_Vertex aVprev;
5216     TColStd_MapOfInteger UsedNums;
5217     int NbEdges = Edges.Length();
5218     int i = 1;
5219     for(; i<=NbEdges; i++) {
5220       int k = 0;
5221       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5222       for(; itLSM!=LSM.end(); itLSM++) {
5223         k++;
5224         if(UsedNums.Contains(k)) continue;
5225         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5226         SMESH_subMesh* locTrack = *itLSM;
5227         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5228         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5229         bool aN1isOK = false, aN2isOK = false;
5230         if ( aVprev.IsNull() ) {
5231           // if previous vertex is not yet defined, it means that we in the beginning of wire
5232           // and we have to find initial vertex corresponding to starting node theN1
5233           const SMDS_MeshNode* aN1 = 0;
5234           const SMDS_MeshNode* aN2 = 0;
5235
5236           if ( locTrack->GetFather()->GetSubMesh(aV1) && locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS() ) {
5237             aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5238             aN1 = aItN->next();
5239           }
5240           if ( locTrack->GetFather()->GetSubMesh(aV2) && locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS() ) {
5241             aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5242             aN2 = aItN->next();
5243           }
5244           // starting node must be aN1 or aN2
5245           aN1isOK = ( aN1 && aN1 == theN1 );
5246           aN2isOK = ( aN2 && aN2 == theN1 );
5247         }
5248         else {
5249           // we have specified ending vertex of the previous edge on the previous iteration
5250           // and we have just to check that it corresponds to any vertex in current segment
5251           aN1isOK = aVprev.IsSame( aV1 );
5252           aN2isOK = aVprev.IsSame( aV2 );
5253         }
5254         if ( !aN1isOK && !aN2isOK ) continue;
5255         // 2. Collect parameters on the track edge
5256         aPrms.clear();
5257         aItN = locMeshDS->GetNodes();
5258         while ( aItN->more() ) {
5259           const SMDS_MeshNode*     pNode = aItN->next();
5260           const SMDS_EdgePosition* pEPos =
5261             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5262           double aT = pEPos->GetUParameter();
5263           aPrms.push_back( aT );
5264         }
5265         list<SMESH_MeshEditor_PathPoint> LPP;
5266         //Extrusion_Error err =
5267         MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
5268         LLPPs.push_back(LPP);
5269         UsedNums.Add(k);
5270         // update startN for search following egde
5271         if ( aN1isOK ) aVprev = aV2;
5272         else           aVprev = aV1;
5273         break;
5274       }
5275     }
5276     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5277     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5278     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5279     for(; itPP!=firstList.end(); itPP++) {
5280       fullList.push_back( *itPP );
5281     }
5282     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5283     fullList.pop_back();
5284     itLLPP++;
5285     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5286       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5287       itPP = currList.begin();
5288       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5289       gp_Dir D1 = PP1.Tangent();
5290       gp_Dir D2 = PP2.Tangent();
5291       gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
5292       PP1.SetTangent(Dnew);
5293       fullList.push_back(PP1);
5294       itPP++;
5295       for(; itPP!=currList.end(); itPP++) {
5296         fullList.push_back( *itPP );
5297       }
5298       PP1 = fullList.back();
5299       fullList.pop_back();
5300     }
5301     // if wire not closed
5302     fullList.push_back(PP1);
5303     // else ???
5304   }
5305   else {
5306     return EXTR_BAD_PATH_SHAPE;
5307   }
5308
5309   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5310                           theHasRefPoint, theRefPoint, theMakeGroups);
5311 }
5312
5313
5314 //=======================================================================
5315 //function : MakeEdgePathPoints
5316 //purpose  : auxilary for ExtrusionAlongTrack
5317 //=======================================================================
5318 SMESH_MeshEditor::Extrusion_Error
5319 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5320                                      const TopoDS_Edge& aTrackEdge,
5321                                      bool FirstIsStart,
5322                                      list<SMESH_MeshEditor_PathPoint>& LPP)
5323 {
5324   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5325   aTolVec=1.e-7;
5326   aTolVec2=aTolVec*aTolVec;
5327   double aT1, aT2;
5328   TopoDS_Vertex aV1, aV2;
5329   TopExp::Vertices( aTrackEdge, aV1, aV2 );
5330   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5331   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5332   // 2. Collect parameters on the track edge
5333   aPrms.push_front( aT1 );
5334   aPrms.push_back( aT2 );
5335   // sort parameters
5336   aPrms.sort();
5337   if( FirstIsStart ) {
5338     if ( aT1 > aT2 ) {
5339       aPrms.reverse();
5340     }
5341   }
5342   else {
5343     if ( aT2 > aT1 ) {
5344       aPrms.reverse();
5345     }
5346   }
5347   // 3. Path Points
5348   SMESH_MeshEditor_PathPoint aPP;
5349   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5350   std::list<double>::iterator aItD = aPrms.begin();
5351   for(; aItD != aPrms.end(); ++aItD) {
5352     double aT = *aItD;
5353     gp_Pnt aP3D;
5354     gp_Vec aVec;
5355     aC3D->D1( aT, aP3D, aVec );
5356     aL2 = aVec.SquareMagnitude();
5357     if ( aL2 < aTolVec2 )
5358       return EXTR_CANT_GET_TANGENT;
5359     gp_Dir aTgt( aVec );
5360     aPP.SetPnt( aP3D );
5361     aPP.SetTangent( aTgt );
5362     aPP.SetParameter( aT );
5363     LPP.push_back(aPP);
5364   }
5365   return EXTR_OK;
5366 }
5367
5368
5369 //=======================================================================
5370 //function : MakeExtrElements
5371 //purpose  : auxilary for ExtrusionAlongTrack
5372 //=======================================================================
5373 SMESH_MeshEditor::Extrusion_Error
5374 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet&  theElements,
5375                                    list<SMESH_MeshEditor_PathPoint>& fullList,
5376                                    const bool theHasAngles,
5377                                    list<double>& theAngles,
5378                                    const bool theLinearVariation,
5379                                    const bool theHasRefPoint,
5380                                    const gp_Pnt& theRefPoint,
5381                                    const bool theMakeGroups)
5382 {
5383   MESSAGE("MakeExtrElements");
5384   //cout<<"MakeExtrElements  fullList.size() = "<<fullList.size()<<endl;
5385   int aNbTP = fullList.size();
5386   vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5387   // Angles
5388   if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5389     LinearAngleVariation(aNbTP-1, theAngles);
5390   }
5391   vector<double> aAngles( aNbTP );
5392   int j = 0;
5393   for(; j<aNbTP; ++j) {
5394     aAngles[j] = 0.;
5395   }
5396   if ( theHasAngles ) {
5397     double anAngle;;
5398     std::list<double>::iterator aItD = theAngles.begin();
5399     for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5400       anAngle = *aItD;
5401       aAngles[j] = anAngle;
5402     }
5403   }
5404   // fill vector of path points with angles
5405   //aPPs.resize(fullList.size());
5406   j = -1;
5407   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5408   for(; itPP!=fullList.end(); itPP++) {
5409     j++;
5410     SMESH_MeshEditor_PathPoint PP = *itPP;
5411     PP.SetAngle(aAngles[j]);
5412     aPPs[j] = PP;
5413   }
5414
5415   TNodeOfNodeListMap mapNewNodes;
5416   TElemOfVecOfNnlmiMap mapElemNewNodes;
5417   TElemOfElemListMap newElemsMap;
5418   TIDSortedElemSet::iterator itElem;
5419   double aX, aY, aZ;
5420   int aNb;
5421   SMDSAbs_ElementType aTypeE;
5422   // source elements for each generated one
5423   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5424
5425   // 3. Center of rotation aV0
5426   gp_Pnt aV0 = theRefPoint;
5427   gp_XYZ aGC;
5428   if ( !theHasRefPoint ) {
5429     aNb = 0;
5430     aGC.SetCoord( 0.,0.,0. );
5431
5432     itElem = theElements.begin();
5433     for ( ; itElem != theElements.end(); itElem++ ) {
5434       const SMDS_MeshElement* elem = *itElem;
5435
5436       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5437       while ( itN->more() ) {
5438         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5439         aX = node->X();
5440         aY = node->Y();
5441         aZ = node->Z();
5442
5443         if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5444           list<const SMDS_MeshNode*> aLNx;
5445           mapNewNodes[node] = aLNx;
5446           //
5447           gp_XYZ aXYZ( aX, aY, aZ );
5448           aGC += aXYZ;
5449           ++aNb;
5450         }
5451       }
5452     }
5453     aGC /= aNb;
5454     aV0.SetXYZ( aGC );
5455   } // if (!theHasRefPoint) {
5456   mapNewNodes.clear();
5457
5458   // 4. Processing the elements
5459   SMESHDS_Mesh* aMesh = GetMeshDS();
5460
5461   for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5462     // check element type
5463     const SMDS_MeshElement* elem = *itElem;
5464     aTypeE = elem->GetType();
5465     if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5466       continue;
5467
5468     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5469     newNodesItVec.reserve( elem->NbNodes() );
5470
5471     // loop on elem nodes
5472     int nodeIndex = -1;
5473     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5474     while ( itN->more() )
5475     {
5476       ++nodeIndex;
5477       // check if a node has been already processed
5478       const SMDS_MeshNode* node =
5479         static_cast<const SMDS_MeshNode*>( itN->next() );
5480       TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5481       if ( nIt == mapNewNodes.end() ) {
5482         nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5483         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5484
5485         // make new nodes
5486         aX = node->X();  aY = node->Y(); aZ = node->Z();
5487
5488         Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5489         gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5490         gp_Ax1 anAx1, anAxT1T0;
5491         gp_Dir aDT1x, aDT0x, aDT1T0;
5492
5493         aTolAng=1.e-4;
5494
5495         aV0x = aV0;
5496         aPN0.SetCoord(aX, aY, aZ);
5497
5498         const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5499         aP0x = aPP0.Pnt();
5500         aDT0x= aPP0.Tangent();
5501         //cout<<"j = 0   PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5502
5503         for ( j = 1; j < aNbTP; ++j ) {
5504           const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5505           aP1x = aPP1.Pnt();
5506           aDT1x = aPP1.Tangent();
5507           aAngle1x = aPP1.Angle();
5508
5509           gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5510           // Translation
5511           gp_Vec aV01x( aP0x, aP1x );
5512           aTrsf.SetTranslation( aV01x );
5513
5514           // traslated point
5515           aV1x = aV0x.Transformed( aTrsf );
5516           aPN1 = aPN0.Transformed( aTrsf );
5517
5518           // rotation 1 [ T1,T0 ]
5519           aAngleT1T0=-aDT1x.Angle( aDT0x );
5520           if (fabs(aAngleT1T0) > aTolAng) {
5521             aDT1T0=aDT1x^aDT0x;
5522             anAxT1T0.SetLocation( aV1x );
5523             anAxT1T0.SetDirection( aDT1T0 );
5524             aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5525
5526             aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5527           }
5528
5529           // rotation 2
5530           if ( theHasAngles ) {
5531             anAx1.SetLocation( aV1x );
5532             anAx1.SetDirection( aDT1x );
5533             aTrsfRot.SetRotation( anAx1, aAngle1x );
5534
5535             aPN1 = aPN1.Transformed( aTrsfRot );
5536           }
5537
5538           // make new node
5539           //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5540           if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5541             // create additional node
5542             double x = ( aPN1.X() + aPN0.X() )/2.;
5543             double y = ( aPN1.Y() + aPN0.Y() )/2.;
5544             double z = ( aPN1.Z() + aPN0.Z() )/2.;
5545             const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5546             myLastCreatedNodes.Append(newNode);
5547             srcNodes.Append( node );
5548             listNewNodes.push_back( newNode );
5549           }
5550           aX = aPN1.X();
5551           aY = aPN1.Y();
5552           aZ = aPN1.Z();
5553           const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5554           myLastCreatedNodes.Append(newNode);
5555           srcNodes.Append( node );
5556           listNewNodes.push_back( newNode );
5557
5558           aPN0 = aPN1;
5559           aP0x = aP1x;
5560           aV0x = aV1x;
5561           aDT0x = aDT1x;
5562         }
5563       }
5564
5565       else {
5566         // if current elem is quadratic and current node is not medium
5567         // we have to check - may be it is needed to insert additional nodes
5568         if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5569           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5570           if(listNewNodes.size()==aNbTP-1) {
5571             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5572             gp_XYZ P(node->X(), node->Y(), node->Z());
5573             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5574             int i;
5575             for(i=0; i<aNbTP-1; i++) {
5576               const SMDS_MeshNode* N = *it;
5577               double x = ( N->X() + P.X() )/2.;
5578               double y = ( N->Y() + P.Y() )/2.;
5579               double z = ( N->Z() + P.Z() )/2.;
5580               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5581               srcNodes.Append( node );
5582               myLastCreatedNodes.Append(newN);
5583               aNodes[2*i] = newN;
5584               aNodes[2*i+1] = N;
5585               P = gp_XYZ(N->X(),N->Y(),N->Z());
5586             }
5587             listNewNodes.clear();
5588             for(i=0; i<2*(aNbTP-1); i++) {
5589               listNewNodes.push_back(aNodes[i]);
5590             }
5591           }
5592         }
5593       }
5594
5595       newNodesItVec.push_back( nIt );
5596     }
5597     // make new elements
5598     //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5599     //              newNodesItVec[0]->second.size(), myLastCreatedElems );
5600     sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5601   }
5602
5603   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5604
5605   if ( theMakeGroups )
5606     generateGroups( srcNodes, srcElems, "extruded");
5607
5608   return EXTR_OK;
5609 }
5610
5611
5612 //=======================================================================
5613 //function : LinearAngleVariation
5614 //purpose  : auxilary for ExtrusionAlongTrack
5615 //=======================================================================
5616 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5617                                             list<double>& Angles)
5618 {
5619   int nbAngles = Angles.size();
5620   if( nbSteps > nbAngles ) {
5621     vector<double> theAngles(nbAngles);
5622     list<double>::iterator it = Angles.begin();
5623     int i = -1;
5624     for(; it!=Angles.end(); it++) {
5625       i++;
5626       theAngles[i] = (*it);
5627     }
5628     list<double> res;
5629     double rAn2St = double( nbAngles ) / double( nbSteps );
5630     double angPrev = 0, angle;
5631     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5632       double angCur = rAn2St * ( iSt+1 );
5633       double angCurFloor  = floor( angCur );
5634       double angPrevFloor = floor( angPrev );
5635       if ( angPrevFloor == angCurFloor )
5636         angle = rAn2St * theAngles[ int( angCurFloor ) ];
5637       else {
5638         int iP = int( angPrevFloor );
5639         double angPrevCeil = ceil(angPrev);
5640         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5641
5642         int iC = int( angCurFloor );
5643         if ( iC < nbAngles )
5644           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5645
5646         iP = int( angPrevCeil );
5647         while ( iC-- > iP )
5648           angle += theAngles[ iC ];
5649       }
5650       res.push_back(angle);
5651       angPrev = angCur;
5652     }
5653     Angles.clear();
5654     it = res.begin();
5655     for(; it!=res.end(); it++)
5656       Angles.push_back( *it );
5657   }
5658 }
5659
5660
5661 //================================================================================
5662 /*!
5663  * \brief Move or copy theElements applying theTrsf to their nodes
5664  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5665  *  \param theTrsf - transformation to apply
5666  *  \param theCopy - if true, create translated copies of theElems
5667  *  \param theMakeGroups - if true and theCopy, create translated groups
5668  *  \param theTargetMesh - mesh to copy translated elements into
5669  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5670  */
5671 //================================================================================
5672
5673 SMESH_MeshEditor::PGroupIDs
5674 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5675                              const gp_Trsf&     theTrsf,
5676                              const bool         theCopy,
5677                              const bool         theMakeGroups,
5678                              SMESH_Mesh*        theTargetMesh)
5679 {
5680   myLastCreatedElems.Clear();
5681   myLastCreatedNodes.Clear();
5682
5683   bool needReverse = false;
5684   string groupPostfix;
5685   switch ( theTrsf.Form() ) {
5686   case gp_PntMirror:
5687     MESSAGE("gp_PntMirror");
5688     needReverse = true;
5689     groupPostfix = "mirrored";
5690     break;
5691   case gp_Ax1Mirror:
5692     MESSAGE("gp_Ax1Mirror");
5693     groupPostfix = "mirrored";
5694     break;
5695   case gp_Ax2Mirror:
5696     MESSAGE("gp_Ax2Mirror");
5697     needReverse = true;
5698     groupPostfix = "mirrored";
5699     break;
5700   case gp_Rotation:
5701     MESSAGE("gp_Rotation");
5702     groupPostfix = "rotated";
5703     break;
5704   case gp_Translation:
5705     MESSAGE("gp_Translation");
5706     groupPostfix = "translated";
5707     break;
5708   case gp_Scale:
5709     MESSAGE("gp_Scale");
5710     groupPostfix = "scaled";
5711     break;
5712   case gp_CompoundTrsf: // different scale by axis
5713     MESSAGE("gp_CompoundTrsf");
5714     groupPostfix = "scaled";
5715     break;
5716   default:
5717     MESSAGE("default");
5718     needReverse = false;
5719     groupPostfix = "transformed";
5720   }
5721
5722   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5723   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5724   SMESHDS_Mesh* aMesh    = GetMeshDS();
5725
5726
5727   // map old node to new one
5728   TNodeNodeMap nodeMap;
5729
5730   // elements sharing moved nodes; those of them which have all
5731   // nodes mirrored but are not in theElems are to be reversed
5732   TIDSortedElemSet inverseElemSet;
5733
5734   // source elements for each generated one
5735   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5736
5737   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5738   TIDSortedElemSet orphanNode;
5739
5740   if ( theElems.empty() ) // transform the whole mesh
5741   {
5742     // add all elements
5743     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5744     while ( eIt->more() ) theElems.insert( eIt->next() );
5745     // add orphan nodes
5746     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5747     while ( nIt->more() )
5748     {
5749       const SMDS_MeshNode* node = nIt->next();
5750       if ( node->NbInverseElements() == 0)
5751         orphanNode.insert( node );
5752     }
5753   }
5754
5755   // loop on elements to transform nodes : first orphan nodes then elems
5756   TIDSortedElemSet::iterator itElem;
5757   TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5758   for (int i=0; i<2; i++)
5759   for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5760     const SMDS_MeshElement* elem = *itElem;
5761     if ( !elem )
5762       continue;
5763
5764     // loop on elem nodes
5765     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5766     while ( itN->more() ) {
5767
5768       const SMDS_MeshNode* node = cast2Node( itN->next() );
5769       // check if a node has been already transformed
5770       pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5771         nodeMap.insert( make_pair ( node, node ));
5772       if ( !n2n_isnew.second )
5773         continue;
5774
5775       double coord[3];
5776       coord[0] = node->X();
5777       coord[1] = node->Y();
5778       coord[2] = node->Z();
5779       theTrsf.Transforms( coord[0], coord[1], coord[2] );
5780       if ( theTargetMesh ) {
5781         const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5782         n2n_isnew.first->second = newNode;
5783         myLastCreatedNodes.Append(newNode);
5784         srcNodes.Append( node );
5785       }
5786       else if ( theCopy ) {
5787         const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5788         n2n_isnew.first->second = newNode;
5789         myLastCreatedNodes.Append(newNode);
5790         srcNodes.Append( node );
5791       }
5792       else {
5793         aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5794         // node position on shape becomes invalid
5795         const_cast< SMDS_MeshNode* > ( node )->SetPosition
5796           ( SMDS_SpacePosition::originSpacePosition() );
5797       }
5798
5799       // keep inverse elements
5800       if ( !theCopy && !theTargetMesh && needReverse ) {
5801         SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5802         while ( invElemIt->more() ) {
5803           const SMDS_MeshElement* iel = invElemIt->next();
5804           inverseElemSet.insert( iel );
5805         }
5806       }
5807     }
5808   }
5809
5810   // either create new elements or reverse mirrored ones
5811   if ( !theCopy && !needReverse && !theTargetMesh )
5812     return PGroupIDs();
5813
5814   TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5815   for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5816     theElems.insert( *invElemIt );
5817
5818   // Replicate or reverse elements
5819
5820   std::vector<int> iForw;
5821   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5822   {
5823     const SMDS_MeshElement* elem = *itElem;
5824     if ( !elem ) continue;
5825
5826     SMDSAbs_GeometryType geomType = elem->GetGeomType();
5827     int                  nbNodes  = elem->NbNodes();
5828     if ( geomType == SMDSGeom_NONE ) continue; // node
5829
5830     switch ( geomType ) {
5831
5832     case SMDSGeom_POLYGON:  // ---------------------- polygon
5833       {
5834         vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5835         int iNode = 0;
5836         SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5837         while (itN->more()) {
5838           const SMDS_MeshNode* node =
5839             static_cast<const SMDS_MeshNode*>(itN->next());
5840           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5841           if (nodeMapIt == nodeMap.end())
5842             break; // not all nodes transformed
5843           if (needReverse) {
5844             // reverse mirrored faces and volumes
5845             poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5846           } else {
5847             poly_nodes[iNode] = (*nodeMapIt).second;
5848           }
5849           iNode++;
5850         }
5851         if ( iNode != nbNodes )
5852           continue; // not all nodes transformed
5853
5854         if ( theTargetMesh ) {
5855           myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5856           srcElems.Append( elem );
5857         }
5858         else if ( theCopy ) {
5859           myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5860           srcElems.Append( elem );
5861         }
5862         else {
5863           aMesh->ChangePolygonNodes(elem, poly_nodes);
5864         }
5865       }
5866       break;
5867
5868     case SMDSGeom_POLYHEDRA:  // ------------------ polyhedral volume
5869       {
5870         const SMDS_VtkVolume* aPolyedre =
5871           dynamic_cast<const SMDS_VtkVolume*>( elem );
5872         if (!aPolyedre) {
5873           MESSAGE("Warning: bad volumic element");
5874           continue;
5875         }
5876
5877         vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5878         vector<int> quantities; quantities.reserve( nbNodes );
5879
5880         bool allTransformed = true;
5881         int nbFaces = aPolyedre->NbFaces();
5882         for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5883           int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5884           for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5885             const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5886             TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5887             if (nodeMapIt == nodeMap.end()) {
5888               allTransformed = false; // not all nodes transformed
5889             } else {
5890               poly_nodes.push_back((*nodeMapIt).second);
5891             }
5892             if ( needReverse && allTransformed )
5893               std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5894           }
5895           quantities.push_back(nbFaceNodes);
5896         }
5897         if ( !allTransformed )
5898           continue; // not all nodes transformed
5899
5900         if ( theTargetMesh ) {
5901           myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5902           srcElems.Append( elem );
5903         }
5904         else if ( theCopy ) {
5905           myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5906           srcElems.Append( elem );
5907         }
5908         else {
5909           aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5910         }
5911       }
5912       break;
5913
5914     case SMDSGeom_BALL: // -------------------- Ball
5915       {
5916         if ( !theCopy && !theTargetMesh ) continue;
5917
5918         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
5919         if (nodeMapIt == nodeMap.end())
5920           continue; // not all nodes transformed
5921
5922         double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
5923         if ( theTargetMesh ) {
5924           myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
5925           srcElems.Append( elem );
5926         }
5927         else {
5928           myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
5929           srcElems.Append( elem );
5930         }
5931       }
5932       break;
5933
5934     default: // ----------------------- Regular elements
5935
5936       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
5937       const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
5938       const std::vector<int>& i = needReverse ? iRev : iForw;
5939
5940       // find transformed nodes
5941       vector<const SMDS_MeshNode*> nodes(nbNodes);
5942       int iNode = 0;
5943       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5944       while ( itN->more() ) {
5945         const SMDS_MeshNode* node =
5946           static_cast<const SMDS_MeshNode*>( itN->next() );
5947         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5948         if ( nodeMapIt == nodeMap.end() )
5949           break; // not all nodes transformed
5950         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5951       }
5952       if ( iNode != nbNodes )
5953         continue; // not all nodes transformed
5954
5955       if ( theTargetMesh ) {
5956         if ( SMDS_MeshElement* copy =
5957              targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5958           myLastCreatedElems.Append( copy );
5959           srcElems.Append( elem );
5960         }
5961       }
5962       else if ( theCopy ) {
5963         if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5964           srcElems.Append( elem );
5965       }
5966       else {
5967         // reverse element as it was reversed by transformation
5968         if ( nbNodes > 2 )
5969           aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5970       }
5971     } // switch ( geomType )
5972
5973   } // loop on elements
5974
5975   PGroupIDs newGroupIDs;
5976
5977   if ( ( theMakeGroups && theCopy ) ||
5978        ( theMakeGroups && theTargetMesh ) )
5979     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5980
5981   return newGroupIDs;
5982 }
5983
5984 //=======================================================================
5985 /*!
5986  * \brief Create groups of elements made during transformation
5987  * \param nodeGens - nodes making corresponding myLastCreatedNodes
5988  * \param elemGens - elements making corresponding myLastCreatedElems
5989  * \param postfix - to append to names of new groups
5990  */
5991 //=======================================================================
5992
5993 SMESH_MeshEditor::PGroupIDs
5994 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5995                                  const SMESH_SequenceOfElemPtr& elemGens,
5996                                  const std::string&             postfix,
5997                                  SMESH_Mesh*                    targetMesh)
5998 {
5999   PGroupIDs newGroupIDs( new list<int> );
6000   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6001
6002   // Sort existing groups by types and collect their names
6003
6004   // to store an old group and a generated new ones
6005   using boost::tuple;
6006   using boost::make_tuple;
6007   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6008   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6009   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6010   // group names
6011   set< string > groupNames;
6012
6013   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6014   if ( !groupIt->more() ) return newGroupIDs;
6015
6016   int newGroupID = mesh->GetGroupIds().back()+1;
6017   while ( groupIt->more() )
6018   {
6019     SMESH_Group * group = groupIt->next();
6020     if ( !group ) continue;
6021     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6022     if ( !groupDS || groupDS->IsEmpty() ) continue;
6023     groupNames.insert    ( group->GetName() );
6024     groupDS->SetStoreName( group->GetName() );
6025     const SMDSAbs_ElementType type = groupDS->GetType();
6026     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6027     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6028     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6029     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6030   }
6031
6032   // Loop on nodes and elements to add them in new groups
6033
6034   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6035   {
6036     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6037     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6038     if ( gens.Length() != elems.Length() )
6039       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6040
6041     // loop on created elements
6042     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6043     {
6044       const SMDS_MeshElement* sourceElem = gens( iElem );
6045       if ( !sourceElem ) {
6046         MESSAGE("generateGroups(): NULL source element");
6047         continue;
6048       }
6049       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6050       if ( groupsOldNew.empty() ) { // no groups of this type at all
6051         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6052           ++iElem; // skip all elements made by sourceElem
6053         continue;
6054       }
6055       // collect all elements made by the iElem-th sourceElem
6056       list< const SMDS_MeshElement* > resultElems;
6057       if ( const SMDS_MeshElement* resElem = elems( iElem ))
6058         if ( resElem != sourceElem )
6059           resultElems.push_back( resElem );
6060       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6061         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6062           if ( resElem != sourceElem )
6063             resultElems.push_back( resElem );
6064
6065       // there must be a top element
6066       const SMDS_MeshElement* topElem = 0;
6067       if ( isNodes )
6068       {
6069         topElem = resultElems.back();
6070         resultElems.pop_back();
6071       }
6072       else
6073       {
6074         list< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6075         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6076           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6077           {
6078             topElem = *resElemIt;
6079             resultElems.erase( --(resElemIt.base()) ); // erase *resElemIt
6080             break;
6081           }
6082       }
6083
6084       // add resultElems to groups originted from ones the sourceElem belongs to
6085       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6086       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6087       {
6088         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6089         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6090         {
6091           // fill in a new group
6092           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6093           list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6094           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6095             newGroup.Add( *resElemIt );
6096
6097           // fill a "top" group
6098           if ( topElem )
6099           {
6100             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6101             newTopGroup.Add( topElem );
6102           }
6103         }
6104       }
6105     } // loop on created elements
6106   }// loop on nodes and elements
6107
6108   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6109
6110   list<int> topGrouIds;
6111   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6112   {
6113     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6114     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6115                                       orderedOldNewGroups[i]->get<2>() };
6116     const int nbNewGroups = !newGroups[0]->IsEmpty() + !newGroups[1]->IsEmpty();
6117     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6118     {
6119       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6120       if ( newGroupDS->IsEmpty() )
6121       {
6122         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6123       }
6124       else
6125       {
6126         // set group type
6127         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6128
6129         // make a name
6130         const bool isTop = ( nbNewGroups == 2 &&
6131                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6132                              is2nd );
6133
6134         string name = oldGroupDS->GetStoreName();
6135         if ( !targetMesh ) {
6136           string suffix = ( isTop ? "top": postfix.c_str() );
6137           name += "_";
6138           name += suffix;
6139           int nb = 1;
6140           while ( !groupNames.insert( name ).second ) // name exists
6141             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6142         }
6143         else if ( isTop ) {
6144           name += "_top";
6145         }
6146         newGroupDS->SetStoreName( name.c_str() );
6147
6148         // make a SMESH_Groups
6149         mesh->AddGroup( newGroupDS );
6150         if ( isTop )
6151           topGrouIds.push_back( newGroupDS->GetID() );
6152         else
6153           newGroupIDs->push_back( newGroupDS->GetID() );
6154       }
6155     }
6156   }
6157   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6158
6159   return newGroupIDs;
6160 }
6161
6162 //================================================================================
6163 /*!
6164  * \brief Return list of group of nodes close to each other within theTolerance
6165  *        Search among theNodes or in the whole mesh if theNodes is empty using
6166  *        an Octree algorithm
6167  */
6168 //================================================================================
6169
6170 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6171                                             const double         theTolerance,
6172                                             TListOfListOfNodes & theGroupsOfNodes)
6173 {
6174   myLastCreatedElems.Clear();
6175   myLastCreatedNodes.Clear();
6176
6177   if ( theNodes.empty() )
6178   { // get all nodes in the mesh
6179     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6180     while ( nIt->more() )
6181       theNodes.insert( theNodes.end(),nIt->next());
6182   }
6183
6184   SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6185 }
6186
6187
6188 //=======================================================================
6189 /*!
6190  * \brief Implementation of search for the node closest to point
6191  */
6192 //=======================================================================
6193
6194 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
6195 {
6196   //---------------------------------------------------------------------
6197   /*!
6198    * \brief Constructor
6199    */
6200   SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
6201   {
6202     myMesh = ( SMESHDS_Mesh* ) theMesh;
6203
6204     TIDSortedNodeSet nodes;
6205     if ( theMesh ) {
6206       SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
6207       while ( nIt->more() )
6208         nodes.insert( nodes.end(), nIt->next() );
6209     }
6210     myOctreeNode = new SMESH_OctreeNode(nodes) ;
6211
6212     // get max size of a leaf box
6213     SMESH_OctreeNode* tree = myOctreeNode;
6214     while ( !tree->isLeaf() )
6215     {
6216       SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6217       if ( cIt->more() )
6218         tree = cIt->next();
6219     }
6220     myHalfLeafSize = tree->maxSize() / 2.;
6221   }
6222
6223   //---------------------------------------------------------------------
6224   /*!
6225    * \brief Move node and update myOctreeNode accordingly
6226    */
6227   void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
6228   {
6229     myOctreeNode->UpdateByMoveNode( node, toPnt );
6230     myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
6231   }
6232
6233   //---------------------------------------------------------------------
6234   /*!
6235    * \brief Do it's job
6236    */
6237   const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
6238   {
6239     map<double, const SMDS_MeshNode*> dist2Nodes;
6240     myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize );
6241     if ( !dist2Nodes.empty() )
6242       return dist2Nodes.begin()->second;
6243     list<const SMDS_MeshNode*> nodes;
6244     //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
6245
6246     double minSqDist = DBL_MAX;
6247     if ( nodes.empty() )  // get all nodes of OctreeNode's closest to thePnt
6248     {
6249       // sort leafs by their distance from thePnt
6250       typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
6251       TDistTreeMap treeMap;
6252       list< SMESH_OctreeNode* > treeList;
6253       list< SMESH_OctreeNode* >::iterator trIt;
6254       treeList.push_back( myOctreeNode );
6255
6256       gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
6257       bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize );
6258       for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
6259       {
6260         SMESH_OctreeNode* tree = *trIt;
6261         if ( !tree->isLeaf() ) // put children to the queue
6262         {
6263           if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue;
6264           SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6265           while ( cIt->more() )
6266             treeList.push_back( cIt->next() );
6267         }
6268         else if ( tree->NbNodes() ) // put a tree to the treeMap
6269         {
6270           const Bnd_B3d& box = *tree->getBox();
6271           double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
6272           pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
6273           if ( !it_in.second ) // not unique distance to box center
6274             treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
6275         }
6276       }
6277       // find distance after which there is no sense to check tree's
6278       double sqLimit = DBL_MAX;
6279       TDistTreeMap::iterator sqDist_tree = treeMap.begin();
6280       if ( treeMap.size() > 5 ) {
6281         SMESH_OctreeNode* closestTree = sqDist_tree->second;
6282         const Bnd_B3d& box = *closestTree->getBox();
6283         double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
6284         sqLimit = limit * limit;
6285       }
6286       // get all nodes from trees
6287       for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
6288         if ( sqDist_tree->first > sqLimit )
6289           break;
6290         SMESH_OctreeNode* tree = sqDist_tree->second;
6291         tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
6292       }
6293     }
6294     // find closest among nodes
6295     minSqDist = DBL_MAX;
6296     const SMDS_MeshNode* closestNode = 0;
6297     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6298     for ( ; nIt != nodes.end(); ++nIt ) {
6299       double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) );
6300       if ( minSqDist > sqDist ) {
6301         closestNode = *nIt;
6302         minSqDist = sqDist;
6303       }
6304     }
6305     return closestNode;
6306   }
6307
6308   //---------------------------------------------------------------------
6309   /*!
6310    * \brief Destructor
6311    */
6312   ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
6313
6314   //---------------------------------------------------------------------
6315   /*!
6316    * \brief Return the node tree
6317    */
6318   const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
6319
6320 private:
6321   SMESH_OctreeNode* myOctreeNode;
6322   SMESHDS_Mesh*     myMesh;
6323   double            myHalfLeafSize; // max size of a leaf box
6324 };
6325
6326 //=======================================================================
6327 /*!
6328  * \brief Return SMESH_NodeSearcher
6329  */
6330 //=======================================================================
6331
6332 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher()
6333 {
6334   return new SMESH_NodeSearcherImpl( GetMeshDS() );
6335 }
6336
6337 // ========================================================================
6338 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
6339 {
6340   const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
6341   const int MaxLevel         = 7;  // maximal tree height -> nb terminal boxes: 8^7 = 2097152
6342   const double NodeRadius = 1e-9;  // to enlarge bnd box of element
6343
6344   //=======================================================================
6345   /*!
6346    * \brief Octal tree of bounding boxes of elements
6347    */
6348   //=======================================================================
6349
6350   class ElementBndBoxTree : public SMESH_Octree
6351   {
6352   public:
6353
6354     ElementBndBoxTree(const SMDS_Mesh&     mesh,
6355                       SMDSAbs_ElementType  elemType,
6356                       SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(),
6357                       double               tolerance = NodeRadius );
6358     void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems );
6359     void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
6360     void getElementsInSphere ( const gp_XYZ& center,
6361                                const double  radius, TIDSortedElemSet& foundElems);
6362     size_t getSize() { return std::max( _size, _elements.size() ); }
6363     ~ElementBndBoxTree();
6364
6365   protected:
6366     ElementBndBoxTree():_size(0) {}
6367     SMESH_Octree* newChild() const { return new ElementBndBoxTree; }
6368     void          buildChildrenData();
6369     Bnd_B3d*      buildRootBox();
6370   private:
6371     //!< Bounding box of element
6372     struct ElementBox : public Bnd_B3d
6373     {
6374       const SMDS_MeshElement* _element;
6375       int                     _refCount; // an ElementBox can be included in several tree branches
6376       ElementBox(const SMDS_MeshElement* elem, double tolerance);
6377     };
6378     vector< ElementBox* > _elements;
6379     size_t                _size;
6380   };
6381
6382   //================================================================================
6383   /*!
6384    * \brief ElementBndBoxTree creation
6385    */
6386   //================================================================================
6387
6388   ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
6389     :SMESH_Octree( new SMESH_TreeLimit( MaxLevel, /*minSize=*/0. ))
6390   {
6391     int nbElems = mesh.GetMeshInfo().NbElements( elemType );
6392     _elements.reserve( nbElems );
6393
6394     SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType );
6395     while ( elemIt->more() )
6396       _elements.push_back( new ElementBox( elemIt->next(),tolerance  ));
6397
6398     compute();
6399   }
6400
6401   //================================================================================
6402   /*!
6403    * \brief Destructor
6404    */
6405   //================================================================================
6406
6407   ElementBndBoxTree::~ElementBndBoxTree()
6408   {
6409     for ( int i = 0; i < _elements.size(); ++i )
6410       if ( --_elements[i]->_refCount <= 0 )
6411         delete _elements[i];
6412   }
6413
6414   //================================================================================
6415   /*!
6416    * \brief Return the maximal box
6417    */
6418   //================================================================================
6419
6420   Bnd_B3d* ElementBndBoxTree::buildRootBox()
6421   {
6422     Bnd_B3d* box = new Bnd_B3d;
6423     for ( int i = 0; i < _elements.size(); ++i )
6424       box->Add( *_elements[i] );
6425     return box;
6426   }
6427
6428   //================================================================================
6429   /*!
6430    * \brief Redistrubute element boxes among children
6431    */
6432   //================================================================================
6433
6434   void ElementBndBoxTree::buildChildrenData()
6435   {
6436     for ( int i = 0; i < _elements.size(); ++i )
6437     {
6438       for (int j = 0; j < 8; j++)
6439       {
6440         if ( !_elements[i]->IsOut( *myChildren[j]->getBox() ))
6441         {
6442           _elements[i]->_refCount++;
6443           ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
6444         }
6445       }
6446       _elements[i]->_refCount--;
6447     }
6448     _size = _elements.size();
6449     SMESHUtils::FreeVector( _elements ); // = _elements.clear() + free memory
6450
6451     for (int j = 0; j < 8; j++)
6452     {
6453       ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
6454       if ( child->_elements.size() <= MaxNbElemsInLeaf )
6455         child->myIsLeaf = true;
6456
6457       if ( child->_elements.capacity() - child->_elements.size() > 1000 )
6458         SMESHUtils::CompactVector( child->_elements );
6459     }
6460   }
6461
6462   //================================================================================
6463   /*!
6464    * \brief Return elements which can include the point
6465    */
6466   //================================================================================
6467
6468   void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt&     point,
6469                                                 TIDSortedElemSet& foundElems)
6470   {
6471     if ( getBox()->IsOut( point.XYZ() ))
6472       return;
6473
6474     if ( isLeaf() )
6475     {
6476       for ( int i = 0; i < _elements.size(); ++i )
6477         if ( !_elements[i]->IsOut( point.XYZ() ))
6478           foundElems.insert( _elements[i]->_element );
6479     }
6480     else
6481     {
6482       for (int i = 0; i < 8; i++)
6483         ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6484     }
6485   }
6486
6487   //================================================================================
6488   /*!
6489    * \brief Return elements which can be intersected by the line
6490    */
6491   //================================================================================
6492
6493   void ElementBndBoxTree::getElementsNearLine( const gp_Ax1&     line,
6494                                                TIDSortedElemSet& foundElems)
6495   {
6496     if ( getBox()->IsOut( line ))
6497       return;
6498
6499     if ( isLeaf() )
6500     {
6501       for ( int i = 0; i < _elements.size(); ++i )
6502         if ( !_elements[i]->IsOut( line ))
6503           foundElems.insert( _elements[i]->_element );
6504     }
6505     else
6506     {
6507       for (int i = 0; i < 8; i++)
6508         ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6509     }
6510   }
6511
6512   //================================================================================
6513   /*!
6514    * \brief Return elements from leaves intersecting the sphere
6515    */
6516   //================================================================================
6517
6518   void ElementBndBoxTree::getElementsInSphere ( const gp_XYZ&     center,
6519                                                 const double      radius,
6520                                                 TIDSortedElemSet& foundElems)
6521   {
6522     if ( getBox()->IsOut( center, radius ))
6523       return;
6524
6525     if ( isLeaf() )
6526     {
6527       for ( int i = 0; i < _elements.size(); ++i )
6528         if ( !_elements[i]->IsOut( center, radius ))
6529           foundElems.insert( _elements[i]->_element );
6530     }
6531     else
6532     {
6533       for (int i = 0; i < 8; i++)
6534         ((ElementBndBoxTree*) myChildren[i])->getElementsInSphere( center, radius, foundElems );
6535     }
6536   }
6537
6538   //================================================================================
6539   /*!
6540    * \brief Construct the element box
6541    */
6542   //================================================================================
6543
6544   ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance)
6545   {
6546     _element  = elem;
6547     _refCount = 1;
6548     SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6549     while ( nIt->more() )
6550       Add( SMESH_TNodeXYZ( nIt->next() ));
6551     Enlarge( tolerance );
6552   }
6553
6554 } // namespace
6555
6556 //=======================================================================
6557 /*!
6558  * \brief Implementation of search for the elements by point and
6559  *        of classification of point in 2D mesh
6560  */
6561 //=======================================================================
6562
6563 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6564 {
6565   SMESHDS_Mesh*                _mesh;
6566   SMDS_ElemIteratorPtr         _meshPartIt;
6567   ElementBndBoxTree*           _ebbTree;
6568   SMESH_NodeSearcherImpl*      _nodeSearcher;
6569   SMDSAbs_ElementType          _elementType;
6570   double                       _tolerance;
6571   bool                         _outerFacesFound;
6572   set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6573
6574   SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr())
6575     : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {}
6576   ~SMESH_ElementSearcherImpl()
6577   {
6578     if ( _ebbTree )      delete _ebbTree;      _ebbTree      = 0;
6579     if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6580   }
6581   virtual int FindElementsByPoint(const gp_Pnt&                      point,
6582                                   SMDSAbs_ElementType                type,
6583                                   vector< const SMDS_MeshElement* >& foundElements);
6584   virtual TopAbs_State GetPointState(const gp_Pnt& point);
6585   virtual const SMDS_MeshElement* FindClosestTo( const gp_Pnt&       point,
6586                                                  SMDSAbs_ElementType type );
6587
6588   void GetElementsNearLine( const gp_Ax1&                      line,
6589                             SMDSAbs_ElementType                type,
6590                             vector< const SMDS_MeshElement* >& foundElems);
6591   double getTolerance();
6592   bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6593                             const double tolerance, double & param);
6594   void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6595   bool isOuterBoundary(const SMDS_MeshElement* face) const
6596   {
6597     return _outerFaces.empty() || _outerFaces.count(face);
6598   }
6599   struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6600   {
6601     const SMDS_MeshElement* _face;
6602     gp_Vec                  _faceNorm;
6603     bool                    _coincides; //!< the line lays in face plane
6604     TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6605       : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6606   };
6607   struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6608   {
6609     SMESH_TLink      _link;
6610     TIDSortedElemSet _faces;
6611     TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6612       : _link( n1, n2 ), _faces( &face, &face + 1) {}
6613   };
6614 };
6615
6616 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6617 {
6618   return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6619              << ", _coincides="<<i._coincides << ")";
6620 }
6621
6622 //=======================================================================
6623 /*!
6624  * \brief define tolerance for search
6625  */
6626 //=======================================================================
6627
6628 double SMESH_ElementSearcherImpl::getTolerance()
6629 {
6630   if ( _tolerance < 0 )
6631   {
6632     const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6633
6634     _tolerance = 0;
6635     if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6636     {
6637       double boxSize = _nodeSearcher->getTree()->maxSize();
6638       _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6639     }
6640     else if ( _ebbTree && meshInfo.NbElements() > 0 )
6641     {
6642       double boxSize = _ebbTree->maxSize();
6643       _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6644     }
6645     if ( _tolerance == 0 )
6646     {
6647       // define tolerance by size of a most complex element
6648       int complexType = SMDSAbs_Volume;
6649       while ( complexType > SMDSAbs_All &&
6650               meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6651         --complexType;
6652       if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6653       double elemSize;
6654       if ( complexType == int( SMDSAbs_Node ))
6655       {
6656         SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6657         elemSize = 1;
6658         if ( meshInfo.NbNodes() > 2 )
6659           elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6660       }
6661       else
6662       {
6663         SMDS_ElemIteratorPtr elemIt =
6664             _mesh->elementsIterator( SMDSAbs_ElementType( complexType ));
6665         const SMDS_MeshElement* elem = elemIt->next();
6666         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6667         SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() ));
6668         elemSize = 0;
6669         while ( nodeIt->more() )
6670         {
6671           double dist = n1.Distance( cast2Node( nodeIt->next() ));
6672           elemSize = max( dist, elemSize );
6673         }
6674       }
6675       _tolerance = 1e-4 * elemSize;
6676     }
6677   }
6678   return _tolerance;
6679 }
6680
6681 //================================================================================
6682 /*!
6683  * \brief Find intersection of the line and an edge of face and return parameter on line
6684  */
6685 //================================================================================
6686
6687 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin&           line,
6688                                                      const SMDS_MeshElement* face,
6689                                                      const double            tol,
6690                                                      double &                param)
6691 {
6692   int nbInts = 0;
6693   param = 0;
6694
6695   GeomAPI_ExtremaCurveCurve anExtCC;
6696   Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6697
6698   int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6699   for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6700   {
6701     GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )),
6702                          SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) ));
6703     anExtCC.Init( lineCurve, edge);
6704     if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6705     {
6706       Quantity_Parameter pl, pe;
6707       anExtCC.LowerDistanceParameters( pl, pe );
6708       param += pl;
6709       if ( ++nbInts == 2 )
6710         break;
6711     }
6712   }
6713   if ( nbInts > 0 ) param /= nbInts;
6714   return nbInts > 0;
6715 }
6716 //================================================================================
6717 /*!
6718  * \brief Find all faces belonging to the outer boundary of mesh
6719  */
6720 //================================================================================
6721
6722 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6723 {
6724   if ( _outerFacesFound ) return;
6725
6726   // Collect all outer faces by passing from one outer face to another via their links
6727   // and BTW find out if there are internal faces at all.
6728
6729   // checked links and links where outer boundary meets internal one
6730   set< SMESH_TLink > visitedLinks, seamLinks;
6731
6732   // links to treat with already visited faces sharing them
6733   list < TFaceLink > startLinks;
6734
6735   // load startLinks with the first outerFace
6736   startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6737   _outerFaces.insert( outerFace );
6738
6739   TIDSortedElemSet emptySet;
6740   while ( !startLinks.empty() )
6741   {
6742     const SMESH_TLink& link  = startLinks.front()._link;
6743     TIDSortedElemSet&  faces = startLinks.front()._faces;
6744
6745     outerFace = *faces.begin();
6746     // find other faces sharing the link
6747     const SMDS_MeshElement* f;
6748     while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6749       faces.insert( f );
6750
6751     // select another outer face among the found
6752     const SMDS_MeshElement* outerFace2 = 0;
6753     if ( faces.size() == 2 )
6754     {
6755       outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6756     }
6757     else if ( faces.size() > 2 )
6758     {
6759       seamLinks.insert( link );
6760
6761       // link direction within the outerFace
6762       gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()),
6763                    SMESH_TNodeXYZ( link.node2()));
6764       int i1 = outerFace->GetNodeIndex( link.node1() );
6765       int i2 = outerFace->GetNodeIndex( link.node2() );
6766       bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6767       if ( rev ) n1n2.Reverse();
6768       // outerFace normal
6769       gp_XYZ ofNorm, fNorm;
6770       if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6771       {
6772         // direction from the link inside outerFace
6773         gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6774         // sort all other faces by angle with the dirInOF
6775         map< double, const SMDS_MeshElement* > angle2Face;
6776         set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6777         for ( ; face != faces.end(); ++face )
6778         {
6779           if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6780             continue;
6781           gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6782           double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6783           if ( angle < 0 ) angle += 2. * M_PI;
6784           angle2Face.insert( make_pair( angle, *face ));
6785         }
6786         if ( !angle2Face.empty() )
6787           outerFace2 = angle2Face.begin()->second;
6788       }
6789     }
6790     // store the found outer face and add its links to continue seaching from
6791     if ( outerFace2 )
6792     {
6793       _outerFaces.insert( outerFace );
6794       int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6795       for ( int i = 0; i < nbNodes; ++i )
6796       {
6797         SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6798         if ( visitedLinks.insert( link2 ).second )
6799           startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6800       }
6801     }
6802     startLinks.pop_front();
6803   }
6804   _outerFacesFound = true;
6805
6806   if ( !seamLinks.empty() )
6807   {
6808     // There are internal boundaries touching the outher one,
6809     // find all faces of internal boundaries in order to find
6810     // faces of boundaries of holes, if any.
6811
6812   }
6813   else
6814   {
6815     _outerFaces.clear();
6816   }
6817 }
6818
6819 //=======================================================================
6820 /*!
6821  * \brief Find elements of given type where the given point is IN or ON.
6822  *        Returns nb of found elements and elements them-selves.
6823  *
6824  * 'ALL' type means elements of any type excluding nodes, balls and 0D elements
6825  */
6826 //=======================================================================
6827
6828 int SMESH_ElementSearcherImpl::
6829 FindElementsByPoint(const gp_Pnt&                      point,
6830                     SMDSAbs_ElementType                type,
6831                     vector< const SMDS_MeshElement* >& foundElements)
6832 {
6833   foundElements.clear();
6834
6835   double tolerance = getTolerance();
6836
6837   // =================================================================================
6838   if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement || type == SMDSAbs_Ball)
6839   {
6840     if ( !_nodeSearcher )
6841       _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6842
6843     const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6844     if ( !closeNode ) return foundElements.size();
6845
6846     if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance )
6847       return foundElements.size(); // to far from any node
6848
6849     if ( type == SMDSAbs_Node )
6850     {
6851       foundElements.push_back( closeNode );
6852     }
6853     else
6854     {
6855       SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( type );
6856       while ( elemIt->more() )
6857         foundElements.push_back( elemIt->next() );
6858     }
6859   }
6860   // =================================================================================
6861   else // elements more complex than 0D
6862   {
6863     if ( !_ebbTree || _elementType != type )
6864     {
6865       if ( _ebbTree ) delete _ebbTree;
6866       _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance );
6867     }
6868     TIDSortedElemSet suspectElems;
6869     _ebbTree->getElementsNearPoint( point, suspectElems );
6870     TIDSortedElemSet::iterator elem = suspectElems.begin();
6871     for ( ; elem != suspectElems.end(); ++elem )
6872       if ( !SMESH_MeshEditor::IsOut( *elem, point, tolerance ))
6873         foundElements.push_back( *elem );
6874   }
6875   return foundElements.size();
6876 }
6877
6878 //=======================================================================
6879 /*!
6880  * \brief Find an element of given type most close to the given point
6881  *
6882  * WARNING: Only face search is implemeneted so far
6883  */
6884 //=======================================================================
6885
6886 const SMDS_MeshElement*
6887 SMESH_ElementSearcherImpl::FindClosestTo( const gp_Pnt&       point,
6888                                           SMDSAbs_ElementType type )
6889 {
6890   const SMDS_MeshElement* closestElem = 0;
6891
6892   if ( type == SMDSAbs_Face )
6893   {
6894     if ( !_ebbTree || _elementType != type )
6895     {
6896       if ( _ebbTree ) delete _ebbTree;
6897       _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
6898     }
6899     TIDSortedElemSet suspectElems;
6900     _ebbTree->getElementsNearPoint( point, suspectElems );
6901
6902     if ( suspectElems.empty() && _ebbTree->maxSize() > 0 )
6903     {
6904       gp_Pnt boxCenter = 0.5 * ( _ebbTree->getBox()->CornerMin() +
6905                                  _ebbTree->getBox()->CornerMax() );
6906       double radius;
6907       if ( _ebbTree->getBox()->IsOut( point.XYZ() ))
6908         radius = point.Distance( boxCenter ) - 0.5 * _ebbTree->maxSize();
6909       else
6910         radius = _ebbTree->maxSize() / pow( 2., _ebbTree->getHeight()) / 2;
6911       while ( suspectElems.empty() )
6912       {
6913         _ebbTree->getElementsInSphere( point.XYZ(), radius, suspectElems );
6914         radius *= 1.1;
6915       }
6916     }
6917     double minDist = std::numeric_limits<double>::max();
6918     multimap< double, const SMDS_MeshElement* > dist2face;
6919     TIDSortedElemSet::iterator elem = suspectElems.begin();
6920     for ( ; elem != suspectElems.end(); ++elem )
6921     {
6922       double dist = SMESH_MeshEditor::GetDistance( dynamic_cast<const SMDS_MeshFace*>(*elem),
6923                                                    point );
6924       if ( dist < minDist + 1e-10)
6925       {
6926         minDist = dist;
6927         dist2face.insert( dist2face.begin(), make_pair( dist, *elem ));
6928       }
6929     }
6930     if ( !dist2face.empty() )
6931     {
6932       multimap< double, const SMDS_MeshElement* >::iterator d2f = dist2face.begin();
6933       closestElem = d2f->second;
6934       // if there are several elements at the same distance, select one
6935       // with GC closest to the point
6936       typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
6937       double minDistToGC = 0;
6938       for ( ++d2f; d2f != dist2face.end() && fabs( d2f->first - minDist ) < 1e-10; ++d2f )
6939       {
6940         if ( minDistToGC == 0 )
6941         {
6942           gp_XYZ gc(0,0,0);
6943           gc = accumulate( TXyzIterator(closestElem->nodesIterator()),
6944                            TXyzIterator(), gc ) / closestElem->NbNodes();
6945           minDistToGC = point.SquareDistance( gc );
6946         }
6947         gp_XYZ gc(0,0,0);
6948         gc = accumulate( TXyzIterator( d2f->second->nodesIterator()),
6949                          TXyzIterator(), gc ) / d2f->second->NbNodes();
6950         double d = point.SquareDistance( gc );
6951         if ( d < minDistToGC )
6952         {
6953           minDistToGC = d;
6954           closestElem = d2f->second;
6955         }
6956       }
6957       // cout << "FindClosestTo( " <<point.X()<<", "<<point.Y()<<", "<<point.Z()<<" ) FACE "
6958       //      <<closestElem->GetID() << " DIST " << minDist << endl;
6959     }
6960   }
6961   else
6962   {
6963     // NOT IMPLEMENTED SO FAR
6964   }
6965   return closestElem;
6966 }
6967
6968
6969 //================================================================================
6970 /*!
6971  * \brief Classify the given point in the closed 2D mesh
6972  */
6973 //================================================================================
6974
6975 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6976 {
6977   double tolerance = getTolerance();
6978   if ( !_ebbTree || _elementType != SMDSAbs_Face )
6979   {
6980     if ( _ebbTree ) delete _ebbTree;
6981     _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt );
6982   }
6983   // Algo: analyse transition of a line starting at the point through mesh boundary;
6984   // try three lines parallel to axis of the coordinate system and perform rough
6985   // analysis. If solution is not clear perform thorough analysis.
6986
6987   const int nbAxes = 3;
6988   gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6989   map< double, TInters >   paramOnLine2TInters[ nbAxes ];
6990   list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6991   multimap< int, int > nbInt2Axis; // to find the simplest case
6992   for ( int axis = 0; axis < nbAxes; ++axis )
6993   {
6994     gp_Ax1 lineAxis( point, axisDir[axis]);
6995     gp_Lin line    ( lineAxis );
6996
6997     TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
6998     _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
6999
7000     // Intersect faces with the line
7001
7002     map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
7003     TIDSortedElemSet::iterator face = suspectFaces.begin();
7004     for ( ; face != suspectFaces.end(); ++face )
7005     {
7006       // get face plane
7007       gp_XYZ fNorm;
7008       if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
7009       gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm );
7010
7011       // perform intersection
7012       IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
7013       if ( !intersection.IsDone() )
7014         continue;
7015       if ( intersection.IsInQuadric() )
7016       {
7017         tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
7018       }
7019       else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
7020       {
7021         gp_Pnt intersectionPoint = intersection.Point(1);
7022         if ( !SMESH_MeshEditor::IsOut( *face, intersectionPoint, tolerance ))
7023           u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
7024       }
7025     }
7026     // Analyse intersections roughly
7027
7028     int nbInter = u2inters.size();
7029     if ( nbInter == 0 )
7030       return TopAbs_OUT;
7031
7032     double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
7033     if ( nbInter == 1 ) // not closed mesh
7034       return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7035
7036     if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
7037       return TopAbs_ON;
7038
7039     if ( (f<0) == (l<0) )
7040       return TopAbs_OUT;
7041
7042     int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
7043     int nbIntAfterPoint  = nbInter - nbIntBeforePoint;
7044     if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7045       return TopAbs_IN;
7046
7047     nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
7048
7049     if ( _outerFacesFound ) break; // pass to thorough analysis
7050
7051   } // three attempts - loop on CS axes
7052
7053   // Analyse intersections thoroughly.
7054   // We make two loops maximum, on the first one we only exclude touching intersections,
7055   // on the second, if situation is still unclear, we gather and use information on
7056   // position of faces (internal or outer). If faces position is already gathered,
7057   // we make the second loop right away.
7058
7059   for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
7060   {
7061     multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
7062     for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
7063     {
7064       int axis = nb_axis->second;
7065       map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
7066
7067       gp_Ax1 lineAxis( point, axisDir[axis]);
7068       gp_Lin line    ( lineAxis );
7069
7070       // add tangent intersections to u2inters
7071       double param;
7072       list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
7073       for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
7074         if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
7075           u2inters.insert(make_pair( param, *tgtInt ));
7076       tangentInters[ axis ].clear();
7077
7078       // Count intersections before and after the point excluding touching ones.
7079       // If hasPositionInfo we count intersections of outer boundary only
7080
7081       int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
7082       double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
7083       map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
7084       bool ok = ! u_int1->second._coincides;
7085       while ( ok && u_int1 != u2inters.end() )
7086       {
7087         double u = u_int1->first;
7088         bool touchingInt = false;
7089         if ( ++u_int2 != u2inters.end() )
7090         {
7091           // skip intersections at the same point (if the line passes through edge or node)
7092           int nbSamePnt = 0;
7093           while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
7094           {
7095             ++nbSamePnt;
7096             ++u_int2;
7097           }
7098
7099           // skip tangent intersections
7100           int nbTgt = 0;
7101           const SMDS_MeshElement* prevFace = u_int1->second._face;
7102           while ( ok && u_int2->second._coincides )
7103           {
7104             if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
7105               ok = false;
7106             else
7107             {
7108               nbTgt++;
7109               u_int2++;
7110               ok = ( u_int2 != u2inters.end() );
7111             }
7112           }
7113           if ( !ok ) break;
7114
7115           // skip intersections at the same point after tangent intersections
7116           if ( nbTgt > 0 )
7117           {
7118             double u2 = u_int2->first;
7119             ++u_int2;
7120             while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
7121             {
7122               ++nbSamePnt;
7123               ++u_int2;
7124             }
7125           }
7126           // decide if we skipped a touching intersection
7127           if ( nbSamePnt + nbTgt > 0 )
7128           {
7129             double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
7130             map< double, TInters >::iterator u_int = u_int1;
7131             for ( ; u_int != u_int2; ++u_int )
7132             {
7133               if ( u_int->second._coincides ) continue;
7134               double dot = u_int->second._faceNorm * line.Direction();
7135               if ( dot > maxDot ) maxDot = dot;
7136               if ( dot < minDot ) minDot = dot;
7137             }
7138             touchingInt = ( minDot*maxDot < 0 );
7139           }
7140         }
7141         if ( !touchingInt )
7142         {
7143           if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
7144           {
7145             if ( u < 0 )
7146               ++nbIntBeforePoint;
7147             else
7148               ++nbIntAfterPoint;
7149           }
7150           if ( u < f ) f = u;
7151           if ( u > l ) l = u;
7152         }
7153
7154         u_int1 = u_int2; // to next intersection
7155
7156       } // loop on intersections with one line
7157
7158       if ( ok )
7159       {
7160         if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
7161           return TopAbs_ON;
7162
7163         if ( nbIntBeforePoint == 0  || nbIntAfterPoint == 0)
7164           return TopAbs_OUT;
7165
7166         if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
7167           return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7168
7169         if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7170           return TopAbs_IN;
7171
7172         if ( (f<0) == (l<0) )
7173           return TopAbs_OUT;
7174
7175         if ( hasPositionInfo )
7176           return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
7177       }
7178     } // loop on intersections of the tree lines - thorough analysis
7179
7180     if ( !hasPositionInfo )
7181     {
7182       // gather info on faces position - is face in the outer boundary or not
7183       map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
7184       findOuterBoundary( u2inters.begin()->second._face );
7185     }
7186
7187   } // two attempts - with and w/o faces position info in the mesh
7188
7189   return TopAbs_UNKNOWN;
7190 }
7191
7192 //=======================================================================
7193 /*!
7194  * \brief Return elements possibly intersecting the line
7195  */
7196 //=======================================================================
7197
7198 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1&                      line,
7199                                                      SMDSAbs_ElementType                type,
7200                                                      vector< const SMDS_MeshElement* >& foundElems)
7201 {
7202   if ( !_ebbTree || _elementType != type )
7203   {
7204     if ( _ebbTree ) delete _ebbTree;
7205     _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
7206   }
7207   TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
7208   _ebbTree->getElementsNearLine( line, suspectFaces );
7209   foundElems.assign( suspectFaces.begin(), suspectFaces.end());
7210 }
7211
7212 //=======================================================================
7213 /*!
7214  * \brief Return SMESH_ElementSearcher
7215  */
7216 //=======================================================================
7217
7218 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
7219 {
7220   return new SMESH_ElementSearcherImpl( *GetMeshDS() );
7221 }
7222
7223 //=======================================================================
7224 /*!
7225  * \brief Return SMESH_ElementSearcher acting on a sub-set of elements
7226  */
7227 //=======================================================================
7228
7229 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt)
7230 {
7231   return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt );
7232 }
7233
7234 //=======================================================================
7235 /*!
7236  * \brief Return true if the point is IN or ON of the element
7237  */
7238 //=======================================================================
7239
7240 bool SMESH_MeshEditor::IsOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
7241 {
7242   if ( element->GetType() == SMDSAbs_Volume)
7243   {
7244     return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
7245   }
7246
7247   // get ordered nodes
7248
7249   vector< gp_XYZ > xyz;
7250   vector<const SMDS_MeshNode*> nodeList;
7251
7252   SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
7253   if ( element->IsQuadratic() ) {
7254     if (const SMDS_VtkFace* f=dynamic_cast<const SMDS_VtkFace*>(element))
7255       nodeIt = f->interlacedNodesElemIterator();
7256     else if (const SMDS_VtkEdge*  e =dynamic_cast<const SMDS_VtkEdge*>(element))
7257       nodeIt = e->interlacedNodesElemIterator();
7258   }
7259   while ( nodeIt->more() )
7260     {
7261       const SMDS_MeshNode* node = cast2Node( nodeIt->next() );
7262       xyz.push_back( SMESH_TNodeXYZ(node) );
7263       nodeList.push_back(node);
7264     }
7265
7266   int i, nbNodes = element->NbNodes();
7267
7268   if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
7269   {
7270     // compute face normal
7271     gp_Vec faceNorm(0,0,0);
7272     xyz.push_back( xyz.front() );
7273     nodeList.push_back( nodeList.front() );
7274     for ( i = 0; i < nbNodes; ++i )
7275     {
7276       gp_Vec edge1( xyz[i+1], xyz[i]);
7277       gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
7278       faceNorm += edge1 ^ edge2;
7279     }
7280     double normSize = faceNorm.Magnitude();
7281     if ( normSize <= tol )
7282     {
7283       // degenerated face: point is out if it is out of all face edges
7284       for ( i = 0; i < nbNodes; ++i )
7285       {
7286         SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] );
7287         if ( !IsOut( &edge, point, tol ))
7288           return false;
7289       }
7290       return true;
7291     }
7292     faceNorm /= normSize;
7293
7294     // check if the point lays on face plane
7295     gp_Vec n2p( xyz[0], point );
7296     if ( fabs( n2p * faceNorm ) > tol )
7297       return true; // not on face plane
7298
7299     // check if point is out of face boundary:
7300     // define it by closest transition of a ray point->infinity through face boundary
7301     // on the face plane.
7302     // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
7303     // to find intersections of the ray with the boundary.
7304     gp_Vec ray = n2p;
7305     gp_Vec plnNorm = ray ^ faceNorm;
7306     normSize = plnNorm.Magnitude();
7307     if ( normSize <= tol ) return false; // point coincides with the first node
7308     plnNorm /= normSize;
7309     // for each node of the face, compute its signed distance to the plane
7310     vector<double> dist( nbNodes + 1);
7311     for ( i = 0; i < nbNodes; ++i )
7312     {
7313       gp_Vec n2p( xyz[i], point );
7314       dist[i] = n2p * plnNorm;
7315     }
7316     dist.back() = dist.front();
7317     // find the closest intersection
7318     int    iClosest = -1;
7319     double rClosest, distClosest = 1e100;;
7320     gp_Pnt pClosest;
7321     for ( i = 0; i < nbNodes; ++i )
7322     {
7323       double r;
7324       if ( fabs( dist[i]) < tol )
7325         r = 0.;
7326       else if ( fabs( dist[i+1]) < tol )
7327         r = 1.;
7328       else if ( dist[i] * dist[i+1] < 0 )
7329         r = dist[i] / ( dist[i] - dist[i+1] );
7330       else
7331         continue; // no intersection
7332       gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
7333       gp_Vec p2int ( point, pInt);
7334       if ( p2int * ray > -tol ) // right half-space
7335       {
7336         double intDist = p2int.SquareMagnitude();
7337         if ( intDist < distClosest )
7338         {
7339           iClosest = i;
7340           rClosest = r;
7341           pClosest = pInt;
7342           distClosest = intDist;
7343         }
7344       }
7345     }
7346     if ( iClosest < 0 )
7347       return true; // no intesections - out
7348
7349     // analyse transition
7350     gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
7351     gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
7352     gp_Vec p2int ( point, pClosest );
7353     bool out = (edgeNorm * p2int) < -tol;
7354     if ( rClosest > 0. && rClosest < 1. ) // not node intersection
7355       return out;
7356
7357     // ray pass through a face node; analyze transition through an adjacent edge
7358     gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
7359     gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
7360     gp_Vec edgeAdjacent( p1, p2 );
7361     gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
7362     bool out2 = (edgeNorm2 * p2int) < -tol;
7363
7364     bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
7365     return covexCorner ? (out || out2) : (out && out2);
7366   }
7367   if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
7368   {
7369     // point is out of edge if it is NOT ON any straight part of edge
7370     // (we consider quadratic edge as being composed of two straight parts)
7371     for ( i = 1; i < nbNodes; ++i )
7372     {
7373       gp_Vec edge( xyz[i-1], xyz[i]);
7374       gp_Vec n1p ( xyz[i-1], point);
7375       double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
7376       if ( dist > tol )
7377         continue;
7378       gp_Vec n2p( xyz[i], point );
7379       if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
7380         continue;
7381       return false; // point is ON this part
7382     }
7383     return true;
7384   }
7385   // Node or 0D element -------------------------------------------------------------------------
7386   {
7387     gp_Vec n2p ( xyz[0], point );
7388     return n2p.Magnitude() <= tol;
7389   }
7390   return true;
7391 }
7392
7393 //=======================================================================
7394
7395 namespace
7396 {
7397   // Position of a point relative to a segment
7398   //            .           .
7399   //            .  LEFT     .
7400   //            .           .
7401   //  VERTEX 1  o----ON----->  VERTEX 2
7402   //            .           .
7403   //            .  RIGHT    .
7404   //            .           .
7405   enum PositionName { POS_LEFT = 1, POS_VERTEX = 2, POS_RIGHT = 4, //POS_ON = 8,
7406                       POS_ALL = POS_LEFT | POS_RIGHT | POS_VERTEX };
7407   struct PointPos
7408   {
7409     PositionName _name;
7410     int          _index; // index of vertex or segment
7411
7412     PointPos( PositionName n, int i=-1 ): _name(n), _index(i) {}
7413     bool operator < (const PointPos& other ) const
7414     {
7415       if ( _name == other._name )
7416         return  ( _index < 0 || other._index < 0 ) ? false : _index < other._index;
7417       return _name < other._name;
7418     }
7419   };
7420
7421   //================================================================================
7422   /*!
7423    * \brief Return of a point relative to a segment
7424    *  \param point2D      - the point to analyze position of
7425    *  \param xyVec        - end points of segments
7426    *  \param index0       - 0-based index of the first point of segment
7427    *  \param posToFindOut - flags of positions to detect
7428    *  \retval PointPos - point position
7429    */
7430   //================================================================================
7431
7432   PointPos getPointPosition( const gp_XY& point2D,
7433                              const gp_XY* segEnds,
7434                              const int    index0 = 0,
7435                              const int    posToFindOut = POS_ALL)
7436   {
7437     const gp_XY& p1 = segEnds[ index0   ];
7438     const gp_XY& p2 = segEnds[ index0+1 ];
7439     const gp_XY grad = p2 - p1;
7440
7441     if ( posToFindOut & POS_VERTEX )
7442     {
7443       // check if the point2D is at "vertex 1" zone
7444       gp_XY pp1[2] = { p1, gp_XY( p1.X() - grad.Y(),
7445                                   p1.Y() + grad.X() ) };
7446       if ( getPointPosition( point2D, pp1, 0, POS_LEFT|POS_RIGHT )._name == POS_LEFT )
7447         return PointPos( POS_VERTEX, index0 );
7448
7449       // check if the point2D is at "vertex 2" zone
7450       gp_XY pp2[2] = { p2, gp_XY( p2.X() - grad.Y(),
7451                                   p2.Y() + grad.X() ) };
7452       if ( getPointPosition( point2D, pp2, 0, POS_LEFT|POS_RIGHT )._name == POS_RIGHT )
7453         return PointPos( POS_VERTEX, index0 + 1);
7454     }
7455     double edgeEquation =
7456       ( point2D.X() - p1.X() ) * grad.Y() - ( point2D.Y() - p1.Y() ) * grad.X();
7457     return PointPos( edgeEquation < 0 ? POS_LEFT : POS_RIGHT, index0 );
7458   }
7459 }
7460
7461 //=======================================================================
7462 /*!
7463  * \brief Return minimal distance from a point to a face
7464  *
7465  * Currently we ignore non-planarity and 2nd order of face
7466  */
7467 //=======================================================================
7468
7469 double SMESH_MeshEditor::GetDistance( const SMDS_MeshFace* face,
7470                                       const gp_Pnt&        point )
7471 {
7472   double badDistance = -1;
7473   if ( !face ) return badDistance;
7474
7475   // coordinates of nodes (medium nodes, if any, ignored)
7476   typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
7477   vector<gp_XYZ> xyz( TXyzIterator( face->nodesIterator()), TXyzIterator() );
7478   xyz.resize( face->NbCornerNodes()+1 );
7479
7480   // transformation to get xyz[0] lies on the origin, xyz[1] lies on the Z axis,
7481   // and xyz[2] lies in the XZ plane. This is to pass to 2D space on XZ plane.
7482   gp_Trsf trsf;
7483   gp_Vec OZ ( xyz[0], xyz[1] );
7484   gp_Vec OX ( xyz[0], xyz[2] );
7485   if ( OZ.Magnitude() < std::numeric_limits<double>::min() )
7486   {
7487     if ( xyz.size() < 4 ) return badDistance;
7488     OZ = gp_Vec ( xyz[0], xyz[2] );
7489     OX = gp_Vec ( xyz[0], xyz[3] );
7490   }
7491   gp_Ax3 tgtCS;
7492   try {
7493     tgtCS = gp_Ax3( xyz[0], OZ, OX );
7494   }
7495   catch ( Standard_Failure ) {
7496     return badDistance;
7497   }
7498   trsf.SetTransformation( tgtCS );
7499
7500   // move all the nodes to 2D
7501   vector<gp_XY> xy( xyz.size() );
7502   for ( size_t i = 0;i < xyz.size()-1; ++i )
7503   {
7504     gp_XYZ p3d = xyz[i];
7505     trsf.Transforms( p3d );
7506     xy[i].SetCoord( p3d.X(), p3d.Z() );
7507   }
7508   xyz.back() = xyz.front();
7509   xy.back() = xy.front();
7510
7511   // // move the point in 2D
7512   gp_XYZ tmpPnt = point.XYZ();
7513   trsf.Transforms( tmpPnt );
7514   gp_XY point2D( tmpPnt.X(), tmpPnt.Z() );
7515
7516   // loop on segments of the face to analyze point position ralative to the face
7517   set< PointPos > pntPosSet;
7518   for ( size_t i = 1; i < xy.size(); ++i )
7519   {
7520     PointPos pos = getPointPosition( point2D, &xy[0], i-1 );
7521     pntPosSet.insert( pos );
7522   }
7523
7524   // compute distance
7525   PointPos pos = *pntPosSet.begin();
7526   // cout << "Face " << face->GetID() << " DIST: ";
7527   switch ( pos._name )
7528   {
7529   case POS_LEFT: {
7530     // point is most close to a segment
7531     gp_Vec p0p1( point, xyz[ pos._index ] );
7532     gp_Vec p1p2( xyz[ pos._index ], xyz[ pos._index+1 ]); // segment vector
7533     p1p2.Normalize();
7534     double projDist = p0p1 * p1p2; // distance projected to the segment
7535     gp_Vec projVec = p1p2 * projDist;
7536     gp_Vec distVec = p0p1 - projVec;
7537     // cout << distVec.Magnitude()  << ", SEG " << face->GetNode(pos._index)->GetID()
7538     //      << " - " << face->GetNodeWrap(pos._index+1)->GetID() << endl;
7539     return distVec.Magnitude();
7540   }
7541   case POS_RIGHT: {
7542     // point is inside the face
7543     double distToFacePlane = tmpPnt.Y();
7544     // cout << distToFacePlane << ", INSIDE " << endl;
7545     return Abs( distToFacePlane );
7546   }
7547   case POS_VERTEX: {
7548     // point is most close to a node
7549     gp_Vec distVec( point, xyz[ pos._index ]);
7550     // cout << distVec.Magnitude()  << " VERTEX " << face->GetNode(pos._index)->GetID() << endl;
7551     return distVec.Magnitude();
7552   }
7553   }
7554   return badDistance;
7555 }
7556
7557 //=======================================================================
7558 //function : SimplifyFace
7559 //purpose  :
7560 //=======================================================================
7561 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
7562                                     vector<const SMDS_MeshNode *>&      poly_nodes,
7563                                     vector<int>&                        quantities) const
7564 {
7565   int nbNodes = faceNodes.size();
7566
7567   if (nbNodes < 3)
7568     return 0;
7569
7570   set<const SMDS_MeshNode*> nodeSet;
7571
7572   // get simple seq of nodes
7573   //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7574   vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7575   int iSimple = 0, nbUnique = 0;
7576
7577   simpleNodes[iSimple++] = faceNodes[0];
7578   nbUnique++;
7579   for (int iCur = 1; iCur < nbNodes; iCur++) {
7580     if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7581       simpleNodes[iSimple++] = faceNodes[iCur];
7582       if (nodeSet.insert( faceNodes[iCur] ).second)
7583         nbUnique++;
7584     }
7585   }
7586   int nbSimple = iSimple;
7587   if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7588     nbSimple--;
7589     iSimple--;
7590   }
7591
7592   if (nbUnique < 3)
7593     return 0;
7594
7595   // separate loops
7596   int nbNew = 0;
7597   bool foundLoop = (nbSimple > nbUnique);
7598   while (foundLoop) {
7599     foundLoop = false;
7600     set<const SMDS_MeshNode*> loopSet;
7601     for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7602       const SMDS_MeshNode* n = simpleNodes[iSimple];
7603       if (!loopSet.insert( n ).second) {
7604         foundLoop = true;
7605
7606         // separate loop
7607         int iC = 0, curLast = iSimple;
7608         for (; iC < curLast; iC++) {
7609           if (simpleNodes[iC] == n) break;
7610         }
7611         int loopLen = curLast - iC;
7612         if (loopLen > 2) {
7613           // create sub-element
7614           nbNew++;
7615           quantities.push_back(loopLen);
7616           for (; iC < curLast; iC++) {
7617             poly_nodes.push_back(simpleNodes[iC]);
7618           }
7619         }
7620         // shift the rest nodes (place from the first loop position)
7621         for (iC = curLast + 1; iC < nbSimple; iC++) {
7622           simpleNodes[iC - loopLen] = simpleNodes[iC];
7623         }
7624         nbSimple -= loopLen;
7625         iSimple -= loopLen;
7626       }
7627     } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7628   } // while (foundLoop)
7629
7630   if (iSimple > 2) {
7631     nbNew++;
7632     quantities.push_back(iSimple);
7633     for (int i = 0; i < iSimple; i++)
7634       poly_nodes.push_back(simpleNodes[i]);
7635   }
7636
7637   return nbNew;
7638 }
7639
7640 //=======================================================================
7641 //function : MergeNodes
7642 //purpose  : In each group, the cdr of nodes are substituted by the first one
7643 //           in all elements.
7644 //=======================================================================
7645
7646 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7647 {
7648   MESSAGE("MergeNodes");
7649   myLastCreatedElems.Clear();
7650   myLastCreatedNodes.Clear();
7651
7652   SMESHDS_Mesh* aMesh = GetMeshDS();
7653
7654   TNodeNodeMap nodeNodeMap; // node to replace - new node
7655   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7656   list< int > rmElemIds, rmNodeIds;
7657
7658   // Fill nodeNodeMap and elems
7659
7660   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7661   for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7662     list<const SMDS_MeshNode*>& nodes = *grIt;
7663     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7664     const SMDS_MeshNode* nToKeep = *nIt;
7665     //MESSAGE("node to keep " << nToKeep->GetID());
7666     for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7667       const SMDS_MeshNode* nToRemove = *nIt;
7668       nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7669       if ( nToRemove != nToKeep ) {
7670         //MESSAGE("  node to remove " << nToRemove->GetID());
7671         rmNodeIds.push_back( nToRemove->GetID() );
7672         AddToSameGroups( nToKeep, nToRemove, aMesh );
7673         // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7674         // after MergeNodes() w/o creating node in place of merged ones.
7675         const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7676         if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7677           if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7678             sm->SetIsAlwaysComputed( true );
7679       }
7680
7681       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7682       while ( invElemIt->more() ) {
7683         const SMDS_MeshElement* elem = invElemIt->next();
7684         elems.insert(elem);
7685       }
7686     }
7687   }
7688   // Change element nodes or remove an element
7689
7690   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7691   for ( ; eIt != elems.end(); eIt++ ) {
7692     const SMDS_MeshElement* elem = *eIt;
7693     //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7694     int nbNodes = elem->NbNodes();
7695     int aShapeId = FindShape( elem );
7696
7697     set<const SMDS_MeshNode*> nodeSet;
7698     vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7699     int iUnique = 0, iCur = 0, nbRepl = 0;
7700     vector<int> iRepl( nbNodes );
7701
7702     // get new seq of nodes
7703     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7704     while ( itN->more() ) {
7705       const SMDS_MeshNode* n =
7706         static_cast<const SMDS_MeshNode*>( itN->next() );
7707
7708       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7709       if ( nnIt != nodeNodeMap.end() ) { // n sticks
7710         n = (*nnIt).second;
7711         // BUG 0020185: begin
7712         {
7713           bool stopRecur = false;
7714           set<const SMDS_MeshNode*> nodesRecur;
7715           nodesRecur.insert(n);
7716           while (!stopRecur) {
7717             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7718             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7719               n = (*nnIt_i).second;
7720               if (!nodesRecur.insert(n).second) {
7721                 // error: recursive dependancy
7722                 stopRecur = true;
7723               }
7724             }
7725             else
7726               stopRecur = true;
7727           }
7728         }
7729         // BUG 0020185: end
7730       }
7731       curNodes[ iCur ] = n;
7732       bool isUnique = nodeSet.insert( n ).second;
7733       if ( isUnique )
7734         uniqueNodes[ iUnique++ ] = n;
7735       else
7736         iRepl[ nbRepl++ ] = iCur;
7737       iCur++;
7738     }
7739
7740     // Analyse element topology after replacement
7741
7742     bool isOk = true;
7743     int nbUniqueNodes = nodeSet.size();
7744     //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7745     if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7746       // Polygons and Polyhedral volumes
7747       if (elem->IsPoly()) {
7748
7749         if (elem->GetType() == SMDSAbs_Face) {
7750           // Polygon
7751           vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7752           int inode = 0;
7753           for (; inode < nbNodes; inode++) {
7754             face_nodes[inode] = curNodes[inode];
7755           }
7756
7757           vector<const SMDS_MeshNode *> polygons_nodes;
7758           vector<int> quantities;
7759           int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7760           if (nbNew > 0) {
7761             inode = 0;
7762             for (int iface = 0; iface < nbNew; iface++) {
7763               int nbNodes = quantities[iface];
7764               vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7765               for (int ii = 0; ii < nbNodes; ii++, inode++) {
7766                 poly_nodes[ii] = polygons_nodes[inode];
7767               }
7768               SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7769               myLastCreatedElems.Append(newElem);
7770               if (aShapeId)
7771                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7772             }
7773
7774             MESSAGE("ChangeElementNodes MergeNodes Polygon");
7775             //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7776             vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7777             int quid =0;
7778             if (nbNew > 0) quid = nbNew - 1;
7779             vector<int> newquant(quantities.begin()+quid, quantities.end());
7780             const SMDS_MeshElement* newElem = 0;
7781             newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7782             myLastCreatedElems.Append(newElem);
7783             if ( aShapeId && newElem )
7784               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7785             rmElemIds.push_back(elem->GetID());
7786           }
7787           else {
7788             rmElemIds.push_back(elem->GetID());
7789           }
7790
7791         }
7792         else if (elem->GetType() == SMDSAbs_Volume) {
7793           // Polyhedral volume
7794           if (nbUniqueNodes < 4) {
7795             rmElemIds.push_back(elem->GetID());
7796           }
7797           else {
7798             // each face has to be analyzed in order to check volume validity
7799             const SMDS_VtkVolume* aPolyedre =
7800               dynamic_cast<const SMDS_VtkVolume*>( elem );
7801             if (aPolyedre) {
7802               int nbFaces = aPolyedre->NbFaces();
7803
7804               vector<const SMDS_MeshNode *> poly_nodes;
7805               vector<int> quantities;
7806
7807               for (int iface = 1; iface <= nbFaces; iface++) {
7808                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7809                 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7810
7811                 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7812                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7813                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7814                   if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7815                     faceNode = (*nnIt).second;
7816                   }
7817                   faceNodes[inode - 1] = faceNode;
7818                 }
7819
7820                 SimplifyFace(faceNodes, poly_nodes, quantities);
7821               }
7822
7823               if (quantities.size() > 3) {
7824                 // to be done: remove coincident faces
7825               }
7826
7827               if (quantities.size() > 3)
7828                 {
7829                   MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7830                   //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7831                   const SMDS_MeshElement* newElem = 0;
7832                   newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7833                   myLastCreatedElems.Append(newElem);
7834                   if ( aShapeId && newElem )
7835                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
7836                   rmElemIds.push_back(elem->GetID());
7837                 }
7838             }
7839             else {
7840               rmElemIds.push_back(elem->GetID());
7841             }
7842           }
7843         }
7844         else {
7845         }
7846
7847         continue;
7848       } // poly element
7849
7850       // Regular elements
7851       // TODO not all the possible cases are solved. Find something more generic?
7852       switch ( nbNodes ) {
7853       case 2: ///////////////////////////////////// EDGE
7854         isOk = false; break;
7855       case 3: ///////////////////////////////////// TRIANGLE
7856         isOk = false; break;
7857       case 4:
7858         if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7859           isOk = false;
7860         else { //////////////////////////////////// QUADRANGLE
7861           if ( nbUniqueNodes < 3 )
7862             isOk = false;
7863           else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7864             isOk = false; // opposite nodes stick
7865           //MESSAGE("isOk " << isOk);
7866         }
7867         break;
7868       case 6: ///////////////////////////////////// PENTAHEDRON
7869         if ( nbUniqueNodes == 4 ) {
7870           // ---------------------------------> tetrahedron
7871           if (nbRepl == 3 &&
7872               iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7873             // all top nodes stick: reverse a bottom
7874             uniqueNodes[ 0 ] = curNodes [ 1 ];
7875             uniqueNodes[ 1 ] = curNodes [ 0 ];
7876           }
7877           else if (nbRepl == 3 &&
7878                    iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7879             // all bottom nodes stick: set a top before
7880             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7881             uniqueNodes[ 0 ] = curNodes [ 3 ];
7882             uniqueNodes[ 1 ] = curNodes [ 4 ];
7883             uniqueNodes[ 2 ] = curNodes [ 5 ];
7884           }
7885           else if (nbRepl == 4 &&
7886                    iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7887             // a lateral face turns into a line: reverse a bottom
7888             uniqueNodes[ 0 ] = curNodes [ 1 ];
7889             uniqueNodes[ 1 ] = curNodes [ 0 ];
7890           }
7891           else
7892             isOk = false;
7893         }
7894         else if ( nbUniqueNodes == 5 ) {
7895           // PENTAHEDRON --------------------> 2 tetrahedrons
7896           if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7897             // a bottom node sticks with a linked top one
7898             // 1.
7899             SMDS_MeshElement* newElem =
7900               aMesh->AddVolume(curNodes[ 3 ],
7901                                curNodes[ 4 ],
7902                                curNodes[ 5 ],
7903                                curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7904             myLastCreatedElems.Append(newElem);
7905             if ( aShapeId )
7906               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7907             // 2. : reverse a bottom
7908             uniqueNodes[ 0 ] = curNodes [ 1 ];
7909             uniqueNodes[ 1 ] = curNodes [ 0 ];
7910             nbUniqueNodes = 4;
7911           }
7912           else
7913             isOk = false;
7914         }
7915         else
7916           isOk = false;
7917         break;
7918       case 8: {
7919         if(elem->IsQuadratic()) { // Quadratic quadrangle
7920           //   1    5    2
7921           //    +---+---+
7922           //    |       |
7923           //    |       |
7924           //   4+       +6
7925           //    |       |
7926           //    |       |
7927           //    +---+---+
7928           //   0    7    3
7929           isOk = false;
7930           if(nbRepl==2) {
7931             MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7932           }
7933           if(nbRepl==3) {
7934             MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2]);
7935             nbUniqueNodes = 6;
7936             if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7937               uniqueNodes[0] = curNodes[0];
7938               uniqueNodes[1] = curNodes[2];
7939               uniqueNodes[2] = curNodes[3];
7940               uniqueNodes[3] = curNodes[5];
7941               uniqueNodes[4] = curNodes[6];
7942               uniqueNodes[5] = curNodes[7];
7943               isOk = true;
7944             }
7945             if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7946               uniqueNodes[0] = curNodes[0];
7947               uniqueNodes[1] = curNodes[1];
7948               uniqueNodes[2] = curNodes[2];
7949               uniqueNodes[3] = curNodes[4];
7950               uniqueNodes[4] = curNodes[5];
7951               uniqueNodes[5] = curNodes[6];
7952               isOk = true;
7953             }
7954             if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7955               uniqueNodes[0] = curNodes[1];
7956               uniqueNodes[1] = curNodes[2];
7957               uniqueNodes[2] = curNodes[3];
7958               uniqueNodes[3] = curNodes[5];
7959               uniqueNodes[4] = curNodes[6];
7960               uniqueNodes[5] = curNodes[0];
7961               isOk = true;
7962             }
7963             if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7964               uniqueNodes[0] = curNodes[0];
7965               uniqueNodes[1] = curNodes[1];
7966               uniqueNodes[2] = curNodes[3];
7967               uniqueNodes[3] = curNodes[4];
7968               uniqueNodes[4] = curNodes[6];
7969               uniqueNodes[5] = curNodes[7];
7970               isOk = true;
7971             }
7972             if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7973               uniqueNodes[0] = curNodes[0];
7974               uniqueNodes[1] = curNodes[2];
7975               uniqueNodes[2] = curNodes[3];
7976               uniqueNodes[3] = curNodes[1];
7977               uniqueNodes[4] = curNodes[6];
7978               uniqueNodes[5] = curNodes[7];
7979               isOk = true;
7980             }
7981             if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7982               uniqueNodes[0] = curNodes[0];
7983               uniqueNodes[1] = curNodes[1];
7984               uniqueNodes[2] = curNodes[2];
7985               uniqueNodes[3] = curNodes[4];
7986               uniqueNodes[4] = curNodes[5];
7987               uniqueNodes[5] = curNodes[7];
7988               isOk = true;
7989             }
7990             if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7991               uniqueNodes[0] = curNodes[0];
7992               uniqueNodes[1] = curNodes[1];
7993               uniqueNodes[2] = curNodes[3];
7994               uniqueNodes[3] = curNodes[4];
7995               uniqueNodes[4] = curNodes[2];
7996               uniqueNodes[5] = curNodes[7];
7997               isOk = true;
7998             }
7999             if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
8000               uniqueNodes[0] = curNodes[0];
8001               uniqueNodes[1] = curNodes[1];
8002               uniqueNodes[2] = curNodes[2];
8003               uniqueNodes[3] = curNodes[4];
8004               uniqueNodes[4] = curNodes[5];
8005               uniqueNodes[5] = curNodes[3];
8006               isOk = true;
8007             }
8008           }
8009           if(nbRepl==4) {
8010             MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3]);
8011           }
8012           if(nbRepl==5) {
8013             MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
8014           }
8015           break;
8016         }
8017         //////////////////////////////////// HEXAHEDRON
8018         isOk = false;
8019         SMDS_VolumeTool hexa (elem);
8020         hexa.SetExternalNormal();
8021         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
8022           //////////////////////// HEX ---> 1 tetrahedron
8023           for ( int iFace = 0; iFace < 6; iFace++ ) {
8024             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8025             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
8026                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
8027                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
8028               // one face turns into a point ...
8029               int iOppFace = hexa.GetOppFaceIndex( iFace );
8030               ind = hexa.GetFaceNodesIndices( iOppFace );
8031               int nbStick = 0;
8032               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
8033                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
8034                   nbStick++;
8035               }
8036               if ( nbStick == 1 ) {
8037                 // ... and the opposite one - into a triangle.
8038                 // set a top node
8039                 ind = hexa.GetFaceNodesIndices( iFace );
8040                 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
8041                 isOk = true;
8042               }
8043               break;
8044             }
8045           }
8046         }
8047         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
8048           //////////////////////// HEX ---> 1 prism
8049           int nbTria = 0, iTria[3];
8050           const int *ind; // indices of face nodes
8051           // look for triangular faces
8052           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
8053             ind = hexa.GetFaceNodesIndices( iFace );
8054             TIDSortedNodeSet faceNodes;
8055             for ( iCur = 0; iCur < 4; iCur++ )
8056               faceNodes.insert( curNodes[ind[iCur]] );
8057             if ( faceNodes.size() == 3 )
8058               iTria[ nbTria++ ] = iFace;
8059           }
8060           // check if triangles are opposite
8061           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
8062           {
8063             isOk = true;
8064             // set nodes of the bottom triangle
8065             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
8066             vector<int> indB;
8067             for ( iCur = 0; iCur < 4; iCur++ )
8068               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
8069                 indB.push_back( ind[iCur] );
8070             if ( !hexa.IsForward() )
8071               std::swap( indB[0], indB[2] );
8072             for ( iCur = 0; iCur < 3; iCur++ )
8073               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
8074             // set nodes of the top triangle
8075             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
8076             for ( iCur = 0; iCur < 3; ++iCur )
8077               for ( int j = 0; j < 4; ++j )
8078                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
8079                 {
8080                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
8081                   break;
8082                 }
8083           }
8084           break;
8085         }
8086         else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
8087           //////////////////// HEXAHEDRON ---> 2 tetrahedrons
8088           for ( int iFace = 0; iFace < 6; iFace++ ) {
8089             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8090             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
8091                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
8092                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
8093               // one face turns into a point ...
8094               int iOppFace = hexa.GetOppFaceIndex( iFace );
8095               ind = hexa.GetFaceNodesIndices( iOppFace );
8096               int nbStick = 0;
8097               iUnique = 2;  // reverse a tetrahedron 1 bottom
8098               for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
8099                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
8100                   nbStick++;
8101                 else if ( iUnique >= 0 )
8102                   uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
8103               }
8104               if ( nbStick == 0 ) {
8105                 // ... and the opposite one is a quadrangle
8106                 // set a top node
8107                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
8108                 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
8109                 nbUniqueNodes = 4;
8110                 // tetrahedron 2
8111                 SMDS_MeshElement* newElem =
8112                   aMesh->AddVolume(curNodes[ind[ 0 ]],
8113                                    curNodes[ind[ 3 ]],
8114                                    curNodes[ind[ 2 ]],
8115                                    curNodes[indTop[ 0 ]]);
8116                 myLastCreatedElems.Append(newElem);
8117                 if ( aShapeId )
8118                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
8119                 isOk = true;
8120               }
8121               break;
8122             }
8123           }
8124         }
8125         else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
8126           ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
8127           // find indices of quad and tri faces
8128           int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
8129           for ( iFace = 0; iFace < 6; iFace++ ) {
8130             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8131             nodeSet.clear();
8132             for ( iCur = 0; iCur < 4; iCur++ )
8133               nodeSet.insert( curNodes[ind[ iCur ]] );
8134             nbUniqueNodes = nodeSet.size();
8135             if ( nbUniqueNodes == 3 )
8136               iTriFace[ nbTri++ ] = iFace;
8137             else if ( nbUniqueNodes == 4 )
8138               iQuadFace[ nbQuad++ ] = iFace;
8139           }
8140           if (nbQuad == 2 && nbTri == 4 &&
8141               hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
8142             // 2 opposite quadrangles stuck with a diagonal;
8143             // sample groups of merged indices: (0-4)(2-6)
8144             // --------------------------------------------> 2 tetrahedrons
8145             const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
8146             const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
8147             int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
8148             if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
8149                 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
8150               // stuck with 0-2 diagonal
8151               i0  = ind1[ 3 ];
8152               i1d = ind1[ 0 ];
8153               i2  = ind1[ 1 ];
8154               i3d = ind1[ 2 ];
8155               i0t = ind2[ 1 ];
8156               i2t = ind2[ 3 ];
8157             }
8158             else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
8159                      curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
8160               // stuck with 1-3 diagonal
8161               i0  = ind1[ 0 ];
8162               i1d = ind1[ 1 ];
8163               i2  = ind1[ 2 ];
8164               i3d = ind1[ 3 ];
8165               i0t = ind2[ 0 ];
8166               i2t = ind2[ 1 ];
8167             }
8168             else {
8169               ASSERT(0);
8170             }
8171             // tetrahedron 1
8172             uniqueNodes[ 0 ] = curNodes [ i0 ];
8173             uniqueNodes[ 1 ] = curNodes [ i1d ];
8174             uniqueNodes[ 2 ] = curNodes [ i3d ];
8175             uniqueNodes[ 3 ] = curNodes [ i0t ];
8176             nbUniqueNodes = 4;
8177             // tetrahedron 2
8178             SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
8179                                                          curNodes[ i2 ],
8180                                                          curNodes[ i3d ],
8181                                                          curNodes[ i2t ]);
8182             myLastCreatedElems.Append(newElem);
8183             if ( aShapeId )
8184               aMesh->SetMeshElementOnShape( newElem, aShapeId );
8185             isOk = true;
8186           }
8187           else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
8188                    ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
8189             // --------------------------------------------> prism
8190             // find 2 opposite triangles
8191             nbUniqueNodes = 6;
8192             for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
8193               if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
8194                 // find indices of kept and replaced nodes
8195                 // and fill unique nodes of 2 opposite triangles
8196                 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
8197                 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
8198                 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
8199                 // fill unique nodes
8200                 iUnique = 0;
8201                 isOk = true;
8202                 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
8203                   const SMDS_MeshNode* n     = curNodes[ind1[ iCur ]];
8204                   const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
8205                   if ( n == nInit ) {
8206                     // iCur of a linked node of the opposite face (make normals co-directed):
8207                     int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
8208                     // check that correspondent corners of triangles are linked
8209                     if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
8210                       isOk = false;
8211                     else {
8212                       uniqueNodes[ iUnique ] = n;
8213                       uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
8214                       iUnique++;
8215                     }
8216                   }
8217                 }
8218                 break;
8219               }
8220             }
8221           }
8222         } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
8223         else
8224         {
8225           MESSAGE("MergeNodes() removes hexahedron "<< elem);
8226         }
8227         break;
8228       } // HEXAHEDRON
8229
8230       default:
8231         isOk = false;
8232       } // switch ( nbNodes )
8233
8234     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
8235
8236     if ( isOk ) { // the elem remains valid after sticking nodes
8237       if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
8238       {
8239         // Change nodes of polyedre
8240         const SMDS_VtkVolume* aPolyedre =
8241           dynamic_cast<const SMDS_VtkVolume*>( elem );
8242         if (aPolyedre) {
8243           int nbFaces = aPolyedre->NbFaces();
8244
8245           vector<const SMDS_MeshNode *> poly_nodes;
8246           vector<int> quantities (nbFaces);
8247
8248           for (int iface = 1; iface <= nbFaces; iface++) {
8249             int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
8250             quantities[iface - 1] = nbFaceNodes;
8251
8252             for (inode = 1; inode <= nbFaceNodes; inode++) {
8253               const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
8254
8255               TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
8256               if (nnIt != nodeNodeMap.end()) { // curNode sticks
8257                 curNode = (*nnIt).second;
8258               }
8259               poly_nodes.push_back(curNode);
8260             }
8261           }
8262           aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
8263         }
8264       }
8265       else // replace non-polyhedron elements
8266       {
8267         const SMDSAbs_ElementType etyp = elem->GetType();
8268         const int elemId               = elem->GetID();
8269         const bool isPoly              = (elem->GetEntityType() == SMDSEntity_Polygon);
8270         uniqueNodes.resize(nbUniqueNodes);
8271
8272         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
8273
8274         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
8275         SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
8276         if ( sm && newElem )
8277           sm->AddElement( newElem );
8278         if ( elem != newElem )
8279           ReplaceElemInGroups( elem, newElem, aMesh );
8280       }
8281     }
8282     else {
8283       // Remove invalid regular element or invalid polygon
8284       rmElemIds.push_back( elem->GetID() );
8285     }
8286
8287   } // loop on elements
8288
8289   // Remove bad elements, then equal nodes (order important)
8290
8291   Remove( rmElemIds, false );
8292   Remove( rmNodeIds, true );
8293
8294 }
8295
8296
8297 // ========================================================
8298 // class   : SortableElement
8299 // purpose : allow sorting elements basing on their nodes
8300 // ========================================================
8301 class SortableElement : public set <const SMDS_MeshElement*>
8302 {
8303 public:
8304
8305   SortableElement( const SMDS_MeshElement* theElem )
8306   {
8307     myElem = theElem;
8308     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
8309     while ( nodeIt->more() )
8310       this->insert( nodeIt->next() );
8311   }
8312
8313   const SMDS_MeshElement* Get() const
8314   { return myElem; }
8315
8316   void Set(const SMDS_MeshElement* e) const
8317   { myElem = e; }
8318
8319
8320 private:
8321   mutable const SMDS_MeshElement* myElem;
8322 };
8323
8324 //=======================================================================
8325 //function : FindEqualElements
8326 //purpose  : Return list of group of elements built on the same nodes.
8327 //           Search among theElements or in the whole mesh if theElements is empty
8328 //=======================================================================
8329
8330 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
8331                                          TListOfListOfElementsID & theGroupsOfElementsID)
8332 {
8333   myLastCreatedElems.Clear();
8334   myLastCreatedNodes.Clear();
8335
8336   typedef map< SortableElement, int > TMapOfNodeSet;
8337   typedef list<int> TGroupOfElems;
8338
8339   if ( theElements.empty() )
8340   { // get all elements in the mesh
8341     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
8342     while ( eIt->more() )
8343       theElements.insert( theElements.end(), eIt->next());
8344   }
8345
8346   vector< TGroupOfElems > arrayOfGroups;
8347   TGroupOfElems groupOfElems;
8348   TMapOfNodeSet mapOfNodeSet;
8349
8350   TIDSortedElemSet::iterator elemIt = theElements.begin();
8351   for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
8352     const SMDS_MeshElement* curElem = *elemIt;
8353     SortableElement SE(curElem);
8354     int ind = -1;
8355     // check uniqueness
8356     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8357     if( !(pp.second) ) {
8358       TMapOfNodeSet::iterator& itSE = pp.first;
8359       ind = (*itSE).second;
8360       arrayOfGroups[ind].push_back(curElem->GetID());
8361     }
8362     else {
8363       groupOfElems.clear();
8364       groupOfElems.push_back(curElem->GetID());
8365       arrayOfGroups.push_back(groupOfElems);
8366       i++;
8367     }
8368   }
8369
8370   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8371   for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
8372     groupOfElems = *groupIt;
8373     if ( groupOfElems.size() > 1 ) {
8374       groupOfElems.sort();
8375       theGroupsOfElementsID.push_back(groupOfElems);
8376     }
8377   }
8378 }
8379
8380 //=======================================================================
8381 //function : MergeElements
8382 //purpose  : In each given group, substitute all elements by the first one.
8383 //=======================================================================
8384
8385 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8386 {
8387   myLastCreatedElems.Clear();
8388   myLastCreatedNodes.Clear();
8389
8390   typedef list<int> TListOfIDs;
8391   TListOfIDs rmElemIds; // IDs of elems to remove
8392
8393   SMESHDS_Mesh* aMesh = GetMeshDS();
8394
8395   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8396   while ( groupsIt != theGroupsOfElementsID.end() ) {
8397     TListOfIDs& aGroupOfElemID = *groupsIt;
8398     aGroupOfElemID.sort();
8399     int elemIDToKeep = aGroupOfElemID.front();
8400     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8401     aGroupOfElemID.pop_front();
8402     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8403     while ( idIt != aGroupOfElemID.end() ) {
8404       int elemIDToRemove = *idIt;
8405       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8406       // add the kept element in groups of removed one (PAL15188)
8407       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8408       rmElemIds.push_back( elemIDToRemove );
8409       ++idIt;
8410     }
8411     ++groupsIt;
8412   }
8413
8414   Remove( rmElemIds, false );
8415 }
8416
8417 //=======================================================================
8418 //function : MergeEqualElements
8419 //purpose  : Remove all but one of elements built on the same nodes.
8420 //=======================================================================
8421
8422 void SMESH_MeshEditor::MergeEqualElements()
8423 {
8424   TIDSortedElemSet aMeshElements; /* empty input ==
8425                                      to merge equal elements in the whole mesh */
8426   TListOfListOfElementsID aGroupsOfElementsID;
8427   FindEqualElements(aMeshElements, aGroupsOfElementsID);
8428   MergeElements(aGroupsOfElementsID);
8429 }
8430
8431 //=======================================================================
8432 //function : FindFaceInSet
8433 //purpose  : Return a face having linked nodes n1 and n2 and which is
8434 //           - not in avoidSet,
8435 //           - in elemSet provided that !elemSet.empty()
8436 //           i1 and i2 optionally returns indices of n1 and n2
8437 //=======================================================================
8438
8439 const SMDS_MeshElement*
8440 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode*    n1,
8441                                 const SMDS_MeshNode*    n2,
8442                                 const TIDSortedElemSet& elemSet,
8443                                 const TIDSortedElemSet& avoidSet,
8444                                 int*                    n1ind,
8445                                 int*                    n2ind)
8446
8447 {
8448   int i1, i2;
8449   const SMDS_MeshElement* face = 0;
8450
8451   SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
8452   //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
8453   while ( invElemIt->more() && !face ) // loop on inverse faces of n1
8454   {
8455     //MESSAGE("in while ( invElemIt->more() && !face )");
8456     const SMDS_MeshElement* elem = invElemIt->next();
8457     if (avoidSet.count( elem ))
8458       continue;
8459     if ( !elemSet.empty() && !elemSet.count( elem ))
8460       continue;
8461     // index of n1
8462     i1 = elem->GetNodeIndex( n1 );
8463     // find a n2 linked to n1
8464     int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
8465     for ( int di = -1; di < 2 && !face; di += 2 )
8466     {
8467       i2 = (i1+di+nbN) % nbN;
8468       if ( elem->GetNode( i2 ) == n2 )
8469         face = elem;
8470     }
8471     if ( !face && elem->IsQuadratic())
8472     {
8473       // analysis for quadratic elements using all nodes
8474       const SMDS_VtkFace* F =
8475         dynamic_cast<const SMDS_VtkFace*>(elem);
8476       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8477       // use special nodes iterator
8478       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8479       const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
8480       for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
8481       {
8482         const SMDS_MeshNode* n = cast2Node( anIter->next() );
8483         if ( n1 == prevN && n2 == n )
8484         {
8485           face = elem;
8486         }
8487         else if ( n2 == prevN && n1 == n )
8488         {
8489           face = elem; swap( i1, i2 );
8490         }
8491         prevN = n;
8492       }
8493     }
8494   }
8495   if ( n1ind ) *n1ind = i1;
8496   if ( n2ind ) *n2ind = i2;
8497   return face;
8498 }
8499
8500 //=======================================================================
8501 //function : findAdjacentFace
8502 //purpose  :
8503 //=======================================================================
8504
8505 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8506                                                 const SMDS_MeshNode* n2,
8507                                                 const SMDS_MeshElement* elem)
8508 {
8509   TIDSortedElemSet elemSet, avoidSet;
8510   if ( elem )
8511     avoidSet.insert ( elem );
8512   return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
8513 }
8514
8515 //=======================================================================
8516 //function : FindFreeBorder
8517 //purpose  :
8518 //=======================================================================
8519
8520 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8521
8522 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
8523                                        const SMDS_MeshNode*             theSecondNode,
8524                                        const SMDS_MeshNode*             theLastNode,
8525                                        list< const SMDS_MeshNode* > &   theNodes,
8526                                        list< const SMDS_MeshElement* >& theFaces)
8527 {
8528   if ( !theFirstNode || !theSecondNode )
8529     return false;
8530   // find border face between theFirstNode and theSecondNode
8531   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8532   if ( !curElem )
8533     return false;
8534
8535   theFaces.push_back( curElem );
8536   theNodes.push_back( theFirstNode );
8537   theNodes.push_back( theSecondNode );
8538
8539   //vector<const SMDS_MeshNode*> nodes;
8540   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8541   TIDSortedElemSet foundElems;
8542   bool needTheLast = ( theLastNode != 0 );
8543
8544   while ( nStart != theLastNode ) {
8545     if ( nStart == theFirstNode )
8546       return !needTheLast;
8547
8548     // find all free border faces sharing form nStart
8549
8550     list< const SMDS_MeshElement* > curElemList;
8551     list< const SMDS_MeshNode* > nStartList;
8552     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8553     while ( invElemIt->more() ) {
8554       const SMDS_MeshElement* e = invElemIt->next();
8555       if ( e == curElem || foundElems.insert( e ).second ) {
8556         // get nodes
8557         int iNode = 0, nbNodes = e->NbNodes();
8558         //const SMDS_MeshNode* nodes[nbNodes+1];
8559         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8560
8561         if(e->IsQuadratic()) {
8562           const SMDS_VtkFace* F =
8563             dynamic_cast<const SMDS_VtkFace*>(e);
8564           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8565           // use special nodes iterator
8566           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8567           while( anIter->more() ) {
8568             nodes[ iNode++ ] = cast2Node(anIter->next());
8569           }
8570         }
8571         else {
8572           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8573           while ( nIt->more() )
8574             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8575         }
8576         nodes[ iNode ] = nodes[ 0 ];
8577         // check 2 links
8578         for ( iNode = 0; iNode < nbNodes; iNode++ )
8579           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8580                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8581               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8582           {
8583             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8584             curElemList.push_back( e );
8585           }
8586       }
8587     }
8588     // analyse the found
8589
8590     int nbNewBorders = curElemList.size();
8591     if ( nbNewBorders == 0 ) {
8592       // no free border furthermore
8593       return !needTheLast;
8594     }
8595     else if ( nbNewBorders == 1 ) {
8596       // one more element found
8597       nIgnore = nStart;
8598       nStart = nStartList.front();
8599       curElem = curElemList.front();
8600       theFaces.push_back( curElem );
8601       theNodes.push_back( nStart );
8602     }
8603     else {
8604       // several continuations found
8605       list< const SMDS_MeshElement* >::iterator curElemIt;
8606       list< const SMDS_MeshNode* >::iterator nStartIt;
8607       // check if one of them reached the last node
8608       if ( needTheLast ) {
8609         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8610              curElemIt!= curElemList.end();
8611              curElemIt++, nStartIt++ )
8612           if ( *nStartIt == theLastNode ) {
8613             theFaces.push_back( *curElemIt );
8614             theNodes.push_back( *nStartIt );
8615             return true;
8616           }
8617       }
8618       // find the best free border by the continuations
8619       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8620       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8621       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8622            curElemIt!= curElemList.end();
8623            curElemIt++, nStartIt++ )
8624       {
8625         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8626         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8627         // find one more free border
8628         if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8629           cNL->clear();
8630           cFL->clear();
8631         }
8632         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8633           // choice: clear a worse one
8634           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8635           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8636           contNodes[ iWorse ].clear();
8637           contFaces[ iWorse ].clear();
8638         }
8639       }
8640       if ( contNodes[0].empty() && contNodes[1].empty() )
8641         return false;
8642
8643       // append the best free border
8644       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8645       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8646       theNodes.pop_back(); // remove nIgnore
8647       theNodes.pop_back(); // remove nStart
8648       theFaces.pop_back(); // remove curElem
8649       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8650       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8651       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8652       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8653       return true;
8654
8655     } // several continuations found
8656   } // while ( nStart != theLastNode )
8657
8658   return true;
8659 }
8660
8661 //=======================================================================
8662 //function : CheckFreeBorderNodes
8663 //purpose  : Return true if the tree nodes are on a free border
8664 //=======================================================================
8665
8666 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8667                                             const SMDS_MeshNode* theNode2,
8668                                             const SMDS_MeshNode* theNode3)
8669 {
8670   list< const SMDS_MeshNode* > nodes;
8671   list< const SMDS_MeshElement* > faces;
8672   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8673 }
8674
8675 //=======================================================================
8676 //function : SewFreeBorder
8677 //purpose  :
8678 //=======================================================================
8679
8680 SMESH_MeshEditor::Sew_Error
8681 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8682                                  const SMDS_MeshNode* theBordSecondNode,
8683                                  const SMDS_MeshNode* theBordLastNode,
8684                                  const SMDS_MeshNode* theSideFirstNode,
8685                                  const SMDS_MeshNode* theSideSecondNode,
8686                                  const SMDS_MeshNode* theSideThirdNode,
8687                                  const bool           theSideIsFreeBorder,
8688                                  const bool           toCreatePolygons,
8689                                  const bool           toCreatePolyedrs)
8690 {
8691   myLastCreatedElems.Clear();
8692   myLastCreatedNodes.Clear();
8693
8694   MESSAGE("::SewFreeBorder()");
8695   Sew_Error aResult = SEW_OK;
8696
8697   // ====================================
8698   //    find side nodes and elements
8699   // ====================================
8700
8701   list< const SMDS_MeshNode* > nSide[ 2 ];
8702   list< const SMDS_MeshElement* > eSide[ 2 ];
8703   list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8704   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8705
8706   // Free border 1
8707   // --------------
8708   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8709                       nSide[0], eSide[0])) {
8710     MESSAGE(" Free Border 1 not found " );
8711     aResult = SEW_BORDER1_NOT_FOUND;
8712   }
8713   if (theSideIsFreeBorder) {
8714     // Free border 2
8715     // --------------
8716     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8717                         nSide[1], eSide[1])) {
8718       MESSAGE(" Free Border 2 not found " );
8719       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8720     }
8721   }
8722   if ( aResult != SEW_OK )
8723     return aResult;
8724
8725   if (!theSideIsFreeBorder) {
8726     // Side 2
8727     // --------------
8728
8729     // -------------------------------------------------------------------------
8730     // Algo:
8731     // 1. If nodes to merge are not coincident, move nodes of the free border
8732     //    from the coord sys defined by the direction from the first to last
8733     //    nodes of the border to the correspondent sys of the side 2
8734     // 2. On the side 2, find the links most co-directed with the correspondent
8735     //    links of the free border
8736     // -------------------------------------------------------------------------
8737
8738     // 1. Since sewing may break if there are volumes to split on the side 2,
8739     //    we wont move nodes but just compute new coordinates for them
8740     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8741     TNodeXYZMap nBordXYZ;
8742     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8743     list< const SMDS_MeshNode* >::iterator nBordIt;
8744
8745     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8746     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8747     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8748     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8749     double tol2 = 1.e-8;
8750     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8751     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8752       // Need node movement.
8753
8754       // find X and Z axes to create trsf
8755       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8756       gp_Vec X = Zs ^ Zb;
8757       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8758         // Zb || Zs
8759         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8760
8761       // coord systems
8762       gp_Ax3 toBordAx( Pb1, Zb, X );
8763       gp_Ax3 fromSideAx( Ps1, Zs, X );
8764       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8765       // set trsf
8766       gp_Trsf toBordSys, fromSide2Sys;
8767       toBordSys.SetTransformation( toBordAx );
8768       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8769       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8770
8771       // move
8772       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8773         const SMDS_MeshNode* n = *nBordIt;
8774         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8775         toBordSys.Transforms( xyz );
8776         fromSide2Sys.Transforms( xyz );
8777         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8778       }
8779     }
8780     else {
8781       // just insert nodes XYZ in the nBordXYZ map
8782       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8783         const SMDS_MeshNode* n = *nBordIt;
8784         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8785       }
8786     }
8787
8788     // 2. On the side 2, find the links most co-directed with the correspondent
8789     //    links of the free border
8790
8791     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8792     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8793     sideNodes.push_back( theSideFirstNode );
8794
8795     bool hasVolumes = false;
8796     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8797     set<long> foundSideLinkIDs, checkedLinkIDs;
8798     SMDS_VolumeTool volume;
8799     //const SMDS_MeshNode* faceNodes[ 4 ];
8800
8801     const SMDS_MeshNode*    sideNode;
8802     const SMDS_MeshElement* sideElem;
8803     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8804     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8805     nBordIt = bordNodes.begin();
8806     nBordIt++;
8807     // border node position and border link direction to compare with
8808     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8809     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8810     // choose next side node by link direction or by closeness to
8811     // the current border node:
8812     bool searchByDir = ( *nBordIt != theBordLastNode );
8813     do {
8814       // find the next node on the Side 2
8815       sideNode = 0;
8816       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8817       long linkID;
8818       checkedLinkIDs.clear();
8819       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8820
8821       // loop on inverse elements of current node (prevSideNode) on the Side 2
8822       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8823       while ( invElemIt->more() )
8824       {
8825         const SMDS_MeshElement* elem = invElemIt->next();
8826         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8827         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8828         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8829         bool isVolume = volume.Set( elem );
8830         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8831         if ( isVolume ) // --volume
8832           hasVolumes = true;
8833         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8834           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8835           if(elem->IsQuadratic()) {
8836             const SMDS_VtkFace* F =
8837               dynamic_cast<const SMDS_VtkFace*>(elem);
8838             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8839             // use special nodes iterator
8840             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8841             while( anIter->more() ) {
8842               nodes[ iNode ] = cast2Node(anIter->next());
8843               if ( nodes[ iNode++ ] == prevSideNode )
8844                 iPrevNode = iNode - 1;
8845             }
8846           }
8847           else {
8848             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8849             while ( nIt->more() ) {
8850               nodes[ iNode ] = cast2Node( nIt->next() );
8851               if ( nodes[ iNode++ ] == prevSideNode )
8852                 iPrevNode = iNode - 1;
8853             }
8854           }
8855           // there are 2 links to check
8856           nbNodes = 2;
8857         }
8858         else // --edge
8859           continue;
8860         // loop on links, to be precise, on the second node of links
8861         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8862           const SMDS_MeshNode* n = nodes[ iNode ];
8863           if ( isVolume ) {
8864             if ( !volume.IsLinked( n, prevSideNode ))
8865               continue;
8866           }
8867           else {
8868             if ( iNode ) // a node before prevSideNode
8869               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8870             else         // a node after prevSideNode
8871               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8872           }
8873           // check if this link was already used
8874           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8875           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8876           if (!isJustChecked &&
8877               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8878           {
8879             // test a link geometrically
8880             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8881             bool linkIsBetter = false;
8882             double dot = 0.0, dist = 0.0;
8883             if ( searchByDir ) { // choose most co-directed link
8884               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8885               linkIsBetter = ( dot > maxDot );
8886             }
8887             else { // choose link with the node closest to bordPos
8888               dist = ( nextXYZ - bordPos ).SquareModulus();
8889               linkIsBetter = ( dist < minDist );
8890             }
8891             if ( linkIsBetter ) {
8892               maxDot = dot;
8893               minDist = dist;
8894               linkID = iLink;
8895               sideNode = n;
8896               sideElem = elem;
8897             }
8898           }
8899         }
8900       } // loop on inverse elements of prevSideNode
8901
8902       if ( !sideNode ) {
8903         MESSAGE(" Cant find path by links of the Side 2 ");
8904         return SEW_BAD_SIDE_NODES;
8905       }
8906       sideNodes.push_back( sideNode );
8907       sideElems.push_back( sideElem );
8908       foundSideLinkIDs.insert ( linkID );
8909       prevSideNode = sideNode;
8910
8911       if ( *nBordIt == theBordLastNode )
8912         searchByDir = false;
8913       else {
8914         // find the next border link to compare with
8915         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8916         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8917         // move to next border node if sideNode is before forward border node (bordPos)
8918         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8919           prevBordNode = *nBordIt;
8920           nBordIt++;
8921           bordPos = nBordXYZ[ *nBordIt ];
8922           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8923           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8924         }
8925       }
8926     }
8927     while ( sideNode != theSideSecondNode );
8928
8929     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8930       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8931       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8932     }
8933   } // end nodes search on the side 2
8934
8935   // ============================
8936   // sew the border to the side 2
8937   // ============================
8938
8939   int nbNodes[]  = { nSide[0].size(), nSide[1].size() };
8940   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8941
8942   TListOfListOfNodes nodeGroupsToMerge;
8943   if ( nbNodes[0] == nbNodes[1] ||
8944        ( theSideIsFreeBorder && !theSideThirdNode)) {
8945
8946     // all nodes are to be merged
8947
8948     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8949          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8950          nIt[0]++, nIt[1]++ )
8951     {
8952       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8953       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8954       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8955     }
8956   }
8957   else {
8958
8959     // insert new nodes into the border and the side to get equal nb of segments
8960
8961     // get normalized parameters of nodes on the borders
8962     //double param[ 2 ][ maxNbNodes ];
8963     double* param[ 2 ];
8964     param[0] = new double [ maxNbNodes ];
8965     param[1] = new double [ maxNbNodes ];
8966     int iNode, iBord;
8967     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8968       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8969       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8970       const SMDS_MeshNode* nPrev = *nIt;
8971       double bordLength = 0;
8972       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8973         const SMDS_MeshNode* nCur = *nIt;
8974         gp_XYZ segment (nCur->X() - nPrev->X(),
8975                         nCur->Y() - nPrev->Y(),
8976                         nCur->Z() - nPrev->Z());
8977         double segmentLen = segment.Modulus();
8978         bordLength += segmentLen;
8979         param[ iBord ][ iNode ] = bordLength;
8980         nPrev = nCur;
8981       }
8982       // normalize within [0,1]
8983       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8984         param[ iBord ][ iNode ] /= bordLength;
8985       }
8986     }
8987
8988     // loop on border segments
8989     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8990     int i[ 2 ] = { 0, 0 };
8991     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8992     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8993
8994     TElemOfNodeListMap insertMap;
8995     TElemOfNodeListMap::iterator insertMapIt;
8996     // insertMap is
8997     // key:   elem to insert nodes into
8998     // value: 2 nodes to insert between + nodes to be inserted
8999     do {
9000       bool next[ 2 ] = { false, false };
9001
9002       // find min adjacent segment length after sewing
9003       double nextParam = 10., prevParam = 0;
9004       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
9005         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
9006           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
9007         if ( i[ iBord ] > 0 )
9008           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
9009       }
9010       double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
9011       double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
9012       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
9013
9014       // choose to insert or to merge nodes
9015       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
9016       if ( Abs( du ) <= minSegLen * 0.2 ) {
9017         // merge
9018         // ------
9019         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
9020         const SMDS_MeshNode* n0 = *nIt[0];
9021         const SMDS_MeshNode* n1 = *nIt[1];
9022         nodeGroupsToMerge.back().push_back( n1 );
9023         nodeGroupsToMerge.back().push_back( n0 );
9024         // position of node of the border changes due to merge
9025         param[ 0 ][ i[0] ] += du;
9026         // move n1 for the sake of elem shape evaluation during insertion.
9027         // n1 will be removed by MergeNodes() anyway
9028         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
9029         next[0] = next[1] = true;
9030       }
9031       else {
9032         // insert
9033         // ------
9034         int intoBord = ( du < 0 ) ? 0 : 1;
9035         const SMDS_MeshElement* elem = *eIt[ intoBord ];
9036         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
9037         const SMDS_MeshNode*    n2   = *nIt[ intoBord ];
9038         const SMDS_MeshNode*    nIns = *nIt[ 1 - intoBord ];
9039         if ( intoBord == 1 ) {
9040           // move node of the border to be on a link of elem of the side
9041           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
9042           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
9043           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
9044           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
9045           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
9046         }
9047         insertMapIt = insertMap.find( elem );
9048         bool notFound = ( insertMapIt == insertMap.end() );
9049         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
9050         if ( otherLink ) {
9051           // insert into another link of the same element:
9052           // 1. perform insertion into the other link of the elem
9053           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
9054           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
9055           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
9056           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
9057           // 2. perform insertion into the link of adjacent faces
9058           while (true) {
9059             const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
9060             if ( adjElem )
9061               InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
9062             else
9063               break;
9064           }
9065           if (toCreatePolyedrs) {
9066             // perform insertion into the links of adjacent volumes
9067             UpdateVolumes(n12, n22, nodeList);
9068           }
9069           // 3. find an element appeared on n1 and n2 after the insertion
9070           insertMap.erase( elem );
9071           elem = findAdjacentFace( n1, n2, 0 );
9072         }
9073         if ( notFound || otherLink ) {
9074           // add element and nodes of the side into the insertMap
9075           insertMapIt = insertMap.insert
9076             ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
9077           (*insertMapIt).second.push_back( n1 );
9078           (*insertMapIt).second.push_back( n2 );
9079         }
9080         // add node to be inserted into elem
9081         (*insertMapIt).second.push_back( nIns );
9082         next[ 1 - intoBord ] = true;
9083       }
9084
9085       // go to the next segment
9086       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
9087         if ( next[ iBord ] ) {
9088           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
9089             eIt[ iBord ]++;
9090           nPrev[ iBord ] = *nIt[ iBord ];
9091           nIt[ iBord ]++; i[ iBord ]++;
9092         }
9093       }
9094     }
9095     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
9096
9097     // perform insertion of nodes into elements
9098
9099     for (insertMapIt = insertMap.begin();
9100          insertMapIt != insertMap.end();
9101          insertMapIt++ )
9102     {
9103       const SMDS_MeshElement* elem = (*insertMapIt).first;
9104       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
9105       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
9106       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
9107
9108       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
9109
9110       if ( !theSideIsFreeBorder ) {
9111         // look for and insert nodes into the faces adjacent to elem
9112         while (true) {
9113           const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
9114           if ( adjElem )
9115             InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
9116           else
9117             break;
9118         }
9119       }
9120       if (toCreatePolyedrs) {
9121         // perform insertion into the links of adjacent volumes
9122         UpdateVolumes(n1, n2, nodeList);
9123       }
9124     }
9125
9126     delete param[0];
9127     delete param[1];
9128   } // end: insert new nodes
9129
9130   MergeNodes ( nodeGroupsToMerge );
9131
9132   return aResult;
9133 }
9134
9135 //=======================================================================
9136 //function : InsertNodesIntoLink
9137 //purpose  : insert theNodesToInsert into theFace between theBetweenNode1
9138 //           and theBetweenNode2 and split theElement
9139 //=======================================================================
9140
9141 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theFace,
9142                                            const SMDS_MeshNode*        theBetweenNode1,
9143                                            const SMDS_MeshNode*        theBetweenNode2,
9144                                            list<const SMDS_MeshNode*>& theNodesToInsert,
9145                                            const bool                  toCreatePoly)
9146 {
9147   if ( theFace->GetType() != SMDSAbs_Face ) return;
9148
9149   // find indices of 2 link nodes and of the rest nodes
9150   int iNode = 0, il1, il2, i3, i4;
9151   il1 = il2 = i3 = i4 = -1;
9152   //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
9153   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
9154
9155   if(theFace->IsQuadratic()) {
9156     const SMDS_VtkFace* F =
9157       dynamic_cast<const SMDS_VtkFace*>(theFace);
9158     if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9159     // use special nodes iterator
9160     SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9161     while( anIter->more() ) {
9162       const SMDS_MeshNode* n = cast2Node(anIter->next());
9163       if ( n == theBetweenNode1 )
9164         il1 = iNode;
9165       else if ( n == theBetweenNode2 )
9166         il2 = iNode;
9167       else if ( i3 < 0 )
9168         i3 = iNode;
9169       else
9170         i4 = iNode;
9171       nodes[ iNode++ ] = n;
9172     }
9173   }
9174   else {
9175     SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9176     while ( nodeIt->more() ) {
9177       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9178       if ( n == theBetweenNode1 )
9179         il1 = iNode;
9180       else if ( n == theBetweenNode2 )
9181         il2 = iNode;
9182       else if ( i3 < 0 )
9183         i3 = iNode;
9184       else
9185         i4 = iNode;
9186       nodes[ iNode++ ] = n;
9187     }
9188   }
9189   if ( il1 < 0 || il2 < 0 || i3 < 0 )
9190     return ;
9191
9192   // arrange link nodes to go one after another regarding the face orientation
9193   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
9194   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
9195   if ( reverse ) {
9196     iNode = il1;
9197     il1 = il2;
9198     il2 = iNode;
9199     aNodesToInsert.reverse();
9200   }
9201   // check that not link nodes of a quadrangles are in good order
9202   int nbFaceNodes = theFace->NbNodes();
9203   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
9204     iNode = i3;
9205     i3 = i4;
9206     i4 = iNode;
9207   }
9208
9209   if (toCreatePoly || theFace->IsPoly()) {
9210
9211     iNode = 0;
9212     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
9213
9214     // add nodes of face up to first node of link
9215     bool isFLN = false;
9216
9217     if(theFace->IsQuadratic()) {
9218       const SMDS_VtkFace* F =
9219         dynamic_cast<const SMDS_VtkFace*>(theFace);
9220       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9221       // use special nodes iterator
9222       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9223       while( anIter->more()  && !isFLN ) {
9224         const SMDS_MeshNode* n = cast2Node(anIter->next());
9225         poly_nodes[iNode++] = n;
9226         if (n == nodes[il1]) {
9227           isFLN = true;
9228         }
9229       }
9230       // add nodes to insert
9231       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9232       for (; nIt != aNodesToInsert.end(); nIt++) {
9233         poly_nodes[iNode++] = *nIt;
9234       }
9235       // add nodes of face starting from last node of link
9236       while ( anIter->more() ) {
9237         poly_nodes[iNode++] = cast2Node(anIter->next());
9238       }
9239     }
9240     else {
9241       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9242       while ( nodeIt->more() && !isFLN ) {
9243         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9244         poly_nodes[iNode++] = n;
9245         if (n == nodes[il1]) {
9246           isFLN = true;
9247         }
9248       }
9249       // add nodes to insert
9250       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9251       for (; nIt != aNodesToInsert.end(); nIt++) {
9252         poly_nodes[iNode++] = *nIt;
9253       }
9254       // add nodes of face starting from last node of link
9255       while ( nodeIt->more() ) {
9256         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9257         poly_nodes[iNode++] = n;
9258       }
9259     }
9260
9261     // edit or replace the face
9262     SMESHDS_Mesh *aMesh = GetMeshDS();
9263
9264     if (theFace->IsPoly()) {
9265       aMesh->ChangePolygonNodes(theFace, poly_nodes);
9266     }
9267     else {
9268       int aShapeId = FindShape( theFace );
9269
9270       SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
9271       myLastCreatedElems.Append(newElem);
9272       if ( aShapeId && newElem )
9273         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9274
9275       aMesh->RemoveElement(theFace);
9276     }
9277     return;
9278   }
9279
9280   SMESHDS_Mesh *aMesh = GetMeshDS();
9281   if( !theFace->IsQuadratic() ) {
9282
9283     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
9284     int nbLinkNodes = 2 + aNodesToInsert.size();
9285     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
9286     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
9287     linkNodes[ 0 ] = nodes[ il1 ];
9288     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
9289     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9290     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9291       linkNodes[ iNode++ ] = *nIt;
9292     }
9293     // decide how to split a quadrangle: compare possible variants
9294     // and choose which of splits to be a quadrangle
9295     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
9296     if ( nbFaceNodes == 3 ) {
9297       iBestQuad = nbSplits;
9298       i4 = i3;
9299     }
9300     else if ( nbFaceNodes == 4 ) {
9301       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
9302       double aBestRate = DBL_MAX;
9303       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
9304         i1 = 0; i2 = 1;
9305         double aBadRate = 0;
9306         // evaluate elements quality
9307         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
9308           if ( iSplit == iQuad ) {
9309             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
9310                                    linkNodes[ i2++ ],
9311                                    nodes[ i3 ],
9312                                    nodes[ i4 ]);
9313             aBadRate += getBadRate( &quad, aCrit );
9314           }
9315           else {
9316             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
9317                                    linkNodes[ i2++ ],
9318                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
9319             aBadRate += getBadRate( &tria, aCrit );
9320           }
9321         }
9322         // choice
9323         if ( aBadRate < aBestRate ) {
9324           iBestQuad = iQuad;
9325           aBestRate = aBadRate;
9326         }
9327       }
9328     }
9329
9330     // create new elements
9331     int aShapeId = FindShape( theFace );
9332
9333     i1 = 0; i2 = 1;
9334     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
9335       SMDS_MeshElement* newElem = 0;
9336       if ( iSplit == iBestQuad )
9337         newElem = aMesh->AddFace (linkNodes[ i1++ ],
9338                                   linkNodes[ i2++ ],
9339                                   nodes[ i3 ],
9340                                   nodes[ i4 ]);
9341       else
9342         newElem = aMesh->AddFace (linkNodes[ i1++ ],
9343                                   linkNodes[ i2++ ],
9344                                   nodes[ iSplit < iBestQuad ? i4 : i3 ]);
9345       myLastCreatedElems.Append(newElem);
9346       if ( aShapeId && newElem )
9347         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9348     }
9349
9350     // change nodes of theFace
9351     const SMDS_MeshNode* newNodes[ 4 ];
9352     newNodes[ 0 ] = linkNodes[ i1 ];
9353     newNodes[ 1 ] = linkNodes[ i2 ];
9354     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9355     newNodes[ 3 ] = nodes[ i4 ];
9356     //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
9357     const SMDS_MeshElement* newElem = 0;
9358     if (iSplit == iBestQuad)
9359       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
9360     else
9361       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
9362     myLastCreatedElems.Append(newElem);
9363     if ( aShapeId && newElem )
9364       aMesh->SetMeshElementOnShape( newElem, aShapeId );
9365 } // end if(!theFace->IsQuadratic())
9366   else { // theFace is quadratic
9367     // we have to split theFace on simple triangles and one simple quadrangle
9368     int tmp = il1/2;
9369     int nbshift = tmp*2;
9370     // shift nodes in nodes[] by nbshift
9371     int i,j;
9372     for(i=0; i<nbshift; i++) {
9373       const SMDS_MeshNode* n = nodes[0];
9374       for(j=0; j<nbFaceNodes-1; j++) {
9375         nodes[j] = nodes[j+1];
9376       }
9377       nodes[nbFaceNodes-1] = n;
9378     }
9379     il1 = il1 - nbshift;
9380     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9381     //   n0      n1     n2    n0      n1     n2
9382     //     +-----+-----+        +-----+-----+
9383     //      \         /         |           |
9384     //       \       /          |           |
9385     //      n5+     +n3       n7+           +n3
9386     //         \   /            |           |
9387     //          \ /             |           |
9388     //           +              +-----+-----+
9389     //           n4           n6      n5     n4
9390
9391     // create new elements
9392     int aShapeId = FindShape( theFace );
9393
9394     int n1,n2,n3;
9395     if(nbFaceNodes==6) { // quadratic triangle
9396       SMDS_MeshElement* newElem =
9397         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9398       myLastCreatedElems.Append(newElem);
9399       if ( aShapeId && newElem )
9400         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9401       if(theFace->IsMediumNode(nodes[il1])) {
9402         // create quadrangle
9403         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
9404         myLastCreatedElems.Append(newElem);
9405         if ( aShapeId && newElem )
9406           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9407         n1 = 1;
9408         n2 = 2;
9409         n3 = 3;
9410       }
9411       else {
9412         // create quadrangle
9413         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
9414         myLastCreatedElems.Append(newElem);
9415         if ( aShapeId && newElem )
9416           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9417         n1 = 0;
9418         n2 = 1;
9419         n3 = 5;
9420       }
9421     }
9422     else { // nbFaceNodes==8 - quadratic quadrangle
9423       SMDS_MeshElement* newElem =
9424         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9425       myLastCreatedElems.Append(newElem);
9426       if ( aShapeId && newElem )
9427         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9428       newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
9429       myLastCreatedElems.Append(newElem);
9430       if ( aShapeId && newElem )
9431         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9432       newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
9433       myLastCreatedElems.Append(newElem);
9434       if ( aShapeId && newElem )
9435         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9436       if(theFace->IsMediumNode(nodes[il1])) {
9437         // create quadrangle
9438         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
9439         myLastCreatedElems.Append(newElem);
9440         if ( aShapeId && newElem )
9441           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9442         n1 = 1;
9443         n2 = 2;
9444         n3 = 3;
9445       }
9446       else {
9447         // create quadrangle
9448         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
9449         myLastCreatedElems.Append(newElem);
9450         if ( aShapeId && newElem )
9451           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9452         n1 = 0;
9453         n2 = 1;
9454         n3 = 7;
9455       }
9456     }
9457     // create needed triangles using n1,n2,n3 and inserted nodes
9458     int nbn = 2 + aNodesToInsert.size();
9459     //const SMDS_MeshNode* aNodes[nbn];
9460     vector<const SMDS_MeshNode*> aNodes(nbn);
9461     aNodes[0] = nodes[n1];
9462     aNodes[nbn-1] = nodes[n2];
9463     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9464     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9465       aNodes[iNode++] = *nIt;
9466     }
9467     for(i=1; i<nbn; i++) {
9468       SMDS_MeshElement* newElem =
9469         aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
9470       myLastCreatedElems.Append(newElem);
9471       if ( aShapeId && newElem )
9472         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9473     }
9474   }
9475   // remove old face
9476   aMesh->RemoveElement(theFace);
9477 }
9478
9479 //=======================================================================
9480 //function : UpdateVolumes
9481 //purpose  :
9482 //=======================================================================
9483 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
9484                                       const SMDS_MeshNode*        theBetweenNode2,
9485                                       list<const SMDS_MeshNode*>& theNodesToInsert)
9486 {
9487   myLastCreatedElems.Clear();
9488   myLastCreatedNodes.Clear();
9489
9490   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9491   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9492     const SMDS_MeshElement* elem = invElemIt->next();
9493
9494     // check, if current volume has link theBetweenNode1 - theBetweenNode2
9495     SMDS_VolumeTool aVolume (elem);
9496     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9497       continue;
9498
9499     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9500     int iface, nbFaces = aVolume.NbFaces();
9501     vector<const SMDS_MeshNode *> poly_nodes;
9502     vector<int> quantities (nbFaces);
9503
9504     for (iface = 0; iface < nbFaces; iface++) {
9505       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9506       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9507       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9508
9509       for (int inode = 0; inode < nbFaceNodes; inode++) {
9510         poly_nodes.push_back(faceNodes[inode]);
9511
9512         if (nbInserted == 0) {
9513           if (faceNodes[inode] == theBetweenNode1) {
9514             if (faceNodes[inode + 1] == theBetweenNode2) {
9515               nbInserted = theNodesToInsert.size();
9516
9517               // add nodes to insert
9518               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9519               for (; nIt != theNodesToInsert.end(); nIt++) {
9520                 poly_nodes.push_back(*nIt);
9521               }
9522             }
9523           }
9524           else if (faceNodes[inode] == theBetweenNode2) {
9525             if (faceNodes[inode + 1] == theBetweenNode1) {
9526               nbInserted = theNodesToInsert.size();
9527
9528               // add nodes to insert in reversed order
9529               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9530               nIt--;
9531               for (; nIt != theNodesToInsert.begin(); nIt--) {
9532                 poly_nodes.push_back(*nIt);
9533               }
9534               poly_nodes.push_back(*nIt);
9535             }
9536           }
9537           else {
9538           }
9539         }
9540       }
9541       quantities[iface] = nbFaceNodes + nbInserted;
9542     }
9543
9544     // Replace or update the volume
9545     SMESHDS_Mesh *aMesh = GetMeshDS();
9546
9547     if (elem->IsPoly()) {
9548       aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
9549
9550     }
9551     else {
9552       int aShapeId = FindShape( elem );
9553
9554       SMDS_MeshElement* newElem =
9555         aMesh->AddPolyhedralVolume(poly_nodes, quantities);
9556       myLastCreatedElems.Append(newElem);
9557       if (aShapeId && newElem)
9558         aMesh->SetMeshElementOnShape(newElem, aShapeId);
9559
9560       aMesh->RemoveElement(elem);
9561     }
9562   }
9563 }
9564
9565 namespace
9566 {
9567   //================================================================================
9568   /*!
9569    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9570    */
9571   //================================================================================
9572
9573   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9574                            vector<const SMDS_MeshNode *> & nodes,
9575                            vector<int> &                   nbNodeInFaces )
9576   {
9577     nodes.clear();
9578     nbNodeInFaces.clear();
9579     SMDS_VolumeTool vTool ( elem );
9580     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9581     {
9582       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9583       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9584       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9585     }
9586   }
9587 }
9588
9589 //=======================================================================
9590 /*!
9591  * \brief Convert elements contained in a submesh to quadratic
9592  * \return int - nb of checked elements
9593  */
9594 //=======================================================================
9595
9596 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9597                                              SMESH_MesherHelper& theHelper,
9598                                              const bool          theForce3d)
9599 {
9600   int nbElem = 0;
9601   if( !theSm ) return nbElem;
9602
9603   vector<int> nbNodeInFaces;
9604   vector<const SMDS_MeshNode *> nodes;
9605   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9606   while(ElemItr->more())
9607   {
9608     nbElem++;
9609     const SMDS_MeshElement* elem = ElemItr->next();
9610     if( !elem ) continue;
9611
9612     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9613     if ( elem->IsQuadratic() )
9614     {
9615       bool alreadyOK;
9616       switch ( aGeomType ) {
9617       case SMDSEntity_Quad_Quadrangle:
9618       case SMDSEntity_Quad_Hexa:         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9619       case SMDSEntity_BiQuad_Quadrangle:
9620       case SMDSEntity_TriQuad_Hexa:      alreadyOK = theHelper.GetIsBiQuadratic(); break;
9621       default:                           alreadyOK = true;
9622       }
9623       if ( alreadyOK ) continue;
9624     }
9625     // get elem data needed to re-create it
9626     //
9627     const int id                     = elem->GetID();
9628     const int nbNodes                = elem->NbCornerNodes();
9629     const SMDSAbs_ElementType aType  = elem->GetType();
9630     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9631     if ( aGeomType == SMDSEntity_Polyhedra )
9632       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9633     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9634       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9635
9636     // remove a linear element
9637     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9638
9639     const SMDS_MeshElement* NewElem = 0;
9640
9641     switch( aType )
9642     {
9643     case SMDSAbs_Edge :
9644       {
9645         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9646         break;
9647       }
9648     case SMDSAbs_Face :
9649       {
9650         switch(nbNodes)
9651         {
9652         case 3:
9653           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9654           break;
9655         case 4:
9656           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9657           break;
9658         default:
9659           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9660           continue;
9661         }
9662         break;
9663       }
9664     case SMDSAbs_Volume :
9665       {
9666         switch( aGeomType )
9667         {
9668         case SMDSEntity_Tetra:
9669           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9670           break;
9671         case SMDSEntity_Pyramid:
9672           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9673           break;
9674         case SMDSEntity_Penta:
9675           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9676           break;
9677         case SMDSEntity_Hexa:
9678         case SMDSEntity_Quad_Hexa:
9679         case SMDSEntity_TriQuad_Hexa:
9680           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9681                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9682           break;
9683         case SMDSEntity_Hexagonal_Prism:
9684         default:
9685           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9686         }
9687         break;
9688       }
9689     default :
9690       continue;
9691     }
9692     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9693     if( NewElem )
9694       theSm->AddElement( NewElem );
9695   }
9696   return nbElem;
9697 }
9698 //=======================================================================
9699 //function : ConvertToQuadratic
9700 //purpose  :
9701 //=======================================================================
9702
9703 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9704 {
9705   SMESHDS_Mesh* meshDS = GetMeshDS();
9706
9707   SMESH_MesherHelper aHelper(*myMesh);
9708
9709   aHelper.SetIsQuadratic( true );
9710   aHelper.SetIsBiQuadratic( theToBiQuad );
9711   aHelper.SetElementsOnShape(true);
9712
9713   int nbCheckedElems = 0;
9714   if ( myMesh->HasShapeToMesh() )
9715   {
9716     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9717     {
9718       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9719       while ( smIt->more() ) {
9720         SMESH_subMesh* sm = smIt->next();
9721         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9722           aHelper.SetSubShape( sm->GetSubShape() );
9723           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9724         }
9725       }
9726     }
9727   }
9728   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9729   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9730   {
9731     aHelper.SetElementsOnShape(false);
9732     SMESHDS_SubMesh *smDS = 0;
9733     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9734     while(aEdgeItr->more())
9735     {
9736       const SMDS_MeshEdge* edge = aEdgeItr->next();
9737       if(edge && !edge->IsQuadratic())
9738       {
9739         int id = edge->GetID();
9740         //MESSAGE("edge->GetID() " << id);
9741         const SMDS_MeshNode* n1 = edge->GetNode(0);
9742         const SMDS_MeshNode* n2 = edge->GetNode(1);
9743
9744         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9745
9746         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9747         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9748       }
9749     }
9750     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9751     while(aFaceItr->more())
9752     {
9753       const SMDS_MeshFace* face = aFaceItr->next();
9754       if ( !face ) continue;
9755       
9756       const SMDSAbs_EntityType type = face->GetEntityType();
9757       if (( theToBiQuad  && type == SMDSEntity_BiQuad_Quadrangle ) ||
9758           ( !theToBiQuad && type == SMDSEntity_Quad_Quadrangle ))
9759         continue;
9760
9761       const int id = face->GetID();
9762       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9763
9764       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9765
9766       SMDS_MeshFace * NewFace = 0;
9767       switch( type )
9768       {
9769       case SMDSEntity_Triangle:
9770         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9771         break;
9772       case SMDSEntity_Quadrangle:
9773         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9774         break;
9775       default:
9776         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9777       }
9778       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9779     }
9780     vector<int> nbNodeInFaces;
9781     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9782     while(aVolumeItr->more())
9783     {
9784       const SMDS_MeshVolume* volume = aVolumeItr->next();
9785       if(!volume || volume->IsQuadratic() ) continue;
9786
9787       const SMDSAbs_EntityType type = volume->GetEntityType();
9788       if (( theToBiQuad  && type == SMDSEntity_TriQuad_Hexa ) ||
9789           ( !theToBiQuad && type == SMDSEntity_Quad_Hexa ))
9790         continue;
9791
9792       const int id = volume->GetID();
9793       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9794       if ( type == SMDSEntity_Polyhedra )
9795         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9796       else if ( type == SMDSEntity_Hexagonal_Prism )
9797         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9798
9799       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9800
9801       SMDS_MeshVolume * NewVolume = 0;
9802       switch ( type )
9803       {
9804       case SMDSEntity_Tetra:
9805         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9806         break;
9807       case SMDSEntity_Hexa:
9808       case SMDSEntity_Quad_Hexa:
9809       case SMDSEntity_TriQuad_Hexa:
9810         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9811                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9812         break;
9813       case SMDSEntity_Pyramid:
9814         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9815                                       nodes[3], nodes[4], id, theForce3d);
9816         break;
9817       case SMDSEntity_Penta:
9818         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9819                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9820         break;
9821       case SMDSEntity_Hexagonal_Prism:
9822       default:
9823         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9824       }
9825       ReplaceElemInGroups(volume, NewVolume, meshDS);
9826     }
9827   }
9828
9829   if ( !theForce3d )
9830   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9831     aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9832     aHelper.FixQuadraticElements(myError);
9833   }
9834 }
9835
9836 //================================================================================
9837 /*!
9838  * \brief Makes given elements quadratic
9839  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9840  *  \param theElements - elements to make quadratic
9841  */
9842 //================================================================================
9843
9844 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9845                                           TIDSortedElemSet& theElements,
9846                                           const bool        theToBiQuad)
9847 {
9848   if ( theElements.empty() ) return;
9849
9850   // we believe that all theElements are of the same type
9851   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9852
9853   // get all nodes shared by theElements
9854   TIDSortedNodeSet allNodes;
9855   TIDSortedElemSet::iterator eIt = theElements.begin();
9856   for ( ; eIt != theElements.end(); ++eIt )
9857     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9858
9859   // complete theElements with elements of lower dim whose all nodes are in allNodes
9860
9861   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9862   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9863   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9864   for ( ; nIt != allNodes.end(); ++nIt )
9865   {
9866     const SMDS_MeshNode* n = *nIt;
9867     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9868     while ( invIt->more() )
9869     {
9870       const SMDS_MeshElement* e = invIt->next();
9871       if ( e->IsQuadratic() )
9872       {
9873         bool alreadyOK;
9874         switch ( e->GetEntityType() ) {
9875         case SMDSEntity_Quad_Quadrangle:
9876         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9877         case SMDSEntity_BiQuad_Quadrangle:
9878         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9879         default:                           alreadyOK = true;
9880         }
9881         if ( alreadyOK )
9882         {
9883           quadAdjacentElems[ e->GetType() ].insert( e );
9884           continue;
9885         }
9886       }
9887       if ( e->GetType() >= elemType )
9888       {
9889         continue; // same type of more complex linear element
9890       }
9891
9892       if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9893         continue; // e is already checked
9894
9895       // check nodes
9896       bool allIn = true;
9897       SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9898       while ( nodeIt->more() && allIn )
9899         allIn = allNodes.count( cast2Node( nodeIt->next() ));
9900       if ( allIn )
9901         theElements.insert(e );
9902     }
9903   }
9904
9905   SMESH_MesherHelper helper(*myMesh);
9906   helper.SetIsQuadratic( true );
9907   helper.SetIsBiQuadratic( theToBiQuad );
9908
9909   // add links of quadratic adjacent elements to the helper
9910
9911   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9912     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9913           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9914     {
9915       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9916     }
9917   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9918     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9919           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9920     {
9921       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9922     }
9923   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9924     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9925           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9926     {
9927       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9928     }
9929
9930   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9931
9932   SMESHDS_Mesh*  meshDS = GetMeshDS();
9933   SMESHDS_SubMesh* smDS = 0;
9934   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9935   {
9936     const SMDS_MeshElement* elem = *eIt;
9937     if( elem->NbNodes() < 2 || elem->IsPoly() )
9938       continue;
9939
9940     if ( elem->IsQuadratic() )
9941     {
9942       bool alreadyOK;
9943       switch ( elem->GetEntityType() ) {
9944       case SMDSEntity_Quad_Quadrangle:
9945       case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9946       case SMDSEntity_BiQuad_Quadrangle:
9947       case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9948       default:                           alreadyOK = true;
9949       }
9950       if ( alreadyOK ) continue;
9951     }
9952
9953     const SMDSAbs_ElementType type = elem->GetType();
9954     const int                   id = elem->GetID();
9955     const int              nbNodes = elem->NbCornerNodes();
9956     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9957
9958     if ( !smDS || !smDS->Contains( elem ))
9959       smDS = meshDS->MeshElements( elem->getshapeId() );
9960     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9961
9962     SMDS_MeshElement * newElem = 0;
9963     switch( nbNodes )
9964     {
9965     case 4: // cases for most frequently used element types go first (for optimization)
9966       if ( type == SMDSAbs_Volume )
9967         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9968       else
9969         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9970       break;
9971     case 8:
9972       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9973                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9974       break;
9975     case 3:
9976       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9977       break;
9978     case 2:
9979       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9980       break;
9981     case 5:
9982       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9983                                  nodes[4], id, theForce3d);
9984       break;
9985     case 6:
9986       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9987                                  nodes[4], nodes[5], id, theForce3d);
9988       break;
9989     default:;
9990     }
9991     ReplaceElemInGroups( elem, newElem, meshDS);
9992     if( newElem && smDS )
9993       smDS->AddElement( newElem );
9994   }
9995
9996   if ( !theForce3d  && !getenv("NO_FixQuadraticElements"))
9997   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9998     helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9999     helper.FixQuadraticElements( myError );
10000   }
10001 }
10002
10003 //=======================================================================
10004 /*!
10005  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
10006  * \return int - nb of checked elements
10007  */
10008 //=======================================================================
10009
10010 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
10011                                      SMDS_ElemIteratorPtr theItr,
10012                                      const int            theShapeID)
10013 {
10014   int nbElem = 0;
10015   SMESHDS_Mesh* meshDS = GetMeshDS();
10016
10017   while( theItr->more() )
10018   {
10019     const SMDS_MeshElement* elem = theItr->next();
10020     nbElem++;
10021     if( elem && elem->IsQuadratic())
10022     {
10023       int id                    = elem->GetID();
10024       int nbCornerNodes         = elem->NbCornerNodes();
10025       SMDSAbs_ElementType aType = elem->GetType();
10026
10027       vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
10028
10029       //remove a quadratic element
10030       if ( !theSm || !theSm->Contains( elem ))
10031         theSm = meshDS->MeshElements( elem->getshapeId() );
10032       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
10033
10034       // remove medium nodes
10035       for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
10036         if ( nodes[i]->NbInverseElements() == 0 )
10037           meshDS->RemoveFreeNode( nodes[i], theSm );
10038
10039       // add a linear element
10040       nodes.resize( nbCornerNodes );
10041       SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
10042       ReplaceElemInGroups(elem, newElem, meshDS);
10043       if( theSm && newElem )
10044         theSm->AddElement( newElem );
10045     }
10046   }
10047   return nbElem;
10048 }
10049
10050 //=======================================================================
10051 //function : ConvertFromQuadratic
10052 //purpose  :
10053 //=======================================================================
10054
10055 bool SMESH_MeshEditor::ConvertFromQuadratic()
10056 {
10057   int nbCheckedElems = 0;
10058   if ( myMesh->HasShapeToMesh() )
10059   {
10060     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
10061     {
10062       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
10063       while ( smIt->more() ) {
10064         SMESH_subMesh* sm = smIt->next();
10065         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
10066           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
10067       }
10068     }
10069   }
10070
10071   int totalNbElems =
10072     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
10073   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
10074   {
10075     SMESHDS_SubMesh *aSM = 0;
10076     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
10077   }
10078
10079   return true;
10080 }
10081
10082 namespace
10083 {
10084   //================================================================================
10085   /*!
10086    * \brief Return true if all medium nodes of the element are in the node set
10087    */
10088   //================================================================================
10089
10090   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
10091   {
10092     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
10093       if ( !nodeSet.count( elem->GetNode(i) ))
10094         return false;
10095     return true;
10096   }
10097 }
10098
10099 //================================================================================
10100 /*!
10101  * \brief Makes given elements linear
10102  */
10103 //================================================================================
10104
10105 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
10106 {
10107   if ( theElements.empty() ) return;
10108
10109   // collect IDs of medium nodes of theElements; some of these nodes will be removed
10110   set<int> mediumNodeIDs;
10111   TIDSortedElemSet::iterator eIt = theElements.begin();
10112   for ( ; eIt != theElements.end(); ++eIt )
10113   {
10114     const SMDS_MeshElement* e = *eIt;
10115     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
10116       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
10117   }
10118
10119   // replace given elements by linear ones
10120   typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
10121   SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
10122   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10123
10124   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
10125   // except those elements sharing medium nodes of quadratic element whose medium nodes
10126   // are not all in mediumNodeIDs
10127
10128   // get remaining medium nodes
10129   TIDSortedNodeSet mediumNodes;
10130   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
10131   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
10132     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
10133       mediumNodes.insert( mediumNodes.end(), n );
10134
10135   // find more quadratic elements to convert
10136   TIDSortedElemSet moreElemsToConvert;
10137   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
10138   for ( ; nIt != mediumNodes.end(); ++nIt )
10139   {
10140     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
10141     while ( invIt->more() )
10142     {
10143       const SMDS_MeshElement* e = invIt->next();
10144       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
10145       {
10146         // find a more complex element including e and
10147         // whose medium nodes are not in mediumNodes
10148         bool complexFound = false;
10149         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
10150         {
10151           SMDS_ElemIteratorPtr invIt2 =
10152             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
10153           while ( invIt2->more() )
10154           {
10155             const SMDS_MeshElement* eComplex = invIt2->next();
10156             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
10157             {
10158               int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
10159               if ( nbCommonNodes == e->NbNodes())
10160               {
10161                 complexFound = true;
10162                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
10163                 break;
10164               }
10165             }
10166           }
10167         }
10168         if ( !complexFound )
10169           moreElemsToConvert.insert( e );
10170       }
10171     }
10172   }
10173   elemIt = SMDS_ElemIteratorPtr
10174     (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
10175   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10176 }
10177
10178 //=======================================================================
10179 //function : SewSideElements
10180 //purpose  :
10181 //=======================================================================
10182
10183 SMESH_MeshEditor::Sew_Error
10184 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
10185                                    TIDSortedElemSet&    theSide2,
10186                                    const SMDS_MeshNode* theFirstNode1,
10187                                    const SMDS_MeshNode* theFirstNode2,
10188                                    const SMDS_MeshNode* theSecondNode1,
10189                                    const SMDS_MeshNode* theSecondNode2)
10190 {
10191   myLastCreatedElems.Clear();
10192   myLastCreatedNodes.Clear();
10193
10194   MESSAGE ("::::SewSideElements()");
10195   if ( theSide1.size() != theSide2.size() )
10196     return SEW_DIFF_NB_OF_ELEMENTS;
10197
10198   Sew_Error aResult = SEW_OK;
10199   // Algo:
10200   // 1. Build set of faces representing each side
10201   // 2. Find which nodes of the side 1 to merge with ones on the side 2
10202   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10203
10204   // =======================================================================
10205   // 1. Build set of faces representing each side:
10206   // =======================================================================
10207   // a. build set of nodes belonging to faces
10208   // b. complete set of faces: find missing faces whose nodes are in set of nodes
10209   // c. create temporary faces representing side of volumes if correspondent
10210   //    face does not exist
10211
10212   SMESHDS_Mesh* aMesh = GetMeshDS();
10213   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
10214   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
10215   TIDSortedElemSet             faceSet1, faceSet2;
10216   set<const SMDS_MeshElement*> volSet1,  volSet2;
10217   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
10218   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
10219   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
10220   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
10221   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
10222   int iSide, iFace, iNode;
10223
10224   list<const SMDS_MeshElement* > tempFaceList;
10225   for ( iSide = 0; iSide < 2; iSide++ ) {
10226     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
10227     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
10228     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
10229     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
10230     set<const SMDS_MeshElement*>::iterator vIt;
10231     TIDSortedElemSet::iterator eIt;
10232     set<const SMDS_MeshNode*>::iterator    nIt;
10233
10234     // check that given nodes belong to given elements
10235     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
10236     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
10237     int firstIndex = -1, secondIndex = -1;
10238     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10239       const SMDS_MeshElement* elem = *eIt;
10240       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
10241       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
10242       if ( firstIndex > -1 && secondIndex > -1 ) break;
10243     }
10244     if ( firstIndex < 0 || secondIndex < 0 ) {
10245       // we can simply return until temporary faces created
10246       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
10247     }
10248
10249     // -----------------------------------------------------------
10250     // 1a. Collect nodes of existing faces
10251     //     and build set of face nodes in order to detect missing
10252     //     faces corresponding to sides of volumes
10253     // -----------------------------------------------------------
10254
10255     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
10256
10257     // loop on the given element of a side
10258     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10259       //const SMDS_MeshElement* elem = *eIt;
10260       const SMDS_MeshElement* elem = *eIt;
10261       if ( elem->GetType() == SMDSAbs_Face ) {
10262         faceSet->insert( elem );
10263         set <const SMDS_MeshNode*> faceNodeSet;
10264         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10265         while ( nodeIt->more() ) {
10266           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10267           nodeSet->insert( n );
10268           faceNodeSet.insert( n );
10269         }
10270         setOfFaceNodeSet.insert( faceNodeSet );
10271       }
10272       else if ( elem->GetType() == SMDSAbs_Volume )
10273         volSet->insert( elem );
10274     }
10275     // ------------------------------------------------------------------------------
10276     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10277     // ------------------------------------------------------------------------------
10278
10279     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10280       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10281       while ( fIt->more() ) { // loop on faces sharing a node
10282         const SMDS_MeshElement* f = fIt->next();
10283         if ( faceSet->find( f ) == faceSet->end() ) {
10284           // check if all nodes are in nodeSet and
10285           // complete setOfFaceNodeSet if they are
10286           set <const SMDS_MeshNode*> faceNodeSet;
10287           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10288           bool allInSet = true;
10289           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10290             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10291             if ( nodeSet->find( n ) == nodeSet->end() )
10292               allInSet = false;
10293             else
10294               faceNodeSet.insert( n );
10295           }
10296           if ( allInSet ) {
10297             faceSet->insert( f );
10298             setOfFaceNodeSet.insert( faceNodeSet );
10299           }
10300         }
10301       }
10302     }
10303
10304     // -------------------------------------------------------------------------
10305     // 1c. Create temporary faces representing sides of volumes if correspondent
10306     //     face does not exist
10307     // -------------------------------------------------------------------------
10308
10309     if ( !volSet->empty() ) {
10310       //int nodeSetSize = nodeSet->size();
10311
10312       // loop on given volumes
10313       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10314         SMDS_VolumeTool vol (*vIt);
10315         // loop on volume faces: find free faces
10316         // --------------------------------------
10317         list<const SMDS_MeshElement* > freeFaceList;
10318         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10319           if ( !vol.IsFreeFace( iFace ))
10320             continue;
10321           // check if there is already a face with same nodes in a face set
10322           const SMDS_MeshElement* aFreeFace = 0;
10323           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10324           int nbNodes = vol.NbFaceNodes( iFace );
10325           set <const SMDS_MeshNode*> faceNodeSet;
10326           vol.GetFaceNodes( iFace, faceNodeSet );
10327           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10328           if ( isNewFace ) {
10329             // no such a face is given but it still can exist, check it
10330             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10331             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10332           }
10333           if ( !aFreeFace ) {
10334             // create a temporary face
10335             if ( nbNodes == 3 ) {
10336               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10337               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10338             }
10339             else if ( nbNodes == 4 ) {
10340               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10341               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10342             }
10343             else {
10344               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10345               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10346               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10347             }
10348             if ( aFreeFace )
10349               tempFaceList.push_back( aFreeFace );
10350           }
10351
10352           if ( aFreeFace )
10353             freeFaceList.push_back( aFreeFace );
10354
10355         } // loop on faces of a volume
10356
10357         // choose one of several free faces of a volume
10358         // --------------------------------------------
10359         if ( freeFaceList.size() > 1 ) {
10360           // choose a face having max nb of nodes shared by other elems of a side
10361           int maxNbNodes = -1;
10362           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10363           while ( fIt != freeFaceList.end() ) { // loop on free faces
10364             int nbSharedNodes = 0;
10365             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10366             while ( nodeIt->more() ) { // loop on free face nodes
10367               const SMDS_MeshNode* n =
10368                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10369               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10370               while ( invElemIt->more() ) {
10371                 const SMDS_MeshElement* e = invElemIt->next();
10372                 nbSharedNodes += faceSet->count( e );
10373                 nbSharedNodes += elemSet->count( e );
10374               }
10375             }
10376             if ( nbSharedNodes > maxNbNodes ) {
10377               maxNbNodes = nbSharedNodes;
10378               freeFaceList.erase( freeFaceList.begin(), fIt++ );
10379             }
10380             else if ( nbSharedNodes == maxNbNodes ) {
10381               fIt++;
10382             }
10383             else {
10384               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10385             }
10386           }
10387           if ( freeFaceList.size() > 1 )
10388           {
10389             // could not choose one face, use another way
10390             // choose a face most close to the bary center of the opposite side
10391             gp_XYZ aBC( 0., 0., 0. );
10392             set <const SMDS_MeshNode*> addedNodes;
10393             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10394             eIt = elemSet2->begin();
10395             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10396               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10397               while ( nodeIt->more() ) { // loop on free face nodes
10398                 const SMDS_MeshNode* n =
10399                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10400                 if ( addedNodes.insert( n ).second )
10401                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10402               }
10403             }
10404             aBC /= addedNodes.size();
10405             double minDist = DBL_MAX;
10406             fIt = freeFaceList.begin();
10407             while ( fIt != freeFaceList.end() ) { // loop on free faces
10408               double dist = 0;
10409               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10410               while ( nodeIt->more() ) { // loop on free face nodes
10411                 const SMDS_MeshNode* n =
10412                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10413                 gp_XYZ p( n->X(),n->Y(),n->Z() );
10414                 dist += ( aBC - p ).SquareModulus();
10415               }
10416               if ( dist < minDist ) {
10417                 minDist = dist;
10418                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10419               }
10420               else
10421                 fIt = freeFaceList.erase( fIt++ );
10422             }
10423           }
10424         } // choose one of several free faces of a volume
10425
10426         if ( freeFaceList.size() == 1 ) {
10427           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10428           faceSet->insert( aFreeFace );
10429           // complete a node set with nodes of a found free face
10430           //           for ( iNode = 0; iNode < ; iNode++ )
10431           //             nodeSet->insert( fNodes[ iNode ] );
10432         }
10433
10434       } // loop on volumes of a side
10435
10436       //       // complete a set of faces if new nodes in a nodeSet appeared
10437       //       // ----------------------------------------------------------
10438       //       if ( nodeSetSize != nodeSet->size() ) {
10439       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10440       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10441       //           while ( fIt->more() ) { // loop on faces sharing a node
10442       //             const SMDS_MeshElement* f = fIt->next();
10443       //             if ( faceSet->find( f ) == faceSet->end() ) {
10444       //               // check if all nodes are in nodeSet and
10445       //               // complete setOfFaceNodeSet if they are
10446       //               set <const SMDS_MeshNode*> faceNodeSet;
10447       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10448       //               bool allInSet = true;
10449       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10450       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10451       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10452       //                   allInSet = false;
10453       //                 else
10454       //                   faceNodeSet.insert( n );
10455       //               }
10456       //               if ( allInSet ) {
10457       //                 faceSet->insert( f );
10458       //                 setOfFaceNodeSet.insert( faceNodeSet );
10459       //               }
10460       //             }
10461       //           }
10462       //         }
10463       //       }
10464     } // Create temporary faces, if there are volumes given
10465   } // loop on sides
10466
10467   if ( faceSet1.size() != faceSet2.size() ) {
10468     // delete temporary faces: they are in reverseElements of actual nodes
10469 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10470 //    while ( tmpFaceIt->more() )
10471 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10472 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10473 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10474 //      aMesh->RemoveElement(*tmpFaceIt);
10475     MESSAGE("Diff nb of faces");
10476     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10477   }
10478
10479   // ============================================================
10480   // 2. Find nodes to merge:
10481   //              bind a node to remove to a node to put instead
10482   // ============================================================
10483
10484   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10485   if ( theFirstNode1 != theFirstNode2 )
10486     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10487   if ( theSecondNode1 != theSecondNode2 )
10488     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10489
10490   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10491   set< long > linkIdSet; // links to process
10492   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10493
10494   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10495   list< NLink > linkList[2];
10496   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10497   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10498   // loop on links in linkList; find faces by links and append links
10499   // of the found faces to linkList
10500   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10501   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10502   {
10503     NLink link[] = { *linkIt[0], *linkIt[1] };
10504     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10505     if ( !linkIdSet.count( linkID ) )
10506       continue;
10507
10508     // by links, find faces in the face sets,
10509     // and find indices of link nodes in the found faces;
10510     // in a face set, there is only one or no face sharing a link
10511     // ---------------------------------------------------------------
10512
10513     const SMDS_MeshElement* face[] = { 0, 0 };
10514     vector<const SMDS_MeshNode*> fnodes[2];
10515     int iLinkNode[2][2];
10516     TIDSortedElemSet avoidSet;
10517     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10518       const SMDS_MeshNode* n1 = link[iSide].first;
10519       const SMDS_MeshNode* n2 = link[iSide].second;
10520       //cout << "Side " << iSide << " ";
10521       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10522       // find a face by two link nodes
10523       face[ iSide ] = FindFaceInSet( n1, n2, *faceSetPtr[ iSide ], avoidSet,
10524                                      &iLinkNode[iSide][0], &iLinkNode[iSide][1] );
10525       if ( face[ iSide ])
10526       {
10527         //cout << " F " << face[ iSide]->GetID() <<endl;
10528         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10529         // put face nodes to fnodes
10530         if ( face[ iSide ]->IsQuadratic() )
10531         {
10532           // use interlaced nodes iterator
10533           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10534           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10535           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10536           while ( nIter->more() )
10537             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10538         }
10539         else
10540         {
10541           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10542                                   face[ iSide ]->end_nodes() );
10543         }
10544         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10545       }
10546     }
10547
10548     // check similarity of elements of the sides
10549     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10550       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10551       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10552         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10553       }
10554       else {
10555         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10556       }
10557       break; // do not return because it's necessary to remove tmp faces
10558     }
10559
10560     // set nodes to merge
10561     // -------------------
10562
10563     if ( face[0] && face[1] )  {
10564       const int nbNodes = face[0]->NbNodes();
10565       if ( nbNodes != face[1]->NbNodes() ) {
10566         MESSAGE("Diff nb of face nodes");
10567         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10568         break; // do not return because it s necessary to remove tmp faces
10569       }
10570       bool reverse[] = { false, false }; // order of nodes in the link
10571       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10572         // analyse link orientation in faces
10573         int i1 = iLinkNode[ iSide ][ 0 ];
10574         int i2 = iLinkNode[ iSide ][ 1 ];
10575         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10576       }
10577       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10578       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10579       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10580       {
10581         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10582                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10583       }
10584
10585       // add other links of the faces to linkList
10586       // -----------------------------------------
10587
10588       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10589         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10590         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10591         if ( !iter_isnew.second ) { // already in a set: no need to process
10592           linkIdSet.erase( iter_isnew.first );
10593         }
10594         else // new in set == encountered for the first time: add
10595         {
10596           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10597           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10598           linkList[0].push_back ( NLink( n1, n2 ));
10599           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10600         }
10601       }
10602     } // 2 faces found
10603
10604     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10605       break;
10606
10607   } // loop on link lists
10608
10609   if ( aResult == SEW_OK &&
10610        ( //linkIt[0] != linkList[0].end() ||
10611          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10612     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10613              " " << (faceSetPtr[1]->empty()));
10614     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10615   }
10616
10617   // ====================================================================
10618   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10619   // ====================================================================
10620
10621   // delete temporary faces
10622 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10623 //  while ( tmpFaceIt->more() )
10624 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10625   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10626   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10627     aMesh->RemoveElement(*tmpFaceIt);
10628
10629   if ( aResult != SEW_OK)
10630     return aResult;
10631
10632   list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10633   // loop on nodes replacement map
10634   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10635   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10636     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10637       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10638       nodeIDsToRemove.push_back( nToRemove->GetID() );
10639       // loop on elements sharing nToRemove
10640       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10641       while ( invElemIt->more() ) {
10642         const SMDS_MeshElement* e = invElemIt->next();
10643         // get a new suite of nodes: make replacement
10644         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10645         vector< const SMDS_MeshNode*> nodes( nbNodes );
10646         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10647         while ( nIt->more() ) {
10648           const SMDS_MeshNode* n =
10649             static_cast<const SMDS_MeshNode*>( nIt->next() );
10650           nnIt = nReplaceMap.find( n );
10651           if ( nnIt != nReplaceMap.end() ) {
10652             nbReplaced++;
10653             n = (*nnIt).second;
10654           }
10655           nodes[ i++ ] = n;
10656         }
10657         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10658         //         elemIDsToRemove.push_back( e->GetID() );
10659         //       else
10660         if ( nbReplaced )
10661           {
10662             SMDSAbs_ElementType etyp = e->GetType();
10663             SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10664             if (newElem)
10665               {
10666                 myLastCreatedElems.Append(newElem);
10667                 AddToSameGroups(newElem, e, aMesh);
10668                 int aShapeId = e->getshapeId();
10669                 if ( aShapeId )
10670                   {
10671                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
10672                   }
10673               }
10674             aMesh->RemoveElement(e);
10675           }
10676       }
10677     }
10678
10679   Remove( nodeIDsToRemove, true );
10680
10681   return aResult;
10682 }
10683
10684 //================================================================================
10685 /*!
10686  * \brief Find corresponding nodes in two sets of faces
10687  * \param theSide1 - first face set
10688  * \param theSide2 - second first face
10689  * \param theFirstNode1 - a boundary node of set 1
10690  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10691  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10692  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10693  * \param nReplaceMap - output map of corresponding nodes
10694  * \return bool  - is a success or not
10695  */
10696 //================================================================================
10697
10698 #ifdef _DEBUG_
10699 //#define DEBUG_MATCHING_NODES
10700 #endif
10701
10702 SMESH_MeshEditor::Sew_Error
10703 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10704                                     set<const SMDS_MeshElement*>& theSide2,
10705                                     const SMDS_MeshNode*          theFirstNode1,
10706                                     const SMDS_MeshNode*          theFirstNode2,
10707                                     const SMDS_MeshNode*          theSecondNode1,
10708                                     const SMDS_MeshNode*          theSecondNode2,
10709                                     TNodeNodeMap &                nReplaceMap)
10710 {
10711   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10712
10713   nReplaceMap.clear();
10714   if ( theFirstNode1 != theFirstNode2 )
10715     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10716   if ( theSecondNode1 != theSecondNode2 )
10717     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10718
10719   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10720   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10721
10722   list< NLink > linkList[2];
10723   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10724   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10725
10726   // loop on links in linkList; find faces by links and append links
10727   // of the found faces to linkList
10728   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10729   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10730     NLink link[] = { *linkIt[0], *linkIt[1] };
10731     if ( linkSet.find( link[0] ) == linkSet.end() )
10732       continue;
10733
10734     // by links, find faces in the face sets,
10735     // and find indices of link nodes in the found faces;
10736     // in a face set, there is only one or no face sharing a link
10737     // ---------------------------------------------------------------
10738
10739     const SMDS_MeshElement* face[] = { 0, 0 };
10740     list<const SMDS_MeshNode*> notLinkNodes[2];
10741     //bool reverse[] = { false, false }; // order of notLinkNodes
10742     int nbNodes[2];
10743     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10744     {
10745       const SMDS_MeshNode* n1 = link[iSide].first;
10746       const SMDS_MeshNode* n2 = link[iSide].second;
10747       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10748       set< const SMDS_MeshElement* > facesOfNode1;
10749       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10750       {
10751         // during a loop of the first node, we find all faces around n1,
10752         // during a loop of the second node, we find one face sharing both n1 and n2
10753         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10754         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10755         while ( fIt->more() ) { // loop on faces sharing a node
10756           const SMDS_MeshElement* f = fIt->next();
10757           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10758               ! facesOfNode1.insert( f ).second ) // f encounters twice
10759           {
10760             if ( face[ iSide ] ) {
10761               MESSAGE( "2 faces per link " );
10762               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10763             }
10764             face[ iSide ] = f;
10765             faceSet->erase( f );
10766
10767             // get not link nodes
10768             int nbN = f->NbNodes();
10769             if ( f->IsQuadratic() )
10770               nbN /= 2;
10771             nbNodes[ iSide ] = nbN;
10772             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10773             int i1 = f->GetNodeIndex( n1 );
10774             int i2 = f->GetNodeIndex( n2 );
10775             int iEnd = nbN, iBeg = -1, iDelta = 1;
10776             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10777             if ( reverse ) {
10778               std::swap( iEnd, iBeg ); iDelta = -1;
10779             }
10780             int i = i2;
10781             while ( true ) {
10782               i += iDelta;
10783               if ( i == iEnd ) i = iBeg + iDelta;
10784               if ( i == i1 ) break;
10785               nodes.push_back ( f->GetNode( i ) );
10786             }
10787           }
10788         }
10789       }
10790     }
10791     // check similarity of elements of the sides
10792     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10793       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10794       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10795         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10796       }
10797       else {
10798         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10799       }
10800     }
10801
10802     // set nodes to merge
10803     // -------------------
10804
10805     if ( face[0] && face[1] )  {
10806       if ( nbNodes[0] != nbNodes[1] ) {
10807         MESSAGE("Diff nb of face nodes");
10808         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10809       }
10810 #ifdef DEBUG_MATCHING_NODES
10811       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10812                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10813                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10814 #endif
10815       int nbN = nbNodes[0];
10816       {
10817         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10818         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10819         for ( int i = 0 ; i < nbN - 2; ++i ) {
10820 #ifdef DEBUG_MATCHING_NODES
10821           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10822 #endif
10823           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10824         }
10825       }
10826
10827       // add other links of the face 1 to linkList
10828       // -----------------------------------------
10829
10830       const SMDS_MeshElement* f0 = face[0];
10831       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10832       for ( int i = 0; i < nbN; i++ )
10833       {
10834         const SMDS_MeshNode* n2 = f0->GetNode( i );
10835         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10836           linkSet.insert( SMESH_TLink( n1, n2 ));
10837         if ( !iter_isnew.second ) { // already in a set: no need to process
10838           linkSet.erase( iter_isnew.first );
10839         }
10840         else // new in set == encountered for the first time: add
10841         {
10842 #ifdef DEBUG_MATCHING_NODES
10843           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10844                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10845 #endif
10846           linkList[0].push_back ( NLink( n1, n2 ));
10847           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10848         }
10849         n1 = n2;
10850       }
10851     } // 2 faces found
10852   } // loop on link lists
10853
10854   return SEW_OK;
10855 }
10856
10857 //================================================================================
10858 /*!
10859   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10860   \param theElems - the list of elements (edges or faces) to be replicated
10861   The nodes for duplication could be found from these elements
10862   \param theNodesNot - list of nodes to NOT replicate
10863   \param theAffectedElems - the list of elements (cells and edges) to which the
10864   replicated nodes should be associated to.
10865   \return TRUE if operation has been completed successfully, FALSE otherwise
10866 */
10867 //================================================================================
10868
10869 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10870                                     const TIDSortedElemSet& theNodesNot,
10871                                     const TIDSortedElemSet& theAffectedElems )
10872 {
10873   myLastCreatedElems.Clear();
10874   myLastCreatedNodes.Clear();
10875
10876   if ( theElems.size() == 0 )
10877     return false;
10878
10879   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10880   if ( !aMeshDS )
10881     return false;
10882
10883   bool res = false;
10884   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10885   // duplicate elements and nodes
10886   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10887   // replce nodes by duplications
10888   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10889   return res;
10890 }
10891
10892 //================================================================================
10893 /*!
10894   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10895   \param theMeshDS - mesh instance
10896   \param theElems - the elements replicated or modified (nodes should be changed)
10897   \param theNodesNot - nodes to NOT replicate
10898   \param theNodeNodeMap - relation of old node to new created node
10899   \param theIsDoubleElem - flag os to replicate element or modify
10900   \return TRUE if operation has been completed successfully, FALSE otherwise
10901 */
10902 //================================================================================
10903
10904 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh*     theMeshDS,
10905                                     const TIDSortedElemSet& theElems,
10906                                     const TIDSortedElemSet& theNodesNot,
10907                                     std::map< const SMDS_MeshNode*,
10908                                     const SMDS_MeshNode* >& theNodeNodeMap,
10909                                     const bool theIsDoubleElem )
10910 {
10911   MESSAGE("doubleNodes");
10912   // iterate on through element and duplicate them (by nodes duplication)
10913   bool res = false;
10914   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10915   for ( ;  elemItr != theElems.end(); ++elemItr )
10916   {
10917     const SMDS_MeshElement* anElem = *elemItr;
10918     if (!anElem)
10919       continue;
10920
10921     bool isDuplicate = false;
10922     // duplicate nodes to duplicate element
10923     std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10924     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10925     int ind = 0;
10926     while ( anIter->more() )
10927     {
10928
10929       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10930       SMDS_MeshNode* aNewNode = aCurrNode;
10931       if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10932         aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10933       else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10934       {
10935         // duplicate node
10936         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10937         theNodeNodeMap[ aCurrNode ] = aNewNode;
10938         myLastCreatedNodes.Append( aNewNode );
10939       }
10940       isDuplicate |= (aCurrNode != aNewNode);
10941       newNodes[ ind++ ] = aNewNode;
10942     }
10943     if ( !isDuplicate )
10944       continue;
10945
10946     if ( theIsDoubleElem )
10947       AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10948     else
10949       {
10950       MESSAGE("ChangeElementNodes");
10951       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10952       }
10953     res = true;
10954   }
10955   return res;
10956 }
10957
10958 //================================================================================
10959 /*!
10960   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10961   \param theNodes - identifiers of nodes to be doubled
10962   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10963          nodes. If list of element identifiers is empty then nodes are doubled but
10964          they not assigned to elements
10965   \return TRUE if operation has been completed successfully, FALSE otherwise
10966 */
10967 //================================================================================
10968
10969 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10970                                     const std::list< int >& theListOfModifiedElems )
10971 {
10972   MESSAGE("DoubleNodes");
10973   myLastCreatedElems.Clear();
10974   myLastCreatedNodes.Clear();
10975
10976   if ( theListOfNodes.size() == 0 )
10977     return false;
10978
10979   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10980   if ( !aMeshDS )
10981     return false;
10982
10983   // iterate through nodes and duplicate them
10984
10985   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10986
10987   std::list< int >::const_iterator aNodeIter;
10988   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10989   {
10990     int aCurr = *aNodeIter;
10991     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10992     if ( !aNode )
10993       continue;
10994
10995     // duplicate node
10996
10997     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10998     if ( aNewNode )
10999     {
11000       anOldNodeToNewNode[ aNode ] = aNewNode;
11001       myLastCreatedNodes.Append( aNewNode );
11002     }
11003   }
11004
11005   // Create map of new nodes for modified elements
11006
11007   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
11008
11009   std::list< int >::const_iterator anElemIter;
11010   for ( anElemIter = theListOfModifiedElems.begin();
11011         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
11012   {
11013     int aCurr = *anElemIter;
11014     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
11015     if ( !anElem )
11016       continue;
11017
11018     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
11019
11020     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11021     int ind = 0;
11022     while ( anIter->more() )
11023     {
11024       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
11025       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
11026       {
11027         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
11028         aNodeArr[ ind++ ] = aNewNode;
11029       }
11030       else
11031         aNodeArr[ ind++ ] = aCurrNode;
11032     }
11033     anElemToNodes[ anElem ] = aNodeArr;
11034   }
11035
11036   // Change nodes of elements
11037
11038   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
11039     anElemToNodesIter = anElemToNodes.begin();
11040   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
11041   {
11042     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
11043     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
11044     if ( anElem )
11045       {
11046       MESSAGE("ChangeElementNodes");
11047       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
11048       }
11049   }
11050
11051   return true;
11052 }
11053
11054 namespace {
11055
11056   //================================================================================
11057   /*!
11058   \brief Check if element located inside shape
11059   \return TRUE if IN or ON shape, FALSE otherwise
11060   */
11061   //================================================================================
11062
11063   template<class Classifier>
11064   bool isInside(const SMDS_MeshElement* theElem,
11065                 Classifier&             theClassifier,
11066                 const double            theTol)
11067   {
11068     gp_XYZ centerXYZ (0, 0, 0);
11069     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
11070     while (aNodeItr->more())
11071       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
11072
11073     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11074     theClassifier.Perform(aPnt, theTol);
11075     TopAbs_State aState = theClassifier.State();
11076     return (aState == TopAbs_IN || aState == TopAbs_ON );
11077   }
11078
11079   //================================================================================
11080   /*!
11081    * \brief Classifier of the 3D point on the TopoDS_Face
11082    *        with interaface suitable for isInside()
11083    */
11084   //================================================================================
11085
11086   struct _FaceClassifier
11087   {
11088     Extrema_ExtPS       _extremum;
11089     BRepAdaptor_Surface _surface;
11090     TopAbs_State        _state;
11091
11092     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11093     {
11094       _extremum.Initialize( _surface,
11095                             _surface.FirstUParameter(), _surface.LastUParameter(),
11096                             _surface.FirstVParameter(), _surface.LastVParameter(),
11097                             _surface.Tolerance(), _surface.Tolerance() );
11098     }
11099     void Perform(const gp_Pnt& aPnt, double theTol)
11100     {
11101       _state = TopAbs_OUT;
11102       _extremum.Perform(aPnt);
11103       if ( _extremum.IsDone() )
11104         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11105 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
11106           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11107 #else
11108           _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11109 #endif
11110     }
11111     TopAbs_State State() const
11112     {
11113       return _state;
11114     }
11115   };
11116 }
11117
11118 //================================================================================
11119 /*!
11120   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed.
11121   This method is the first step of DoubleNodeElemGroupsInRegion.
11122   \param theElems - list of groups of elements (edges or faces) to be replicated
11123   \param theNodesNot - list of groups of nodes not to replicated
11124   \param theShape - shape to detect affected elements (element which geometric center
11125          located on or inside shape).
11126          The replicated nodes should be associated to affected elements.
11127   \return groups of affected elements
11128   \sa DoubleNodeElemGroupsInRegion()
11129  */
11130 //================================================================================
11131
11132 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11133                                                    const TIDSortedElemSet& theNodesNot,
11134                                                    const TopoDS_Shape&     theShape,
11135                                                    TIDSortedElemSet&       theAffectedElems)
11136 {
11137   if ( theShape.IsNull() )
11138     return false;
11139
11140   const double aTol = Precision::Confusion();
11141   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11142   auto_ptr<_FaceClassifier>              aFaceClassifier;
11143   if ( theShape.ShapeType() == TopAbs_SOLID )
11144   {
11145     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11146     bsc3d->PerformInfinitePoint(aTol);
11147   }
11148   else if (theShape.ShapeType() == TopAbs_FACE )
11149   {
11150     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11151   }
11152
11153   // iterates on indicated elements and get elements by back references from their nodes
11154   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11155   for ( ;  elemItr != theElems.end(); ++elemItr )
11156   {
11157     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11158     if (!anElem)
11159       continue;
11160
11161     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11162     while ( nodeItr->more() )
11163     {
11164       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11165       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11166         continue;
11167       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11168       while ( backElemItr->more() )
11169       {
11170         const SMDS_MeshElement* curElem = backElemItr->next();
11171         if ( curElem && theElems.find(curElem) == theElems.end() &&
11172              ( bsc3d.get() ?
11173                isInside( curElem, *bsc3d, aTol ) :
11174                isInside( curElem, *aFaceClassifier, aTol )))
11175           theAffectedElems.insert( curElem );
11176       }
11177     }
11178   }
11179   return true;
11180 }
11181
11182 //================================================================================
11183 /*!
11184   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11185   \param theElems - group of of elements (edges or faces) to be replicated
11186   \param theNodesNot - group of nodes not to replicate
11187   \param theShape - shape to detect affected elements (element which geometric center
11188   located on or inside shape).
11189   The replicated nodes should be associated to affected elements.
11190   \return TRUE if operation has been completed successfully, FALSE otherwise
11191 */
11192 //================================================================================
11193
11194 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11195                                             const TIDSortedElemSet& theNodesNot,
11196                                             const TopoDS_Shape&     theShape )
11197 {
11198   if ( theShape.IsNull() )
11199     return false;
11200
11201   const double aTol = Precision::Confusion();
11202   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11203   auto_ptr<_FaceClassifier>              aFaceClassifier;
11204   if ( theShape.ShapeType() == TopAbs_SOLID )
11205   {
11206     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11207     bsc3d->PerformInfinitePoint(aTol);
11208   }
11209   else if (theShape.ShapeType() == TopAbs_FACE )
11210   {
11211     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11212   }
11213
11214   // iterates on indicated elements and get elements by back references from their nodes
11215   TIDSortedElemSet anAffected;
11216   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11217   for ( ;  elemItr != theElems.end(); ++elemItr )
11218   {
11219     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11220     if (!anElem)
11221       continue;
11222
11223     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11224     while ( nodeItr->more() )
11225     {
11226       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11227       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11228         continue;
11229       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11230       while ( backElemItr->more() )
11231       {
11232         const SMDS_MeshElement* curElem = backElemItr->next();
11233         if ( curElem && theElems.find(curElem) == theElems.end() &&
11234              ( bsc3d.get() ?
11235                isInside( curElem, *bsc3d, aTol ) :
11236                isInside( curElem, *aFaceClassifier, aTol )))
11237           anAffected.insert( curElem );
11238       }
11239     }
11240   }
11241   return DoubleNodes( theElems, theNodesNot, anAffected );
11242 }
11243
11244 /*!
11245  *  \brief compute an oriented angle between two planes defined by four points.
11246  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11247  *  @param p0 base of the rotation axe
11248  *  @param p1 extremity of the rotation axe
11249  *  @param g1 belongs to the first plane
11250  *  @param g2 belongs to the second plane
11251  */
11252 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11253 {
11254 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11255 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11256 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11257 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11258   gp_Vec vref(p0, p1);
11259   gp_Vec v1(p0, g1);
11260   gp_Vec v2(p0, g2);
11261   gp_Vec n1 = vref.Crossed(v1);
11262   gp_Vec n2 = vref.Crossed(v2);
11263   return n2.AngleWithRef(n1, vref);
11264 }
11265
11266 /*!
11267  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11268  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11269  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11270  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11271  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11272  * 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.
11273  * 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.
11274  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11275  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11276  * @param theElems - list of groups of volumes, where a group of volume is a set of
11277  * SMDS_MeshElements sorted by Id.
11278  * @param createJointElems - if TRUE, create the elements
11279  * @return TRUE if operation has been completed successfully, FALSE otherwise
11280  */
11281 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11282                                                      bool createJointElems)
11283 {
11284   MESSAGE("----------------------------------------------");
11285   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11286   MESSAGE("----------------------------------------------");
11287
11288   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11289   meshDS->BuildDownWardConnectivity(true);
11290   CHRONO(50);
11291   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11292
11293   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11294   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11295   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11296
11297   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11298   std::map<int,int>celldom; // cell vtkId --> domain
11299   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11300   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11301   faceDomains.clear();
11302   celldom.clear();
11303   cellDomains.clear();
11304   nodeDomains.clear();
11305   std::map<int,int> emptyMap;
11306   std::set<int> emptySet;
11307   emptyMap.clear();
11308
11309   MESSAGE(".. Number of domains :"<<theElems.size());
11310
11311   // Check if the domains do not share an element
11312   for (int idom = 0; idom < theElems.size()-1; idom++)
11313     {
11314 //       MESSAGE("... Check of domain #" << idom);
11315       const TIDSortedElemSet& domain = theElems[idom];
11316       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11317       for (; elemItr != domain.end(); ++elemItr)
11318         {
11319           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11320           int idombisdeb = idom + 1 ;
11321           for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
11322           {
11323             const TIDSortedElemSet& domainbis = theElems[idombis];
11324             if ( domainbis.count(anElem) )
11325             {
11326               MESSAGE(".... Domain #" << idom);
11327               MESSAGE(".... Domain #" << idombis);
11328               throw SALOME_Exception("The domains are not disjoint.");
11329               return false ;
11330             }
11331           }
11332         }
11333     }
11334
11335   for (int idom = 0; idom < theElems.size(); idom++)
11336     {
11337
11338       // --- build a map (face to duplicate --> volume to modify)
11339       //     with all the faces shared by 2 domains (group of elements)
11340       //     and corresponding volume of this domain, for each shared face.
11341       //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11342
11343       MESSAGE("... Neighbors of domain #" << idom);
11344       const TIDSortedElemSet& domain = theElems[idom];
11345       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11346       for (; elemItr != domain.end(); ++elemItr)
11347         {
11348           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11349           if (!anElem)
11350             continue;
11351           int vtkId = anElem->getVtkId();
11352           //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11353           int neighborsVtkIds[NBMAXNEIGHBORS];
11354           int downIds[NBMAXNEIGHBORS];
11355           unsigned char downTypes[NBMAXNEIGHBORS];
11356           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11357           for (int n = 0; n < nbNeighbors; n++)
11358             {
11359               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11360               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11361               if (! domain.count(elem)) // neighbor is in another domain : face is shared
11362                 {
11363                   bool ok = false ;
11364                   for (int idombis = 0; idombis < theElems.size(); idombis++) // check if the neighbor belongs to another domain of the list
11365                   {
11366                     // MESSAGE("Domain " << idombis);
11367                     const TIDSortedElemSet& domainbis = theElems[idombis];
11368                     if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11369                   }
11370                   if ( ok ) // the characteristics of the face is stored
11371                   {
11372                     DownIdType face(downIds[n], downTypes[n]);
11373                     if (!faceDomains.count(face))
11374                       faceDomains[face] = emptyMap; // create an empty entry for face
11375                     if (!faceDomains[face].count(idom))
11376                       {
11377                         faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11378                         celldom[vtkId] = idom;
11379                         //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11380                       }
11381                   }
11382                 }
11383             }
11384         }
11385     }
11386
11387   //MESSAGE("Number of shared faces " << faceDomains.size());
11388   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11389
11390   // --- explore the shared faces domain by domain,
11391   //     explore the nodes of the face and see if they belong to a cell in the domain,
11392   //     which has only a node or an edge on the border (not a shared face)
11393
11394   for (int idomain = 0; idomain < theElems.size(); idomain++)
11395     {
11396       //MESSAGE("Domain " << idomain);
11397       const TIDSortedElemSet& domain = theElems[idomain];
11398       itface = faceDomains.begin();
11399       for (; itface != faceDomains.end(); ++itface)
11400         {
11401           std::map<int, int> domvol = itface->second;
11402           if (!domvol.count(idomain))
11403             continue;
11404           DownIdType face = itface->first;
11405           //MESSAGE(" --- face " << face.cellId);
11406           std::set<int> oldNodes;
11407           oldNodes.clear();
11408           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11409           std::set<int>::iterator itn = oldNodes.begin();
11410           for (; itn != oldNodes.end(); ++itn)
11411             {
11412               int oldId = *itn;
11413               //MESSAGE("     node " << oldId);
11414               vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11415               for (int i=0; i<l.ncells; i++)
11416                 {
11417                   int vtkId = l.cells[i];
11418                   const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11419                   if (!domain.count(anElem))
11420                     continue;
11421                   int vtkType = grid->GetCellType(vtkId);
11422                   int downId = grid->CellIdToDownId(vtkId);
11423                   if (downId < 0)
11424                     {
11425                       MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11426                       continue; // not OK at this stage of the algorithm:
11427                                 //no cells created after BuildDownWardConnectivity
11428                     }
11429                   DownIdType aCell(downId, vtkType);
11430                   if (!cellDomains.count(aCell))
11431                     cellDomains[aCell] = emptyMap; // create an empty entry for cell
11432                   cellDomains[aCell][idomain] = vtkId;
11433                   celldom[vtkId] = idomain;
11434                   //MESSAGE("       cell " << vtkId << " domain " << idomain);
11435                 }
11436             }
11437         }
11438     }
11439
11440   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11441   //     for each shared face, get the nodes
11442   //     for each node, for each domain of the face, create a clone of the node
11443
11444   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11445   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11446   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11447
11448   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11449   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11450   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11451
11452   MESSAGE(".. Duplication of the nodes");
11453   for (int idomain = 0; idomain < theElems.size(); idomain++)
11454     {
11455       itface = faceDomains.begin();
11456       for (; itface != faceDomains.end(); ++itface)
11457         {
11458           std::map<int, int> domvol = itface->second;
11459           if (!domvol.count(idomain))
11460             continue;
11461           DownIdType face = itface->first;
11462           //MESSAGE(" --- face " << face.cellId);
11463           std::set<int> oldNodes;
11464           oldNodes.clear();
11465           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11466           std::set<int>::iterator itn = oldNodes.begin();
11467           for (; itn != oldNodes.end(); ++itn)
11468             {
11469               int oldId = *itn;
11470               //MESSAGE("-+-+-a node " << oldId);
11471               if (!nodeDomains.count(oldId))
11472                 nodeDomains[oldId] = emptyMap; // create an empty entry for node
11473               if (nodeDomains[oldId].empty())
11474                 {
11475                   nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11476                   //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11477                 }
11478               std::map<int, int>::iterator itdom = domvol.begin();
11479               for (; itdom != domvol.end(); ++itdom)
11480                 {
11481                   int idom = itdom->first;
11482                   //MESSAGE("         domain " << idom);
11483                   if (!nodeDomains[oldId].count(idom)) // --- node to clone
11484                     {
11485                       if (nodeDomains[oldId].size() >= 2) // a multiple node
11486                         {
11487                           vector<int> orderedDoms;
11488                           //MESSAGE("multiple node " << oldId);
11489                           if (mutipleNodes.count(oldId))
11490                             orderedDoms = mutipleNodes[oldId];
11491                           else
11492                             {
11493                               map<int,int>::iterator it = nodeDomains[oldId].begin();
11494                               for (; it != nodeDomains[oldId].end(); ++it)
11495                                 orderedDoms.push_back(it->first);
11496                             }
11497                           orderedDoms.push_back(idom); // TODO order ==> push_front or back
11498                           //stringstream txt;
11499                           //for (int i=0; i<orderedDoms.size(); i++)
11500                           //  txt << orderedDoms[i] << " ";
11501                           //MESSAGE("orderedDoms " << txt.str());
11502                           mutipleNodes[oldId] = orderedDoms;
11503                         }
11504                       double *coords = grid->GetPoint(oldId);
11505                       SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11506                       int newId = newNode->getVtkId();
11507                       nodeDomains[oldId][idom] = newId; // cloned node for other domains
11508                       //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11509                     }
11510                 }
11511             }
11512         }
11513     }
11514
11515   MESSAGE(".. Creation of elements");
11516   for (int idomain = 0; idomain < theElems.size(); idomain++)
11517     {
11518       itface = faceDomains.begin();
11519       for (; itface != faceDomains.end(); ++itface)
11520         {
11521           std::map<int, int> domvol = itface->second;
11522           if (!domvol.count(idomain))
11523             continue;
11524           DownIdType face = itface->first;
11525           //MESSAGE(" --- face " << face.cellId);
11526           std::set<int> oldNodes;
11527           oldNodes.clear();
11528           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11529           int nbMultipleNodes = 0;
11530           std::set<int>::iterator itn = oldNodes.begin();
11531           for (; itn != oldNodes.end(); ++itn)
11532             {
11533               int oldId = *itn;
11534               if (mutipleNodes.count(oldId))
11535                 nbMultipleNodes++;
11536             }
11537           if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11538             {
11539               //MESSAGE("multiple Nodes detected on a shared face");
11540               int downId = itface->first.cellId;
11541               unsigned char cellType = itface->first.cellType;
11542               // --- shared edge or shared face ?
11543               if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11544                 {
11545                   int nodes[3];
11546                   int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11547                   for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11548                     if (mutipleNodes.count(nodes[i]))
11549                       if (!mutipleNodesToFace.count(nodes[i]))
11550                         mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11551                 }
11552               else // shared face (between two volumes)
11553                 {
11554                   int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11555                   const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11556                   const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11557                   for (int ie =0; ie < nbEdges; ie++)
11558                     {
11559                       int nodes[3];
11560                       int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11561                       if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11562                         {
11563                           vector<int> vn0 = mutipleNodes[nodes[0]];
11564                           vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11565                           vector<int> doms;
11566                           for (int i0 = 0; i0 < vn0.size(); i0++)
11567                             for (int i1 = 0; i1 < vn1.size(); i1++)
11568                               if (vn0[i0] == vn1[i1])
11569                                 doms.push_back(vn0[i0]);
11570                           if (doms.size() >2)
11571                             {
11572                               //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11573                               double *coords = grid->GetPoint(nodes[0]);
11574                               gp_Pnt p0(coords[0], coords[1], coords[2]);
11575                               coords = grid->GetPoint(nodes[nbNodes - 1]);
11576                               gp_Pnt p1(coords[0], coords[1], coords[2]);
11577                               gp_Pnt gref;
11578                               int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11579                               map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11580                               map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11581                               int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11582                               for (int id=0; id < doms.size(); id++)
11583                                 {
11584                                   int idom = doms[id];
11585                                   for (int ivol=0; ivol<nbvol; ivol++)
11586                                     {
11587                                       int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11588                                       SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11589                                       if (theElems[idom].count(elem))
11590                                         {
11591                                           SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11592                                           domvol[idom] = svol;
11593                                           //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11594                                           double values[3];
11595                                           vtkIdType npts = 0;
11596                                           vtkIdType* pts = 0;
11597                                           grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11598                                           SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11599                                           if (id ==0)
11600                                             {
11601                                               gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11602                                               angleDom[idom] = 0;
11603                                             }
11604                                           else
11605                                             {
11606                                               gp_Pnt g(values[0], values[1], values[2]);
11607                                               angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11608                                               //MESSAGE("  angle=" << angleDom[idom]);
11609                                             }
11610                                           break;
11611                                         }
11612                                     }
11613                                 }
11614                               map<double, int> sortedDom; // sort domains by angle
11615                               for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11616                                 sortedDom[ia->second] = ia->first;
11617                               vector<int> vnodes;
11618                               vector<int> vdom;
11619                               for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11620                                 {
11621                                   vdom.push_back(ib->second);
11622                                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11623                                 }
11624                               for (int ino = 0; ino < nbNodes; ino++)
11625                                 vnodes.push_back(nodes[ino]);
11626                               edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11627                             }
11628                         }
11629                     }
11630                 }
11631             }
11632         }
11633     }
11634
11635   // --- iterate on shared faces (volumes to modify, face to extrude)
11636   //     get node id's of the face (id SMDS = id VTK)
11637   //     create flat element with old and new nodes if requested
11638
11639   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11640   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11641
11642   std::map<int, std::map<long,int> > nodeQuadDomains;
11643   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11644
11645   MESSAGE(".. Creation of elements: simple junction");
11646   if (createJointElems)
11647     {
11648       int idg;
11649       string joints2DName = "joints2D";
11650       mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11651       SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11652       string joints3DName = "joints3D";
11653       mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11654       SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11655
11656       itface = faceDomains.begin();
11657       for (; itface != faceDomains.end(); ++itface)
11658         {
11659           DownIdType face = itface->first;
11660           std::set<int> oldNodes;
11661           std::set<int>::iterator itn;
11662           oldNodes.clear();
11663           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11664
11665           std::map<int, int> domvol = itface->second;
11666           std::map<int, int>::iterator itdom = domvol.begin();
11667           int dom1 = itdom->first;
11668           int vtkVolId = itdom->second;
11669           itdom++;
11670           int dom2 = itdom->first;
11671           SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11672                                                              nodeQuadDomains);
11673           stringstream grpname;
11674           grpname << "j_";
11675           if (dom1 < dom2)
11676             grpname << dom1 << "_" << dom2;
11677           else
11678             grpname << dom2 << "_" << dom1;
11679           string namegrp = grpname.str();
11680           if (!mapOfJunctionGroups.count(namegrp))
11681             mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11682           SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11683           if (sgrp)
11684             sgrp->Add(vol->GetID());
11685           if (vol->GetType() == SMDSAbs_Volume)
11686             joints3DGrp->Add(vol->GetID());
11687           else if (vol->GetType() == SMDSAbs_Face)
11688             joints2DGrp->Add(vol->GetID());
11689         }
11690     }
11691
11692   // --- create volumes on multiple domain intersection if requested
11693   //     iterate on mutipleNodesToFace
11694   //     iterate on edgesMultiDomains
11695
11696   MESSAGE(".. Creation of elements: multiple junction");
11697   if (createJointElems)
11698     {
11699       // --- iterate on mutipleNodesToFace
11700
11701       std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11702       for (; itn != mutipleNodesToFace.end(); ++itn)
11703         {
11704           int node = itn->first;
11705           vector<int> orderDom = itn->second;
11706           vector<vtkIdType> orderedNodes;
11707           for (int idom = 0; idom <orderDom.size(); idom++)
11708             orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11709             SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11710
11711             stringstream grpname;
11712             grpname << "m2j_";
11713             grpname << 0 << "_" << 0;
11714             int idg;
11715             string namegrp = grpname.str();
11716             if (!mapOfJunctionGroups.count(namegrp))
11717               mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11718             SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11719             if (sgrp)
11720               sgrp->Add(face->GetID());
11721         }
11722
11723       // --- iterate on edgesMultiDomains
11724
11725       std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11726       for (; ite != edgesMultiDomains.end(); ++ite)
11727         {
11728           vector<int> nodes = ite->first;
11729           vector<int> orderDom = ite->second;
11730           vector<vtkIdType> orderedNodes;
11731           if (nodes.size() == 2)
11732             {
11733               //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11734               for (int ino=0; ino < nodes.size(); ino++)
11735                 if (orderDom.size() == 3)
11736                   for (int idom = 0; idom <orderDom.size(); idom++)
11737                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11738                 else
11739                   for (int idom = orderDom.size()-1; idom >=0; idom--)
11740                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11741               SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11742
11743               int idg;
11744               string namegrp = "jointsMultiples";
11745               if (!mapOfJunctionGroups.count(namegrp))
11746                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11747               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11748               if (sgrp)
11749                 sgrp->Add(vol->GetID());
11750             }
11751           else
11752             {
11753               INFOS("Quadratic multiple joints not implemented");
11754               // TODO quadratic nodes
11755             }
11756         }
11757     }
11758
11759   // --- list the explicit faces and edges of the mesh that need to be modified,
11760   //     i.e. faces and edges built with one or more duplicated nodes.
11761   //     associate these faces or edges to their corresponding domain.
11762   //     only the first domain found is kept when a face or edge is shared
11763
11764   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11765   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11766   faceOrEdgeDom.clear();
11767   feDom.clear();
11768
11769   MESSAGE(".. Modification of elements");
11770   for (int idomain = 0; idomain < theElems.size(); idomain++)
11771     {
11772       std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11773       for (; itnod != nodeDomains.end(); ++itnod)
11774         {
11775           int oldId = itnod->first;
11776           //MESSAGE("     node " << oldId);
11777           vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11778           for (int i = 0; i < l.ncells; i++)
11779             {
11780               int vtkId = l.cells[i];
11781               int vtkType = grid->GetCellType(vtkId);
11782               int downId = grid->CellIdToDownId(vtkId);
11783               if (downId < 0)
11784                 continue; // new cells: not to be modified
11785               DownIdType aCell(downId, vtkType);
11786               int volParents[1000];
11787               int nbvol = grid->GetParentVolumes(volParents, vtkId);
11788               for (int j = 0; j < nbvol; j++)
11789                 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11790                   if (!feDom.count(vtkId))
11791                     {
11792                       feDom[vtkId] = idomain;
11793                       faceOrEdgeDom[aCell] = emptyMap;
11794                       faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11795                       //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11796                       //        << " type " << vtkType << " downId " << downId);
11797                     }
11798             }
11799         }
11800     }
11801
11802   // --- iterate on shared faces (volumes to modify, face to extrude)
11803   //     get node id's of the face
11804   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11805
11806   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11807   for (int m=0; m<3; m++)
11808     {
11809       std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11810       itface = (*amap).begin();
11811       for (; itface != (*amap).end(); ++itface)
11812         {
11813           DownIdType face = itface->first;
11814           std::set<int> oldNodes;
11815           std::set<int>::iterator itn;
11816           oldNodes.clear();
11817           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11818           //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11819           std::map<int, int> localClonedNodeIds;
11820
11821           std::map<int, int> domvol = itface->second;
11822           std::map<int, int>::iterator itdom = domvol.begin();
11823           for (; itdom != domvol.end(); ++itdom)
11824             {
11825               int idom = itdom->first;
11826               int vtkVolId = itdom->second;
11827               //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11828               localClonedNodeIds.clear();
11829               for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11830                 {
11831                   int oldId = *itn;
11832                   if (nodeDomains[oldId].count(idom))
11833                     {
11834                       localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11835                       //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11836                     }
11837                 }
11838               meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11839             }
11840         }
11841     }
11842
11843   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11844   grid->BuildLinks();
11845
11846   CHRONOSTOP(50);
11847   counters::stats();
11848   return true;
11849 }
11850
11851 /*!
11852  * \brief Double nodes on some external faces and create flat elements.
11853  * Flat elements are mainly used by some types of mechanic calculations.
11854  *
11855  * Each group of the list must be constituted of faces.
11856  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11857  * @param theElems - list of groups of faces, where a group of faces is a set of
11858  * SMDS_MeshElements sorted by Id.
11859  * @return TRUE if operation has been completed successfully, FALSE otherwise
11860  */
11861 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11862 {
11863   MESSAGE("-------------------------------------------------");
11864   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11865   MESSAGE("-------------------------------------------------");
11866
11867   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11868
11869   // --- For each group of faces
11870   //     duplicate the nodes, create a flat element based on the face
11871   //     replace the nodes of the faces by their clones
11872
11873   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11874   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11875   clonedNodes.clear();
11876   intermediateNodes.clear();
11877   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11878   mapOfJunctionGroups.clear();
11879
11880   for (int idom = 0; idom < theElems.size(); idom++)
11881     {
11882       const TIDSortedElemSet& domain = theElems[idom];
11883       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11884       for (; elemItr != domain.end(); ++elemItr)
11885         {
11886           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11887           SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11888           if (!aFace)
11889             continue;
11890           // MESSAGE("aFace=" << aFace->GetID());
11891           bool isQuad = aFace->IsQuadratic();
11892           vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11893
11894           // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11895
11896           SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11897           while (nodeIt->more())
11898             {
11899               const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11900               bool isMedium = isQuad && (aFace->IsMediumNode(node));
11901               if (isMedium)
11902                 ln2.push_back(node);
11903               else
11904                 ln0.push_back(node);
11905
11906               const SMDS_MeshNode* clone = 0;
11907               if (!clonedNodes.count(node))
11908                 {
11909                   clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11910                   clonedNodes[node] = clone;
11911                 }
11912               else
11913                 clone = clonedNodes[node];
11914
11915               if (isMedium)
11916                 ln3.push_back(clone);
11917               else
11918                 ln1.push_back(clone);
11919
11920               const SMDS_MeshNode* inter = 0;
11921               if (isQuad && (!isMedium))
11922                 {
11923                   if (!intermediateNodes.count(node))
11924                     {
11925                       inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11926                       intermediateNodes[node] = inter;
11927                     }
11928                   else
11929                     inter = intermediateNodes[node];
11930                   ln4.push_back(inter);
11931                 }
11932             }
11933
11934           // --- extrude the face
11935
11936           vector<const SMDS_MeshNode*> ln;
11937           SMDS_MeshVolume* vol = 0;
11938           vtkIdType aType = aFace->GetVtkType();
11939           switch (aType)
11940           {
11941             case VTK_TRIANGLE:
11942               vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11943               // MESSAGE("vol prism " << vol->GetID());
11944               ln.push_back(ln1[0]);
11945               ln.push_back(ln1[1]);
11946               ln.push_back(ln1[2]);
11947               break;
11948             case VTK_QUAD:
11949               vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11950               // MESSAGE("vol hexa " << vol->GetID());
11951               ln.push_back(ln1[0]);
11952               ln.push_back(ln1[1]);
11953               ln.push_back(ln1[2]);
11954               ln.push_back(ln1[3]);
11955               break;
11956             case VTK_QUADRATIC_TRIANGLE:
11957               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11958                                       ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11959               // MESSAGE("vol quad prism " << vol->GetID());
11960               ln.push_back(ln1[0]);
11961               ln.push_back(ln1[1]);
11962               ln.push_back(ln1[2]);
11963               ln.push_back(ln3[0]);
11964               ln.push_back(ln3[1]);
11965               ln.push_back(ln3[2]);
11966               break;
11967             case VTK_QUADRATIC_QUAD:
11968 //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11969 //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11970 //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11971               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11972                                       ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11973                                       ln4[0], ln4[1], ln4[2], ln4[3]);
11974               // MESSAGE("vol quad hexa " << vol->GetID());
11975               ln.push_back(ln1[0]);
11976               ln.push_back(ln1[1]);
11977               ln.push_back(ln1[2]);
11978               ln.push_back(ln1[3]);
11979               ln.push_back(ln3[0]);
11980               ln.push_back(ln3[1]);
11981               ln.push_back(ln3[2]);
11982               ln.push_back(ln3[3]);
11983               break;
11984             case VTK_POLYGON:
11985               break;
11986             default:
11987               break;
11988           }
11989
11990           if (vol)
11991             {
11992               stringstream grpname;
11993               grpname << "jf_";
11994               grpname << idom;
11995               int idg;
11996               string namegrp = grpname.str();
11997               if (!mapOfJunctionGroups.count(namegrp))
11998                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11999               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12000               if (sgrp)
12001                 sgrp->Add(vol->GetID());
12002             }
12003
12004           // --- modify the face
12005
12006           aFace->ChangeNodes(&ln[0], ln.size());
12007         }
12008     }
12009   return true;
12010 }
12011
12012 /*!
12013  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
12014  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
12015  *  groups of faces to remove inside the object, (idem edges).
12016  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12017  */
12018 void SMESH_MeshEditor::CreateHoleSkin(double radius,
12019                                       const TopoDS_Shape& theShape,
12020                                       SMESH_NodeSearcher* theNodeSearcher,
12021                                       const char* groupName,
12022                                       std::vector<double>&   nodesCoords,
12023                                       std::vector<std::vector<int> >& listOfListOfNodes)
12024 {
12025   MESSAGE("--------------------------------");
12026   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12027   MESSAGE("--------------------------------");
12028
12029   // --- zone of volumes to remove is given :
12030   //     1 either by a geom shape (one or more vertices) and a radius,
12031   //     2 either by a group of nodes (representative of the shape)to use with the radius,
12032   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12033   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
12034   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12035   //     defined by it's name.
12036
12037   SMESHDS_GroupBase* groupDS = 0;
12038   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12039   while ( groupIt->more() )
12040     {
12041       groupDS = 0;
12042       SMESH_Group * group = groupIt->next();
12043       if ( !group ) continue;
12044       groupDS = group->GetGroupDS();
12045       if ( !groupDS || groupDS->IsEmpty() ) continue;
12046       std::string grpName = group->GetName();
12047       //MESSAGE("grpName=" << grpName);
12048       if (grpName == groupName)
12049         break;
12050       else
12051         groupDS = 0;
12052     }
12053
12054   bool isNodeGroup = false;
12055   bool isNodeCoords = false;
12056   if (groupDS)
12057     {
12058       if (groupDS->GetType() != SMDSAbs_Node)
12059         return;
12060       isNodeGroup = true;     // a group of nodes exists and it is in this mesh
12061     }
12062
12063   if (nodesCoords.size() > 0)
12064     isNodeCoords = true; // a list o nodes given by their coordinates
12065   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12066
12067   // --- define groups to build
12068
12069   int idg; // --- group of SMDS volumes
12070   string grpvName = groupName;
12071   grpvName += "_vol";
12072   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
12073   if (!grp)
12074     {
12075       MESSAGE("group not created " << grpvName);
12076       return;
12077     }
12078   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12079
12080   int idgs; // --- group of SMDS faces on the skin
12081   string grpsName = groupName;
12082   grpsName += "_skin";
12083   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12084   if (!grps)
12085     {
12086       MESSAGE("group not created " << grpsName);
12087       return;
12088     }
12089   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12090
12091   int idgi; // --- group of SMDS faces internal (several shapes)
12092   string grpiName = groupName;
12093   grpiName += "_internalFaces";
12094   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12095   if (!grpi)
12096     {
12097       MESSAGE("group not created " << grpiName);
12098       return;
12099     }
12100   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12101
12102   int idgei; // --- group of SMDS faces internal (several shapes)
12103   string grpeiName = groupName;
12104   grpeiName += "_internalEdges";
12105   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12106   if (!grpei)
12107     {
12108       MESSAGE("group not created " << grpeiName);
12109       return;
12110     }
12111   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12112
12113   // --- build downward connectivity
12114
12115   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12116   meshDS->BuildDownWardConnectivity(true);
12117   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12118
12119   // --- set of volumes detected inside
12120
12121   std::set<int> setOfInsideVol;
12122   std::set<int> setOfVolToCheck;
12123
12124   std::vector<gp_Pnt> gpnts;
12125   gpnts.clear();
12126
12127   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12128     {
12129       MESSAGE("group of nodes provided");
12130       SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12131       while ( elemIt->more() )
12132         {
12133           const SMDS_MeshElement* elem = elemIt->next();
12134           if (!elem)
12135             continue;
12136           const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12137           if (!node)
12138             continue;
12139           SMDS_MeshElement* vol = 0;
12140           SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12141           while (volItr->more())
12142             {
12143               vol = (SMDS_MeshElement*)volItr->next();
12144               setOfInsideVol.insert(vol->getVtkId());
12145               sgrp->Add(vol->GetID());
12146             }
12147         }
12148     }
12149   else if (isNodeCoords)
12150     {
12151       MESSAGE("list of nodes coordinates provided");
12152       int i = 0;
12153       int k = 0;
12154       while (i < nodesCoords.size()-2)
12155         {
12156           double x = nodesCoords[i++];
12157           double y = nodesCoords[i++];
12158           double z = nodesCoords[i++];
12159           gp_Pnt p = gp_Pnt(x, y ,z);
12160           gpnts.push_back(p);
12161           MESSAGE("TopoDS_Vertex " << k++ << " " << p.X() << " " << p.Y() << " " << p.Z());
12162         }
12163     }
12164   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12165     {
12166       MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12167       TopTools_IndexedMapOfShape vertexMap;
12168       TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12169       gp_Pnt p = gp_Pnt(0,0,0);
12170       if (vertexMap.Extent() < 1)
12171         return;
12172
12173       for ( int i = 1; i <= vertexMap.Extent(); ++i )
12174         {
12175           const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12176           p = BRep_Tool::Pnt(vertex);
12177           gpnts.push_back(p);
12178           MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12179         }
12180     }
12181
12182   if (gpnts.size() > 0)
12183     {
12184       int nodeId = 0;
12185       const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12186       if (startNode)
12187         nodeId = startNode->GetID();
12188       MESSAGE("nodeId " << nodeId);
12189
12190       double radius2 = radius*radius;
12191       MESSAGE("radius2 " << radius2);
12192
12193       // --- volumes on start node
12194
12195       setOfVolToCheck.clear();
12196       SMDS_MeshElement* startVol = 0;
12197       SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12198       while (volItr->more())
12199         {
12200           startVol = (SMDS_MeshElement*)volItr->next();
12201           setOfVolToCheck.insert(startVol->getVtkId());
12202         }
12203       if (setOfVolToCheck.empty())
12204         {
12205           MESSAGE("No volumes found");
12206           return;
12207         }
12208
12209       // --- starting with central volumes then their neighbors, check if they are inside
12210       //     or outside the domain, until no more new neighbor volume is inside.
12211       //     Fill the group of inside volumes
12212
12213       std::map<int, double> mapOfNodeDistance2;
12214       mapOfNodeDistance2.clear();
12215       std::set<int> setOfOutsideVol;
12216       while (!setOfVolToCheck.empty())
12217         {
12218           std::set<int>::iterator it = setOfVolToCheck.begin();
12219           int vtkId = *it;
12220           MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12221           bool volInside = false;
12222           vtkIdType npts = 0;
12223           vtkIdType* pts = 0;
12224           grid->GetCellPoints(vtkId, npts, pts);
12225           for (int i=0; i<npts; i++)
12226             {
12227               double distance2 = 0;
12228               if (mapOfNodeDistance2.count(pts[i]))
12229                 {
12230                   distance2 = mapOfNodeDistance2[pts[i]];
12231                   MESSAGE("point " << pts[i] << " distance2 " << distance2);
12232                 }
12233               else
12234                 {
12235                   double *coords = grid->GetPoint(pts[i]);
12236                   gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12237                   distance2 = 1.E40;
12238                   for (int j=0; j<gpnts.size(); j++)
12239                     {
12240                       double d2 = aPoint.SquareDistance(gpnts[j]);
12241                       if (d2 < distance2)
12242                         {
12243                           distance2 = d2;
12244                           if (distance2 < radius2)
12245                             break;
12246                         }
12247                     }
12248                   mapOfNodeDistance2[pts[i]] = distance2;
12249                   MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12250                 }
12251               if (distance2 < radius2)
12252                 {
12253                   volInside = true; // one or more nodes inside the domain
12254                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12255                   break;
12256                 }
12257             }
12258           if (volInside)
12259             {
12260               setOfInsideVol.insert(vtkId);
12261               MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12262               int neighborsVtkIds[NBMAXNEIGHBORS];
12263               int downIds[NBMAXNEIGHBORS];
12264               unsigned char downTypes[NBMAXNEIGHBORS];
12265               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12266               for (int n = 0; n < nbNeighbors; n++)
12267                 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12268                   setOfVolToCheck.insert(neighborsVtkIds[n]);
12269             }
12270           else
12271             {
12272               setOfOutsideVol.insert(vtkId);
12273               MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12274             }
12275           setOfVolToCheck.erase(vtkId);
12276         }
12277     }
12278
12279   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12280   //     If yes, add the volume to the inside set
12281
12282   bool addedInside = true;
12283   std::set<int> setOfVolToReCheck;
12284   while (addedInside)
12285     {
12286       MESSAGE(" --------------------------- re check");
12287       addedInside = false;
12288       std::set<int>::iterator itv = setOfInsideVol.begin();
12289       for (; itv != setOfInsideVol.end(); ++itv)
12290         {
12291           int vtkId = *itv;
12292           int neighborsVtkIds[NBMAXNEIGHBORS];
12293           int downIds[NBMAXNEIGHBORS];
12294           unsigned char downTypes[NBMAXNEIGHBORS];
12295           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12296           for (int n = 0; n < nbNeighbors; n++)
12297             if (!setOfInsideVol.count(neighborsVtkIds[n]))
12298               setOfVolToReCheck.insert(neighborsVtkIds[n]);
12299         }
12300       setOfVolToCheck = setOfVolToReCheck;
12301       setOfVolToReCheck.clear();
12302       while  (!setOfVolToCheck.empty())
12303         {
12304           std::set<int>::iterator it = setOfVolToCheck.begin();
12305           int vtkId = *it;
12306           if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12307             {
12308               MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12309               int countInside = 0;
12310               int neighborsVtkIds[NBMAXNEIGHBORS];
12311               int downIds[NBMAXNEIGHBORS];
12312               unsigned char downTypes[NBMAXNEIGHBORS];
12313               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12314               for (int n = 0; n < nbNeighbors; n++)
12315                 if (setOfInsideVol.count(neighborsVtkIds[n]))
12316                   countInside++;
12317               MESSAGE("countInside " << countInside);
12318               if (countInside > 1)
12319                 {
12320                   MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12321                   setOfInsideVol.insert(vtkId);
12322                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12323                   addedInside = true;
12324                 }
12325               else
12326                 setOfVolToReCheck.insert(vtkId);
12327             }
12328           setOfVolToCheck.erase(vtkId);
12329         }
12330     }
12331
12332   // --- map of Downward faces at the boundary, inside the global volume
12333   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12334   //     fill group of SMDS faces inside the volume (when several volume shapes)
12335   //     fill group of SMDS faces on the skin of the global volume (if skin)
12336
12337   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12338   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12339   std::set<int>::iterator it = setOfInsideVol.begin();
12340   for (; it != setOfInsideVol.end(); ++it)
12341     {
12342       int vtkId = *it;
12343       //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12344       int neighborsVtkIds[NBMAXNEIGHBORS];
12345       int downIds[NBMAXNEIGHBORS];
12346       unsigned char downTypes[NBMAXNEIGHBORS];
12347       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12348       for (int n = 0; n < nbNeighbors; n++)
12349         {
12350           int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12351           if (neighborDim == 3)
12352             {
12353               if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12354                 {
12355                   DownIdType face(downIds[n], downTypes[n]);
12356                   boundaryFaces[face] = vtkId;
12357                 }
12358               // if the face between to volumes is in the mesh, get it (internal face between shapes)
12359               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12360               if (vtkFaceId >= 0)
12361                 {
12362                   sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12363                   // find also the smds edges on this face
12364                   int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12365                   const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12366                   const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12367                   for (int i = 0; i < nbEdges; i++)
12368                     {
12369                       int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12370                       if (vtkEdgeId >= 0)
12371                         sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12372                     }
12373                 }
12374             }
12375           else if (neighborDim == 2) // skin of the volume
12376             {
12377               DownIdType face(downIds[n], downTypes[n]);
12378               skinFaces[face] = vtkId;
12379               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12380               if (vtkFaceId >= 0)
12381                 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12382             }
12383         }
12384     }
12385
12386   // --- identify the edges constituting the wire of each subshape on the skin
12387   //     define polylines with the nodes of edges, equivalent to wires
12388   //     project polylines on subshapes, and partition, to get geom faces
12389
12390   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12391   std::set<int> emptySet;
12392   emptySet.clear();
12393   std::set<int> shapeIds;
12394
12395   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12396   while (itelem->more())
12397     {
12398       const SMDS_MeshElement *elem = itelem->next();
12399       int shapeId = elem->getshapeId();
12400       int vtkId = elem->getVtkId();
12401       if (!shapeIdToVtkIdSet.count(shapeId))
12402         {
12403           shapeIdToVtkIdSet[shapeId] = emptySet;
12404           shapeIds.insert(shapeId);
12405         }
12406       shapeIdToVtkIdSet[shapeId].insert(vtkId);
12407     }
12408
12409   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12410   std::set<DownIdType, DownIdCompare> emptyEdges;
12411   emptyEdges.clear();
12412
12413   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12414   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12415     {
12416       int shapeId = itShape->first;
12417       MESSAGE(" --- Shape ID --- "<< shapeId);
12418       shapeIdToEdges[shapeId] = emptyEdges;
12419
12420       std::vector<int> nodesEdges;
12421
12422       std::set<int>::iterator its = itShape->second.begin();
12423       for (; its != itShape->second.end(); ++its)
12424         {
12425           int vtkId = *its;
12426           MESSAGE("     " << vtkId);
12427           int neighborsVtkIds[NBMAXNEIGHBORS];
12428           int downIds[NBMAXNEIGHBORS];
12429           unsigned char downTypes[NBMAXNEIGHBORS];
12430           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12431           for (int n = 0; n < nbNeighbors; n++)
12432             {
12433               if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12434                 continue;
12435               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12436               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12437               if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12438                 {
12439                   DownIdType edge(downIds[n], downTypes[n]);
12440                   if (!shapeIdToEdges[shapeId].count(edge))
12441                     {
12442                       shapeIdToEdges[shapeId].insert(edge);
12443                       int vtkNodeId[3];
12444                       int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12445                       nodesEdges.push_back(vtkNodeId[0]);
12446                       nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12447                       MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12448                     }
12449                 }
12450             }
12451         }
12452
12453       std::list<int> order;
12454       order.clear();
12455       if (nodesEdges.size() > 0)
12456         {
12457           order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12458           nodesEdges[0] = -1;
12459           order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
12460           nodesEdges[1] = -1; // do not reuse this edge
12461           bool found = true;
12462           while (found)
12463             {
12464               int nodeTofind = order.back(); // try first to push back
12465               int i = 0;
12466               for (i = 0; i<nodesEdges.size(); i++)
12467                 if (nodesEdges[i] == nodeTofind)
12468                   break;
12469               if (i == nodesEdges.size())
12470                 found = false; // no follower found on back
12471               else
12472                 {
12473                   if (i%2) // odd ==> use the previous one
12474                     if (nodesEdges[i-1] < 0)
12475                       found = false;
12476                     else
12477                       {
12478                         order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
12479                         nodesEdges[i-1] = -1;
12480                       }
12481                   else // even ==> use the next one
12482                     if (nodesEdges[i+1] < 0)
12483                       found = false;
12484                     else
12485                       {
12486                         order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
12487                         nodesEdges[i+1] = -1;
12488                       }
12489                 }
12490               if (found)
12491                 continue;
12492               // try to push front
12493               found = true;
12494               nodeTofind = order.front(); // try to push front
12495               for (i = 0; i<nodesEdges.size(); i++)
12496                 if (nodesEdges[i] == nodeTofind)
12497                   break;
12498               if (i == nodesEdges.size())
12499                 {
12500                   found = false; // no predecessor found on front
12501                   continue;
12502                 }
12503               if (i%2) // odd ==> use the previous one
12504                 if (nodesEdges[i-1] < 0)
12505                   found = false;
12506                 else
12507                   {
12508                     order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
12509                     nodesEdges[i-1] = -1;
12510                   }
12511               else // even ==> use the next one
12512                 if (nodesEdges[i+1] < 0)
12513                   found = false;
12514                 else
12515                   {
12516                     order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
12517                     nodesEdges[i+1] = -1;
12518                   }
12519             }
12520         }
12521
12522
12523       std::vector<int> nodes;
12524       nodes.push_back(shapeId);
12525       std::list<int>::iterator itl = order.begin();
12526       for (; itl != order.end(); itl++)
12527         {
12528           nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12529           MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12530         }
12531       listOfListOfNodes.push_back(nodes);
12532     }
12533
12534   //     partition geom faces with blocFissure
12535   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12536   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12537
12538   return;
12539 }
12540
12541
12542 //================================================================================
12543 /*!
12544  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12545  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12546  * \return TRUE if operation has been completed successfully, FALSE otherwise
12547  */
12548 //================================================================================
12549
12550 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12551 {
12552   // iterates on volume elements and detect all free faces on them
12553   SMESHDS_Mesh* aMesh = GetMeshDS();
12554   if (!aMesh)
12555     return false;
12556   //bool res = false;
12557   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12558   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12559   while(vIt->more())
12560   {
12561     const SMDS_MeshVolume* volume = vIt->next();
12562     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12563     vTool.SetExternalNormal();
12564     //const bool isPoly = volume->IsPoly();
12565     const int iQuad = volume->IsQuadratic();
12566     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12567     {
12568       if (!vTool.IsFreeFace(iface))
12569         continue;
12570       nbFree++;
12571       vector<const SMDS_MeshNode *> nodes;
12572       int nbFaceNodes = vTool.NbFaceNodes(iface);
12573       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12574       int inode = 0;
12575       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12576         nodes.push_back(faceNodes[inode]);
12577       if (iQuad) { // add medium nodes
12578         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12579           nodes.push_back(faceNodes[inode]);
12580         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12581           nodes.push_back(faceNodes[8]);
12582       }
12583       // add new face based on volume nodes
12584       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12585         nbExisted++;
12586         continue; // face already exsist
12587       }
12588       AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12589       nbCreated++;
12590     }
12591   }
12592   return ( nbFree==(nbExisted+nbCreated) );
12593 }
12594
12595 namespace
12596 {
12597   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12598   {
12599     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12600       return n;
12601     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12602   }
12603 }
12604 //================================================================================
12605 /*!
12606  * \brief Creates missing boundary elements
12607  *  \param elements - elements whose boundary is to be checked
12608  *  \param dimension - defines type of boundary elements to create
12609  *  \param group - a group to store created boundary elements in
12610  *  \param targetMesh - a mesh to store created boundary elements in
12611  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12612  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12613  *                                boundary elements will be copied into the targetMesh
12614  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12615  *                                boundary elements will be added into the new group
12616  *  \param aroundElements - if true, elements will be created on boundary of given
12617  *                          elements else, on boundary of the whole mesh.
12618  * \return nb of added boundary elements
12619  */
12620 //================================================================================
12621
12622 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12623                                        Bnd_Dimension           dimension,
12624                                        SMESH_Group*            group/*=0*/,
12625                                        SMESH_Mesh*             targetMesh/*=0*/,
12626                                        bool                    toCopyElements/*=false*/,
12627                                        bool                    toCopyExistingBoundary/*=false*/,
12628                                        bool                    toAddExistingBondary/*= false*/,
12629                                        bool                    aroundElements/*= false*/)
12630 {
12631   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12632   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12633   // hope that all elements are of the same type, do not check them all
12634   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12635     throw SALOME_Exception(LOCALIZED("wrong element type"));
12636
12637   if ( !targetMesh )
12638     toCopyElements = toCopyExistingBoundary = false;
12639
12640   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12641   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12642   int nbAddedBnd = 0;
12643
12644   // editor adding present bnd elements and optionally holding elements to add to the group
12645   SMESH_MeshEditor* presentEditor;
12646   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12647   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12648
12649   SMESH_MesherHelper helper( *myMesh );
12650   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12651   SMDS_VolumeTool vTool;
12652   TIDSortedElemSet avoidSet;
12653   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12654   int inode;
12655
12656   typedef vector<const SMDS_MeshNode*> TConnectivity;
12657
12658   SMDS_ElemIteratorPtr eIt;
12659   if (elements.empty())
12660     eIt = aMesh->elementsIterator(elemType);
12661   else
12662     eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12663
12664   while (eIt->more())
12665   {
12666     const SMDS_MeshElement* elem = eIt->next();
12667     const int iQuad = elem->IsQuadratic();
12668
12669     // ------------------------------------------------------------------------------------
12670     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12671     // ------------------------------------------------------------------------------------
12672     vector<const SMDS_MeshElement*> presentBndElems;
12673     vector<TConnectivity>           missingBndElems;
12674     TConnectivity nodes;
12675     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12676     {
12677       vTool.SetExternalNormal();
12678       const SMDS_MeshElement* otherVol = 0;
12679       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12680       {
12681         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12682              ( !aroundElements || elements.count( otherVol )))
12683           continue;
12684         const int nbFaceNodes = vTool.NbFaceNodes(iface);
12685         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12686         if ( missType == SMDSAbs_Edge ) // boundary edges
12687         {
12688           nodes.resize( 2+iQuad );
12689           for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12690           {
12691             for ( int j = 0; j < nodes.size(); ++j )
12692               nodes[j] =nn[i+j];
12693             if ( const SMDS_MeshElement* edge =
12694                  aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12695               presentBndElems.push_back( edge );
12696             else
12697               missingBndElems.push_back( nodes );
12698           }
12699         }
12700         else // boundary face
12701         {
12702           nodes.clear();
12703           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12704             nodes.push_back( nn[inode] );
12705           if (iQuad) // add medium nodes
12706             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12707               nodes.push_back( nn[inode] );
12708           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12709           if ( iCenter > 0 )
12710             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12711
12712           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12713                                                                SMDSAbs_Face, /*noMedium=*/false ))
12714             presentBndElems.push_back( f );
12715           else
12716             missingBndElems.push_back( nodes );
12717
12718           if ( targetMesh != myMesh )
12719           {
12720             // add 1D elements on face boundary to be added to a new mesh
12721             const SMDS_MeshElement* edge;
12722             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12723             {
12724               if ( iQuad )
12725                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12726               else
12727                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12728               if ( edge && avoidSet.insert( edge ).second )
12729                 presentBndElems.push_back( edge );
12730             }
12731           }
12732         }
12733       }
12734     }
12735     else                     // elem is a face ------------------------------------------
12736     {
12737       avoidSet.clear(), avoidSet.insert( elem );
12738       int nbNodes = elem->NbCornerNodes();
12739       nodes.resize( 2 /*+ iQuad*/);
12740       for ( int i = 0; i < nbNodes; i++ )
12741       {
12742         nodes[0] = elem->GetNode(i);
12743         nodes[1] = elem->GetNode((i+1)%nbNodes);
12744         if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12745           continue; // not free link
12746
12747         //if ( iQuad )
12748         //nodes[2] = elem->GetNode( i + nbNodes );
12749         if ( const SMDS_MeshElement* edge =
12750              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
12751           presentBndElems.push_back( edge );
12752         else
12753           missingBndElems.push_back( nodes );
12754       }
12755     }
12756
12757     // ---------------------------------
12758     // 2. Add missing boundary elements
12759     // ---------------------------------
12760     if ( targetMesh != myMesh )
12761       // instead of making a map of nodes in this mesh and targetMesh,
12762       // we create nodes with same IDs.
12763       for ( int i = 0; i < missingBndElems.size(); ++i )
12764       {
12765         TConnectivity& srcNodes = missingBndElems[i];
12766         TConnectivity  nodes( srcNodes.size() );
12767         for ( inode = 0; inode < nodes.size(); ++inode )
12768           nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12769         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12770                                                                    missType,
12771                                                                    /*noMedium=*/false))
12772           continue;
12773         tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12774         ++nbAddedBnd;
12775       }
12776     else
12777       for ( int i = 0; i < missingBndElems.size(); ++i )
12778       {
12779         TConnectivity& nodes = missingBndElems[i];
12780         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12781                                                                    missType,
12782                                                                    /*noMedium=*/false))
12783           continue;
12784         SMDS_MeshElement* elem =
12785           tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12786         ++nbAddedBnd;
12787
12788         // try to set a new element to a shape
12789         if ( myMesh->HasShapeToMesh() )
12790         {
12791           bool ok = true;
12792           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12793           const int nbN = nodes.size() / (iQuad+1 );
12794           for ( inode = 0; inode < nbN && ok; ++inode )
12795           {
12796             pair<int, TopAbs_ShapeEnum> i_stype =
12797               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12798             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12799               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12800           }
12801           if ( ok && mediumShapes.size() > 1 )
12802           {
12803             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12804             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12805             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12806             {
12807               if (( ok = ( stype_i->first != stype_i_0.first )))
12808                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12809                                         aMesh->IndexToShape( stype_i_0.second ));
12810             }
12811           }
12812           if ( ok && mediumShapes.begin()->first == missShapeType )
12813             aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12814         }
12815       }
12816
12817     // ----------------------------------
12818     // 3. Copy present boundary elements
12819     // ----------------------------------
12820     if ( toCopyExistingBoundary )
12821       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12822       {
12823         const SMDS_MeshElement* e = presentBndElems[i];
12824         TConnectivity nodes( e->NbNodes() );
12825         for ( inode = 0; inode < nodes.size(); ++inode )
12826           nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12827         presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12828       }
12829     else // store present elements to add them to a group
12830       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12831       {
12832         presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12833       }
12834
12835   } // loop on given elements
12836
12837   // ---------------------------------------------
12838   // 4. Fill group with boundary elements
12839   // ---------------------------------------------
12840   if ( group )
12841   {
12842     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12843       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12844         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12845   }
12846   tgtEditor.myLastCreatedElems.Clear();
12847   tgtEditor2.myLastCreatedElems.Clear();
12848
12849   // -----------------------
12850   // 5. Copy given elements
12851   // -----------------------
12852   if ( toCopyElements && targetMesh != myMesh )
12853   {
12854     if (elements.empty())
12855       eIt = aMesh->elementsIterator(elemType);
12856     else
12857       eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12858     while (eIt->more())
12859     {
12860       const SMDS_MeshElement* elem = eIt->next();
12861       TConnectivity nodes( elem->NbNodes() );
12862       for ( inode = 0; inode < nodes.size(); ++inode )
12863         nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12864       tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12865
12866       tgtEditor.myLastCreatedElems.Clear();
12867     }
12868   }
12869   return nbAddedBnd;
12870 }