Salome HOME
e8756102484c9a0be90835252e8074d3c47ac1a8
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2012  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 map<const SMDS_MeshElement*, list<const SMDS_MeshNode*> >    TElemOfNodeListMap;
111 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshElement*> > TElemOfElemListMap;
112
113 typedef SMDS_SetIterator< SMDS_pElement, TIDSortedElemSet::const_iterator> TSetIterator;
114
115 //=======================================================================
116 //function : SMESH_MeshEditor
117 //purpose  :
118 //=======================================================================
119
120 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
121   :myMesh( theMesh ) // theMesh may be NULL
122 {
123 }
124
125 //================================================================================
126 /*!
127  * \brief Clears myLastCreatedNodes and myLastCreatedElems
128  */
129 //================================================================================
130
131 void SMESH_MeshEditor::CrearLastCreated()
132 {
133   myLastCreatedNodes.Clear();
134   myLastCreatedElems.Clear();
135 }
136
137
138 //=======================================================================
139 /*!
140  * \brief Add element
141  */
142 //=======================================================================
143
144 SMDS_MeshElement*
145 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
146                              const SMDSAbs_ElementType            type,
147                              const bool                           isPoly,
148                              const int                            ID,
149                              const double                         ballDiameter)
150 {
151   //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
152   SMDS_MeshElement* e = 0;
153   int nbnode = node.size();
154   SMESHDS_Mesh* mesh = GetMeshDS();
155   switch ( type ) {
156   case SMDSAbs_Face:
157     if ( !isPoly ) {
158       if      (nbnode == 3) {
159         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
160         else           e = mesh->AddFace      (node[0], node[1], node[2] );
161       }
162       else if (nbnode == 4) {
163         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
164         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
165       }
166       else if (nbnode == 6) {
167         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
168                                                node[4], node[5], ID);
169         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
170                                                node[4], node[5] );
171       }
172       else if (nbnode == 8) {
173         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
174                                                node[4], node[5], node[6], node[7], ID);
175         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
176                                                node[4], node[5], node[6], node[7] );
177       }
178       else if (nbnode == 9) {
179         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
180                                                node[4], node[5], node[6], node[7], node[8], ID);
181         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
182                                                node[4], node[5], node[6], node[7], node[8] );
183       }
184     } else {
185       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
186       else           e = mesh->AddPolygonalFace      (node    );
187     }
188     break;
189
190   case SMDSAbs_Volume:
191     if ( !isPoly ) {
192       if      (nbnode == 4) {
193         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
194         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
195       }
196       else if (nbnode == 5) {
197         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
198                                                  node[4], ID);
199         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
200                                                  node[4] );
201       }
202       else if (nbnode == 6) {
203         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
204                                                  node[4], node[5], ID);
205         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
206                                                  node[4], node[5] );
207       }
208       else if (nbnode == 8) {
209         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
210                                                  node[4], node[5], node[6], node[7], ID);
211         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
212                                                  node[4], node[5], node[6], node[7] );
213       }
214       else if (nbnode == 10) {
215         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
216                                                  node[4], node[5], node[6], node[7],
217                                                  node[8], node[9], ID);
218         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
219                                                  node[4], node[5], node[6], node[7],
220                                                  node[8], node[9] );
221       }
222       else if (nbnode == 12) {
223         if ( ID >= 1 ) e = mesh->AddVolumeWithID(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], ID);
226         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
227                                                  node[4], node[5], node[6], node[7],
228                                                  node[8], node[9], node[10], node[11] );
229       }
230       else if (nbnode == 13) {
231         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
232                                                  node[4], node[5], node[6], node[7],
233                                                  node[8], node[9], node[10],node[11],
234                                                  node[12],ID);
235         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
236                                                  node[4], node[5], node[6], node[7],
237                                                  node[8], node[9], node[10],node[11],
238                                                  node[12] );
239       }
240       else if (nbnode == 15) {
241         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
242                                                  node[4], node[5], node[6], node[7],
243                                                  node[8], node[9], node[10],node[11],
244                                                  node[12],node[13],node[14],ID);
245         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
246                                                  node[4], node[5], node[6], node[7],
247                                                  node[8], node[9], node[10],node[11],
248                                                  node[12],node[13],node[14] );
249       }
250       else if (nbnode == 20) {
251         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
252                                                  node[4], node[5], node[6], node[7],
253                                                  node[8], node[9], node[10],node[11],
254                                                  node[12],node[13],node[14],node[15],
255                                                  node[16],node[17],node[18],node[19],ID);
256         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
257                                                  node[4], node[5], node[6], node[7],
258                                                  node[8], node[9], node[10],node[11],
259                                                  node[12],node[13],node[14],node[15],
260                                                  node[16],node[17],node[18],node[19] );
261       }
262       else if (nbnode == 27) {
263         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
264                                                  node[4], node[5], node[6], node[7],
265                                                  node[8], node[9], node[10],node[11],
266                                                  node[12],node[13],node[14],node[15],
267                                                  node[16],node[17],node[18],node[19],
268                                                  node[20],node[21],node[22],node[23],
269                                                  node[24],node[25],node[26], ID);
270         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
271                                                  node[4], node[5], node[6], node[7],
272                                                  node[8], node[9], node[10],node[11],
273                                                  node[12],node[13],node[14],node[15],
274                                                  node[16],node[17],node[18],node[19],
275                                                  node[20],node[21],node[22],node[23],
276                                                  node[24],node[25],node[26] );
277       }
278     }
279     break;
280
281   case SMDSAbs_Edge:
282     if ( nbnode == 2 ) {
283       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
284       else           e = mesh->AddEdge      (node[0], node[1] );
285     }
286     else if ( nbnode == 3 ) {
287       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
288       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
289     }
290     break;
291
292   case SMDSAbs_0DElement:
293     if ( nbnode == 1 ) {
294       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
295       else           e = mesh->Add0DElement      (node[0] );
296     }
297     break;
298
299   case SMDSAbs_Node:
300     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
301     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z());
302     break;
303
304   case SMDSAbs_Ball:
305     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
306     else           e = mesh->AddBall      (node[0], ballDiameter);
307     break;
308
309   default:;
310   }
311   if ( e ) myLastCreatedElems.Append( e );
312   return e;
313 }
314
315 //=======================================================================
316 /*!
317  * \brief Add element
318  */
319 //=======================================================================
320
321 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> &       nodeIDs,
322                                                const SMDSAbs_ElementType type,
323                                                const bool                isPoly,
324                                                const int                 ID)
325 {
326   vector<const SMDS_MeshNode*> nodes;
327   nodes.reserve( nodeIDs.size() );
328   vector<int>::const_iterator id = nodeIDs.begin();
329   while ( id != nodeIDs.end() ) {
330     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
331       nodes.push_back( node );
332     else
333       return 0;
334   }
335   return AddElement( nodes, type, isPoly, ID );
336 }
337
338 //=======================================================================
339 //function : Remove
340 //purpose  : Remove a node or an element.
341 //           Modify a compute state of sub-meshes which become empty
342 //=======================================================================
343
344 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
345                               const bool         isNodes )
346 {
347   myLastCreatedElems.Clear();
348   myLastCreatedNodes.Clear();
349
350   SMESHDS_Mesh* aMesh = GetMeshDS();
351   set< SMESH_subMesh *> smmap;
352
353   int removed = 0;
354   list<int>::const_iterator it = theIDs.begin();
355   for ( ; it != theIDs.end(); it++ ) {
356     const SMDS_MeshElement * elem;
357     if ( isNodes )
358       elem = aMesh->FindNode( *it );
359     else
360       elem = aMesh->FindElement( *it );
361     if ( !elem )
362       continue;
363
364     // Notify VERTEX sub-meshes about modification
365     if ( isNodes ) {
366       const SMDS_MeshNode* node = cast2Node( elem );
367       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
368         if ( int aShapeID = node->getshapeId() )
369           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
370             smmap.insert( sm );
371     }
372     // Find sub-meshes to notify about modification
373     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
374     //     while ( nodeIt->more() ) {
375     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
376     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
377     //       if ( aPosition.get() ) {
378     //         if ( int aShapeID = aPosition->GetShapeId() ) {
379     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
380     //             smmap.insert( sm );
381     //         }
382     //       }
383     //     }
384
385     // Do remove
386     if ( isNodes )
387       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
388     else
389       aMesh->RemoveElement( elem );
390     removed++;
391   }
392
393   // Notify sub-meshes about modification
394   if ( !smmap.empty() ) {
395     set< SMESH_subMesh *>::iterator smIt;
396     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
397       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
398   }
399
400   //   // Check if the whole mesh becomes empty
401   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
402   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
403
404   return removed;
405 }
406
407 //================================================================================
408 /*!
409  * \brief Create 0D elements on all nodes of the given object except those
410  *        nodes on which a 0D element already exists.
411  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
412  *                    the all mesh is treated
413  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
414  */
415 //================================================================================
416
417 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
418                                                    TIDSortedElemSet&       all0DElems )
419 {
420   typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::const_iterator> TSetIterator;
421   SMDS_ElemIteratorPtr elemIt;
422   if ( elements.empty() )
423     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
424   else
425     elemIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
426
427   while ( elemIt->more() )
428   {
429     const SMDS_MeshElement* e = elemIt->next();
430     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
431     while ( nodeIt->more() )
432     {
433       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
434       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
435       if ( it0D->more() )
436         all0DElems.insert( it0D->next() );
437       else {
438         myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
439         all0DElems.insert( myLastCreatedElems.Last() );
440       }
441     }
442   }
443 }
444
445 //=======================================================================
446 //function : FindShape
447 //purpose  : Return an index of the shape theElem is on
448 //           or zero if a shape not found
449 //=======================================================================
450
451 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
452 {
453   myLastCreatedElems.Clear();
454   myLastCreatedNodes.Clear();
455
456   SMESHDS_Mesh * aMesh = GetMeshDS();
457   if ( aMesh->ShapeToMesh().IsNull() )
458     return 0;
459
460   int aShapeID = theElem->getshapeId();
461   if ( aShapeID < 1 )
462     return 0;
463
464   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
465     if ( sm->Contains( theElem ))
466       return aShapeID;
467
468   if ( theElem->GetType() == SMDSAbs_Node ) {
469     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
470   }
471   else {
472     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
473   }
474
475   TopoDS_Shape aShape; // the shape a node of theElem is on
476   if ( theElem->GetType() != SMDSAbs_Node )
477   {
478     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
479     while ( nodeIt->more() ) {
480       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
481       if ((aShapeID = node->getshapeId()) > 0) {
482         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
483           if ( sm->Contains( theElem ))
484             return aShapeID;
485           if ( aShape.IsNull() )
486             aShape = aMesh->IndexToShape( aShapeID );
487         }
488       }
489     }
490   }
491
492   // None of nodes is on a proper shape,
493   // find the shape among ancestors of aShape on which a node is
494   if ( !aShape.IsNull() ) {
495     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
496     for ( ; ancIt.More(); ancIt.Next() ) {
497       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
498       if ( sm && sm->Contains( theElem ))
499         return aMesh->ShapeToIndex( ancIt.Value() );
500     }
501   }
502   else
503   {
504     const map<int,SMESHDS_SubMesh*>& id2sm = GetMeshDS()->SubMeshes();
505     map<int,SMESHDS_SubMesh*>::const_iterator id_sm = id2sm.begin();
506     for ( ; id_sm != id2sm.end(); ++id_sm )
507       if ( id_sm->second->Contains( theElem ))
508         return id_sm->first;
509   }
510
511   //MESSAGE ("::FindShape() - SHAPE NOT FOUND")
512   return 0;
513 }
514
515 //=======================================================================
516 //function : IsMedium
517 //purpose  :
518 //=======================================================================
519
520 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
521                                 const SMDSAbs_ElementType typeToCheck)
522 {
523   bool isMedium = false;
524   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
525   while (it->more() && !isMedium ) {
526     const SMDS_MeshElement* elem = it->next();
527     isMedium = elem->IsMediumNode(node);
528   }
529   return isMedium;
530 }
531
532 //=======================================================================
533 //function : ShiftNodesQuadTria
534 //purpose  : auxilary
535 //           Shift nodes in the array corresponded to quadratic triangle
536 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
537 //=======================================================================
538 static void ShiftNodesQuadTria(const SMDS_MeshNode* aNodes[])
539 {
540   const SMDS_MeshNode* nd1 = aNodes[0];
541   aNodes[0] = aNodes[1];
542   aNodes[1] = aNodes[2];
543   aNodes[2] = nd1;
544   const SMDS_MeshNode* nd2 = aNodes[3];
545   aNodes[3] = aNodes[4];
546   aNodes[4] = aNodes[5];
547   aNodes[5] = nd2;
548 }
549
550 //=======================================================================
551 //function : edgeConnectivity
552 //purpose  : auxilary
553 //           return number of the edges connected with the theNode.
554 //           if theEdges has connections with the other type of the
555 //           elements, return -1
556 //=======================================================================
557 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
558 {
559   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
560   int nb=0;
561   while(elemIt->more()) {
562     elemIt->next();
563     nb++;
564   }
565   return nb;
566 }
567
568
569 //=======================================================================
570 //function : GetNodesFromTwoTria
571 //purpose  : auxilary
572 //           Shift nodes in the array corresponded to quadratic triangle
573 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
574 //=======================================================================
575 static bool GetNodesFromTwoTria(const SMDS_MeshElement * theTria1,
576                                 const SMDS_MeshElement * theTria2,
577                                 const SMDS_MeshNode* N1[],
578                                 const SMDS_MeshNode* N2[])
579 {
580   SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
581   int i=0;
582   while(i<6) {
583     N1[i] = static_cast<const SMDS_MeshNode*>( it->next() );
584     i++;
585   }
586   if(it->more()) return false;
587   it = theTria2->nodesIterator();
588   i=0;
589   while(i<6) {
590     N2[i] = static_cast<const SMDS_MeshNode*>( it->next() );
591     i++;
592   }
593   if(it->more()) return false;
594
595   int sames[3] = {-1,-1,-1};
596   int nbsames = 0;
597   int j;
598   for(i=0; i<3; i++) {
599     for(j=0; j<3; j++) {
600       if(N1[i]==N2[j]) {
601         sames[i] = j;
602         nbsames++;
603         break;
604       }
605     }
606   }
607   if(nbsames!=2) return false;
608   if(sames[0]>-1) {
609     ShiftNodesQuadTria(N1);
610     if(sames[1]>-1) {
611       ShiftNodesQuadTria(N1);
612     }
613   }
614   i = sames[0] + sames[1] + sames[2];
615   for(; i<2; i++) {
616     ShiftNodesQuadTria(N2);
617   }
618   // now we receive following N1 and N2 (using numeration as above image)
619   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
620   // i.e. first nodes from both arrays determ new diagonal
621   return true;
622 }
623
624 //=======================================================================
625 //function : InverseDiag
626 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
627 //           but having other common link.
628 //           Return False if args are improper
629 //=======================================================================
630
631 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
632                                     const SMDS_MeshElement * theTria2 )
633 {
634   MESSAGE("InverseDiag");
635   myLastCreatedElems.Clear();
636   myLastCreatedNodes.Clear();
637
638   if (!theTria1 || !theTria2)
639     return false;
640
641   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
642   if (!F1) return false;
643   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
644   if (!F2) return false;
645   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
646       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
647
648     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
649     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
650     //    |/ |                                         | \|
651     //  B +--+ 2                                     B +--+ 2
652
653     // put nodes in array and find out indices of the same ones
654     const SMDS_MeshNode* aNodes [6];
655     int sameInd [] = { 0, 0, 0, 0, 0, 0 };
656     int i = 0;
657     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
658     while ( it->more() ) {
659       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
660
661       if ( i > 2 ) // theTria2
662         // find same node of theTria1
663         for ( int j = 0; j < 3; j++ )
664           if ( aNodes[ i ] == aNodes[ j ]) {
665             sameInd[ j ] = i;
666             sameInd[ i ] = j;
667             break;
668           }
669       // next
670       i++;
671       if ( i == 3 ) {
672         if ( it->more() )
673           return false; // theTria1 is not a triangle
674         it = theTria2->nodesIterator();
675       }
676       if ( i == 6 && it->more() )
677         return false; // theTria2 is not a triangle
678     }
679
680     // find indices of 1,2 and of A,B in theTria1
681     int iA = 0, iB = 0, i1 = 0, i2 = 0;
682     for ( i = 0; i < 6; i++ ) {
683       if ( sameInd [ i ] == 0 ) {
684         if ( i < 3 ) i1 = i;
685         else         i2 = i;
686       }
687       else if (i < 3) {
688         if ( iA ) iB = i;
689         else      iA = i;
690       }
691     }
692     // nodes 1 and 2 should not be the same
693     if ( aNodes[ i1 ] == aNodes[ i2 ] )
694       return false;
695
696     // theTria1: A->2
697     aNodes[ iA ] = aNodes[ i2 ];
698     // theTria2: B->1
699     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
700
701     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
702     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
703
704     return true;
705
706   } // end if(F1 && F2)
707
708   // check case of quadratic faces
709   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle)
710     return false;
711   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle)
712     return false;
713
714   //       5
715   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
716   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
717   //    |   / |
718   //  7 +  +  + 6
719   //    | /9  |
720   //    |/    |
721   //  4 +--+--+ 3
722   //       8
723
724   const SMDS_MeshNode* N1 [6];
725   const SMDS_MeshNode* N2 [6];
726   if(!GetNodesFromTwoTria(theTria1,theTria2,N1,N2))
727     return false;
728   // now we receive following N1 and N2 (using numeration as above image)
729   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
730   // i.e. first nodes from both arrays determ new diagonal
731
732   const SMDS_MeshNode* N1new [6];
733   const SMDS_MeshNode* N2new [6];
734   N1new[0] = N1[0];
735   N1new[1] = N2[0];
736   N1new[2] = N2[1];
737   N1new[3] = N1[4];
738   N1new[4] = N2[3];
739   N1new[5] = N1[5];
740   N2new[0] = N1[0];
741   N2new[1] = N1[1];
742   N2new[2] = N2[0];
743   N2new[3] = N1[3];
744   N2new[4] = N2[5];
745   N2new[5] = N1[4];
746   // replaces nodes in faces
747   GetMeshDS()->ChangeElementNodes( theTria1, N1new, 6 );
748   GetMeshDS()->ChangeElementNodes( theTria2, N2new, 6 );
749
750   return true;
751 }
752
753 //=======================================================================
754 //function : findTriangles
755 //purpose  : find triangles sharing theNode1-theNode2 link
756 //=======================================================================
757
758 static bool findTriangles(const SMDS_MeshNode *    theNode1,
759                           const SMDS_MeshNode *    theNode2,
760                           const SMDS_MeshElement*& theTria1,
761                           const SMDS_MeshElement*& theTria2)
762 {
763   if ( !theNode1 || !theNode2 ) return false;
764
765   theTria1 = theTria2 = 0;
766
767   set< const SMDS_MeshElement* > emap;
768   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
769   while (it->more()) {
770     const SMDS_MeshElement* elem = it->next();
771     if ( elem->NbNodes() == 3 )
772       emap.insert( elem );
773   }
774   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
775   while (it->more()) {
776     const SMDS_MeshElement* elem = it->next();
777     if ( emap.find( elem ) != emap.end() ) {
778       if ( theTria1 ) {
779         // theTria1 must be element with minimum ID
780         if( theTria1->GetID() < elem->GetID() ) {
781           theTria2 = elem;
782         }
783         else {
784           theTria2 = theTria1;
785           theTria1 = elem;
786         }
787         break;
788       }
789       else {
790         theTria1 = elem;
791       }
792     }
793   }
794   return ( theTria1 && theTria2 );
795 }
796
797 //=======================================================================
798 //function : InverseDiag
799 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
800 //           with ones built on the same 4 nodes but having other common link.
801 //           Return false if proper faces not found
802 //=======================================================================
803
804 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
805                                     const SMDS_MeshNode * theNode2)
806 {
807   myLastCreatedElems.Clear();
808   myLastCreatedNodes.Clear();
809
810   MESSAGE( "::InverseDiag()" );
811
812   const SMDS_MeshElement *tr1, *tr2;
813   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
814     return false;
815
816   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
817   if (!F1) return false;
818   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
819   if (!F2) return false;
820   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
821       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
822
823     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
824     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
825     //    |/ |                                    | \|
826     //  B +--+ 2                                B +--+ 2
827
828     // put nodes in array
829     // and find indices of 1,2 and of A in tr1 and of B in tr2
830     int i, iA1 = 0, i1 = 0;
831     const SMDS_MeshNode* aNodes1 [3];
832     SMDS_ElemIteratorPtr it;
833     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
834       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
835       if ( aNodes1[ i ] == theNode1 )
836         iA1 = i; // node A in tr1
837       else if ( aNodes1[ i ] != theNode2 )
838         i1 = i;  // node 1
839     }
840     int iB2 = 0, i2 = 0;
841     const SMDS_MeshNode* aNodes2 [3];
842     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
843       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
844       if ( aNodes2[ i ] == theNode2 )
845         iB2 = i; // node B in tr2
846       else if ( aNodes2[ i ] != theNode1 )
847         i2 = i;  // node 2
848     }
849
850     // nodes 1 and 2 should not be the same
851     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
852       return false;
853
854     // tr1: A->2
855     aNodes1[ iA1 ] = aNodes2[ i2 ];
856     // tr2: B->1
857     aNodes2[ iB2 ] = aNodes1[ i1 ];
858
859     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
860     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
861
862     return true;
863   }
864
865   // check case of quadratic faces
866   return InverseDiag(tr1,tr2);
867 }
868
869 //=======================================================================
870 //function : getQuadrangleNodes
871 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
872 //           fusion of triangles tr1 and tr2 having shared link on
873 //           theNode1 and theNode2
874 //=======================================================================
875
876 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
877                         const SMDS_MeshNode *    theNode1,
878                         const SMDS_MeshNode *    theNode2,
879                         const SMDS_MeshElement * tr1,
880                         const SMDS_MeshElement * tr2 )
881 {
882   if( tr1->NbNodes() != tr2->NbNodes() )
883     return false;
884   // find the 4-th node to insert into tr1
885   const SMDS_MeshNode* n4 = 0;
886   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
887   int i=0;
888   while ( !n4 && i<3 ) {
889     const SMDS_MeshNode * n = cast2Node( it->next() );
890     i++;
891     bool isDiag = ( n == theNode1 || n == theNode2 );
892     if ( !isDiag )
893       n4 = n;
894   }
895   // Make an array of nodes to be in a quadrangle
896   int iNode = 0, iFirstDiag = -1;
897   it = tr1->nodesIterator();
898   i=0;
899   while ( i<3 ) {
900     const SMDS_MeshNode * n = cast2Node( it->next() );
901     i++;
902     bool isDiag = ( n == theNode1 || n == theNode2 );
903     if ( isDiag ) {
904       if ( iFirstDiag < 0 )
905         iFirstDiag = iNode;
906       else if ( iNode - iFirstDiag == 1 )
907         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
908     }
909     else if ( n == n4 ) {
910       return false; // tr1 and tr2 should not have all the same nodes
911     }
912     theQuadNodes[ iNode++ ] = n;
913   }
914   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
915     theQuadNodes[ iNode ] = n4;
916
917   return true;
918 }
919
920 //=======================================================================
921 //function : DeleteDiag
922 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
923 //           with a quadrangle built on the same 4 nodes.
924 //           Return false if proper faces not found
925 //=======================================================================
926
927 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
928                                    const SMDS_MeshNode * theNode2)
929 {
930   myLastCreatedElems.Clear();
931   myLastCreatedNodes.Clear();
932
933   MESSAGE( "::DeleteDiag()" );
934
935   const SMDS_MeshElement *tr1, *tr2;
936   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
937     return false;
938
939   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
940   if (!F1) return false;
941   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
942   if (!F2) return false;
943   SMESHDS_Mesh * aMesh = GetMeshDS();
944
945   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
946       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
947
948     const SMDS_MeshNode* aNodes [ 4 ];
949     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
950       return false;
951
952     const SMDS_MeshElement* newElem = 0;
953     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
954     myLastCreatedElems.Append(newElem);
955     AddToSameGroups( newElem, tr1, aMesh );
956     int aShapeId = tr1->getshapeId();
957     if ( aShapeId )
958       {
959         aMesh->SetMeshElementOnShape( newElem, aShapeId );
960       }
961     aMesh->RemoveElement( tr1 );
962     aMesh->RemoveElement( tr2 );
963
964     return true;
965   }
966
967   // check case of quadratic faces
968   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
969     return false;
970   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
971     return false;
972
973   //       5
974   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
975   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
976   //    |   / |
977   //  7 +  +  + 6
978   //    | /9  |
979   //    |/    |
980   //  4 +--+--+ 3
981   //       8
982
983   const SMDS_MeshNode* N1 [6];
984   const SMDS_MeshNode* N2 [6];
985   if(!GetNodesFromTwoTria(tr1,tr2,N1,N2))
986     return false;
987   // now we receive following N1 and N2 (using numeration as above image)
988   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
989   // i.e. first nodes from both arrays determ new diagonal
990
991   const SMDS_MeshNode* aNodes[8];
992   aNodes[0] = N1[0];
993   aNodes[1] = N1[1];
994   aNodes[2] = N2[0];
995   aNodes[3] = N2[1];
996   aNodes[4] = N1[3];
997   aNodes[5] = N2[5];
998   aNodes[6] = N2[3];
999   aNodes[7] = N1[5];
1000
1001   const SMDS_MeshElement* newElem = 0;
1002   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1003                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1004   myLastCreatedElems.Append(newElem);
1005   AddToSameGroups( newElem, tr1, aMesh );
1006   int aShapeId = tr1->getshapeId();
1007   if ( aShapeId )
1008     {
1009       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1010     }
1011   aMesh->RemoveElement( tr1 );
1012   aMesh->RemoveElement( tr2 );
1013
1014   // remove middle node (9)
1015   GetMeshDS()->RemoveNode( N1[4] );
1016
1017   return true;
1018 }
1019
1020 //=======================================================================
1021 //function : Reorient
1022 //purpose  : Reverse theElement orientation
1023 //=======================================================================
1024
1025 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1026 {
1027   MESSAGE("Reorient");
1028   myLastCreatedElems.Clear();
1029   myLastCreatedNodes.Clear();
1030
1031   if (!theElem)
1032     return false;
1033   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1034   if ( !it || !it->more() )
1035     return false;
1036
1037   switch ( theElem->GetType() ) {
1038
1039   case SMDSAbs_Edge:
1040   case SMDSAbs_Face: {
1041     if(!theElem->IsQuadratic()) {
1042       int i = theElem->NbNodes();
1043       vector<const SMDS_MeshNode*> aNodes( i );
1044       while ( it->more() )
1045         aNodes[ --i ]= static_cast<const SMDS_MeshNode*>( it->next() );
1046       return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], theElem->NbNodes() );
1047     }
1048     else {
1049       // quadratic elements
1050       if(theElem->GetType()==SMDSAbs_Edge) {
1051         vector<const SMDS_MeshNode*> aNodes(3);
1052         aNodes[1]= static_cast<const SMDS_MeshNode*>( it->next() );
1053         aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1054         aNodes[2]= static_cast<const SMDS_MeshNode*>( it->next() );
1055         return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], 3 );
1056       }
1057       else {
1058         int nbn = theElem->NbNodes();
1059         vector<const SMDS_MeshNode*> aNodes(nbn);
1060         aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1061         int i=1;
1062         for(; i<nbn/2; i++) {
1063           aNodes[nbn/2-i]= static_cast<const SMDS_MeshNode*>( it->next() );
1064         }
1065         for(i=0; i<nbn/2; i++) {
1066           aNodes[nbn-i-1]= static_cast<const SMDS_MeshNode*>( it->next() );
1067         }
1068         return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], nbn );
1069       }
1070     }
1071   }
1072   case SMDSAbs_Volume: {
1073     if (theElem->IsPoly()) {
1074       // TODO reorient vtk polyhedron
1075       MESSAGE("reorient vtk polyhedron ?");
1076       const SMDS_VtkVolume* aPolyedre =
1077         dynamic_cast<const SMDS_VtkVolume*>( theElem );
1078       if (!aPolyedre) {
1079         MESSAGE("Warning: bad volumic element");
1080         return false;
1081       }
1082
1083       int nbFaces = aPolyedre->NbFaces();
1084       vector<const SMDS_MeshNode *> poly_nodes;
1085       vector<int> quantities (nbFaces);
1086
1087       // reverse each face of the polyedre
1088       for (int iface = 1; iface <= nbFaces; iface++) {
1089         int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1090         quantities[iface - 1] = nbFaceNodes;
1091
1092         for (inode = nbFaceNodes; inode >= 1; inode--) {
1093           const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1094           poly_nodes.push_back(curNode);
1095         }
1096       }
1097
1098       return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1099
1100     }
1101     else {
1102       SMDS_VolumeTool vTool;
1103       if ( !vTool.Set( theElem ))
1104         return false;
1105       vTool.Inverse();
1106       MESSAGE("ChangeElementNodes reorient: check vTool.Inverse");
1107       return GetMeshDS()->ChangeElementNodes( theElem, vTool.GetNodes(), vTool.NbNodes() );
1108     }
1109   }
1110   default:;
1111   }
1112
1113   return false;
1114 }
1115
1116 //================================================================================
1117 /*!
1118  * \brief Reorient faces.
1119  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1120  * \param theDirection - desired direction of normal of \a theFace
1121  * \param theFace - one of \a theFaces that sould be oriented according to
1122  *        \a theDirection and whose orientation defines orientation of other faces
1123  * \return number of reoriented faces.
1124  */
1125 //================================================================================
1126
1127 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1128                                   const gp_Dir&            theDirection,
1129                                   const SMDS_MeshElement * theFace)
1130 {
1131   int nbReori = 0;
1132   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1133
1134   if ( theFaces.empty() )
1135   {
1136     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1137     while ( fIt->more() )
1138       theFaces.insert( theFaces.end(), fIt->next() );
1139   }
1140
1141   // orient theFace according to theDirection
1142   gp_XYZ normal;
1143   SMESH_Algo::FaceNormal( theFace, normal, /*normalized=*/false );
1144   if ( normal * theDirection.XYZ() < 0 )
1145     nbReori += Reorient( theFace );
1146
1147   // Orient other faces
1148
1149   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1150   TIDSortedElemSet avoidSet;
1151   set< SMESH_TLink > checkedLinks;
1152   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1153
1154   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1155     theFaces.erase( theFace );
1156   startFaces.insert( theFace );
1157
1158   int nodeInd1, nodeInd2;
1159   const SMDS_MeshElement*           otherFace;
1160   vector< const SMDS_MeshElement* > facesNearLink;
1161   vector< std::pair< int, int > >   nodeIndsOfFace;
1162
1163   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1164   while ( !startFaces.empty() )
1165   {
1166     startFace = startFaces.begin();
1167     theFace = *startFace;
1168     startFaces.erase( startFace );
1169     if ( !visitedFaces.insert( theFace ).second )
1170       continue;
1171
1172     avoidSet.clear();
1173     avoidSet.insert(theFace);
1174
1175     NLink link( theFace->GetNode( 0 ), 0 );
1176
1177     const int nbNodes = theFace->NbCornerNodes();
1178     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1179     {
1180       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1181       linkIt_isNew = checkedLinks.insert( link );
1182       if ( !linkIt_isNew.second )
1183       {
1184         // link has already been checked and won't be encountered more
1185         // if the group (theFaces) is manifold
1186         //checkedLinks.erase( linkIt_isNew.first );
1187       }
1188       else
1189       {
1190         facesNearLink.clear();
1191         nodeIndsOfFace.clear();
1192         while (( otherFace = FindFaceInSet( link.first, link.second,
1193                                             theFaces, avoidSet, &nodeInd1, &nodeInd2 )))
1194           if ( otherFace != theFace)
1195           {
1196             facesNearLink.push_back( otherFace );
1197             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1198             avoidSet.insert( otherFace );
1199           }
1200         if ( facesNearLink.size() > 1 )
1201         {
1202           // NON-MANIFOLD mesh shell !
1203           // select a face most co-directed with theFace,
1204           // other faces won't be visited this time
1205           gp_XYZ NF, NOF;
1206           SMESH_Algo::FaceNormal( theFace, NF, /*normalized=*/false );
1207           double proj, maxProj = -1;
1208           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1209             SMESH_Algo::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1210             if (( proj = Abs( NF * NOF )) > maxProj ) {
1211               maxProj = proj;
1212               otherFace = facesNearLink[i];
1213               nodeInd1  = nodeIndsOfFace[i].first;
1214               nodeInd2  = nodeIndsOfFace[i].second;
1215             }
1216           }
1217           // not to visit rejected faces
1218           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1219             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1220               visitedFaces.insert( facesNearLink[i] );
1221         }
1222         else if ( facesNearLink.size() == 1 )
1223         {
1224           otherFace = facesNearLink[0];
1225           nodeInd1  = nodeIndsOfFace.back().first;
1226           nodeInd2  = nodeIndsOfFace.back().second;
1227         }
1228         if ( otherFace && otherFace != theFace)
1229         {
1230           // link must be reverse in otherFace if orientation ot otherFace
1231           // is same as that of theFace
1232           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1233           {
1234             nbReori += Reorient( otherFace );
1235           }
1236           startFaces.insert( otherFace );
1237         }
1238       }
1239       std::swap( link.first, link.second ); // reverse the link
1240     }
1241   }
1242   return nbReori;
1243 }
1244
1245 //=======================================================================
1246 //function : getBadRate
1247 //purpose  :
1248 //=======================================================================
1249
1250 static double getBadRate (const SMDS_MeshElement*               theElem,
1251                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1252 {
1253   SMESH::Controls::TSequenceOfXYZ P;
1254   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1255     return 1e100;
1256   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1257   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1258 }
1259
1260 //=======================================================================
1261 //function : QuadToTri
1262 //purpose  : Cut quadrangles into triangles.
1263 //           theCrit is used to select a diagonal to cut
1264 //=======================================================================
1265
1266 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1267                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1268 {
1269   myLastCreatedElems.Clear();
1270   myLastCreatedNodes.Clear();
1271
1272   MESSAGE( "::QuadToTri()" );
1273
1274   if ( !theCrit.get() )
1275     return false;
1276
1277   SMESHDS_Mesh * aMesh = GetMeshDS();
1278
1279   Handle(Geom_Surface) surface;
1280   SMESH_MesherHelper   helper( *GetMesh() );
1281
1282   TIDSortedElemSet::iterator itElem;
1283   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1284     const SMDS_MeshElement* elem = *itElem;
1285     if ( !elem || elem->GetType() != SMDSAbs_Face )
1286       continue;
1287     if ( elem->NbCornerNodes() != 4 )
1288       continue;
1289
1290     // retrieve element nodes
1291     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1292
1293     // compare two sets of possible triangles
1294     double aBadRate1, aBadRate2; // to what extent a set is bad
1295     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1296     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1297     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1298
1299     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1300     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1301     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1302
1303     int aShapeId = FindShape( elem );
1304     const SMDS_MeshElement* newElem1 = 0;
1305     const SMDS_MeshElement* newElem2 = 0;
1306
1307     if( !elem->IsQuadratic() ) {
1308
1309       // split liner quadrangle
1310       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1311       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1312       if ( aBadRate1 <= aBadRate2 ) {
1313         // tr1 + tr2 is better
1314         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1315         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1316       }
1317       else {
1318         // tr3 + tr4 is better
1319         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1320         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1321       }
1322     }
1323     else {
1324
1325       // split quadratic quadrangle
1326
1327       // get surface elem is on
1328       if ( aShapeId != helper.GetSubShapeID() ) {
1329         surface.Nullify();
1330         TopoDS_Shape shape;
1331         if ( aShapeId > 0 )
1332           shape = aMesh->IndexToShape( aShapeId );
1333         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1334           TopoDS_Face face = TopoDS::Face( shape );
1335           surface = BRep_Tool::Surface( face );
1336           if ( !surface.IsNull() )
1337             helper.SetSubShape( shape );
1338         }
1339       }
1340       // find middle point for (0,1,2,3)
1341       // and create a node in this point;
1342       const SMDS_MeshNode* newN = 0;
1343       if ( aNodes.size() == 9 )
1344       {
1345         // SMDSEntity_BiQuad_Quadrangle
1346         newN = aNodes.back();
1347       }
1348       else
1349       {
1350         gp_XYZ p( 0,0,0 );
1351         if ( surface.IsNull() )
1352         {
1353           for ( int i = 0; i < 4; i++ )
1354             p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1355           p /= 4;
1356         }
1357         else
1358         {
1359           const SMDS_MeshNode* inFaceNode = 0;
1360           if ( helper.GetNodeUVneedInFaceNode() )
1361             for ( size_t i = 0; i < aNodes.size() && !inFaceNode; ++i )
1362               if ( aNodes[ i ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1363                 inFaceNode = aNodes[ i ];
1364
1365           TopoDS_Face face = TopoDS::Face( helper.GetSubShape() );
1366           gp_XY uv( 0,0 );
1367           for ( int i = 0; i < 4; i++ )
1368             uv += helper.GetNodeUV( face, aNodes[i], inFaceNode );
1369           uv /= 4.;
1370           p = surface->Value( uv.X(), uv.Y() ).XYZ();
1371         }
1372         newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1373         myLastCreatedNodes.Append(newN);
1374       }
1375       // create a new element
1376       if ( aBadRate1 <= aBadRate2 ) {
1377         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1378                                   aNodes[6], aNodes[7], newN );
1379         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
1380                                   newN,      aNodes[4], aNodes[5] );
1381       }
1382       else {
1383         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1384                                   aNodes[7], aNodes[4], newN );
1385         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
1386                                   newN,      aNodes[5], aNodes[6] );
1387       }
1388     } // quadratic case
1389
1390     // care of a new element
1391
1392     myLastCreatedElems.Append(newElem1);
1393     myLastCreatedElems.Append(newElem2);
1394     AddToSameGroups( newElem1, elem, aMesh );
1395     AddToSameGroups( newElem2, elem, aMesh );
1396
1397     // put a new triangle on the same shape
1398     if ( aShapeId )
1399       {
1400         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1401         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1402       }
1403     aMesh->RemoveElement( elem );
1404   }
1405   return true;
1406 }
1407
1408 //=======================================================================
1409 //function : BestSplit
1410 //purpose  : Find better diagonal for cutting.
1411 //=======================================================================
1412
1413 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1414                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1415 {
1416   myLastCreatedElems.Clear();
1417   myLastCreatedNodes.Clear();
1418
1419   if (!theCrit.get())
1420     return -1;
1421
1422   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1423     return -1;
1424
1425   if( theQuad->NbNodes()==4 ||
1426       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1427
1428     // retrieve element nodes
1429     const SMDS_MeshNode* aNodes [4];
1430     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1431     int i = 0;
1432     //while (itN->more())
1433     while (i<4) {
1434       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1435     }
1436     // compare two sets of possible triangles
1437     double aBadRate1, aBadRate2; // to what extent a set is bad
1438     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1439     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1440     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1441
1442     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1443     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1444     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1445     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1446     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1447     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1448       return 1; // diagonal 1-3
1449
1450     return 2; // diagonal 2-4
1451   }
1452   return -1;
1453 }
1454
1455 namespace
1456 {
1457   // Methods of splitting volumes into tetra
1458
1459   const int theHexTo5_1[5*4+1] =
1460     {
1461       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1462     };
1463   const int theHexTo5_2[5*4+1] =
1464     {
1465       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1466     };
1467   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1468
1469   const int theHexTo6_1[6*4+1] =
1470     {
1471       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
1472     };
1473   const int theHexTo6_2[6*4+1] =
1474     {
1475       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
1476     };
1477   const int theHexTo6_3[6*4+1] =
1478     {
1479       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
1480     };
1481   const int theHexTo6_4[6*4+1] =
1482     {
1483       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
1484     };
1485   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1486
1487   const int thePyraTo2_1[2*4+1] =
1488     {
1489       0, 1, 2, 4,    0, 2, 3, 4,   -1
1490     };
1491   const int thePyraTo2_2[2*4+1] =
1492     {
1493       1, 2, 3, 4,    1, 3, 0, 4,   -1
1494     };
1495   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1496
1497   const int thePentaTo3_1[3*4+1] =
1498     {
1499       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1500     };
1501   const int thePentaTo3_2[3*4+1] =
1502     {
1503       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1504     };
1505   const int thePentaTo3_3[3*4+1] =
1506     {
1507       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1508     };
1509   const int thePentaTo3_4[3*4+1] =
1510     {
1511       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1512     };
1513   const int thePentaTo3_5[3*4+1] =
1514     {
1515       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1516     };
1517   const int thePentaTo3_6[3*4+1] =
1518     {
1519       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1520     };
1521   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1522                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1523
1524   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1525   {
1526     int _n1, _n2, _n3;
1527     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1528     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1529     bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1530   };
1531   struct TSplitMethod
1532   {
1533     int        _nbTetra;
1534     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1535     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1536     bool       _ownConn;      //!< to delete _connectivity in destructor
1537     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1538
1539     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1540       : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1541     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1542     bool hasFacet( const TTriangleFacet& facet ) const
1543     {
1544       const int* tetConn = _connectivity;
1545       for ( ; tetConn[0] >= 0; tetConn += 4 )
1546         if (( facet.contains( tetConn[0] ) +
1547               facet.contains( tetConn[1] ) +
1548               facet.contains( tetConn[2] ) +
1549               facet.contains( tetConn[3] )) == 3 )
1550           return true;
1551       return false;
1552     }
1553   };
1554
1555   //=======================================================================
1556   /*!
1557    * \brief return TSplitMethod for the given element
1558    */
1559   //=======================================================================
1560
1561   TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1562   {
1563     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1564
1565     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1566     // an edge and a face barycenter; tertaherdons are based on triangles and
1567     // a volume barycenter
1568     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1569
1570     // Find out how adjacent volumes are split
1571
1572     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1573     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1574     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1575     {
1576       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1577       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1578       if ( nbNodes < 4 ) continue;
1579
1580       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1581       const int* nInd = vol.GetFaceNodesIndices( iF );
1582       if ( nbNodes == 4 )
1583       {
1584         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1585         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1586         if      ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1587         else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1588       }
1589       else
1590       {
1591         int iCom = 0; // common node of triangle faces to split into
1592         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1593         {
1594           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1595                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1596                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1597           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1598                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1599                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1600           if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1601           {
1602             triaSplits.push_back( t012 );
1603             triaSplits.push_back( t023 );
1604             break;
1605           }
1606         }
1607       }
1608       if ( !triaSplits.empty() )
1609         hasAdjacentSplits = true;
1610     }
1611
1612     // Among variants of split method select one compliant with adjacent volumes
1613
1614     TSplitMethod method;
1615     if ( !vol.Element()->IsPoly() && !is24TetMode )
1616     {
1617       int nbVariants = 2, nbTet = 0;
1618       const int** connVariants = 0;
1619       switch ( vol.Element()->GetEntityType() )
1620       {
1621       case SMDSEntity_Hexa:
1622       case SMDSEntity_Quad_Hexa:
1623       case SMDSEntity_TriQuad_Hexa:
1624         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1625           connVariants = theHexTo5, nbTet = 5;
1626         else
1627           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1628         break;
1629       case SMDSEntity_Pyramid:
1630       case SMDSEntity_Quad_Pyramid:
1631         connVariants = thePyraTo2;  nbTet = 2;
1632         break;
1633       case SMDSEntity_Penta:
1634       case SMDSEntity_Quad_Penta:
1635         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1636         break;
1637       default:
1638         nbVariants = 0;
1639       }
1640       for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1641       {
1642         // check method compliancy with adjacent tetras,
1643         // all found splits must be among facets of tetras described by this method
1644         method = TSplitMethod( nbTet, connVariants[variant] );
1645         if ( hasAdjacentSplits && method._nbTetra > 0 )
1646         {
1647           bool facetCreated = true;
1648           for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1649           {
1650             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1651             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1652               facetCreated = method.hasFacet( *facet );
1653           }
1654           if ( !facetCreated )
1655             method = TSplitMethod(0); // incompatible method
1656         }
1657       }
1658     }
1659     if ( method._nbTetra < 1 )
1660     {
1661       // No standard method is applicable, use a generic solution:
1662       // each facet of a volume is split into triangles and
1663       // each of triangles and a volume barycenter form a tetrahedron.
1664
1665       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1666
1667       int* connectivity = new int[ maxTetConnSize + 1 ];
1668       method._connectivity = connectivity;
1669       method._ownConn = true;
1670       method._baryNode = !isHex27; // to create central node or not
1671
1672       int connSize = 0;
1673       int baryCenInd = vol.NbNodes() - int( isHex27 );
1674       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1675       {
1676         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1677         const int*   nInd = vol.GetFaceNodesIndices( iF );
1678         // find common node of triangle facets of tetra to create
1679         int iCommon = 0; // index in linear numeration
1680         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1681         if ( !triaSplits.empty() )
1682         {
1683           // by found facets
1684           const TTriangleFacet* facet = &triaSplits.front();
1685           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1686             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1687                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1688               break;
1689         }
1690         else if ( nbNodes > 3 && !is24TetMode )
1691         {
1692           // find the best method of splitting into triangles by aspect ratio
1693           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1694           map< double, int > badness2iCommon;
1695           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1696           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1697           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1698           {
1699             double badness = 0;
1700             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1701             {
1702               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1703                                       nodes[ iQ*((iLast-1)%nbNodes)],
1704                                       nodes[ iQ*((iLast  )%nbNodes)]);
1705               badness += getBadRate( &tria, aspectRatio );
1706             }
1707             badness2iCommon.insert( make_pair( badness, iCommon ));
1708           }
1709           // use iCommon with lowest badness
1710           iCommon = badness2iCommon.begin()->second;
1711         }
1712         if ( iCommon >= nbNodes )
1713           iCommon = 0; // something wrong
1714
1715         // fill connectivity of tetrahedra based on a current face
1716         int nbTet = nbNodes - 2;
1717         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1718         {
1719           int faceBaryCenInd;
1720           if ( isHex27 )
1721           {
1722             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1723             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1724           }
1725           else
1726           {
1727             method._faceBaryNode[ iF ] = 0;
1728             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1729           }
1730           nbTet = nbNodes;
1731           for ( int i = 0; i < nbTet; ++i )
1732           {
1733             int i1 = i, i2 = (i+1) % nbNodes;
1734             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1735             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1736             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1737             connectivity[ connSize++ ] = faceBaryCenInd;
1738             connectivity[ connSize++ ] = baryCenInd;
1739           }
1740         }
1741         else
1742         {
1743           for ( int i = 0; i < nbTet; ++i )
1744           {
1745             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1746             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1747             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1748             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1749             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1750             connectivity[ connSize++ ] = baryCenInd;
1751           }
1752         }
1753         method._nbTetra += nbTet;
1754
1755       } // loop on volume faces
1756
1757       connectivity[ connSize++ ] = -1;
1758
1759     } // end of generic solution
1760
1761     return method;
1762   }
1763   //================================================================================
1764   /*!
1765    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1766    */
1767   //================================================================================
1768
1769   bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1770   {
1771     // find the tetrahedron including the three nodes of facet
1772     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1773     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1774     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1775     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1776     while ( volIt1->more() )
1777     {
1778       const SMDS_MeshElement* v = volIt1->next();
1779       SMDSAbs_EntityType type = v->GetEntityType();
1780       if ( type != SMDSEntity_Tetra && type != SMDSEntity_Quad_Tetra )
1781         continue;
1782       if ( type == SMDSEntity_Quad_Tetra && v->GetNodeIndex( n1 ) > 3 )
1783         continue; // medium node not allowed
1784       const int ind2 = v->GetNodeIndex( n2 );
1785       if ( ind2 < 0 || 3 < ind2 )
1786         continue;
1787       const int ind3 = v->GetNodeIndex( n3 );
1788       if ( ind3 < 0 || 3 < ind3 )
1789         continue;
1790       return true;
1791     }
1792     return false;
1793   }
1794
1795   //=======================================================================
1796   /*!
1797    * \brief A key of a face of volume
1798    */
1799   //=======================================================================
1800
1801   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
1802   {
1803     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1804     {
1805       TIDSortedNodeSet sortedNodes;
1806       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1807       int nbNodes = vol.NbFaceNodes( iF );
1808       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1809       for ( int i = 0; i < nbNodes; i += iQ )
1810         sortedNodes.insert( fNodes[i] );
1811       TIDSortedNodeSet::iterator n = sortedNodes.begin();
1812       first.first   = (*(n++))->GetID();
1813       first.second  = (*(n++))->GetID();
1814       second.first  = (*(n++))->GetID();
1815       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
1816     }
1817   };
1818 } // namespace
1819
1820 //=======================================================================
1821 //function : SplitVolumesIntoTetra
1822 //purpose  : Split volume elements into tetrahedra.
1823 //=======================================================================
1824
1825 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1826                                               const int                theMethodFlags)
1827 {
1828   // std-like iterator on coordinates of nodes of mesh element
1829   typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1830   NXyzIterator xyzEnd;
1831
1832   SMDS_VolumeTool    volTool;
1833   SMESH_MesherHelper helper( *GetMesh());
1834
1835   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1836   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1837
1838   SMESH_SequenceOfElemPtr newNodes, newElems;
1839
1840   // map face of volume to it's baricenrtic node
1841   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1842   double bc[3];
1843
1844   TIDSortedElemSet::const_iterator elem = theElems.begin();
1845   for ( ; elem != theElems.end(); ++elem )
1846   {
1847     if ( (*elem)->GetType() != SMDSAbs_Volume )
1848       continue;
1849     SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1850     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
1851       continue;
1852
1853     if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange...
1854
1855     TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1856     if ( splitMethod._nbTetra < 1 ) continue;
1857
1858     // find submesh to add new tetras to
1859     if ( !subMesh || !subMesh->Contains( *elem ))
1860     {
1861       int shapeID = FindShape( *elem );
1862       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1863       subMesh = GetMeshDS()->MeshElements( shapeID );
1864     }
1865     int iQ;
1866     if ( (*elem)->IsQuadratic() )
1867     {
1868       iQ = 2;
1869       // add quadratic links to the helper
1870       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1871       {
1872         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1873         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
1874         for ( int iN = 0; iN < nbN; iN += iQ )
1875           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
1876       }
1877       helper.SetIsQuadratic( true );
1878     }
1879     else
1880     {
1881       iQ = 1;
1882       helper.SetIsQuadratic( false );
1883     }
1884     vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1885     helper.SetElementsOnShape( true );
1886     if ( splitMethod._baryNode )
1887     {
1888       // make a node at barycenter
1889       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1890       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1891       nodes.push_back( gcNode );
1892       newNodes.Append( gcNode );
1893     }
1894     if ( !splitMethod._faceBaryNode.empty() )
1895     {
1896       // make or find baricentric nodes of faces
1897       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1898       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1899       {
1900         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1901           volFace2BaryNode.insert
1902           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
1903         if ( !f_n->second )
1904         {
1905           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1906           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1907         }
1908         nodes.push_back( iF_n->second = f_n->second );
1909       }
1910     }
1911
1912     // make tetras
1913     vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1914     const int* tetConn = splitMethod._connectivity;
1915     for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1916       newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1917                                                        nodes[ tetConn[1] ],
1918                                                        nodes[ tetConn[2] ],
1919                                                        nodes[ tetConn[3] ]));
1920
1921     ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
1922
1923     // Split faces on sides of the split volume
1924
1925     const SMDS_MeshNode** volNodes = volTool.GetNodes();
1926     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1927     {
1928       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
1929       if ( nbNodes < 4 ) continue;
1930
1931       // find an existing face
1932       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
1933                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
1934       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
1935                                                                        /*noMedium=*/false))
1936       {
1937         // make triangles
1938         helper.SetElementsOnShape( false );
1939         vector< const SMDS_MeshElement* > triangles;
1940
1941         // find submesh to add new triangles in
1942         if ( !fSubMesh || !fSubMesh->Contains( face ))
1943         {
1944           int shapeID = FindShape( face );
1945           fSubMesh = GetMeshDS()->MeshElements( shapeID );
1946         }
1947         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
1948         if ( iF_n != splitMethod._faceBaryNode.end() )
1949         {
1950           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
1951           {
1952             const SMDS_MeshNode* n1 = fNodes[iN];
1953             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
1954             const SMDS_MeshNode *n3 = iF_n->second;
1955             if ( !volTool.IsFaceExternal( iF ))
1956               swap( n2, n3 );
1957             triangles.push_back( helper.AddFace( n1,n2,n3 ));
1958
1959             if ( fSubMesh && n3->getshapeId() < 1 )
1960               fSubMesh->AddNode( n3 );
1961           }
1962         }
1963         else
1964         {
1965           // among possible triangles create ones discribed by split method
1966           const int* nInd = volTool.GetFaceNodesIndices( iF );
1967           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1968           int iCom = 0; // common node of triangle faces to split into
1969           list< TTriangleFacet > facets;
1970           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
1971           {
1972             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
1973                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
1974                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
1975             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
1976                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
1977                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
1978             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
1979             {
1980               facets.push_back( t012 );
1981               facets.push_back( t023 );
1982               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
1983                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
1984                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
1985                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
1986               break;
1987             }
1988           }
1989           list< TTriangleFacet >::iterator facet = facets.begin();
1990           for ( ; facet != facets.end(); ++facet )
1991           {
1992             if ( !volTool.IsFaceExternal( iF ))
1993               swap( facet->_n2, facet->_n3 );
1994             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
1995                                                  volNodes[ facet->_n2 ],
1996                                                  volNodes[ facet->_n3 ]));
1997           }
1998         }
1999         for ( int i = 0; i < triangles.size(); ++i )
2000         {
2001           if ( !triangles[i] ) continue;
2002           if ( fSubMesh )
2003             fSubMesh->AddElement( triangles[i]);
2004           newElems.Append( triangles[i] );
2005         }
2006         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2007         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2008       }
2009
2010     } // loop on volume faces to split them into triangles
2011
2012     GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
2013
2014     if ( geomType == SMDSEntity_TriQuad_Hexa )
2015     {
2016       // remove medium nodes that could become free
2017       for ( int i = 20; i < volTool.NbNodes(); ++i )
2018         if ( volNodes[i]->NbInverseElements() == 0 )
2019           GetMeshDS()->RemoveNode( volNodes[i] );
2020     }
2021   } // loop on volumes to split
2022
2023   myLastCreatedNodes = newNodes;
2024   myLastCreatedElems = newElems;
2025 }
2026
2027 //=======================================================================
2028 //function : AddToSameGroups
2029 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2030 //=======================================================================
2031
2032 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2033                                         const SMDS_MeshElement* elemInGroups,
2034                                         SMESHDS_Mesh *          aMesh)
2035 {
2036   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2037   if (!groups.empty()) {
2038     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2039     for ( ; grIt != groups.end(); grIt++ ) {
2040       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2041       if ( group && group->Contains( elemInGroups ))
2042         group->SMDSGroup().Add( elemToAdd );
2043     }
2044   }
2045 }
2046
2047
2048 //=======================================================================
2049 //function : RemoveElemFromGroups
2050 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2051 //=======================================================================
2052 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2053                                              SMESHDS_Mesh *          aMesh)
2054 {
2055   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2056   if (!groups.empty())
2057   {
2058     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2059     for (; GrIt != groups.end(); GrIt++)
2060     {
2061       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2062       if (!grp || grp->IsEmpty()) continue;
2063       grp->SMDSGroup().Remove(removeelem);
2064     }
2065   }
2066 }
2067
2068 //================================================================================
2069 /*!
2070  * \brief Replace elemToRm by elemToAdd in the all groups
2071  */
2072 //================================================================================
2073
2074 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2075                                             const SMDS_MeshElement* elemToAdd,
2076                                             SMESHDS_Mesh *          aMesh)
2077 {
2078   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2079   if (!groups.empty()) {
2080     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2081     for ( ; grIt != groups.end(); grIt++ ) {
2082       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2083       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2084         group->SMDSGroup().Add( elemToAdd );
2085     }
2086   }
2087 }
2088
2089 //================================================================================
2090 /*!
2091  * \brief Replace elemToRm by elemToAdd in the all groups
2092  */
2093 //================================================================================
2094
2095 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2096                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2097                                             SMESHDS_Mesh *                         aMesh)
2098 {
2099   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2100   if (!groups.empty())
2101   {
2102     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2103     for ( ; grIt != groups.end(); grIt++ ) {
2104       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2105       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2106         for ( int i = 0; i < elemToAdd.size(); ++i )
2107           group->SMDSGroup().Add( elemToAdd[ i ] );
2108     }
2109   }
2110 }
2111
2112 //=======================================================================
2113 //function : QuadToTri
2114 //purpose  : Cut quadrangles into triangles.
2115 //           theCrit is used to select a diagonal to cut
2116 //=======================================================================
2117
2118 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2119                                   const bool         the13Diag)
2120 {
2121   myLastCreatedElems.Clear();
2122   myLastCreatedNodes.Clear();
2123
2124   MESSAGE( "::QuadToTri()" );
2125
2126   SMESHDS_Mesh * aMesh = GetMeshDS();
2127
2128   Handle(Geom_Surface) surface;
2129   SMESH_MesherHelper   helper( *GetMesh() );
2130
2131   TIDSortedElemSet::iterator itElem;
2132   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2133     const SMDS_MeshElement* elem = *itElem;
2134     if ( !elem || elem->GetType() != SMDSAbs_Face )
2135       continue;
2136     bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2137     if(!isquad) continue;
2138
2139     if(elem->NbNodes()==4) {
2140       // retrieve element nodes
2141       const SMDS_MeshNode* aNodes [4];
2142       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2143       int i = 0;
2144       while ( itN->more() )
2145         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2146
2147       int aShapeId = FindShape( elem );
2148       const SMDS_MeshElement* newElem1 = 0;
2149       const SMDS_MeshElement* newElem2 = 0;
2150       if ( the13Diag ) {
2151         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2152         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2153       }
2154       else {
2155         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2156         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2157       }
2158       myLastCreatedElems.Append(newElem1);
2159       myLastCreatedElems.Append(newElem2);
2160       // put a new triangle on the same shape and add to the same groups
2161       if ( aShapeId )
2162         {
2163           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2164           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2165         }
2166       AddToSameGroups( newElem1, elem, aMesh );
2167       AddToSameGroups( newElem2, elem, aMesh );
2168       //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2169       aMesh->RemoveElement( elem );
2170     }
2171
2172     // Quadratic quadrangle
2173
2174     if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2175
2176       // get surface elem is on
2177       int aShapeId = FindShape( elem );
2178       if ( aShapeId != helper.GetSubShapeID() ) {
2179         surface.Nullify();
2180         TopoDS_Shape shape;
2181         if ( aShapeId > 0 )
2182           shape = aMesh->IndexToShape( aShapeId );
2183         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2184           TopoDS_Face face = TopoDS::Face( shape );
2185           surface = BRep_Tool::Surface( face );
2186           if ( !surface.IsNull() )
2187             helper.SetSubShape( shape );
2188         }
2189       }
2190
2191       const SMDS_MeshNode* aNodes [8];
2192       const SMDS_MeshNode* inFaceNode = 0;
2193       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2194       int i = 0;
2195       while ( itN->more() ) {
2196         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2197         if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2198              aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2199         {
2200           inFaceNode = aNodes[ i-1 ];
2201         }
2202       }
2203
2204       // find middle point for (0,1,2,3)
2205       // and create a node in this point;
2206       gp_XYZ p( 0,0,0 );
2207       if ( surface.IsNull() ) {
2208         for(i=0; i<4; i++)
2209           p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2210         p /= 4;
2211       }
2212       else {
2213         TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2214         gp_XY uv( 0,0 );
2215         for(i=0; i<4; i++)
2216           uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2217         uv /= 4.;
2218         p = surface->Value( uv.X(), uv.Y() ).XYZ();
2219       }
2220       const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2221       myLastCreatedNodes.Append(newN);
2222
2223       // create a new element
2224       const SMDS_MeshElement* newElem1 = 0;
2225       const SMDS_MeshElement* newElem2 = 0;
2226       if ( the13Diag ) {
2227         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2228                                   aNodes[6], aNodes[7], newN );
2229         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2230                                   newN,      aNodes[4], aNodes[5] );
2231       }
2232       else {
2233         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2234                                   aNodes[7], aNodes[4], newN );
2235         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2236                                   newN,      aNodes[5], aNodes[6] );
2237       }
2238       myLastCreatedElems.Append(newElem1);
2239       myLastCreatedElems.Append(newElem2);
2240       // put a new triangle on the same shape and add to the same groups
2241       if ( aShapeId )
2242         {
2243           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2244           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2245         }
2246       AddToSameGroups( newElem1, elem, aMesh );
2247       AddToSameGroups( newElem2, elem, aMesh );
2248       aMesh->RemoveElement( elem );
2249     }
2250   }
2251
2252   return true;
2253 }
2254
2255 //=======================================================================
2256 //function : getAngle
2257 //purpose  :
2258 //=======================================================================
2259
2260 double getAngle(const SMDS_MeshElement * tr1,
2261                 const SMDS_MeshElement * tr2,
2262                 const SMDS_MeshNode *    n1,
2263                 const SMDS_MeshNode *    n2)
2264 {
2265   double angle = 2. * M_PI; // bad angle
2266
2267   // get normals
2268   SMESH::Controls::TSequenceOfXYZ P1, P2;
2269   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2270        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2271     return angle;
2272   gp_Vec N1,N2;
2273   if(!tr1->IsQuadratic())
2274     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2275   else
2276     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2277   if ( N1.SquareMagnitude() <= gp::Resolution() )
2278     return angle;
2279   if(!tr2->IsQuadratic())
2280     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2281   else
2282     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2283   if ( N2.SquareMagnitude() <= gp::Resolution() )
2284     return angle;
2285
2286   // find the first diagonal node n1 in the triangles:
2287   // take in account a diagonal link orientation
2288   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2289   for ( int t = 0; t < 2; t++ ) {
2290     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2291     int i = 0, iDiag = -1;
2292     while ( it->more()) {
2293       const SMDS_MeshElement *n = it->next();
2294       if ( n == n1 || n == n2 ) {
2295         if ( iDiag < 0)
2296           iDiag = i;
2297         else {
2298           if ( i - iDiag == 1 )
2299             nFirst[ t ] = ( n == n1 ? n2 : n1 );
2300           else
2301             nFirst[ t ] = n;
2302           break;
2303         }
2304       }
2305       i++;
2306     }
2307   }
2308   if ( nFirst[ 0 ] == nFirst[ 1 ] )
2309     N2.Reverse();
2310
2311   angle = N1.Angle( N2 );
2312   //SCRUTE( angle );
2313   return angle;
2314 }
2315
2316 // =================================================
2317 // class generating a unique ID for a pair of nodes
2318 // and able to return nodes by that ID
2319 // =================================================
2320 class LinkID_Gen {
2321 public:
2322
2323   LinkID_Gen( const SMESHDS_Mesh* theMesh )
2324     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2325   {}
2326
2327   long GetLinkID (const SMDS_MeshNode * n1,
2328                   const SMDS_MeshNode * n2) const
2329   {
2330     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2331   }
2332
2333   bool GetNodes (const long             theLinkID,
2334                  const SMDS_MeshNode* & theNode1,
2335                  const SMDS_MeshNode* & theNode2) const
2336   {
2337     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2338     if ( !theNode1 ) return false;
2339     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2340     if ( !theNode2 ) return false;
2341     return true;
2342   }
2343
2344 private:
2345   LinkID_Gen();
2346   const SMESHDS_Mesh* myMesh;
2347   long                myMaxID;
2348 };
2349
2350
2351 //=======================================================================
2352 //function : TriToQuad
2353 //purpose  : Fuse neighbour triangles into quadrangles.
2354 //           theCrit is used to select a neighbour to fuse with.
2355 //           theMaxAngle is a max angle between element normals at which
2356 //           fusion is still performed.
2357 //=======================================================================
2358
2359 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
2360                                   SMESH::Controls::NumericalFunctorPtr theCrit,
2361                                   const double                         theMaxAngle)
2362 {
2363   myLastCreatedElems.Clear();
2364   myLastCreatedNodes.Clear();
2365
2366   MESSAGE( "::TriToQuad()" );
2367
2368   if ( !theCrit.get() )
2369     return false;
2370
2371   SMESHDS_Mesh * aMesh = GetMeshDS();
2372
2373   // Prepare data for algo: build
2374   // 1. map of elements with their linkIDs
2375   // 2. map of linkIDs with their elements
2376
2377   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2378   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2379   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
2380   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2381
2382   TIDSortedElemSet::iterator itElem;
2383   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2384     const SMDS_MeshElement* elem = *itElem;
2385     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2386     bool IsTria = elem->NbNodes()==3 || (elem->NbNodes()==6 && elem->IsQuadratic());
2387     if(!IsTria) continue;
2388
2389     // retrieve element nodes
2390     const SMDS_MeshNode* aNodes [4];
2391     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2392     int i = 0;
2393     while ( i<3 )
2394       aNodes[ i++ ] = cast2Node( itN->next() );
2395     aNodes[ 3 ] = aNodes[ 0 ];
2396
2397     // fill maps
2398     for ( i = 0; i < 3; i++ ) {
2399       SMESH_TLink link( aNodes[i], aNodes[i+1] );
2400       // check if elements sharing a link can be fused
2401       itLE = mapLi_listEl.find( link );
2402       if ( itLE != mapLi_listEl.end() ) {
2403         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2404           continue;
2405         const SMDS_MeshElement* elem2 = (*itLE).second.front();
2406         //if ( FindShape( elem ) != FindShape( elem2 ))
2407         //  continue; // do not fuse triangles laying on different shapes
2408         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2409           continue; // avoid making badly shaped quads
2410         (*itLE).second.push_back( elem );
2411       }
2412       else {
2413         mapLi_listEl[ link ].push_back( elem );
2414       }
2415       mapEl_setLi [ elem ].insert( link );
2416     }
2417   }
2418   // Clean the maps from the links shared by a sole element, ie
2419   // links to which only one element is bound in mapLi_listEl
2420
2421   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2422     int nbElems = (*itLE).second.size();
2423     if ( nbElems < 2  ) {
2424       const SMDS_MeshElement* elem = (*itLE).second.front();
2425       SMESH_TLink link = (*itLE).first;
2426       mapEl_setLi[ elem ].erase( link );
2427       if ( mapEl_setLi[ elem ].empty() )
2428         mapEl_setLi.erase( elem );
2429     }
2430   }
2431
2432   // Algo: fuse triangles into quadrangles
2433
2434   while ( ! mapEl_setLi.empty() ) {
2435     // Look for the start element:
2436     // the element having the least nb of shared links
2437     const SMDS_MeshElement* startElem = 0;
2438     int minNbLinks = 4;
2439     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2440       int nbLinks = (*itEL).second.size();
2441       if ( nbLinks < minNbLinks ) {
2442         startElem = (*itEL).first;
2443         minNbLinks = nbLinks;
2444         if ( minNbLinks == 1 )
2445           break;
2446       }
2447     }
2448
2449     // search elements to fuse starting from startElem or links of elements
2450     // fused earlyer - startLinks
2451     list< SMESH_TLink > startLinks;
2452     while ( startElem || !startLinks.empty() ) {
2453       while ( !startElem && !startLinks.empty() ) {
2454         // Get an element to start, by a link
2455         SMESH_TLink linkId = startLinks.front();
2456         startLinks.pop_front();
2457         itLE = mapLi_listEl.find( linkId );
2458         if ( itLE != mapLi_listEl.end() ) {
2459           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2460           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2461           for ( ; itE != listElem.end() ; itE++ )
2462             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2463               startElem = (*itE);
2464           mapLi_listEl.erase( itLE );
2465         }
2466       }
2467
2468       if ( startElem ) {
2469         // Get candidates to be fused
2470         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2471         const SMESH_TLink *link12, *link13;
2472         startElem = 0;
2473         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2474         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2475         ASSERT( !setLi.empty() );
2476         set< SMESH_TLink >::iterator itLi;
2477         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2478         {
2479           const SMESH_TLink & link = (*itLi);
2480           itLE = mapLi_listEl.find( link );
2481           if ( itLE == mapLi_listEl.end() )
2482             continue;
2483
2484           const SMDS_MeshElement* elem = (*itLE).second.front();
2485           if ( elem == tr1 )
2486             elem = (*itLE).second.back();
2487           mapLi_listEl.erase( itLE );
2488           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2489             continue;
2490           if ( tr2 ) {
2491             tr3 = elem;
2492             link13 = &link;
2493           }
2494           else {
2495             tr2 = elem;
2496             link12 = &link;
2497           }
2498
2499           // add other links of elem to list of links to re-start from
2500           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2501           set< SMESH_TLink >::iterator it;
2502           for ( it = links.begin(); it != links.end(); it++ ) {
2503             const SMESH_TLink& link2 = (*it);
2504             if ( link2 != link )
2505               startLinks.push_back( link2 );
2506           }
2507         }
2508
2509         // Get nodes of possible quadrangles
2510         const SMDS_MeshNode *n12 [4], *n13 [4];
2511         bool Ok12 = false, Ok13 = false;
2512         const SMDS_MeshNode *linkNode1, *linkNode2;
2513         if(tr2) {
2514           linkNode1 = link12->first;
2515           linkNode2 = link12->second;
2516           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2517             Ok12 = true;
2518         }
2519         if(tr3) {
2520           linkNode1 = link13->first;
2521           linkNode2 = link13->second;
2522           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2523             Ok13 = true;
2524         }
2525
2526         // Choose a pair to fuse
2527         if ( Ok12 && Ok13 ) {
2528           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2529           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2530           double aBadRate12 = getBadRate( &quad12, theCrit );
2531           double aBadRate13 = getBadRate( &quad13, theCrit );
2532           if (  aBadRate13 < aBadRate12 )
2533             Ok12 = false;
2534           else
2535             Ok13 = false;
2536         }
2537
2538         // Make quadrangles
2539         // and remove fused elems and removed links from the maps
2540         mapEl_setLi.erase( tr1 );
2541         if ( Ok12 ) {
2542           mapEl_setLi.erase( tr2 );
2543           mapLi_listEl.erase( *link12 );
2544           if(tr1->NbNodes()==3) {
2545             const SMDS_MeshElement* newElem = 0;
2546             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2547             myLastCreatedElems.Append(newElem);
2548             AddToSameGroups( newElem, tr1, aMesh );
2549             int aShapeId = tr1->getshapeId();
2550             if ( aShapeId )
2551               {
2552                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2553               }
2554             aMesh->RemoveElement( tr1 );
2555             aMesh->RemoveElement( tr2 );
2556           }
2557           else {
2558             const SMDS_MeshNode* N1 [6];
2559             const SMDS_MeshNode* N2 [6];
2560             GetNodesFromTwoTria(tr1,tr2,N1,N2);
2561             // now we receive following N1 and N2 (using numeration as above image)
2562             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
2563             // i.e. first nodes from both arrays determ new diagonal
2564             const SMDS_MeshNode* aNodes[8];
2565             aNodes[0] = N1[0];
2566             aNodes[1] = N1[1];
2567             aNodes[2] = N2[0];
2568             aNodes[3] = N2[1];
2569             aNodes[4] = N1[3];
2570             aNodes[5] = N2[5];
2571             aNodes[6] = N2[3];
2572             aNodes[7] = N1[5];
2573             const SMDS_MeshElement* newElem = 0;
2574             newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2575                                      aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2576             myLastCreatedElems.Append(newElem);
2577             AddToSameGroups( newElem, tr1, aMesh );
2578             int aShapeId = tr1->getshapeId();
2579             if ( aShapeId )
2580               {
2581                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2582               }
2583             aMesh->RemoveElement( tr1 );
2584             aMesh->RemoveElement( tr2 );
2585             // remove middle node (9)
2586             GetMeshDS()->RemoveNode( N1[4] );
2587           }
2588         }
2589         else if ( Ok13 ) {
2590           mapEl_setLi.erase( tr3 );
2591           mapLi_listEl.erase( *link13 );
2592           if(tr1->NbNodes()==3) {
2593             const SMDS_MeshElement* newElem = 0;
2594             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2595             myLastCreatedElems.Append(newElem);
2596             AddToSameGroups( newElem, tr1, aMesh );
2597             int aShapeId = tr1->getshapeId();
2598             if ( aShapeId )
2599               {
2600                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2601               }
2602             aMesh->RemoveElement( tr1 );
2603             aMesh->RemoveElement( tr3 );
2604           }
2605           else {
2606             const SMDS_MeshNode* N1 [6];
2607             const SMDS_MeshNode* N2 [6];
2608             GetNodesFromTwoTria(tr1,tr3,N1,N2);
2609             // now we receive following N1 and N2 (using numeration as above image)
2610             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
2611             // i.e. first nodes from both arrays determ new diagonal
2612             const SMDS_MeshNode* aNodes[8];
2613             aNodes[0] = N1[0];
2614             aNodes[1] = N1[1];
2615             aNodes[2] = N2[0];
2616             aNodes[3] = N2[1];
2617             aNodes[4] = N1[3];
2618             aNodes[5] = N2[5];
2619             aNodes[6] = N2[3];
2620             aNodes[7] = N1[5];
2621             const SMDS_MeshElement* newElem = 0;
2622             newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2623                                      aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2624             myLastCreatedElems.Append(newElem);
2625             AddToSameGroups( newElem, tr1, aMesh );
2626             int aShapeId = tr1->getshapeId();
2627             if ( aShapeId )
2628               {
2629                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2630               }
2631             aMesh->RemoveElement( tr1 );
2632             aMesh->RemoveElement( tr3 );
2633             // remove middle node (9)
2634             GetMeshDS()->RemoveNode( N1[4] );
2635           }
2636         }
2637
2638         // Next element to fuse: the rejected one
2639         if ( tr3 )
2640           startElem = Ok12 ? tr3 : tr2;
2641
2642       } // if ( startElem )
2643     } // while ( startElem || !startLinks.empty() )
2644   } // while ( ! mapEl_setLi.empty() )
2645
2646   return true;
2647 }
2648
2649
2650 /*#define DUMPSO(txt) \
2651 //  cout << txt << endl;
2652 //=============================================================================
2653 //
2654 //
2655 //
2656 //=============================================================================
2657 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2658 {
2659 if ( i1 == i2 )
2660 return;
2661 int tmp = idNodes[ i1 ];
2662 idNodes[ i1 ] = idNodes[ i2 ];
2663 idNodes[ i2 ] = tmp;
2664 gp_Pnt Ptmp = P[ i1 ];
2665 P[ i1 ] = P[ i2 ];
2666 P[ i2 ] = Ptmp;
2667 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2668 }
2669
2670 //=======================================================================
2671 //function : SortQuadNodes
2672 //purpose  : Set 4 nodes of a quadrangle face in a good order.
2673 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
2674 //           1 or 2 else 0.
2675 //=======================================================================
2676
2677 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2678 int               idNodes[] )
2679 {
2680   gp_Pnt P[4];
2681   int i;
2682   for ( i = 0; i < 4; i++ ) {
2683     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2684     if ( !n ) return 0;
2685     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2686   }
2687
2688   gp_Vec V1(P[0], P[1]);
2689   gp_Vec V2(P[0], P[2]);
2690   gp_Vec V3(P[0], P[3]);
2691
2692   gp_Vec Cross1 = V1 ^ V2;
2693   gp_Vec Cross2 = V2 ^ V3;
2694
2695   i = 0;
2696   if (Cross1.Dot(Cross2) < 0)
2697   {
2698     Cross1 = V2 ^ V1;
2699     Cross2 = V1 ^ V3;
2700
2701     if (Cross1.Dot(Cross2) < 0)
2702       i = 2;
2703     else
2704       i = 1;
2705     swap ( i, i + 1, idNodes, P );
2706
2707     //     for ( int ii = 0; ii < 4; ii++ ) {
2708     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2709     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2710     //     }
2711   }
2712   return i;
2713 }
2714
2715 //=======================================================================
2716 //function : SortHexaNodes
2717 //purpose  : Set 8 nodes of a hexahedron in a good order.
2718 //           Return success status
2719 //=======================================================================
2720
2721 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2722                                       int               idNodes[] )
2723 {
2724   gp_Pnt P[8];
2725   int i;
2726   DUMPSO( "INPUT: ========================================");
2727   for ( i = 0; i < 8; i++ ) {
2728     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2729     if ( !n ) return false;
2730     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2731     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2732   }
2733   DUMPSO( "========================================");
2734
2735
2736   set<int> faceNodes;  // ids of bottom face nodes, to be found
2737   set<int> checkedId1; // ids of tried 2-nd nodes
2738   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2739   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
2740   int iMin, iLoop1 = 0;
2741
2742   // Loop to try the 2-nd nodes
2743
2744   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2745   {
2746     // Find not checked 2-nd node
2747     for ( i = 1; i < 8; i++ )
2748       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2749         int id1 = idNodes[i];
2750         swap ( 1, i, idNodes, P );
2751         checkedId1.insert ( id1 );
2752         break;
2753       }
2754
2755     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2756     // ie that all but meybe one (id3 which is on the same face) nodes
2757     // lay on the same side from the triangle plane.
2758
2759     bool manyInPlane = false; // more than 4 nodes lay in plane
2760     int iLoop2 = 0;
2761     while ( ++iLoop2 < 6 ) {
2762
2763       // get 1-2-3 plane coeffs
2764       Standard_Real A, B, C, D;
2765       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2766       if ( N.SquareMagnitude() > gp::Resolution() )
2767       {
2768         gp_Pln pln ( P[0], N );
2769         pln.Coefficients( A, B, C, D );
2770
2771         // find the node (iMin) closest to pln
2772         Standard_Real dist[ 8 ], minDist = DBL_MAX;
2773         set<int> idInPln;
2774         for ( i = 3; i < 8; i++ ) {
2775           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2776           if ( fabs( dist[i] ) < minDist ) {
2777             minDist = fabs( dist[i] );
2778             iMin = i;
2779           }
2780           if ( fabs( dist[i] ) <= tol )
2781             idInPln.insert( idNodes[i] );
2782         }
2783
2784         // there should not be more than 4 nodes in bottom plane
2785         if ( idInPln.size() > 1 )
2786         {
2787           DUMPSO( "### idInPln.size() = " << idInPln.size());
2788           // idInPlane does not contain the first 3 nodes
2789           if ( manyInPlane || idInPln.size() == 5)
2790             return false; // all nodes in one plane
2791           manyInPlane = true;
2792
2793           // set the 1-st node to be not in plane
2794           for ( i = 3; i < 8; i++ ) {
2795             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2796               DUMPSO( "### Reset 0-th node");
2797               swap( 0, i, idNodes, P );
2798               break;
2799             }
2800           }
2801
2802           // reset to re-check second nodes
2803           leastDist = DBL_MAX;
2804           faceNodes.clear();
2805           checkedId1.clear();
2806           iLoop1 = 0;
2807           break; // from iLoop2;
2808         }
2809
2810         // check that the other 4 nodes are on the same side
2811         bool sameSide = true;
2812         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2813         for ( i = 3; sameSide && i < 8; i++ ) {
2814           if ( i != iMin )
2815             sameSide = ( isNeg == dist[i] <= 0.);
2816         }
2817
2818         // keep best solution
2819         if ( sameSide && minDist < leastDist ) {
2820           leastDist = minDist;
2821           faceNodes.clear();
2822           faceNodes.insert( idNodes[ 1 ] );
2823           faceNodes.insert( idNodes[ 2 ] );
2824           faceNodes.insert( idNodes[ iMin ] );
2825           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2826                   << " leastDist = " << leastDist);
2827           if ( leastDist <= DBL_MIN )
2828             break;
2829         }
2830       }
2831
2832       // set next 3-d node to check
2833       int iNext = 2 + iLoop2;
2834       if ( iNext < 8 ) {
2835         DUMPSO( "Try 2-nd");
2836         swap ( 2, iNext, idNodes, P );
2837       }
2838     } // while ( iLoop2 < 6 )
2839   } // iLoop1
2840
2841   if ( faceNodes.empty() ) return false;
2842
2843   // Put the faceNodes in proper places
2844   for ( i = 4; i < 8; i++ ) {
2845     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2846       // find a place to put
2847       int iTo = 1;
2848       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2849         iTo++;
2850       DUMPSO( "Set faceNodes");
2851       swap ( iTo, i, idNodes, P );
2852     }
2853   }
2854
2855
2856   // Set nodes of the found bottom face in good order
2857   DUMPSO( " Found bottom face: ");
2858   i = SortQuadNodes( theMesh, idNodes );
2859   if ( i ) {
2860     gp_Pnt Ptmp = P[ i ];
2861     P[ i ] = P[ i+1 ];
2862     P[ i+1 ] = Ptmp;
2863   }
2864   //   else
2865   //     for ( int ii = 0; ii < 4; ii++ ) {
2866   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2867   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2868   //    }
2869
2870   // Gravity center of the top and bottom faces
2871   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2872   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2873
2874   // Get direction from the bottom to the top face
2875   gp_Vec upDir ( aGCb, aGCt );
2876   Standard_Real upDirSize = upDir.Magnitude();
2877   if ( upDirSize <= gp::Resolution() ) return false;
2878   upDir / upDirSize;
2879
2880   // Assure that the bottom face normal points up
2881   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2882   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2883   if ( Nb.Dot( upDir ) < 0 ) {
2884     DUMPSO( "Reverse bottom face");
2885     swap( 1, 3, idNodes, P );
2886   }
2887
2888   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2889   Standard_Real minDist = DBL_MAX;
2890   for ( i = 4; i < 8; i++ ) {
2891     // projection of P[i] to the plane defined by P[0] and upDir
2892     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2893     Standard_Real sqDist = P[0].SquareDistance( Pp );
2894     if ( sqDist < minDist ) {
2895       minDist = sqDist;
2896       iMin = i;
2897     }
2898   }
2899   DUMPSO( "Set 4-th");
2900   swap ( 4, iMin, idNodes, P );
2901
2902   // Set nodes of the top face in good order
2903   DUMPSO( "Sort top face");
2904   i = SortQuadNodes( theMesh, &idNodes[4] );
2905   if ( i ) {
2906     i += 4;
2907     gp_Pnt Ptmp = P[ i ];
2908     P[ i ] = P[ i+1 ];
2909     P[ i+1 ] = Ptmp;
2910   }
2911
2912   // Assure that direction of the top face normal is from the bottom face
2913   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
2914   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
2915   if ( Nt.Dot( upDir ) < 0 ) {
2916     DUMPSO( "Reverse top face");
2917     swap( 5, 7, idNodes, P );
2918   }
2919
2920   //   DUMPSO( "OUTPUT: ========================================");
2921   //   for ( i = 0; i < 8; i++ ) {
2922   //     float *p = ugrid->GetPoint(idNodes[i]);
2923   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
2924   //   }
2925
2926   return true;
2927 }*/
2928
2929 //================================================================================
2930 /*!
2931  * \brief Return nodes linked to the given one
2932  * \param theNode - the node
2933  * \param linkedNodes - the found nodes
2934  * \param type - the type of elements to check
2935  *
2936  * Medium nodes are ignored
2937  */
2938 //================================================================================
2939
2940 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
2941                                        TIDSortedElemSet &   linkedNodes,
2942                                        SMDSAbs_ElementType  type )
2943 {
2944   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
2945   while ( elemIt->more() )
2946   {
2947     const SMDS_MeshElement* elem = elemIt->next();
2948     if(elem->GetType() == SMDSAbs_0DElement)
2949       continue;
2950
2951     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
2952     if ( elem->GetType() == SMDSAbs_Volume )
2953     {
2954       SMDS_VolumeTool vol( elem );
2955       while ( nodeIt->more() ) {
2956         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2957         if ( theNode != n && vol.IsLinked( theNode, n ))
2958           linkedNodes.insert( n );
2959       }
2960     }
2961     else
2962     {
2963       for ( int i = 0; nodeIt->more(); ++i ) {
2964         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2965         if ( n == theNode ) {
2966           int iBefore = i - 1;
2967           int iAfter  = i + 1;
2968           if ( elem->IsQuadratic() ) {
2969             int nb = elem->NbNodes() / 2;
2970             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
2971             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
2972           }
2973           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
2974           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
2975         }
2976       }
2977     }
2978   }
2979 }
2980
2981 //=======================================================================
2982 //function : laplacianSmooth
2983 //purpose  : pulls theNode toward the center of surrounding nodes directly
2984 //           connected to that node along an element edge
2985 //=======================================================================
2986
2987 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
2988                      const Handle(Geom_Surface)&          theSurface,
2989                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2990 {
2991   // find surrounding nodes
2992
2993   TIDSortedElemSet nodeSet;
2994   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
2995
2996   // compute new coodrs
2997
2998   double coord[] = { 0., 0., 0. };
2999   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3000   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3001     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3002     if ( theSurface.IsNull() ) { // smooth in 3D
3003       coord[0] += node->X();
3004       coord[1] += node->Y();
3005       coord[2] += node->Z();
3006     }
3007     else { // smooth in 2D
3008       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3009       gp_XY* uv = theUVMap[ node ];
3010       coord[0] += uv->X();
3011       coord[1] += uv->Y();
3012     }
3013   }
3014   int nbNodes = nodeSet.size();
3015   if ( !nbNodes )
3016     return;
3017   coord[0] /= nbNodes;
3018   coord[1] /= nbNodes;
3019
3020   if ( !theSurface.IsNull() ) {
3021     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3022     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3023     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3024     coord[0] = p3d.X();
3025     coord[1] = p3d.Y();
3026     coord[2] = p3d.Z();
3027   }
3028   else
3029     coord[2] /= nbNodes;
3030
3031   // move node
3032
3033   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3034 }
3035
3036 //=======================================================================
3037 //function : centroidalSmooth
3038 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3039 //           surrounding elements
3040 //=======================================================================
3041
3042 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3043                       const Handle(Geom_Surface)&          theSurface,
3044                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3045 {
3046   gp_XYZ aNewXYZ(0.,0.,0.);
3047   SMESH::Controls::Area anAreaFunc;
3048   double totalArea = 0.;
3049   int nbElems = 0;
3050
3051   // compute new XYZ
3052
3053   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3054   while ( elemIt->more() )
3055   {
3056     const SMDS_MeshElement* elem = elemIt->next();
3057     nbElems++;
3058
3059     gp_XYZ elemCenter(0.,0.,0.);
3060     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3061     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3062     int nn = elem->NbNodes();
3063     if(elem->IsQuadratic()) nn = nn/2;
3064     int i=0;
3065     //while ( itN->more() ) {
3066     while ( i<nn ) {
3067       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3068       i++;
3069       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3070       aNodePoints.push_back( aP );
3071       if ( !theSurface.IsNull() ) { // smooth in 2D
3072         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3073         gp_XY* uv = theUVMap[ aNode ];
3074         aP.SetCoord( uv->X(), uv->Y(), 0. );
3075       }
3076       elemCenter += aP;
3077     }
3078     double elemArea = anAreaFunc.GetValue( aNodePoints );
3079     totalArea += elemArea;
3080     elemCenter /= nn;
3081     aNewXYZ += elemCenter * elemArea;
3082   }
3083   aNewXYZ /= totalArea;
3084   if ( !theSurface.IsNull() ) {
3085     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3086     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3087   }
3088
3089   // move node
3090
3091   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3092 }
3093
3094 //=======================================================================
3095 //function : getClosestUV
3096 //purpose  : return UV of closest projection
3097 //=======================================================================
3098
3099 static bool getClosestUV (Extrema_GenExtPS& projector,
3100                           const gp_Pnt&     point,
3101                           gp_XY &           result)
3102 {
3103   projector.Perform( point );
3104   if ( projector.IsDone() ) {
3105     double u, v, minVal = DBL_MAX;
3106     for ( int i = projector.NbExt(); i > 0; i-- )
3107 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3108       if ( projector.SquareDistance( i ) < minVal ) {
3109         minVal = projector.SquareDistance( i );
3110 #else
3111       if ( projector.Value( i ) < minVal ) {
3112         minVal = projector.Value( i );
3113 #endif
3114         projector.Point( i ).Parameter( u, v );
3115       }
3116     result.SetCoord( u, v );
3117     return true;
3118   }
3119   return false;
3120 }
3121
3122 //=======================================================================
3123 //function : Smooth
3124 //purpose  : Smooth theElements during theNbIterations or until a worst
3125 //           element has aspect ratio <= theTgtAspectRatio.
3126 //           Aspect Ratio varies in range [1.0, inf].
3127 //           If theElements is empty, the whole mesh is smoothed.
3128 //           theFixedNodes contains additionally fixed nodes. Nodes built
3129 //           on edges and boundary nodes are always fixed.
3130 //=======================================================================
3131
3132 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3133                                set<const SMDS_MeshNode*> & theFixedNodes,
3134                                const SmoothMethod          theSmoothMethod,
3135                                const int                   theNbIterations,
3136                                double                      theTgtAspectRatio,
3137                                const bool                  the2D)
3138 {
3139   myLastCreatedElems.Clear();
3140   myLastCreatedNodes.Clear();
3141
3142   MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3143
3144   if ( theTgtAspectRatio < 1.0 )
3145     theTgtAspectRatio = 1.0;
3146
3147   const double disttol = 1.e-16;
3148
3149   SMESH::Controls::AspectRatio aQualityFunc;
3150
3151   SMESHDS_Mesh* aMesh = GetMeshDS();
3152
3153   if ( theElems.empty() ) {
3154     // add all faces to theElems
3155     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3156     while ( fIt->more() ) {
3157       const SMDS_MeshElement* face = fIt->next();
3158       theElems.insert( theElems.end(), face );
3159     }
3160   }
3161   // get all face ids theElems are on
3162   set< int > faceIdSet;
3163   TIDSortedElemSet::iterator itElem;
3164   if ( the2D )
3165     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3166       int fId = FindShape( *itElem );
3167       // check that corresponding submesh exists and a shape is face
3168       if (fId &&
3169           faceIdSet.find( fId ) == faceIdSet.end() &&
3170           aMesh->MeshElements( fId )) {
3171         TopoDS_Shape F = aMesh->IndexToShape( fId );
3172         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3173           faceIdSet.insert( fId );
3174       }
3175     }
3176   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3177
3178   // ===============================================
3179   // smooth elements on each TopoDS_Face separately
3180   // ===============================================
3181
3182   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3183   for ( ; fId != faceIdSet.rend(); ++fId ) {
3184     // get face surface and submesh
3185     Handle(Geom_Surface) surface;
3186     SMESHDS_SubMesh* faceSubMesh = 0;
3187     TopoDS_Face face;
3188     double fToler2 = 0, f,l;
3189     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3190     bool isUPeriodic = false, isVPeriodic = false;
3191     if ( *fId ) {
3192       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3193       surface = BRep_Tool::Surface( face );
3194       faceSubMesh = aMesh->MeshElements( *fId );
3195       fToler2 = BRep_Tool::Tolerance( face );
3196       fToler2 *= fToler2 * 10.;
3197       isUPeriodic = surface->IsUPeriodic();
3198       if ( isUPeriodic )
3199         surface->UPeriod();
3200       isVPeriodic = surface->IsVPeriodic();
3201       if ( isVPeriodic )
3202         surface->VPeriod();
3203       surface->Bounds( u1, u2, v1, v2 );
3204     }
3205     // ---------------------------------------------------------
3206     // for elements on a face, find movable and fixed nodes and
3207     // compute UV for them
3208     // ---------------------------------------------------------
3209     bool checkBoundaryNodes = false;
3210     bool isQuadratic = false;
3211     set<const SMDS_MeshNode*> setMovableNodes;
3212     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3213     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3214     list< const SMDS_MeshElement* > elemsOnFace;
3215
3216     Extrema_GenExtPS projector;
3217     GeomAdaptor_Surface surfAdaptor;
3218     if ( !surface.IsNull() ) {
3219       surfAdaptor.Load( surface );
3220       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3221     }
3222     int nbElemOnFace = 0;
3223     itElem = theElems.begin();
3224     // loop on not yet smoothed elements: look for elems on a face
3225     while ( itElem != theElems.end() ) {
3226       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3227         break; // all elements found
3228
3229       const SMDS_MeshElement* elem = *itElem;
3230       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3231            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3232         ++itElem;
3233         continue;
3234       }
3235       elemsOnFace.push_back( elem );
3236       theElems.erase( itElem++ );
3237       nbElemOnFace++;
3238
3239       if ( !isQuadratic )
3240         isQuadratic = elem->IsQuadratic();
3241
3242       // get movable nodes of elem
3243       const SMDS_MeshNode* node;
3244       SMDS_TypeOfPosition posType;
3245       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3246       int nn = 0, nbn =  elem->NbNodes();
3247       if(elem->IsQuadratic())
3248         nbn = nbn/2;
3249       while ( nn++ < nbn ) {
3250         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3251         const SMDS_PositionPtr& pos = node->GetPosition();
3252         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3253         if (posType != SMDS_TOP_EDGE &&
3254             posType != SMDS_TOP_VERTEX &&
3255             theFixedNodes.find( node ) == theFixedNodes.end())
3256         {
3257           // check if all faces around the node are on faceSubMesh
3258           // because a node on edge may be bound to face
3259           SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3260           bool all = true;
3261           if ( faceSubMesh ) {
3262             while ( eIt->more() && all ) {
3263               const SMDS_MeshElement* e = eIt->next();
3264               all = faceSubMesh->Contains( e );
3265             }
3266           }
3267           if ( all )
3268             setMovableNodes.insert( node );
3269           else
3270             checkBoundaryNodes = true;
3271         }
3272         if ( posType == SMDS_TOP_3DSPACE )
3273           checkBoundaryNodes = true;
3274       }
3275
3276       if ( surface.IsNull() )
3277         continue;
3278
3279       // get nodes to check UV
3280       list< const SMDS_MeshNode* > uvCheckNodes;
3281       itN = elem->nodesIterator();
3282       nn = 0; nbn =  elem->NbNodes();
3283       if(elem->IsQuadratic())
3284         nbn = nbn/2;
3285       while ( nn++ < nbn ) {
3286         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3287         if ( uvMap.find( node ) == uvMap.end() )
3288           uvCheckNodes.push_back( node );
3289         // add nodes of elems sharing node
3290         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3291         //         while ( eIt->more() ) {
3292         //           const SMDS_MeshElement* e = eIt->next();
3293         //           if ( e != elem ) {
3294         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3295         //             while ( nIt->more() ) {
3296         //               const SMDS_MeshNode* n =
3297         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3298         //               if ( uvMap.find( n ) == uvMap.end() )
3299         //                 uvCheckNodes.push_back( n );
3300         //             }
3301         //           }
3302         //         }
3303       }
3304       // check UV on face
3305       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3306       for ( ; n != uvCheckNodes.end(); ++n ) {
3307         node = *n;
3308         gp_XY uv( 0, 0 );
3309         const SMDS_PositionPtr& pos = node->GetPosition();
3310         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3311         // get existing UV
3312         switch ( posType ) {
3313         case SMDS_TOP_FACE: {
3314           SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3315           uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3316           break;
3317         }
3318         case SMDS_TOP_EDGE: {
3319           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3320           Handle(Geom2d_Curve) pcurve;
3321           if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3322             pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3323           if ( !pcurve.IsNull() ) {
3324             double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3325             uv = pcurve->Value( u ).XY();
3326           }
3327           break;
3328         }
3329         case SMDS_TOP_VERTEX: {
3330           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3331           if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3332             uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3333           break;
3334         }
3335         default:;
3336         }
3337         // check existing UV
3338         bool project = true;
3339         gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3340         double dist1 = DBL_MAX, dist2 = 0;
3341         if ( posType != SMDS_TOP_3DSPACE ) {
3342           dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3343           project = dist1 > fToler2;
3344         }
3345         if ( project ) { // compute new UV
3346           gp_XY newUV;
3347           if ( !getClosestUV( projector, pNode, newUV )) {
3348             MESSAGE("Node Projection Failed " << node);
3349           }
3350           else {
3351             if ( isUPeriodic )
3352               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3353             if ( isVPeriodic )
3354               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3355             // check new UV
3356             if ( posType != SMDS_TOP_3DSPACE )
3357               dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3358             if ( dist2 < dist1 )
3359               uv = newUV;
3360           }
3361         }
3362         // store UV in the map
3363         listUV.push_back( uv );
3364         uvMap.insert( make_pair( node, &listUV.back() ));
3365       }
3366     } // loop on not yet smoothed elements
3367
3368     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3369       checkBoundaryNodes = true;
3370
3371     // fix nodes on mesh boundary
3372
3373     if ( checkBoundaryNodes ) {
3374       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3375       map< SMESH_TLink, int >::iterator link_nb;
3376       // put all elements links to linkNbMap
3377       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3378       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3379         const SMDS_MeshElement* elem = (*elemIt);
3380         int nbn =  elem->NbCornerNodes();
3381         // loop on elem links: insert them in linkNbMap
3382         for ( int iN = 0; iN < nbn; ++iN ) {
3383           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3384           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3385           SMESH_TLink link( n1, n2 );
3386           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3387           link_nb->second++;
3388         }
3389       }
3390       // remove nodes that are in links encountered only once from setMovableNodes
3391       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3392         if ( link_nb->second == 1 ) {
3393           setMovableNodes.erase( link_nb->first.node1() );
3394           setMovableNodes.erase( link_nb->first.node2() );
3395         }
3396       }
3397     }
3398
3399     // -----------------------------------------------------
3400     // for nodes on seam edge, compute one more UV ( uvMap2 );
3401     // find movable nodes linked to nodes on seam and which
3402     // are to be smoothed using the second UV ( uvMap2 )
3403     // -----------------------------------------------------
3404
3405     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3406     if ( !surface.IsNull() ) {
3407       TopExp_Explorer eExp( face, TopAbs_EDGE );
3408       for ( ; eExp.More(); eExp.Next() ) {
3409         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3410         if ( !BRep_Tool::IsClosed( edge, face ))
3411           continue;
3412         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3413         if ( !sm ) continue;
3414         // find out which parameter varies for a node on seam
3415         double f,l;
3416         gp_Pnt2d uv1, uv2;
3417         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3418         if ( pcurve.IsNull() ) continue;
3419         uv1 = pcurve->Value( f );
3420         edge.Reverse();
3421         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3422         if ( pcurve.IsNull() ) continue;
3423         uv2 = pcurve->Value( f );
3424         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3425         // assure uv1 < uv2
3426         if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3427           gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3428         }
3429         // get nodes on seam and its vertices
3430         list< const SMDS_MeshNode* > seamNodes;
3431         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3432         while ( nSeamIt->more() ) {
3433           const SMDS_MeshNode* node = nSeamIt->next();
3434           if ( !isQuadratic || !IsMedium( node ))
3435             seamNodes.push_back( node );
3436         }
3437         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3438         for ( ; vExp.More(); vExp.Next() ) {
3439           sm = aMesh->MeshElements( vExp.Current() );
3440           if ( sm ) {
3441             nSeamIt = sm->GetNodes();
3442             while ( nSeamIt->more() )
3443               seamNodes.push_back( nSeamIt->next() );
3444           }
3445         }
3446         // loop on nodes on seam
3447         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3448         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3449           const SMDS_MeshNode* nSeam = *noSeIt;
3450           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3451           if ( n_uv == uvMap.end() )
3452             continue;
3453           // set the first UV
3454           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3455           // set the second UV
3456           listUV.push_back( *n_uv->second );
3457           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3458           if ( uvMap2.empty() )
3459             uvMap2 = uvMap; // copy the uvMap contents
3460           uvMap2[ nSeam ] = &listUV.back();
3461
3462           // collect movable nodes linked to ones on seam in nodesNearSeam
3463           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3464           while ( eIt->more() ) {
3465             const SMDS_MeshElement* e = eIt->next();
3466             int nbUseMap1 = 0, nbUseMap2 = 0;
3467             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3468             int nn = 0, nbn =  e->NbNodes();
3469             if(e->IsQuadratic()) nbn = nbn/2;
3470             while ( nn++ < nbn )
3471             {
3472               const SMDS_MeshNode* n =
3473                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3474               if (n == nSeam ||
3475                   setMovableNodes.find( n ) == setMovableNodes.end() )
3476                 continue;
3477               // add only nodes being closer to uv2 than to uv1
3478               gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3479                            0.5 * ( n->Y() + nSeam->Y() ),
3480                            0.5 * ( n->Z() + nSeam->Z() ));
3481               gp_XY uv;
3482               getClosestUV( projector, pMid, uv );
3483               if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3484                 nodesNearSeam.insert( n );
3485                 nbUseMap2++;
3486               }
3487               else
3488                 nbUseMap1++;
3489             }
3490             // for centroidalSmooth all element nodes must
3491             // be on one side of a seam
3492             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3493               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3494               nn = 0;
3495               while ( nn++ < nbn ) {
3496                 const SMDS_MeshNode* n =
3497                   static_cast<const SMDS_MeshNode*>( nIt->next() );
3498                 setMovableNodes.erase( n );
3499               }
3500             }
3501           }
3502         } // loop on nodes on seam
3503       } // loop on edge of a face
3504     } // if ( !face.IsNull() )
3505
3506     if ( setMovableNodes.empty() ) {
3507       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3508       continue; // goto next face
3509     }
3510
3511     // -------------
3512     // SMOOTHING //
3513     // -------------
3514
3515     int it = -1;
3516     double maxRatio = -1., maxDisplacement = -1.;
3517     set<const SMDS_MeshNode*>::iterator nodeToMove;
3518     for ( it = 0; it < theNbIterations; it++ ) {
3519       maxDisplacement = 0.;
3520       nodeToMove = setMovableNodes.begin();
3521       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3522         const SMDS_MeshNode* node = (*nodeToMove);
3523         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3524
3525         // smooth
3526         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3527         if ( theSmoothMethod == LAPLACIAN )
3528           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3529         else
3530           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3531
3532         // node displacement
3533         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3534         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3535         if ( aDispl > maxDisplacement )
3536           maxDisplacement = aDispl;
3537       }
3538       // no node movement => exit
3539       //if ( maxDisplacement < 1.e-16 ) {
3540       if ( maxDisplacement < disttol ) {
3541         MESSAGE("-- no node movement --");
3542         break;
3543       }
3544
3545       // check elements quality
3546       maxRatio  = 0;
3547       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3548       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3549         const SMDS_MeshElement* elem = (*elemIt);
3550         if ( !elem || elem->GetType() != SMDSAbs_Face )
3551           continue;
3552         SMESH::Controls::TSequenceOfXYZ aPoints;
3553         if ( aQualityFunc.GetPoints( elem, aPoints )) {
3554           double aValue = aQualityFunc.GetValue( aPoints );
3555           if ( aValue > maxRatio )
3556             maxRatio = aValue;
3557         }
3558       }
3559       if ( maxRatio <= theTgtAspectRatio ) {
3560         MESSAGE("-- quality achived --");
3561         break;
3562       }
3563       if (it+1 == theNbIterations) {
3564         MESSAGE("-- Iteration limit exceeded --");
3565       }
3566     } // smoothing iterations
3567
3568     MESSAGE(" Face id: " << *fId <<
3569             " Nb iterstions: " << it <<
3570             " Displacement: " << maxDisplacement <<
3571             " Aspect Ratio " << maxRatio);
3572
3573     // ---------------------------------------
3574     // new nodes positions are computed,
3575     // record movement in DS and set new UV
3576     // ---------------------------------------
3577     nodeToMove = setMovableNodes.begin();
3578     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3579       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3580       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3581       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3582       if ( node_uv != uvMap.end() ) {
3583         gp_XY* uv = node_uv->second;
3584         node->SetPosition
3585           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3586       }
3587     }
3588
3589     // move medium nodes of quadratic elements
3590     if ( isQuadratic )
3591     {
3592       SMESH_MesherHelper helper( *GetMesh() );
3593       if ( !face.IsNull() )
3594         helper.SetSubShape( face );
3595       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3596       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3597         const SMDS_VtkFace* QF =
3598           dynamic_cast<const SMDS_VtkFace*> (*elemIt);
3599         if(QF && QF->IsQuadratic()) {
3600           vector<const SMDS_MeshNode*> Ns;
3601           Ns.reserve(QF->NbNodes()+1);
3602           SMDS_ElemIteratorPtr anIter = QF->interlacedNodesElemIterator();
3603           while ( anIter->more() )
3604             Ns.push_back( cast2Node(anIter->next()) );
3605           Ns.push_back( Ns[0] );
3606           double x, y, z;
3607           for(int i=0; i<QF->NbNodes(); i=i+2) {
3608             if ( !surface.IsNull() ) {
3609               gp_XY uv1 = helper.GetNodeUV( face, Ns[i], Ns[i+2] );
3610               gp_XY uv2 = helper.GetNodeUV( face, Ns[i+2], Ns[i] );
3611               gp_XY uv = ( uv1 + uv2 ) / 2.;
3612               gp_Pnt xyz = surface->Value( uv.X(), uv.Y() );
3613               x = xyz.X(); y = xyz.Y(); z = xyz.Z();
3614             }
3615             else {
3616               x = (Ns[i]->X() + Ns[i+2]->X())/2;
3617               y = (Ns[i]->Y() + Ns[i+2]->Y())/2;
3618               z = (Ns[i]->Z() + Ns[i+2]->Z())/2;
3619             }
3620             if( fabs( Ns[i+1]->X() - x ) > disttol ||
3621                 fabs( Ns[i+1]->Y() - y ) > disttol ||
3622                 fabs( Ns[i+1]->Z() - z ) > disttol ) {
3623               // we have to move i+1 node
3624               aMesh->MoveNode( Ns[i+1], x, y, z );
3625             }
3626           }
3627         }
3628       }
3629     }
3630
3631   } // loop on face ids
3632
3633 }
3634
3635 //=======================================================================
3636 //function : isReverse
3637 //purpose  : Return true if normal of prevNodes is not co-directied with
3638 //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3639 //           iNotSame is where prevNodes and nextNodes are different.
3640 //           If result is true then future volume orientation is OK
3641 //=======================================================================
3642
3643 static bool isReverse(const SMDS_MeshElement*             face,
3644                       const vector<const SMDS_MeshNode*>& prevNodes,
3645                       const vector<const SMDS_MeshNode*>& nextNodes,
3646                       const int                           iNotSame)
3647 {
3648
3649   SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
3650   SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
3651   gp_XYZ extrDir( pN - pP ), faceNorm;
3652   SMESH_Algo::FaceNormal( face, faceNorm, /*normalized=*/false );
3653
3654   return faceNorm * extrDir < 0.0;
3655 }
3656
3657 //=======================================================================
3658 /*!
3659  * \brief Create elements by sweeping an element
3660  * \param elem - element to sweep
3661  * \param newNodesItVec - nodes generated from each node of the element
3662  * \param newElems - generated elements
3663  * \param nbSteps - number of sweeping steps
3664  * \param srcElements - to append elem for each generated element
3665  */
3666 //=======================================================================
3667
3668 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
3669                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3670                                     list<const SMDS_MeshElement*>&        newElems,
3671                                     const int                             nbSteps,
3672                                     SMESH_SequenceOfElemPtr&              srcElements)
3673 {
3674   //MESSAGE("sweepElement " << nbSteps);
3675   SMESHDS_Mesh* aMesh = GetMeshDS();
3676
3677   const int           nbNodes = elem->NbNodes();
3678   const int         nbCorners = elem->NbCornerNodes();
3679   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
3680                                                           polyhedron creation !!! */
3681   // Loop on elem nodes:
3682   // find new nodes and detect same nodes indices
3683   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3684   vector<const SMDS_MeshNode*> prevNod( nbNodes );
3685   vector<const SMDS_MeshNode*> nextNod( nbNodes );
3686   vector<const SMDS_MeshNode*> midlNod( nbNodes );
3687
3688   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
3689   vector<int> sames(nbNodes);
3690   vector<bool> isSingleNode(nbNodes);
3691
3692   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3693     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
3694     const SMDS_MeshNode*                         node = nnIt->first;
3695     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3696     if ( listNewNodes.empty() )
3697       return;
3698
3699     itNN   [ iNode ] = listNewNodes.begin();
3700     prevNod[ iNode ] = node;
3701     nextNod[ iNode ] = listNewNodes.front();
3702
3703     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
3704                                                              corner node of linear */
3705     if ( prevNod[ iNode ] != nextNod [ iNode ])
3706       nbDouble += !isSingleNode[iNode];
3707
3708     if( iNode < nbCorners ) { // check corners only
3709       if ( prevNod[ iNode ] == nextNod [ iNode ])
3710         sames[nbSame++] = iNode;
3711       else
3712         iNotSameNode = iNode;
3713     }
3714   }
3715
3716   if ( nbSame == nbNodes || nbSame > 2) {
3717     MESSAGE( " Too many same nodes of element " << elem->GetID() );
3718     return;
3719   }
3720
3721   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
3722   {
3723     // fix nodes order to have bottom normal external
3724     if ( baseType == SMDSEntity_Polygon )
3725     {
3726       std::reverse( itNN.begin(), itNN.end() );
3727       std::reverse( prevNod.begin(), prevNod.end() );
3728       std::reverse( midlNod.begin(), midlNod.end() );
3729       std::reverse( nextNod.begin(), nextNod.end() );
3730       std::reverse( isSingleNode.begin(), isSingleNode.end() );
3731     }
3732     else
3733     {
3734       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
3735       SMDS_MeshCell::applyInterlace( ind, itNN );
3736       SMDS_MeshCell::applyInterlace( ind, prevNod );
3737       SMDS_MeshCell::applyInterlace( ind, nextNod );
3738       SMDS_MeshCell::applyInterlace( ind, midlNod );
3739       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3740       if ( nbSame > 0 )
3741       {
3742         sames[nbSame] = iNotSameNode;
3743         for ( int j = 0; j <= nbSame; ++j )
3744           for ( size_t i = 0; i < ind.size(); ++i )
3745             if ( ind[i] == sames[j] )
3746             {
3747               sames[j] = i;
3748               break;
3749             }
3750         iNotSameNode = sames[nbSame];
3751       }
3752     }
3753   }
3754
3755   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3756   if ( nbSame > 0 ) {
3757     iSameNode    = sames[ nbSame-1 ];
3758     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
3759     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
3760     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
3761   }
3762
3763   // make new elements
3764   for (int iStep = 0; iStep < nbSteps; iStep++ )
3765   {
3766     // get next nodes
3767     for ( iNode = 0; iNode < nbNodes; iNode++ )
3768     {
3769       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
3770       nextNod[ iNode ] = *itNN[ iNode ]++;
3771     }
3772
3773     SMDS_MeshElement* aNewElem = 0;
3774     /*if(!elem->IsPoly())*/ {
3775       switch ( baseType ) {
3776       case SMDSEntity_0D:
3777       case SMDSEntity_Node: { // sweep NODE
3778         if ( nbSame == 0 ) {
3779           if ( isSingleNode[0] )
3780             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3781           else
3782             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3783         }
3784         else
3785           return;
3786         break;
3787       }
3788       case SMDSEntity_Edge: { // sweep EDGE
3789         if ( nbDouble == 0 )
3790         {
3791           if ( nbSame == 0 ) // ---> quadrangle
3792             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3793                                       nextNod[ 1 ], nextNod[ 0 ] );
3794           else               // ---> triangle
3795             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3796                                       nextNod[ iNotSameNode ] );
3797         }
3798         else                 // ---> polygon
3799         {
3800           vector<const SMDS_MeshNode*> poly_nodes;
3801           poly_nodes.push_back( prevNod[0] );
3802           poly_nodes.push_back( prevNod[1] );
3803           if ( prevNod[1] != nextNod[1] )
3804           {
3805             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
3806             poly_nodes.push_back( nextNod[1] );
3807           }
3808           if ( prevNod[0] != nextNod[0] )
3809           {
3810             poly_nodes.push_back( nextNod[0] );
3811             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
3812           }
3813           switch ( poly_nodes.size() ) {
3814           case 3:
3815             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
3816             break;
3817           case 4:
3818             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
3819                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
3820             break;
3821           default:
3822             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
3823           }
3824         }
3825         break;
3826       }
3827       case SMDSEntity_Triangle: // TRIANGLE --->
3828         {
3829           if ( nbDouble > 0 ) break;
3830           if ( nbSame == 0 )       // ---> pentahedron
3831             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3832                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
3833
3834           else if ( nbSame == 1 )  // ---> pyramid
3835             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3836                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
3837                                          nextNod[ iSameNode ]);
3838
3839           else // 2 same nodes:       ---> tetrahedron
3840             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3841                                          nextNod[ iNotSameNode ]);
3842           break;
3843         }
3844       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
3845         {
3846           if ( nbSame == 2 )
3847             return;
3848           if ( nbDouble+nbSame == 2 )
3849           {
3850             if(nbSame==0) {      // ---> quadratic quadrangle
3851               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3852                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
3853             }
3854             else { //(nbSame==1) // ---> quadratic triangle
3855               if(sames[0]==2) {
3856                 return; // medium node on axis
3857               }
3858               else if(sames[0]==0)
3859                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3860                                           nextNod[2], midlNod[1], prevNod[2]);
3861               else // sames[0]==1
3862                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3863                                           midlNod[0], nextNod[2], prevNod[2]);
3864             }
3865           }
3866           else if ( nbDouble == 3 )
3867           {
3868             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
3869               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3870                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
3871             }
3872           }
3873           else
3874             return;
3875           break;
3876         }
3877       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
3878         if ( nbDouble > 0 ) break;
3879
3880         if ( nbSame == 0 )       // ---> hexahedron
3881           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
3882                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
3883
3884         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
3885           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3886                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
3887                                        nextNod[ iSameNode ]);
3888           newElems.push_back( aNewElem );
3889           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
3890                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3891                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
3892         }
3893         else if ( nbSame == 2 ) { // ---> pentahedron
3894           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3895             // iBeforeSame is same too
3896             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3897                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
3898                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
3899           else
3900             // iAfterSame is same too
3901             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
3902                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3903                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
3904         }
3905         break;
3906       }
3907       case SMDSEntity_Quad_Triangle: { // sweep Quadratic TRIANGLE --->
3908         if ( nbDouble+nbSame != 3 ) break;
3909         if(nbSame==0) {
3910           // --->  pentahedron with 15 nodes
3911           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3912                                        nextNod[0], nextNod[1], nextNod[2],
3913                                        prevNod[3], prevNod[4], prevNod[5],
3914                                        nextNod[3], nextNod[4], nextNod[5],
3915                                        midlNod[0], midlNod[1], midlNod[2]);
3916         }
3917         else if(nbSame==1) {
3918           // --->  2d order pyramid of 13 nodes
3919           int apex = iSameNode;
3920           int i0 = ( apex + 1 ) % nbCorners;
3921           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
3922           int i0a = apex + 3;
3923           int i1a = i1 + 3;
3924           int i01 = i0 + 3;
3925           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
3926                                       nextNod[i0], nextNod[i1], prevNod[apex],
3927                                       prevNod[i01], midlNod[i0],
3928                                       nextNod[i01], midlNod[i1],
3929                                       prevNod[i1a], prevNod[i0a],
3930                                       nextNod[i0a], nextNod[i1a]);
3931         }
3932         else if(nbSame==2) {
3933           // --->  2d order tetrahedron of 10 nodes
3934           int n1 = iNotSameNode;
3935           int n2 = ( n1 + 1             ) % nbCorners;
3936           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
3937           int n12 = n1 + 3;
3938           int n23 = n2 + 3;
3939           int n31 = n3 + 3;
3940           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
3941                                        prevNod[n12], prevNod[n23], prevNod[n31],
3942                                        midlNod[n1], nextNod[n12], nextNod[n31]);
3943         }
3944         break;
3945       }
3946       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
3947         if( nbSame == 0 ) {
3948           if ( nbDouble != 4 ) break;
3949           // --->  hexahedron with 20 nodes
3950           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3951                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3952                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3953                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3954                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
3955         }
3956         else if(nbSame==1) {
3957           // ---> pyramid + pentahedron - can not be created since it is needed
3958           // additional middle node at the center of face
3959           INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
3960           return;
3961         }
3962         else if( nbSame == 2 ) {
3963           if ( nbDouble != 2 ) break;
3964           // --->  2d order Pentahedron with 15 nodes
3965           int n1,n2,n4,n5;
3966           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
3967             // iBeforeSame is same too
3968             n1 = iBeforeSame;
3969             n2 = iOpposSame;
3970             n4 = iSameNode;
3971             n5 = iAfterSame;
3972           }
3973           else {
3974             // iAfterSame is same too
3975             n1 = iSameNode;
3976             n2 = iBeforeSame;
3977             n4 = iAfterSame;
3978             n5 = iOpposSame;
3979           }
3980           int n12 = n2 + 4;
3981           int n45 = n4 + 4;
3982           int n14 = n1 + 4;
3983           int n25 = n5 + 4;
3984           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
3985                                        prevNod[n4], prevNod[n5], nextNod[n5],
3986                                        prevNod[n12], midlNod[n2], nextNod[n12],
3987                                        prevNod[n45], midlNod[n5], nextNod[n45],
3988                                        prevNod[n14], prevNod[n25], nextNod[n25]);
3989         }
3990         break;
3991       }
3992       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
3993
3994         if( nbSame == 0 && nbDouble == 9 ) {
3995           // --->  tri-quadratic hexahedron with 27 nodes
3996           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3997                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3998                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3999                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4000                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4001                                        prevNod[8], // bottom center
4002                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4003                                        nextNod[8], // top center
4004                                        midlNod[8]);// elem center
4005         }
4006         else
4007         {
4008           return;
4009         }
4010         break;
4011       }
4012       case SMDSEntity_Polygon: { // sweep POLYGON
4013
4014         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4015           // --->  hexagonal prism
4016           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4017                                        prevNod[3], prevNod[4], prevNod[5],
4018                                        nextNod[0], nextNod[1], nextNod[2],
4019                                        nextNod[3], nextNod[4], nextNod[5]);
4020         }
4021         break;
4022       }
4023       case SMDSEntity_Ball:
4024         return;
4025
4026       default:
4027         break;
4028       }
4029     }
4030
4031     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4032     {
4033       if ( baseType != SMDSEntity_Polygon )
4034       {
4035         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4036         SMDS_MeshCell::applyInterlace( ind, prevNod );
4037         SMDS_MeshCell::applyInterlace( ind, nextNod );
4038         SMDS_MeshCell::applyInterlace( ind, midlNod );
4039         SMDS_MeshCell::applyInterlace( ind, itNN );
4040         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4041         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4042       }
4043       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4044       vector<int> quantities (nbNodes + 2);
4045       polyedre_nodes.clear();
4046       quantities.clear();
4047
4048       // bottom of prism
4049       for (int inode = 0; inode < nbNodes; inode++)
4050         polyedre_nodes.push_back( prevNod[inode] );
4051       quantities.push_back( nbNodes );
4052
4053       // top of prism
4054       polyedre_nodes.push_back( nextNod[0] );
4055       for (int inode = nbNodes; inode-1; --inode )
4056         polyedre_nodes.push_back( nextNod[inode-1] );
4057       quantities.push_back( nbNodes );
4058
4059       // side faces
4060       for (int iface = 0; iface < nbNodes; iface++)
4061       {
4062         const int prevNbNodes = polyedre_nodes.size();
4063         int inextface = (iface+1) % nbNodes;
4064         polyedre_nodes.push_back( prevNod[inextface] );
4065         polyedre_nodes.push_back( prevNod[iface] );
4066         if ( prevNod[iface] != nextNod[iface] )
4067         {
4068           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4069           polyedre_nodes.push_back( nextNod[iface] );
4070         }
4071         if ( prevNod[inextface] != nextNod[inextface] )
4072         {
4073           polyedre_nodes.push_back( nextNod[inextface] );
4074           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4075         }
4076         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4077         if ( nbFaceNodes > 2 )
4078           quantities.push_back( nbFaceNodes );
4079         else // degenerated face
4080           polyedre_nodes.resize( prevNbNodes );
4081       }
4082       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4083     }
4084
4085     if ( aNewElem ) {
4086       newElems.push_back( aNewElem );
4087       myLastCreatedElems.Append(aNewElem);
4088       srcElements.Append( elem );
4089     }
4090
4091     // set new prev nodes
4092     for ( iNode = 0; iNode < nbNodes; iNode++ )
4093       prevNod[ iNode ] = nextNod[ iNode ];
4094
4095   } // for steps
4096 }
4097
4098 //=======================================================================
4099 /*!
4100  * \brief Create 1D and 2D elements around swept elements
4101  * \param mapNewNodes - source nodes and ones generated from them
4102  * \param newElemsMap - source elements and ones generated from them
4103  * \param elemNewNodesMap - nodes generated from each node of each element
4104  * \param elemSet - all swept elements
4105  * \param nbSteps - number of sweeping steps
4106  * \param srcElements - to append elem for each generated element
4107  */
4108 //=======================================================================
4109
4110 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4111                                   TElemOfElemListMap &     newElemsMap,
4112                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4113                                   TIDSortedElemSet&        elemSet,
4114                                   const int                nbSteps,
4115                                   SMESH_SequenceOfElemPtr& srcElements)
4116 {
4117   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4118   SMESHDS_Mesh* aMesh = GetMeshDS();
4119
4120   // Find nodes belonging to only one initial element - sweep them to get edges.
4121
4122   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4123   for ( ; nList != mapNewNodes.end(); nList++ )
4124   {
4125     const SMDS_MeshNode* node =
4126       static_cast<const SMDS_MeshNode*>( nList->first );
4127     if ( newElemsMap.count( node ))
4128       continue; // node was extruded into edge
4129     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4130     int nbInitElems = 0;
4131     const SMDS_MeshElement* el = 0;
4132     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4133     while ( eIt->more() && nbInitElems < 2 ) {
4134       el = eIt->next();
4135       SMDSAbs_ElementType type = el->GetType();
4136       if ( type == SMDSAbs_Volume || type < highType ) continue;
4137       if ( type > highType ) {
4138         nbInitElems = 0;
4139         highType = type;
4140       }
4141       nbInitElems += elemSet.count(el);
4142     }
4143     if ( nbInitElems < 2 ) {
4144       bool NotCreateEdge = el && el->IsMediumNode(node);
4145       if(!NotCreateEdge) {
4146         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4147         list<const SMDS_MeshElement*> newEdges;
4148         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4149       }
4150     }
4151   }
4152
4153   // Make a ceiling for each element ie an equal element of last new nodes.
4154   // Find free links of faces - make edges and sweep them into faces.
4155
4156   TElemOfElemListMap::iterator   itElem      = newElemsMap.begin();
4157   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4158   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4159   {
4160     const SMDS_MeshElement* elem = itElem->first;
4161     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4162
4163     if(itElem->second.size()==0) continue;
4164
4165     const bool isQuadratic = elem->IsQuadratic();
4166
4167     if ( elem->GetType() == SMDSAbs_Edge ) {
4168       // create a ceiling edge
4169       if ( !isQuadratic ) {
4170         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4171                                vecNewNodes[ 1 ]->second.back())) {
4172           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4173                                                    vecNewNodes[ 1 ]->second.back()));
4174           srcElements.Append( elem );
4175         }
4176       }
4177       else {
4178         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4179                                vecNewNodes[ 1 ]->second.back(),
4180                                vecNewNodes[ 2 ]->second.back())) {
4181           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4182                                                    vecNewNodes[ 1 ]->second.back(),
4183                                                    vecNewNodes[ 2 ]->second.back()));
4184           srcElements.Append( elem );
4185         }
4186       }
4187     }
4188     if ( elem->GetType() != SMDSAbs_Face )
4189       continue;
4190
4191     bool hasFreeLinks = false;
4192
4193     TIDSortedElemSet avoidSet;
4194     avoidSet.insert( elem );
4195
4196     set<const SMDS_MeshNode*> aFaceLastNodes;
4197     int iNode, nbNodes = vecNewNodes.size();
4198     if ( !isQuadratic ) {
4199       // loop on the face nodes
4200       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4201         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4202         // look for free links of the face
4203         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4204         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4205         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4206         // check if a link n1-n2 is free
4207         if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4208           hasFreeLinks = true;
4209           // make a new edge and a ceiling for a new edge
4210           const SMDS_MeshElement* edge;
4211           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4212             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4213             srcElements.Append( myLastCreatedElems.Last() );
4214           }
4215           n1 = vecNewNodes[ iNode ]->second.back();
4216           n2 = vecNewNodes[ iNext ]->second.back();
4217           if ( !aMesh->FindEdge( n1, n2 )) {
4218             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4219             srcElements.Append( edge );
4220           }
4221         }
4222       }
4223     }
4224     else { // elem is quadratic face
4225       int nbn = nbNodes/2;
4226       for ( iNode = 0; iNode < nbn; iNode++ ) {
4227         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4228         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4229         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4230         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4231         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4232         // check if a link is free
4233         if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4234              ! SMESH_MeshEditor::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4235              ! SMESH_MeshEditor::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4236           hasFreeLinks = true;
4237           // make an edge and a ceiling for a new edge
4238           // find medium node
4239           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4240             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4241             srcElements.Append( elem );
4242           }
4243           n1 = vecNewNodes[ iNode ]->second.back();
4244           n2 = vecNewNodes[ iNext ]->second.back();
4245           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4246           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4247             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4248             srcElements.Append( elem );
4249           }
4250         }
4251       }
4252       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4253         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4254       }
4255     }
4256
4257     // sweep free links into faces
4258
4259     if ( hasFreeLinks )  {
4260       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4261       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4262
4263       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4264       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4265         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4266         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4267       }
4268       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4269         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4270         std::advance( v, volNb );
4271         // find indices of free faces of a volume and their source edges
4272         list< int > freeInd;
4273         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4274         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4275         int iF, nbF = vTool.NbFaces();
4276         for ( iF = 0; iF < nbF; iF ++ ) {
4277           if (vTool.IsFreeFace( iF ) &&
4278               vTool.GetFaceNodes( iF, faceNodeSet ) &&
4279               initNodeSet != faceNodeSet) // except an initial face
4280           {
4281             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4282               continue;
4283             freeInd.push_back( iF );
4284             // find source edge of a free face iF
4285             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4286             commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4287             std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4288                                    initNodeSet.begin(), initNodeSet.end(),
4289                                    commonNodes.begin());
4290             if ( (*v)->IsQuadratic() )
4291               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4292             else
4293               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4294 #ifdef _DEBUG_
4295             if ( !srcEdges.back() )
4296             {
4297               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4298                    << iF << " of volume #" << vTool.ID() << endl;
4299             }
4300 #endif
4301           }
4302         }
4303         if ( freeInd.empty() )
4304           continue;
4305
4306         // create faces for all steps;
4307         // if such a face has been already created by sweep of edge,
4308         // assure that its orientation is OK
4309         for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4310           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4311           vTool.SetExternalNormal();
4312           const int nextShift = vTool.IsForward() ? +1 : -1;
4313           list< int >::iterator ind = freeInd.begin();
4314           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4315           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4316           {
4317             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4318             int nbn = vTool.NbFaceNodes( *ind );
4319             const SMDS_MeshElement * f = 0;
4320             if ( nbn == 3 )              ///// triangle
4321             {
4322               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4323               if ( !f ||
4324                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4325               {
4326                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4327                                                      nodes[ 1 ],
4328                                                      nodes[ 1 + nextShift ] };
4329                 if ( f )
4330                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4331                 else
4332                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4333                                                             newOrder[ 2 ] ));
4334               }
4335             }
4336             else if ( nbn == 4 )       ///// quadrangle
4337             {
4338               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4339               if ( !f ||
4340                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4341               {
4342                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4343                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4344                 if ( f )
4345                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4346                 else
4347                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4348                                                             newOrder[ 2 ], newOrder[ 3 ]));
4349               }
4350             }
4351             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4352             {
4353               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4354               if ( !f ||
4355                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4356               {
4357                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4358                                                      nodes[2],
4359                                                      nodes[2 + 2*nextShift],
4360                                                      nodes[3 - 2*nextShift],
4361                                                      nodes[3],
4362                                                      nodes[3 + 2*nextShift]};
4363                 if ( f )
4364                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4365                 else
4366                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4367                                                             newOrder[ 1 ],
4368                                                             newOrder[ 2 ],
4369                                                             newOrder[ 3 ],
4370                                                             newOrder[ 4 ],
4371                                                             newOrder[ 5 ] ));
4372               }
4373             }
4374             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4375             {
4376               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4377                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4378               if ( !f ||
4379                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4380               {
4381                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4382                                                      nodes[4 - 2*nextShift],
4383                                                      nodes[4],
4384                                                      nodes[4 + 2*nextShift],
4385                                                      nodes[1],
4386                                                      nodes[5 - 2*nextShift],
4387                                                      nodes[5],
4388                                                      nodes[5 + 2*nextShift] };
4389                 if ( f )
4390                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4391                 else
4392                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4393                                                            newOrder[ 2 ], newOrder[ 3 ],
4394                                                            newOrder[ 4 ], newOrder[ 5 ],
4395                                                            newOrder[ 6 ], newOrder[ 7 ]));
4396               }
4397             }
4398             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4399             {
4400               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4401                                       SMDSAbs_Face, /*noMedium=*/false);
4402               if ( !f ||
4403                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4404               {
4405                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4406                                                      nodes[4 - 2*nextShift],
4407                                                      nodes[4],
4408                                                      nodes[4 + 2*nextShift],
4409                                                      nodes[1],
4410                                                      nodes[5 - 2*nextShift],
4411                                                      nodes[5],
4412                                                      nodes[5 + 2*nextShift],
4413                                                      nodes[8] };
4414                 if ( f )
4415                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4416                 else
4417                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4418                                                            newOrder[ 2 ], newOrder[ 3 ],
4419                                                            newOrder[ 4 ], newOrder[ 5 ],
4420                                                            newOrder[ 6 ], newOrder[ 7 ],
4421                                                            newOrder[ 8 ]));
4422               }
4423             }
4424             else  //////// polygon
4425             {
4426               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4427               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4428               if ( !f ||
4429                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4430               {
4431                 if ( !vTool.IsForward() )
4432                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4433                 if ( f )
4434                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4435                 else
4436                   AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4437               }
4438             }
4439
4440             while ( srcElements.Length() < myLastCreatedElems.Length() )
4441               srcElements.Append( *srcEdge );
4442
4443           }  // loop on free faces
4444
4445           // go to the next volume
4446           iVol = 0;
4447           while ( iVol++ < nbVolumesByStep ) v++;
4448
4449         } // loop on steps
4450       } // loop on volumes of one step
4451     } // sweep free links into faces
4452
4453     // Make a ceiling face with a normal external to a volume
4454
4455     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4456
4457     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4458     if ( iF >= 0 ) {
4459       lastVol.SetExternalNormal();
4460       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4461       int nbn = lastVol.NbFaceNodes( iF );
4462       if ( nbn == 3 ) {
4463         if (!hasFreeLinks ||
4464             !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
4465           myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
4466       }
4467       else if ( nbn == 4 )
4468       {
4469         if (!hasFreeLinks ||
4470             !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
4471           myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]));
4472       }
4473       else if ( nbn == 6 && isQuadratic )
4474       {
4475         if (!hasFreeLinks ||
4476             !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5]) )
4477           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
4478                                                    nodes[1], nodes[3], nodes[5]));
4479       }
4480       else if ( nbn == 8 && isQuadratic )
4481       {
4482         if (!hasFreeLinks ||
4483             !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
4484                              nodes[1], nodes[3], nodes[5], nodes[7]) )
4485           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4486                                                    nodes[1], nodes[3], nodes[5], nodes[7]));
4487       }
4488       else if ( nbn == 9 && isQuadratic )
4489       {
4490         if (!hasFreeLinks ||
4491             !aMesh->FindElement(vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4492                                 SMDSAbs_Face, /*noMedium=*/false) )
4493           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4494                                                    nodes[1], nodes[3], nodes[5], nodes[7],
4495                                                    nodes[8]));
4496       }
4497       else {
4498         vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes + nbn );
4499         if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
4500           myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4501       }
4502
4503       while ( srcElements.Length() < myLastCreatedElems.Length() )
4504         srcElements.Append( elem );
4505     }
4506   } // loop on swept elements
4507 }
4508
4509 //=======================================================================
4510 //function : RotationSweep
4511 //purpose  :
4512 //=======================================================================
4513
4514 SMESH_MeshEditor::PGroupIDs
4515 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4516                                 const gp_Ax1&      theAxis,
4517                                 const double       theAngle,
4518                                 const int          theNbSteps,
4519                                 const double       theTol,
4520                                 const bool         theMakeGroups,
4521                                 const bool         theMakeWalls)
4522 {
4523   myLastCreatedElems.Clear();
4524   myLastCreatedNodes.Clear();
4525
4526   // source elements for each generated one
4527   SMESH_SequenceOfElemPtr srcElems, srcNodes;
4528
4529   MESSAGE( "RotationSweep()");
4530   gp_Trsf aTrsf;
4531   aTrsf.SetRotation( theAxis, theAngle );
4532   gp_Trsf aTrsf2;
4533   aTrsf2.SetRotation( theAxis, theAngle/2. );
4534
4535   gp_Lin aLine( theAxis );
4536   double aSqTol = theTol * theTol;
4537
4538   SMESHDS_Mesh* aMesh = GetMeshDS();
4539
4540   TNodeOfNodeListMap mapNewNodes;
4541   TElemOfVecOfNnlmiMap mapElemNewNodes;
4542   TElemOfElemListMap newElemsMap;
4543
4544   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4545                                      myMesh->NbFaces(ORDER_QUADRATIC) +
4546                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
4547   // loop on theElems
4548   TIDSortedElemSet::iterator itElem;
4549   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4550     const SMDS_MeshElement* elem = *itElem;
4551     if ( !elem || elem->GetType() == SMDSAbs_Volume )
4552       continue;
4553     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4554     newNodesItVec.reserve( elem->NbNodes() );
4555
4556     // loop on elem nodes
4557     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4558     while ( itN->more() )
4559     {
4560       // check if a node has been already sweeped
4561       const SMDS_MeshNode* node = cast2Node( itN->next() );
4562
4563       gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4564       double coord[3];
4565       aXYZ.Coord( coord[0], coord[1], coord[2] );
4566       bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4567
4568       TNodeOfNodeListMapItr nIt =
4569         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4570       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4571       if ( listNewNodes.empty() )
4572       {
4573         // check if we are to create medium nodes between corner ones
4574         bool needMediumNodes = false;
4575         if ( isQuadraticMesh )
4576         {
4577           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4578           while (it->more() && !needMediumNodes )
4579           {
4580             const SMDS_MeshElement* invElem = it->next();
4581             if ( invElem != elem && !theElems.count( invElem )) continue;
4582             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4583             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4584               needMediumNodes = true;
4585           }
4586         }
4587
4588         // make new nodes
4589         const SMDS_MeshNode * newNode = node;
4590         for ( int i = 0; i < theNbSteps; i++ ) {
4591           if ( !isOnAxis ) {
4592             if ( needMediumNodes )  // create a medium node
4593             {
4594               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4595               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4596               myLastCreatedNodes.Append(newNode);
4597               srcNodes.Append( node );
4598               listNewNodes.push_back( newNode );
4599               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4600             }
4601             else {
4602               aTrsf.Transforms( coord[0], coord[1], coord[2] );
4603             }
4604             // create a corner node
4605             newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4606             myLastCreatedNodes.Append(newNode);
4607             srcNodes.Append( node );
4608             listNewNodes.push_back( newNode );
4609           }
4610           else {
4611             listNewNodes.push_back( newNode );
4612             // if ( needMediumNodes )
4613             //   listNewNodes.push_back( newNode );
4614           }
4615         }
4616       }
4617       newNodesItVec.push_back( nIt );
4618     }
4619     // make new elements
4620     sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4621   }
4622
4623   if ( theMakeWalls )
4624     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4625
4626   PGroupIDs newGroupIDs;
4627   if ( theMakeGroups )
4628     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4629
4630   return newGroupIDs;
4631 }
4632
4633
4634 //=======================================================================
4635 //function : CreateNode
4636 //purpose  :
4637 //=======================================================================
4638 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4639                                                   const double y,
4640                                                   const double z,
4641                                                   const double tolnode,
4642                                                   SMESH_SequenceOfNode& aNodes)
4643 {
4644   // myLastCreatedElems.Clear();
4645   // myLastCreatedNodes.Clear();
4646
4647   gp_Pnt P1(x,y,z);
4648   SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4649
4650   // try to search in sequence of existing nodes
4651   // if aNodes.Length()>0 we 'nave to use given sequence
4652   // else - use all nodes of mesh
4653   if(aNodes.Length()>0) {
4654     int i;
4655     for(i=1; i<=aNodes.Length(); i++) {
4656       gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4657       if(P1.Distance(P2)<tolnode)
4658         return aNodes.Value(i);
4659     }
4660   }
4661   else {
4662     SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4663     while(itn->more()) {
4664       const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4665       gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4666       if(P1.Distance(P2)<tolnode)
4667         return aN;
4668     }
4669   }
4670
4671   // create new node and return it
4672   const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4673   //myLastCreatedNodes.Append(NewNode);
4674   return NewNode;
4675 }
4676
4677
4678 //=======================================================================
4679 //function : ExtrusionSweep
4680 //purpose  :
4681 //=======================================================================
4682
4683 SMESH_MeshEditor::PGroupIDs
4684 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &  theElems,
4685                                   const gp_Vec&       theStep,
4686                                   const int           theNbSteps,
4687                                   TElemOfElemListMap& newElemsMap,
4688                                   const bool          theMakeGroups,
4689                                   const int           theFlags,
4690                                   const double        theTolerance)
4691 {
4692   ExtrusParam aParams;
4693   aParams.myDir = gp_Dir(theStep);
4694   aParams.myNodes.Clear();
4695   aParams.mySteps = new TColStd_HSequenceOfReal;
4696   int i;
4697   for(i=1; i<=theNbSteps; i++)
4698     aParams.mySteps->Append(theStep.Magnitude());
4699
4700   return
4701     ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4702 }
4703
4704
4705 //=======================================================================
4706 //function : ExtrusionSweep
4707 //purpose  :
4708 //=======================================================================
4709
4710 SMESH_MeshEditor::PGroupIDs
4711 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &  theElems,
4712                                   ExtrusParam&        theParams,
4713                                   TElemOfElemListMap& newElemsMap,
4714                                   const bool          theMakeGroups,
4715                                   const int           theFlags,
4716                                   const double        theTolerance)
4717 {
4718   myLastCreatedElems.Clear();
4719   myLastCreatedNodes.Clear();
4720
4721   // source elements for each generated one
4722   SMESH_SequenceOfElemPtr srcElems, srcNodes;
4723
4724   SMESHDS_Mesh* aMesh = GetMeshDS();
4725
4726   int nbsteps = theParams.mySteps->Length();
4727
4728   TNodeOfNodeListMap mapNewNodes;
4729   //TNodeOfNodeVecMap mapNewNodes;
4730   TElemOfVecOfNnlmiMap mapElemNewNodes;
4731   //TElemOfVecOfMapNodesMap mapElemNewNodes;
4732
4733   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4734                                      myMesh->NbFaces(ORDER_QUADRATIC) +
4735                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
4736   // loop on theElems
4737   TIDSortedElemSet::iterator itElem;
4738   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4739     // check element type
4740     const SMDS_MeshElement* elem = *itElem;
4741     if ( !elem  || elem->GetType() == SMDSAbs_Volume )
4742       continue;
4743
4744     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4745     newNodesItVec.reserve( elem->NbNodes() );
4746
4747     // loop on elem nodes
4748     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4749     while ( itN->more() )
4750     {
4751       // check if a node has been already sweeped
4752       const SMDS_MeshNode* node = cast2Node( itN->next() );
4753       TNodeOfNodeListMap::iterator nIt =
4754         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4755       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4756       if ( listNewNodes.empty() )
4757       {
4758         // make new nodes
4759
4760         // check if we are to create medium nodes between corner ones
4761         bool needMediumNodes = false;
4762         if ( isQuadraticMesh )
4763         {
4764           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4765           while (it->more() && !needMediumNodes )
4766           {
4767             const SMDS_MeshElement* invElem = it->next();
4768             if ( invElem != elem && !theElems.count( invElem )) continue;
4769             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4770             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4771               needMediumNodes = true;
4772           }
4773         }
4774
4775         double coord[] = { node->X(), node->Y(), node->Z() };
4776         for ( int i = 0; i < nbsteps; i++ )
4777         {
4778           if ( needMediumNodes ) // create a medium node
4779           {
4780             double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4781             double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4782             double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4783             if( theFlags & EXTRUSION_FLAG_SEW ) {
4784               const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4785                                                          theTolerance, theParams.myNodes);
4786               listNewNodes.push_back( newNode );
4787             }
4788             else {
4789               const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4790               myLastCreatedNodes.Append(newNode);
4791               srcNodes.Append( node );
4792               listNewNodes.push_back( newNode );
4793             }
4794           }
4795           // create a corner node
4796           coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4797           coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4798           coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4799           if( theFlags & EXTRUSION_FLAG_SEW ) {
4800             const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4801                                                        theTolerance, theParams.myNodes);
4802             listNewNodes.push_back( newNode );
4803           }
4804           else {
4805             const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4806             myLastCreatedNodes.Append(newNode);
4807             srcNodes.Append( node );
4808             listNewNodes.push_back( newNode );
4809           }
4810         }
4811       }
4812       newNodesItVec.push_back( nIt );
4813     }
4814     // make new elements
4815     sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4816   }
4817
4818   if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4819     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4820   }
4821   PGroupIDs newGroupIDs;
4822   if ( theMakeGroups )
4823     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4824
4825   return newGroupIDs;
4826 }
4827
4828 //=======================================================================
4829 //function : ExtrusionAlongTrack
4830 //purpose  :
4831 //=======================================================================
4832 SMESH_MeshEditor::Extrusion_Error
4833 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
4834                                        SMESH_subMesh*       theTrack,
4835                                        const SMDS_MeshNode* theN1,
4836                                        const bool           theHasAngles,
4837                                        list<double>&        theAngles,
4838                                        const bool           theLinearVariation,
4839                                        const bool           theHasRefPoint,
4840                                        const gp_Pnt&        theRefPoint,
4841                                        const bool           theMakeGroups)
4842 {
4843   MESSAGE("ExtrusionAlongTrack");
4844   myLastCreatedElems.Clear();
4845   myLastCreatedNodes.Clear();
4846
4847   int aNbE;
4848   std::list<double> aPrms;
4849   TIDSortedElemSet::iterator itElem;
4850
4851   gp_XYZ aGC;
4852   TopoDS_Edge aTrackEdge;
4853   TopoDS_Vertex aV1, aV2;
4854
4855   SMDS_ElemIteratorPtr aItE;
4856   SMDS_NodeIteratorPtr aItN;
4857   SMDSAbs_ElementType aTypeE;
4858
4859   TNodeOfNodeListMap mapNewNodes;
4860
4861   // 1. Check data
4862   aNbE = theElements.size();
4863   // nothing to do
4864   if ( !aNbE )
4865     return EXTR_NO_ELEMENTS;
4866
4867   // 1.1 Track Pattern
4868   ASSERT( theTrack );
4869
4870   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4871
4872   aItE = pSubMeshDS->GetElements();
4873   while ( aItE->more() ) {
4874     const SMDS_MeshElement* pE = aItE->next();
4875     aTypeE = pE->GetType();
4876     // Pattern must contain links only
4877     if ( aTypeE != SMDSAbs_Edge )
4878       return EXTR_PATH_NOT_EDGE;
4879   }
4880
4881   list<SMESH_MeshEditor_PathPoint> fullList;
4882
4883   const TopoDS_Shape& aS = theTrack->GetSubShape();
4884   // Sub-shape for the Pattern must be an Edge or Wire
4885   if( aS.ShapeType() == TopAbs_EDGE ) {
4886     aTrackEdge = TopoDS::Edge( aS );
4887     // the Edge must not be degenerated
4888     if ( BRep_Tool::Degenerated( aTrackEdge ) )
4889       return EXTR_BAD_PATH_SHAPE;
4890     TopExp::Vertices( aTrackEdge, aV1, aV2 );
4891     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4892     const SMDS_MeshNode* aN1 = aItN->next();
4893     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4894     const SMDS_MeshNode* aN2 = aItN->next();
4895     // starting node must be aN1 or aN2
4896     if ( !( aN1 == theN1 || aN2 == theN1 ) )
4897       return EXTR_BAD_STARTING_NODE;
4898     aItN = pSubMeshDS->GetNodes();
4899     while ( aItN->more() ) {
4900       const SMDS_MeshNode* pNode = aItN->next();
4901       const SMDS_EdgePosition* pEPos =
4902         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4903       double aT = pEPos->GetUParameter();
4904       aPrms.push_back( aT );
4905     }
4906     //Extrusion_Error err =
4907     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4908   } else if( aS.ShapeType() == TopAbs_WIRE ) {
4909     list< SMESH_subMesh* > LSM;
4910     TopTools_SequenceOfShape Edges;
4911     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4912     while(itSM->more()) {
4913       SMESH_subMesh* SM = itSM->next();
4914       LSM.push_back(SM);
4915       const TopoDS_Shape& aS = SM->GetSubShape();
4916       Edges.Append(aS);
4917     }
4918     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4919     int startNid = theN1->GetID();
4920     TColStd_MapOfInteger UsedNums;
4921
4922     int NbEdges = Edges.Length();
4923     int i = 1;
4924     for(; i<=NbEdges; i++) {
4925       int k = 0;
4926       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4927       for(; itLSM!=LSM.end(); itLSM++) {
4928         k++;
4929         if(UsedNums.Contains(k)) continue;
4930         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4931         SMESH_subMesh* locTrack = *itLSM;
4932         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4933         TopExp::Vertices( aTrackEdge, aV1, aV2 );
4934         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4935         const SMDS_MeshNode* aN1 = aItN->next();
4936         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4937         const SMDS_MeshNode* aN2 = aItN->next();
4938         // starting node must be aN1 or aN2
4939         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4940         // 2. Collect parameters on the track edge
4941         aPrms.clear();
4942         aItN = locMeshDS->GetNodes();
4943         while ( aItN->more() ) {
4944           const SMDS_MeshNode* pNode = aItN->next();
4945           const SMDS_EdgePosition* pEPos =
4946             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4947           double aT = pEPos->GetUParameter();
4948           aPrms.push_back( aT );
4949         }
4950         list<SMESH_MeshEditor_PathPoint> LPP;
4951         //Extrusion_Error err =
4952         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4953         LLPPs.push_back(LPP);
4954         UsedNums.Add(k);
4955         // update startN for search following egde
4956         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4957         else startNid = aN1->GetID();
4958         break;
4959       }
4960     }
4961     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4962     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4963     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4964     for(; itPP!=firstList.end(); itPP++) {
4965       fullList.push_back( *itPP );
4966     }
4967     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4968     fullList.pop_back();
4969     itLLPP++;
4970     for(; itLLPP!=LLPPs.end(); itLLPP++) {
4971       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4972       itPP = currList.begin();
4973       SMESH_MeshEditor_PathPoint PP2 = currList.front();
4974       gp_Dir D1 = PP1.Tangent();
4975       gp_Dir D2 = PP2.Tangent();
4976       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4977                            (D1.Z()+D2.Z())/2 ) );
4978       PP1.SetTangent(Dnew);
4979       fullList.push_back(PP1);
4980       itPP++;
4981       for(; itPP!=firstList.end(); itPP++) {
4982         fullList.push_back( *itPP );
4983       }
4984       PP1 = fullList.back();
4985       fullList.pop_back();
4986     }
4987     // if wire not closed
4988     fullList.push_back(PP1);
4989     // else ???
4990   }
4991   else {
4992     return EXTR_BAD_PATH_SHAPE;
4993   }
4994
4995   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4996                           theHasRefPoint, theRefPoint, theMakeGroups);
4997 }
4998
4999
5000 //=======================================================================
5001 //function : ExtrusionAlongTrack
5002 //purpose  :
5003 //=======================================================================
5004 SMESH_MeshEditor::Extrusion_Error
5005 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
5006                                        SMESH_Mesh*          theTrack,
5007                                        const SMDS_MeshNode* theN1,
5008                                        const bool           theHasAngles,
5009                                        list<double>&        theAngles,
5010                                        const bool           theLinearVariation,
5011                                        const bool           theHasRefPoint,
5012                                        const gp_Pnt&        theRefPoint,
5013                                        const bool           theMakeGroups)
5014 {
5015   myLastCreatedElems.Clear();
5016   myLastCreatedNodes.Clear();
5017
5018   int aNbE;
5019   std::list<double> aPrms;
5020   TIDSortedElemSet::iterator itElem;
5021
5022   gp_XYZ aGC;
5023   TopoDS_Edge aTrackEdge;
5024   TopoDS_Vertex aV1, aV2;
5025
5026   SMDS_ElemIteratorPtr aItE;
5027   SMDS_NodeIteratorPtr aItN;
5028   SMDSAbs_ElementType aTypeE;
5029
5030   TNodeOfNodeListMap mapNewNodes;
5031
5032   // 1. Check data
5033   aNbE = theElements.size();
5034   // nothing to do
5035   if ( !aNbE )
5036     return EXTR_NO_ELEMENTS;
5037
5038   // 1.1 Track Pattern
5039   ASSERT( theTrack );
5040
5041   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5042
5043   aItE = pMeshDS->elementsIterator();
5044   while ( aItE->more() ) {
5045     const SMDS_MeshElement* pE = aItE->next();
5046     aTypeE = pE->GetType();
5047     // Pattern must contain links only
5048     if ( aTypeE != SMDSAbs_Edge )
5049       return EXTR_PATH_NOT_EDGE;
5050   }
5051
5052   list<SMESH_MeshEditor_PathPoint> fullList;
5053
5054   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5055
5056   if( aS == SMESH_Mesh::PseudoShape() ) {
5057     //Mesh without shape
5058     const SMDS_MeshNode* currentNode = NULL;
5059     const SMDS_MeshNode* prevNode = theN1;
5060     std::vector<const SMDS_MeshNode*> aNodesList;
5061     aNodesList.push_back(theN1);
5062     int nbEdges = 0, conn=0;
5063     const SMDS_MeshElement* prevElem = NULL;
5064     const SMDS_MeshElement* currentElem = NULL;
5065     int totalNbEdges = theTrack->NbEdges();
5066     SMDS_ElemIteratorPtr nIt;
5067
5068     //check start node
5069     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5070       return EXTR_BAD_STARTING_NODE;
5071     }
5072
5073     conn = nbEdgeConnectivity(theN1);
5074     if(conn > 2)
5075       return EXTR_PATH_NOT_EDGE;
5076
5077     aItE = theN1->GetInverseElementIterator();
5078     prevElem = aItE->next();
5079     currentElem = prevElem;
5080     //Get all nodes
5081     if(totalNbEdges == 1 ) {
5082       nIt = currentElem->nodesIterator();
5083       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5084       if(currentNode == prevNode)
5085         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5086       aNodesList.push_back(currentNode);
5087     } else {
5088       nIt = currentElem->nodesIterator();
5089       while( nIt->more() ) {
5090         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5091         if(currentNode == prevNode)
5092           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5093         aNodesList.push_back(currentNode);
5094
5095         //case of the closed mesh
5096         if(currentNode == theN1) {
5097           nbEdges++;
5098           break;
5099         }
5100
5101         conn = nbEdgeConnectivity(currentNode);
5102         if(conn > 2) {
5103           return EXTR_PATH_NOT_EDGE;
5104         }else if( conn == 1 && nbEdges > 0 ) {
5105           //End of the path
5106           nbEdges++;
5107           break;
5108         }else {
5109           prevNode = currentNode;
5110           aItE = currentNode->GetInverseElementIterator();
5111           currentElem = aItE->next();
5112           if( currentElem  == prevElem)
5113             currentElem = aItE->next();
5114           nIt = currentElem->nodesIterator();
5115           prevElem = currentElem;
5116           nbEdges++;
5117         }
5118       }
5119     }
5120
5121     if(nbEdges != totalNbEdges)
5122       return EXTR_PATH_NOT_EDGE;
5123
5124     TopTools_SequenceOfShape Edges;
5125     double x1,x2,y1,y2,z1,z2;
5126     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5127     int startNid = theN1->GetID();
5128     for(int i = 1; i < aNodesList.size(); i++) {
5129       x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5130       y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5131       z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5132       TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5133       list<SMESH_MeshEditor_PathPoint> LPP;
5134       aPrms.clear();
5135       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5136       LLPPs.push_back(LPP);
5137       if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5138       else startNid = aNodesList[i-1]->GetID();
5139
5140     }
5141
5142     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5143     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5144     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5145     for(; itPP!=firstList.end(); itPP++) {
5146       fullList.push_back( *itPP );
5147     }
5148
5149     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5150     SMESH_MeshEditor_PathPoint PP2;
5151     fullList.pop_back();
5152     itLLPP++;
5153     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5154       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5155       itPP = currList.begin();
5156       PP2 = currList.front();
5157       gp_Dir D1 = PP1.Tangent();
5158       gp_Dir D2 = PP2.Tangent();
5159       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5160                            (D1.Z()+D2.Z())/2 ) );
5161       PP1.SetTangent(Dnew);
5162       fullList.push_back(PP1);
5163       itPP++;
5164       for(; itPP!=currList.end(); itPP++) {
5165         fullList.push_back( *itPP );
5166       }
5167       PP1 = fullList.back();
5168       fullList.pop_back();
5169     }
5170     fullList.push_back(PP1);
5171
5172   } // Sub-shape for the Pattern must be an Edge or Wire
5173   else if( aS.ShapeType() == TopAbs_EDGE ) {
5174     aTrackEdge = TopoDS::Edge( aS );
5175     // the Edge must not be degenerated
5176     if ( BRep_Tool::Degenerated( aTrackEdge ) )
5177       return EXTR_BAD_PATH_SHAPE;
5178     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5179     const SMDS_MeshNode* aN1 = 0;
5180     const SMDS_MeshNode* aN2 = 0;
5181     if ( theTrack->GetSubMesh( aV1 ) && theTrack->GetSubMesh( aV1 )->GetSubMeshDS() ) {
5182       aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5183       aN1 = aItN->next();
5184     }
5185     if ( theTrack->GetSubMesh( aV2 ) && theTrack->GetSubMesh( aV2 )->GetSubMeshDS() ) {
5186       aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5187       aN2 = aItN->next();
5188     }
5189     // starting node must be aN1 or aN2
5190     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5191       return EXTR_BAD_STARTING_NODE;
5192     aItN = pMeshDS->nodesIterator();
5193     while ( aItN->more() ) {
5194       const SMDS_MeshNode* pNode = aItN->next();
5195       if( pNode==aN1 || pNode==aN2 ) continue;
5196       const SMDS_EdgePosition* pEPos =
5197         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5198       double aT = pEPos->GetUParameter();
5199       aPrms.push_back( aT );
5200     }
5201     //Extrusion_Error err =
5202     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5203   }
5204   else if( aS.ShapeType() == TopAbs_WIRE ) {
5205     list< SMESH_subMesh* > LSM;
5206     TopTools_SequenceOfShape Edges;
5207     TopExp_Explorer eExp(aS, TopAbs_EDGE);
5208     for(; eExp.More(); eExp.Next()) {
5209       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5210       if( BRep_Tool::Degenerated(E) ) continue;
5211       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5212       if(SM) {
5213         LSM.push_back(SM);
5214         Edges.Append(E);
5215       }
5216     }
5217     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5218     TopoDS_Vertex aVprev;
5219     TColStd_MapOfInteger UsedNums;
5220     int NbEdges = Edges.Length();
5221     int i = 1;
5222     for(; i<=NbEdges; i++) {
5223       int k = 0;
5224       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5225       for(; itLSM!=LSM.end(); itLSM++) {
5226         k++;
5227         if(UsedNums.Contains(k)) continue;
5228         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5229         SMESH_subMesh* locTrack = *itLSM;
5230         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5231         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5232         bool aN1isOK = false, aN2isOK = false;
5233         if ( aVprev.IsNull() ) {
5234           // if previous vertex is not yet defined, it means that we in the beginning of wire
5235           // and we have to find initial vertex corresponding to starting node theN1
5236           const SMDS_MeshNode* aN1 = 0;
5237           const SMDS_MeshNode* aN2 = 0;
5238
5239           if ( locTrack->GetFather()->GetSubMesh(aV1) && locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS() ) {
5240             aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5241             aN1 = aItN->next();
5242           }
5243           if ( locTrack->GetFather()->GetSubMesh(aV2) && locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS() ) {
5244             aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5245             aN2 = aItN->next();
5246           }
5247           // starting node must be aN1 or aN2
5248           aN1isOK = ( aN1 && aN1 == theN1 );
5249           aN2isOK = ( aN2 && aN2 == theN1 );
5250         }
5251         else {
5252           // we have specified ending vertex of the previous edge on the previous iteration
5253           // and we have just to check that it corresponds to any vertex in current segment
5254           aN1isOK = aVprev.IsSame( aV1 );
5255           aN2isOK = aVprev.IsSame( aV2 );
5256         }
5257         if ( !aN1isOK && !aN2isOK ) continue;
5258         // 2. Collect parameters on the track edge
5259         aPrms.clear();
5260         aItN = locMeshDS->GetNodes();
5261         while ( aItN->more() ) {
5262           const SMDS_MeshNode*     pNode = aItN->next();
5263           const SMDS_EdgePosition* pEPos =
5264             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5265           double aT = pEPos->GetUParameter();
5266           aPrms.push_back( aT );
5267         }
5268         list<SMESH_MeshEditor_PathPoint> LPP;
5269         //Extrusion_Error err =
5270         MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
5271         LLPPs.push_back(LPP);
5272         UsedNums.Add(k);
5273         // update startN for search following egde
5274         if ( aN1isOK ) aVprev = aV2;
5275         else           aVprev = aV1;
5276         break;
5277       }
5278     }
5279     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5280     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5281     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5282     for(; itPP!=firstList.end(); itPP++) {
5283       fullList.push_back( *itPP );
5284     }
5285     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5286     fullList.pop_back();
5287     itLLPP++;
5288     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5289       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5290       itPP = currList.begin();
5291       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5292       gp_Dir D1 = PP1.Tangent();
5293       gp_Dir D2 = PP2.Tangent();
5294       gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
5295       PP1.SetTangent(Dnew);
5296       fullList.push_back(PP1);
5297       itPP++;
5298       for(; itPP!=currList.end(); itPP++) {
5299         fullList.push_back( *itPP );
5300       }
5301       PP1 = fullList.back();
5302       fullList.pop_back();
5303     }
5304     // if wire not closed
5305     fullList.push_back(PP1);
5306     // else ???
5307   }
5308   else {
5309     return EXTR_BAD_PATH_SHAPE;
5310   }
5311
5312   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5313                           theHasRefPoint, theRefPoint, theMakeGroups);
5314 }
5315
5316
5317 //=======================================================================
5318 //function : MakeEdgePathPoints
5319 //purpose  : auxilary for ExtrusionAlongTrack
5320 //=======================================================================
5321 SMESH_MeshEditor::Extrusion_Error
5322 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5323                                      const TopoDS_Edge& aTrackEdge,
5324                                      bool FirstIsStart,
5325                                      list<SMESH_MeshEditor_PathPoint>& LPP)
5326 {
5327   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5328   aTolVec=1.e-7;
5329   aTolVec2=aTolVec*aTolVec;
5330   double aT1, aT2;
5331   TopoDS_Vertex aV1, aV2;
5332   TopExp::Vertices( aTrackEdge, aV1, aV2 );
5333   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5334   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5335   // 2. Collect parameters on the track edge
5336   aPrms.push_front( aT1 );
5337   aPrms.push_back( aT2 );
5338   // sort parameters
5339   aPrms.sort();
5340   if( FirstIsStart ) {
5341     if ( aT1 > aT2 ) {
5342       aPrms.reverse();
5343     }
5344   }
5345   else {
5346     if ( aT2 > aT1 ) {
5347       aPrms.reverse();
5348     }
5349   }
5350   // 3. Path Points
5351   SMESH_MeshEditor_PathPoint aPP;
5352   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5353   std::list<double>::iterator aItD = aPrms.begin();
5354   for(; aItD != aPrms.end(); ++aItD) {
5355     double aT = *aItD;
5356     gp_Pnt aP3D;
5357     gp_Vec aVec;
5358     aC3D->D1( aT, aP3D, aVec );
5359     aL2 = aVec.SquareMagnitude();
5360     if ( aL2 < aTolVec2 )
5361       return EXTR_CANT_GET_TANGENT;
5362     gp_Dir aTgt( aVec );
5363     aPP.SetPnt( aP3D );
5364     aPP.SetTangent( aTgt );
5365     aPP.SetParameter( aT );
5366     LPP.push_back(aPP);
5367   }
5368   return EXTR_OK;
5369 }
5370
5371
5372 //=======================================================================
5373 //function : MakeExtrElements
5374 //purpose  : auxilary for ExtrusionAlongTrack
5375 //=======================================================================
5376 SMESH_MeshEditor::Extrusion_Error
5377 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet&  theElements,
5378                                    list<SMESH_MeshEditor_PathPoint>& fullList,
5379                                    const bool theHasAngles,
5380                                    list<double>& theAngles,
5381                                    const bool theLinearVariation,
5382                                    const bool theHasRefPoint,
5383                                    const gp_Pnt& theRefPoint,
5384                                    const bool theMakeGroups)
5385 {
5386   MESSAGE("MakeExtrElements");
5387   //cout<<"MakeExtrElements  fullList.size() = "<<fullList.size()<<endl;
5388   int aNbTP = fullList.size();
5389   vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5390   // Angles
5391   if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5392     LinearAngleVariation(aNbTP-1, theAngles);
5393   }
5394   vector<double> aAngles( aNbTP );
5395   int j = 0;
5396   for(; j<aNbTP; ++j) {
5397     aAngles[j] = 0.;
5398   }
5399   if ( theHasAngles ) {
5400     double anAngle;;
5401     std::list<double>::iterator aItD = theAngles.begin();
5402     for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5403       anAngle = *aItD;
5404       aAngles[j] = anAngle;
5405     }
5406   }
5407   // fill vector of path points with angles
5408   //aPPs.resize(fullList.size());
5409   j = -1;
5410   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5411   for(; itPP!=fullList.end(); itPP++) {
5412     j++;
5413     SMESH_MeshEditor_PathPoint PP = *itPP;
5414     PP.SetAngle(aAngles[j]);
5415     aPPs[j] = PP;
5416   }
5417
5418   TNodeOfNodeListMap mapNewNodes;
5419   TElemOfVecOfNnlmiMap mapElemNewNodes;
5420   TElemOfElemListMap newElemsMap;
5421   TIDSortedElemSet::iterator itElem;
5422   double aX, aY, aZ;
5423   int aNb;
5424   SMDSAbs_ElementType aTypeE;
5425   // source elements for each generated one
5426   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5427
5428   // 3. Center of rotation aV0
5429   gp_Pnt aV0 = theRefPoint;
5430   gp_XYZ aGC;
5431   if ( !theHasRefPoint ) {
5432     aNb = 0;
5433     aGC.SetCoord( 0.,0.,0. );
5434
5435     itElem = theElements.begin();
5436     for ( ; itElem != theElements.end(); itElem++ ) {
5437       const SMDS_MeshElement* elem = *itElem;
5438
5439       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5440       while ( itN->more() ) {
5441         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5442         aX = node->X();
5443         aY = node->Y();
5444         aZ = node->Z();
5445
5446         if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5447           list<const SMDS_MeshNode*> aLNx;
5448           mapNewNodes[node] = aLNx;
5449           //
5450           gp_XYZ aXYZ( aX, aY, aZ );
5451           aGC += aXYZ;
5452           ++aNb;
5453         }
5454       }
5455     }
5456     aGC /= aNb;
5457     aV0.SetXYZ( aGC );
5458   } // if (!theHasRefPoint) {
5459   mapNewNodes.clear();
5460
5461   // 4. Processing the elements
5462   SMESHDS_Mesh* aMesh = GetMeshDS();
5463
5464   for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5465     // check element type
5466     const SMDS_MeshElement* elem = *itElem;
5467     aTypeE = elem->GetType();
5468     if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5469       continue;
5470
5471     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5472     newNodesItVec.reserve( elem->NbNodes() );
5473
5474     // loop on elem nodes
5475     int nodeIndex = -1;
5476     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5477     while ( itN->more() )
5478     {
5479       ++nodeIndex;
5480       // check if a node has been already processed
5481       const SMDS_MeshNode* node =
5482         static_cast<const SMDS_MeshNode*>( itN->next() );
5483       TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5484       if ( nIt == mapNewNodes.end() ) {
5485         nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5486         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5487
5488         // make new nodes
5489         aX = node->X();  aY = node->Y(); aZ = node->Z();
5490
5491         Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5492         gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5493         gp_Ax1 anAx1, anAxT1T0;
5494         gp_Dir aDT1x, aDT0x, aDT1T0;
5495
5496         aTolAng=1.e-4;
5497
5498         aV0x = aV0;
5499         aPN0.SetCoord(aX, aY, aZ);
5500
5501         const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5502         aP0x = aPP0.Pnt();
5503         aDT0x= aPP0.Tangent();
5504         //cout<<"j = 0   PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5505
5506         for ( j = 1; j < aNbTP; ++j ) {
5507           const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5508           aP1x = aPP1.Pnt();
5509           aDT1x = aPP1.Tangent();
5510           aAngle1x = aPP1.Angle();
5511
5512           gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5513           // Translation
5514           gp_Vec aV01x( aP0x, aP1x );
5515           aTrsf.SetTranslation( aV01x );
5516
5517           // traslated point
5518           aV1x = aV0x.Transformed( aTrsf );
5519           aPN1 = aPN0.Transformed( aTrsf );
5520
5521           // rotation 1 [ T1,T0 ]
5522           aAngleT1T0=-aDT1x.Angle( aDT0x );
5523           if (fabs(aAngleT1T0) > aTolAng) {
5524             aDT1T0=aDT1x^aDT0x;
5525             anAxT1T0.SetLocation( aV1x );
5526             anAxT1T0.SetDirection( aDT1T0 );
5527             aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5528
5529             aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5530           }
5531
5532           // rotation 2
5533           if ( theHasAngles ) {
5534             anAx1.SetLocation( aV1x );
5535             anAx1.SetDirection( aDT1x );
5536             aTrsfRot.SetRotation( anAx1, aAngle1x );
5537
5538             aPN1 = aPN1.Transformed( aTrsfRot );
5539           }
5540
5541           // make new node
5542           //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5543           if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5544             // create additional node
5545             double x = ( aPN1.X() + aPN0.X() )/2.;
5546             double y = ( aPN1.Y() + aPN0.Y() )/2.;
5547             double z = ( aPN1.Z() + aPN0.Z() )/2.;
5548             const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5549             myLastCreatedNodes.Append(newNode);
5550             srcNodes.Append( node );
5551             listNewNodes.push_back( newNode );
5552           }
5553           aX = aPN1.X();
5554           aY = aPN1.Y();
5555           aZ = aPN1.Z();
5556           const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5557           myLastCreatedNodes.Append(newNode);
5558           srcNodes.Append( node );
5559           listNewNodes.push_back( newNode );
5560
5561           aPN0 = aPN1;
5562           aP0x = aP1x;
5563           aV0x = aV1x;
5564           aDT0x = aDT1x;
5565         }
5566       }
5567
5568       else {
5569         // if current elem is quadratic and current node is not medium
5570         // we have to check - may be it is needed to insert additional nodes
5571         if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5572           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5573           if(listNewNodes.size()==aNbTP-1) {
5574             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5575             gp_XYZ P(node->X(), node->Y(), node->Z());
5576             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5577             int i;
5578             for(i=0; i<aNbTP-1; i++) {
5579               const SMDS_MeshNode* N = *it;
5580               double x = ( N->X() + P.X() )/2.;
5581               double y = ( N->Y() + P.Y() )/2.;
5582               double z = ( N->Z() + P.Z() )/2.;
5583               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5584               srcNodes.Append( node );
5585               myLastCreatedNodes.Append(newN);
5586               aNodes[2*i] = newN;
5587               aNodes[2*i+1] = N;
5588               P = gp_XYZ(N->X(),N->Y(),N->Z());
5589             }
5590             listNewNodes.clear();
5591             for(i=0; i<2*(aNbTP-1); i++) {
5592               listNewNodes.push_back(aNodes[i]);
5593             }
5594           }
5595         }
5596       }
5597
5598       newNodesItVec.push_back( nIt );
5599     }
5600     // make new elements
5601     //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5602     //              newNodesItVec[0]->second.size(), myLastCreatedElems );
5603     sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5604   }
5605
5606   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5607
5608   if ( theMakeGroups )
5609     generateGroups( srcNodes, srcElems, "extruded");
5610
5611   return EXTR_OK;
5612 }
5613
5614
5615 //=======================================================================
5616 //function : LinearAngleVariation
5617 //purpose  : auxilary for ExtrusionAlongTrack
5618 //=======================================================================
5619 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5620                                             list<double>& Angles)
5621 {
5622   int nbAngles = Angles.size();
5623   if( nbSteps > nbAngles ) {
5624     vector<double> theAngles(nbAngles);
5625     list<double>::iterator it = Angles.begin();
5626     int i = -1;
5627     for(; it!=Angles.end(); it++) {
5628       i++;
5629       theAngles[i] = (*it);
5630     }
5631     list<double> res;
5632     double rAn2St = double( nbAngles ) / double( nbSteps );
5633     double angPrev = 0, angle;
5634     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5635       double angCur = rAn2St * ( iSt+1 );
5636       double angCurFloor  = floor( angCur );
5637       double angPrevFloor = floor( angPrev );
5638       if ( angPrevFloor == angCurFloor )
5639         angle = rAn2St * theAngles[ int( angCurFloor ) ];
5640       else {
5641         int iP = int( angPrevFloor );
5642         double angPrevCeil = ceil(angPrev);
5643         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5644
5645         int iC = int( angCurFloor );
5646         if ( iC < nbAngles )
5647           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5648
5649         iP = int( angPrevCeil );
5650         while ( iC-- > iP )
5651           angle += theAngles[ iC ];
5652       }
5653       res.push_back(angle);
5654       angPrev = angCur;
5655     }
5656     Angles.clear();
5657     it = res.begin();
5658     for(; it!=res.end(); it++)
5659       Angles.push_back( *it );
5660   }
5661 }
5662
5663
5664 //================================================================================
5665 /*!
5666  * \brief Move or copy theElements applying theTrsf to their nodes
5667  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5668  *  \param theTrsf - transformation to apply
5669  *  \param theCopy - if true, create translated copies of theElems
5670  *  \param theMakeGroups - if true and theCopy, create translated groups
5671  *  \param theTargetMesh - mesh to copy translated elements into
5672  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5673  */
5674 //================================================================================
5675
5676 SMESH_MeshEditor::PGroupIDs
5677 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5678                              const gp_Trsf&     theTrsf,
5679                              const bool         theCopy,
5680                              const bool         theMakeGroups,
5681                              SMESH_Mesh*        theTargetMesh)
5682 {
5683   myLastCreatedElems.Clear();
5684   myLastCreatedNodes.Clear();
5685
5686   bool needReverse = false;
5687   string groupPostfix;
5688   switch ( theTrsf.Form() ) {
5689   case gp_PntMirror:
5690     MESSAGE("gp_PntMirror");
5691     needReverse = true;
5692     groupPostfix = "mirrored";
5693     break;
5694   case gp_Ax1Mirror:
5695     MESSAGE("gp_Ax1Mirror");
5696     groupPostfix = "mirrored";
5697     break;
5698   case gp_Ax2Mirror:
5699     MESSAGE("gp_Ax2Mirror");
5700     needReverse = true;
5701     groupPostfix = "mirrored";
5702     break;
5703   case gp_Rotation:
5704     MESSAGE("gp_Rotation");
5705     groupPostfix = "rotated";
5706     break;
5707   case gp_Translation:
5708     MESSAGE("gp_Translation");
5709     groupPostfix = "translated";
5710     break;
5711   case gp_Scale:
5712     MESSAGE("gp_Scale");
5713     groupPostfix = "scaled";
5714     break;
5715   case gp_CompoundTrsf: // different scale by axis
5716     MESSAGE("gp_CompoundTrsf");
5717     groupPostfix = "scaled";
5718     break;
5719   default:
5720     MESSAGE("default");
5721     needReverse = false;
5722     groupPostfix = "transformed";
5723   }
5724
5725   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5726   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5727   SMESHDS_Mesh* aMesh    = GetMeshDS();
5728
5729
5730   // map old node to new one
5731   TNodeNodeMap nodeMap;
5732
5733   // elements sharing moved nodes; those of them which have all
5734   // nodes mirrored but are not in theElems are to be reversed
5735   TIDSortedElemSet inverseElemSet;
5736
5737   // source elements for each generated one
5738   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5739
5740   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5741   TIDSortedElemSet orphanNode;
5742
5743   if ( theElems.empty() ) // transform the whole mesh
5744   {
5745     // add all elements
5746     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5747     while ( eIt->more() ) theElems.insert( eIt->next() );
5748     // add orphan nodes
5749     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5750     while ( nIt->more() )
5751     {
5752       const SMDS_MeshNode* node = nIt->next();
5753       if ( node->NbInverseElements() == 0)
5754         orphanNode.insert( node );
5755     }
5756   }
5757
5758   // loop on elements to transform nodes : first orphan nodes then elems
5759   TIDSortedElemSet::iterator itElem;
5760   TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5761   for (int i=0; i<2; i++)
5762   for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5763     const SMDS_MeshElement* elem = *itElem;
5764     if ( !elem )
5765       continue;
5766
5767     // loop on elem nodes
5768     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5769     while ( itN->more() ) {
5770
5771       const SMDS_MeshNode* node = cast2Node( itN->next() );
5772       // check if a node has been already transformed
5773       pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5774         nodeMap.insert( make_pair ( node, node ));
5775       if ( !n2n_isnew.second )
5776         continue;
5777
5778       double coord[3];
5779       coord[0] = node->X();
5780       coord[1] = node->Y();
5781       coord[2] = node->Z();
5782       theTrsf.Transforms( coord[0], coord[1], coord[2] );
5783       if ( theTargetMesh ) {
5784         const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5785         n2n_isnew.first->second = newNode;
5786         myLastCreatedNodes.Append(newNode);
5787         srcNodes.Append( node );
5788       }
5789       else if ( theCopy ) {
5790         const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5791         n2n_isnew.first->second = newNode;
5792         myLastCreatedNodes.Append(newNode);
5793         srcNodes.Append( node );
5794       }
5795       else {
5796         aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5797         // node position on shape becomes invalid
5798         const_cast< SMDS_MeshNode* > ( node )->SetPosition
5799           ( SMDS_SpacePosition::originSpacePosition() );
5800       }
5801
5802       // keep inverse elements
5803       if ( !theCopy && !theTargetMesh && needReverse ) {
5804         SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5805         while ( invElemIt->more() ) {
5806           const SMDS_MeshElement* iel = invElemIt->next();
5807           inverseElemSet.insert( iel );
5808         }
5809       }
5810     }
5811   }
5812
5813   // either create new elements or reverse mirrored ones
5814   if ( !theCopy && !needReverse && !theTargetMesh )
5815     return PGroupIDs();
5816
5817   TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5818   for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5819     theElems.insert( *invElemIt );
5820
5821   // Replicate or reverse elements
5822
5823   std::vector<int> iForw;
5824   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5825   {
5826     const SMDS_MeshElement* elem = *itElem;
5827     if ( !elem ) continue;
5828
5829     SMDSAbs_GeometryType geomType = elem->GetGeomType();
5830     int                  nbNodes  = elem->NbNodes();
5831     if ( geomType == SMDSGeom_NONE ) continue; // node
5832
5833     switch ( geomType ) {
5834
5835     case SMDSGeom_POLYGON:  // ---------------------- polygon
5836       {
5837         vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5838         int iNode = 0;
5839         SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5840         while (itN->more()) {
5841           const SMDS_MeshNode* node =
5842             static_cast<const SMDS_MeshNode*>(itN->next());
5843           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5844           if (nodeMapIt == nodeMap.end())
5845             break; // not all nodes transformed
5846           if (needReverse) {
5847             // reverse mirrored faces and volumes
5848             poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5849           } else {
5850             poly_nodes[iNode] = (*nodeMapIt).second;
5851           }
5852           iNode++;
5853         }
5854         if ( iNode != nbNodes )
5855           continue; // not all nodes transformed
5856
5857         if ( theTargetMesh ) {
5858           myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5859           srcElems.Append( elem );
5860         }
5861         else if ( theCopy ) {
5862           myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5863           srcElems.Append( elem );
5864         }
5865         else {
5866           aMesh->ChangePolygonNodes(elem, poly_nodes);
5867         }
5868       }
5869       break;
5870
5871     case SMDSGeom_POLYHEDRA:  // ------------------ polyhedral volume
5872       {
5873         const SMDS_VtkVolume* aPolyedre =
5874           dynamic_cast<const SMDS_VtkVolume*>( elem );
5875         if (!aPolyedre) {
5876           MESSAGE("Warning: bad volumic element");
5877           continue;
5878         }
5879
5880         vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5881         vector<int> quantities; quantities.reserve( nbNodes );
5882
5883         bool allTransformed = true;
5884         int nbFaces = aPolyedre->NbFaces();
5885         for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5886           int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5887           for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5888             const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5889             TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5890             if (nodeMapIt == nodeMap.end()) {
5891               allTransformed = false; // not all nodes transformed
5892             } else {
5893               poly_nodes.push_back((*nodeMapIt).second);
5894             }
5895             if ( needReverse && allTransformed )
5896               std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5897           }
5898           quantities.push_back(nbFaceNodes);
5899         }
5900         if ( !allTransformed )
5901           continue; // not all nodes transformed
5902
5903         if ( theTargetMesh ) {
5904           myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5905           srcElems.Append( elem );
5906         }
5907         else if ( theCopy ) {
5908           myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5909           srcElems.Append( elem );
5910         }
5911         else {
5912           aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5913         }
5914       }
5915       break;
5916
5917     case SMDSGeom_BALL: // -------------------- Ball
5918       {
5919         if ( !theCopy && !theTargetMesh ) continue;
5920
5921         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
5922         if (nodeMapIt == nodeMap.end())
5923           continue; // not all nodes transformed
5924
5925         double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
5926         if ( theTargetMesh ) {
5927           myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
5928           srcElems.Append( elem );
5929         }
5930         else {
5931           myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
5932           srcElems.Append( elem );
5933         }
5934       }
5935       break;
5936
5937     default: // ----------------------- Regular elements
5938
5939       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
5940       const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
5941       const std::vector<int>& i = needReverse ? iRev : iForw;
5942
5943       // find transformed nodes
5944       vector<const SMDS_MeshNode*> nodes(nbNodes);
5945       int iNode = 0;
5946       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5947       while ( itN->more() ) {
5948         const SMDS_MeshNode* node =
5949           static_cast<const SMDS_MeshNode*>( itN->next() );
5950         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5951         if ( nodeMapIt == nodeMap.end() )
5952           break; // not all nodes transformed
5953         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5954       }
5955       if ( iNode != nbNodes )
5956         continue; // not all nodes transformed
5957
5958       if ( theTargetMesh ) {
5959         if ( SMDS_MeshElement* copy =
5960              targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5961           myLastCreatedElems.Append( copy );
5962           srcElems.Append( elem );
5963         }
5964       }
5965       else if ( theCopy ) {
5966         if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5967           srcElems.Append( elem );
5968       }
5969       else {
5970         // reverse element as it was reversed by transformation
5971         if ( nbNodes > 2 )
5972           aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5973       }
5974     } // switch ( geomType )
5975
5976   } // loop on elements
5977
5978   PGroupIDs newGroupIDs;
5979
5980   if ( ( theMakeGroups && theCopy ) ||
5981        ( theMakeGroups && theTargetMesh ) )
5982     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5983
5984   return newGroupIDs;
5985 }
5986
5987 //=======================================================================
5988 /*!
5989  * \brief Create groups of elements made during transformation
5990  * \param nodeGens - nodes making corresponding myLastCreatedNodes
5991  * \param elemGens - elements making corresponding myLastCreatedElems
5992  * \param postfix - to append to names of new groups
5993  */
5994 //=======================================================================
5995
5996 SMESH_MeshEditor::PGroupIDs
5997 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5998                                  const SMESH_SequenceOfElemPtr& elemGens,
5999                                  const std::string&             postfix,
6000                                  SMESH_Mesh*                    targetMesh)
6001 {
6002   PGroupIDs newGroupIDs( new list<int> );
6003   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6004
6005   // Sort existing groups by types and collect their names
6006
6007   // to store an old group and a generated new ones
6008   using boost::tuple;
6009   using boost::make_tuple;
6010   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6011   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6012   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6013   // group names
6014   set< string > groupNames;
6015
6016   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6017   if ( !groupIt->more() ) return newGroupIDs;
6018
6019   int newGroupID = mesh->GetGroupIds().back()+1;
6020   while ( groupIt->more() )
6021   {
6022     SMESH_Group * group = groupIt->next();
6023     if ( !group ) continue;
6024     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6025     if ( !groupDS || groupDS->IsEmpty() ) continue;
6026     groupNames.insert    ( group->GetName() );
6027     groupDS->SetStoreName( group->GetName() );
6028     const SMDSAbs_ElementType type = groupDS->GetType();
6029     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6030     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6031     groupsByType[ groupDS->GetType() ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6032     orderedOldNewGroups.push_back( & groupsByType[ groupDS->GetType() ].back() );
6033   }
6034
6035   // Loop on nodes and elements to add them in new groups
6036
6037   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6038   {
6039     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6040     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6041     if ( gens.Length() != elems.Length() )
6042       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6043
6044     // loop on created elements
6045     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6046     {
6047       const SMDS_MeshElement* sourceElem = gens( iElem );
6048       if ( !sourceElem ) {
6049         MESSAGE("generateGroups(): NULL source element");
6050         continue;
6051       }
6052       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6053       if ( groupsOldNew.empty() ) { // no groups of this type at all
6054         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6055           ++iElem; // skip all elements made by sourceElem
6056         continue;
6057       }
6058       // collect all elements made by the iElem-th sourceElem
6059       list< const SMDS_MeshElement* > resultElems;
6060       if ( const SMDS_MeshElement* resElem = elems( iElem ))
6061         if ( resElem != sourceElem )
6062           resultElems.push_back( resElem );
6063       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6064         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6065           if ( resElem != sourceElem )
6066             resultElems.push_back( resElem );
6067
6068       // add resultElems to groups made by ones the sourceElem belongs to
6069       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6070       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6071       {
6072         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6073         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6074         {
6075           // fill in a new group
6076           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6077           list< const SMDS_MeshElement* > rejectedElems; // elements of other type
6078           list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6079           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6080             if ( !newGroup.Add( *resElemIt ))
6081               rejectedElems.push_back( *resElemIt );
6082
6083           // fill "top" group
6084           if ( !rejectedElems.empty() )
6085           {
6086             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6087             resLast = rejectedElems.end();
6088             for ( resElemIt = rejectedElems.begin(); resElemIt != resLast; ++resElemIt )
6089               !newTopGroup.Add( *resElemIt );
6090           }
6091         }
6092       }
6093     } // loop on created elements
6094   }// loop on nodes and elements
6095
6096   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6097
6098   list<int> topGrouIds;
6099   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6100   {
6101     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6102     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6103                                       orderedOldNewGroups[i]->get<2>() };
6104     const int nbNewGroups = !newGroups[0]->IsEmpty() + !newGroups[1]->IsEmpty();
6105     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6106     {
6107       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6108       if ( newGroupDS->IsEmpty() )
6109       {
6110         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6111       }
6112       else
6113       {
6114         // set group type
6115         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6116
6117         // make a name
6118         const bool isTop = ( nbNewGroups == 2 &&
6119                              newGroupDS->GetType() == oldGroupDS->GetType() );
6120         string name = oldGroupDS->GetStoreName();
6121         if ( !targetMesh ) {
6122           string suffix = ( isTop ? "top": postfix.c_str() );
6123           name += "_";
6124           name += suffix;
6125           int nb = 1;
6126           while ( !groupNames.insert( name ).second ) // name exists
6127             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6128         }
6129         else if ( isTop ) {
6130           name += "_top";
6131         }
6132         newGroupDS->SetStoreName( name.c_str() );
6133
6134         // make a SMESH_Groups
6135         mesh->AddGroup( newGroupDS );
6136         if ( isTop )
6137           topGrouIds.push_back( newGroupDS->GetID() );
6138         else
6139           newGroupIDs->push_back( newGroupDS->GetID() );
6140       }
6141     }
6142   }
6143   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6144
6145   return newGroupIDs;
6146 }
6147
6148 //================================================================================
6149 /*!
6150  * \brief Return list of group of nodes close to each other within theTolerance
6151  *        Search among theNodes or in the whole mesh if theNodes is empty using
6152  *        an Octree algorithm
6153  */
6154 //================================================================================
6155
6156 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6157                                             const double         theTolerance,
6158                                             TListOfListOfNodes & theGroupsOfNodes)
6159 {
6160   myLastCreatedElems.Clear();
6161   myLastCreatedNodes.Clear();
6162
6163   if ( theNodes.empty() )
6164   { // get all nodes in the mesh
6165     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6166     while ( nIt->more() )
6167       theNodes.insert( theNodes.end(),nIt->next());
6168   }
6169
6170   SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6171 }
6172
6173
6174 //=======================================================================
6175 /*!
6176  * \brief Implementation of search for the node closest to point
6177  */
6178 //=======================================================================
6179
6180 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
6181 {
6182   //---------------------------------------------------------------------
6183   /*!
6184    * \brief Constructor
6185    */
6186   SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
6187   {
6188     myMesh = ( SMESHDS_Mesh* ) theMesh;
6189
6190     TIDSortedNodeSet nodes;
6191     if ( theMesh ) {
6192       SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
6193       while ( nIt->more() )
6194         nodes.insert( nodes.end(), nIt->next() );
6195     }
6196     myOctreeNode = new SMESH_OctreeNode(nodes) ;
6197
6198     // get max size of a leaf box
6199     SMESH_OctreeNode* tree = myOctreeNode;
6200     while ( !tree->isLeaf() )
6201     {
6202       SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6203       if ( cIt->more() )
6204         tree = cIt->next();
6205     }
6206     myHalfLeafSize = tree->maxSize() / 2.;
6207   }
6208
6209   //---------------------------------------------------------------------
6210   /*!
6211    * \brief Move node and update myOctreeNode accordingly
6212    */
6213   void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
6214   {
6215     myOctreeNode->UpdateByMoveNode( node, toPnt );
6216     myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
6217   }
6218
6219   //---------------------------------------------------------------------
6220   /*!
6221    * \brief Do it's job
6222    */
6223   const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
6224   {
6225     map<double, const SMDS_MeshNode*> dist2Nodes;
6226     myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize );
6227     if ( !dist2Nodes.empty() )
6228       return dist2Nodes.begin()->second;
6229     list<const SMDS_MeshNode*> nodes;
6230     //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
6231
6232     double minSqDist = DBL_MAX;
6233     if ( nodes.empty() )  // get all nodes of OctreeNode's closest to thePnt
6234     {
6235       // sort leafs by their distance from thePnt
6236       typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
6237       TDistTreeMap treeMap;
6238       list< SMESH_OctreeNode* > treeList;
6239       list< SMESH_OctreeNode* >::iterator trIt;
6240       treeList.push_back( myOctreeNode );
6241
6242       gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
6243       bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize );
6244       for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
6245       {
6246         SMESH_OctreeNode* tree = *trIt;
6247         if ( !tree->isLeaf() ) // put children to the queue
6248         {
6249           if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue;
6250           SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6251           while ( cIt->more() )
6252             treeList.push_back( cIt->next() );
6253         }
6254         else if ( tree->NbNodes() ) // put a tree to the treeMap
6255         {
6256           const Bnd_B3d& box = *tree->getBox();
6257           double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
6258           pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
6259           if ( !it_in.second ) // not unique distance to box center
6260             treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
6261         }
6262       }
6263       // find distance after which there is no sense to check tree's
6264       double sqLimit = DBL_MAX;
6265       TDistTreeMap::iterator sqDist_tree = treeMap.begin();
6266       if ( treeMap.size() > 5 ) {
6267         SMESH_OctreeNode* closestTree = sqDist_tree->second;
6268         const Bnd_B3d& box = *closestTree->getBox();
6269         double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
6270         sqLimit = limit * limit;
6271       }
6272       // get all nodes from trees
6273       for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
6274         if ( sqDist_tree->first > sqLimit )
6275           break;
6276         SMESH_OctreeNode* tree = sqDist_tree->second;
6277         tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
6278       }
6279     }
6280     // find closest among nodes
6281     minSqDist = DBL_MAX;
6282     const SMDS_MeshNode* closestNode = 0;
6283     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6284     for ( ; nIt != nodes.end(); ++nIt ) {
6285       double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) );
6286       if ( minSqDist > sqDist ) {
6287         closestNode = *nIt;
6288         minSqDist = sqDist;
6289       }
6290     }
6291     return closestNode;
6292   }
6293
6294   //---------------------------------------------------------------------
6295   /*!
6296    * \brief Destructor
6297    */
6298   ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
6299
6300   //---------------------------------------------------------------------
6301   /*!
6302    * \brief Return the node tree
6303    */
6304   const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
6305
6306 private:
6307   SMESH_OctreeNode* myOctreeNode;
6308   SMESHDS_Mesh*     myMesh;
6309   double            myHalfLeafSize; // max size of a leaf box
6310 };
6311
6312 //=======================================================================
6313 /*!
6314  * \brief Return SMESH_NodeSearcher
6315  */
6316 //=======================================================================
6317
6318 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher()
6319 {
6320   return new SMESH_NodeSearcherImpl( GetMeshDS() );
6321 }
6322
6323 // ========================================================================
6324 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
6325 {
6326   const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
6327   const int MaxLevel         = 7;  // maximal tree height -> nb terminal boxes: 8^7 = 2097152
6328   const double NodeRadius = 1e-9;  // to enlarge bnd box of element
6329
6330   //=======================================================================
6331   /*!
6332    * \brief Octal tree of bounding boxes of elements
6333    */
6334   //=======================================================================
6335
6336   class ElementBndBoxTree : public SMESH_Octree
6337   {
6338   public:
6339
6340     ElementBndBoxTree(const SMDS_Mesh&     mesh,
6341                       SMDSAbs_ElementType  elemType,
6342                       SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(),
6343                       double               tolerance = NodeRadius );
6344     void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems );
6345     void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
6346     void getElementsInSphere ( const gp_XYZ& center,
6347                                const double  radius, TIDSortedElemSet& foundElems);
6348     size_t getSize() { return std::max( _size, _elements.size() ); }
6349     ~ElementBndBoxTree();
6350
6351   protected:
6352     ElementBndBoxTree():_size(0) {}
6353     SMESH_Octree* newChild() const { return new ElementBndBoxTree; }
6354     void          buildChildrenData();
6355     Bnd_B3d*      buildRootBox();
6356   private:
6357     //!< Bounding box of element
6358     struct ElementBox : public Bnd_B3d
6359     {
6360       const SMDS_MeshElement* _element;
6361       int                     _refCount; // an ElementBox can be included in several tree branches
6362       ElementBox(const SMDS_MeshElement* elem, double tolerance);
6363     };
6364     vector< ElementBox* > _elements;
6365     size_t                _size;
6366   };
6367
6368   //================================================================================
6369   /*!
6370    * \brief ElementBndBoxTree creation
6371    */
6372   //================================================================================
6373
6374   ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
6375     :SMESH_Octree( new SMESH_TreeLimit( MaxLevel, /*minSize=*/0. ))
6376   {
6377     int nbElems = mesh.GetMeshInfo().NbElements( elemType );
6378     _elements.reserve( nbElems );
6379
6380     SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType );
6381     while ( elemIt->more() )
6382       _elements.push_back( new ElementBox( elemIt->next(),tolerance  ));
6383
6384     compute();
6385   }
6386
6387   //================================================================================
6388   /*!
6389    * \brief Destructor
6390    */
6391   //================================================================================
6392
6393   ElementBndBoxTree::~ElementBndBoxTree()
6394   {
6395     for ( int i = 0; i < _elements.size(); ++i )
6396       if ( --_elements[i]->_refCount <= 0 )
6397         delete _elements[i];
6398   }
6399
6400   //================================================================================
6401   /*!
6402    * \brief Return the maximal box
6403    */
6404   //================================================================================
6405
6406   Bnd_B3d* ElementBndBoxTree::buildRootBox()
6407   {
6408     Bnd_B3d* box = new Bnd_B3d;
6409     for ( int i = 0; i < _elements.size(); ++i )
6410       box->Add( *_elements[i] );
6411     return box;
6412   }
6413
6414   //================================================================================
6415   /*!
6416    * \brief Redistrubute element boxes among children
6417    */
6418   //================================================================================
6419
6420   void ElementBndBoxTree::buildChildrenData()
6421   {
6422     for ( int i = 0; i < _elements.size(); ++i )
6423     {
6424       for (int j = 0; j < 8; j++)
6425       {
6426         if ( !_elements[i]->IsOut( *myChildren[j]->getBox() ))
6427         {
6428           _elements[i]->_refCount++;
6429           ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
6430         }
6431       }
6432       _elements[i]->_refCount--;
6433     }
6434     _size = _elements.size();
6435     SMESHUtils::FreeVector( _elements ); // = _elements.clear() + free memory
6436
6437     for (int j = 0; j < 8; j++)
6438     {
6439       ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
6440       if ( child->_elements.size() <= MaxNbElemsInLeaf )
6441         child->myIsLeaf = true;
6442
6443       if ( child->_elements.capacity() - child->_elements.size() > 1000 )
6444         SMESHUtils::CompactVector( child->_elements );
6445     }
6446   }
6447
6448   //================================================================================
6449   /*!
6450    * \brief Return elements which can include the point
6451    */
6452   //================================================================================
6453
6454   void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt&     point,
6455                                                 TIDSortedElemSet& foundElems)
6456   {
6457     if ( getBox()->IsOut( point.XYZ() ))
6458       return;
6459
6460     if ( isLeaf() )
6461     {
6462       for ( int i = 0; i < _elements.size(); ++i )
6463         if ( !_elements[i]->IsOut( point.XYZ() ))
6464           foundElems.insert( _elements[i]->_element );
6465     }
6466     else
6467     {
6468       for (int i = 0; i < 8; i++)
6469         ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6470     }
6471   }
6472
6473   //================================================================================
6474   /*!
6475    * \brief Return elements which can be intersected by the line
6476    */
6477   //================================================================================
6478
6479   void ElementBndBoxTree::getElementsNearLine( const gp_Ax1&     line,
6480                                                TIDSortedElemSet& foundElems)
6481   {
6482     if ( getBox()->IsOut( line ))
6483       return;
6484
6485     if ( isLeaf() )
6486     {
6487       for ( int i = 0; i < _elements.size(); ++i )
6488         if ( !_elements[i]->IsOut( line ))
6489           foundElems.insert( _elements[i]->_element );
6490     }
6491     else
6492     {
6493       for (int i = 0; i < 8; i++)
6494         ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6495     }
6496   }
6497
6498   //================================================================================
6499   /*!
6500    * \brief Return elements from leaves intersecting the sphere
6501    */
6502   //================================================================================
6503
6504   void ElementBndBoxTree::getElementsInSphere ( const gp_XYZ&     center,
6505                                                 const double      radius,
6506                                                 TIDSortedElemSet& foundElems)
6507   {
6508     if ( getBox()->IsOut( center, radius ))
6509       return;
6510
6511     if ( isLeaf() )
6512     {
6513       for ( int i = 0; i < _elements.size(); ++i )
6514         if ( !_elements[i]->IsOut( center, radius ))
6515           foundElems.insert( _elements[i]->_element );
6516     }
6517     else
6518     {
6519       for (int i = 0; i < 8; i++)
6520         ((ElementBndBoxTree*) myChildren[i])->getElementsInSphere( center, radius, foundElems );
6521     }
6522   }
6523
6524   //================================================================================
6525   /*!
6526    * \brief Construct the element box
6527    */
6528   //================================================================================
6529
6530   ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance)
6531   {
6532     _element  = elem;
6533     _refCount = 1;
6534     SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6535     while ( nIt->more() )
6536       Add( SMESH_TNodeXYZ( nIt->next() ));
6537     Enlarge( tolerance );
6538   }
6539
6540 } // namespace
6541
6542 //=======================================================================
6543 /*!
6544  * \brief Implementation of search for the elements by point and
6545  *        of classification of point in 2D mesh
6546  */
6547 //=======================================================================
6548
6549 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6550 {
6551   SMESHDS_Mesh*                _mesh;
6552   SMDS_ElemIteratorPtr         _meshPartIt;
6553   ElementBndBoxTree*           _ebbTree;
6554   SMESH_NodeSearcherImpl*      _nodeSearcher;
6555   SMDSAbs_ElementType          _elementType;
6556   double                       _tolerance;
6557   bool                         _outerFacesFound;
6558   set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6559
6560   SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr())
6561     : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {}
6562   ~SMESH_ElementSearcherImpl()
6563   {
6564     if ( _ebbTree )      delete _ebbTree;      _ebbTree      = 0;
6565     if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6566   }
6567   virtual int FindElementsByPoint(const gp_Pnt&                      point,
6568                                   SMDSAbs_ElementType                type,
6569                                   vector< const SMDS_MeshElement* >& foundElements);
6570   virtual TopAbs_State GetPointState(const gp_Pnt& point);
6571   virtual const SMDS_MeshElement* FindClosestTo( const gp_Pnt&       point,
6572                                                  SMDSAbs_ElementType type );
6573
6574   void GetElementsNearLine( const gp_Ax1&                      line,
6575                             SMDSAbs_ElementType                type,
6576                             vector< const SMDS_MeshElement* >& foundElems);
6577   double getTolerance();
6578   bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6579                             const double tolerance, double & param);
6580   void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6581   bool isOuterBoundary(const SMDS_MeshElement* face) const
6582   {
6583     return _outerFaces.empty() || _outerFaces.count(face);
6584   }
6585   struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6586   {
6587     const SMDS_MeshElement* _face;
6588     gp_Vec                  _faceNorm;
6589     bool                    _coincides; //!< the line lays in face plane
6590     TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6591       : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6592   };
6593   struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6594   {
6595     SMESH_TLink      _link;
6596     TIDSortedElemSet _faces;
6597     TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6598       : _link( n1, n2 ), _faces( &face, &face + 1) {}
6599   };
6600 };
6601
6602 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6603 {
6604   return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6605              << ", _coincides="<<i._coincides << ")";
6606 }
6607
6608 //=======================================================================
6609 /*!
6610  * \brief define tolerance for search
6611  */
6612 //=======================================================================
6613
6614 double SMESH_ElementSearcherImpl::getTolerance()
6615 {
6616   if ( _tolerance < 0 )
6617   {
6618     const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6619
6620     _tolerance = 0;
6621     if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6622     {
6623       double boxSize = _nodeSearcher->getTree()->maxSize();
6624       _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6625     }
6626     else if ( _ebbTree && meshInfo.NbElements() > 0 )
6627     {
6628       double boxSize = _ebbTree->maxSize();
6629       _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6630     }
6631     if ( _tolerance == 0 )
6632     {
6633       // define tolerance by size of a most complex element
6634       int complexType = SMDSAbs_Volume;
6635       while ( complexType > SMDSAbs_All &&
6636               meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6637         --complexType;
6638       if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6639       double elemSize;
6640       if ( complexType == int( SMDSAbs_Node ))
6641       {
6642         SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6643         elemSize = 1;
6644         if ( meshInfo.NbNodes() > 2 )
6645           elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6646       }
6647       else
6648       {
6649         SMDS_ElemIteratorPtr elemIt =
6650             _mesh->elementsIterator( SMDSAbs_ElementType( complexType ));
6651         const SMDS_MeshElement* elem = elemIt->next();
6652         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6653         SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() ));
6654         elemSize = 0;
6655         while ( nodeIt->more() )
6656         {
6657           double dist = n1.Distance( cast2Node( nodeIt->next() ));
6658           elemSize = max( dist, elemSize );
6659         }
6660       }
6661       _tolerance = 1e-4 * elemSize;
6662     }
6663   }
6664   return _tolerance;
6665 }
6666
6667 //================================================================================
6668 /*!
6669  * \brief Find intersection of the line and an edge of face and return parameter on line
6670  */
6671 //================================================================================
6672
6673 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin&           line,
6674                                                      const SMDS_MeshElement* face,
6675                                                      const double            tol,
6676                                                      double &                param)
6677 {
6678   int nbInts = 0;
6679   param = 0;
6680
6681   GeomAPI_ExtremaCurveCurve anExtCC;
6682   Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6683
6684   int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6685   for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6686   {
6687     GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )),
6688                          SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) ));
6689     anExtCC.Init( lineCurve, edge);
6690     if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6691     {
6692       Quantity_Parameter pl, pe;
6693       anExtCC.LowerDistanceParameters( pl, pe );
6694       param += pl;
6695       if ( ++nbInts == 2 )
6696         break;
6697     }
6698   }
6699   if ( nbInts > 0 ) param /= nbInts;
6700   return nbInts > 0;
6701 }
6702 //================================================================================
6703 /*!
6704  * \brief Find all faces belonging to the outer boundary of mesh
6705  */
6706 //================================================================================
6707
6708 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6709 {
6710   if ( _outerFacesFound ) return;
6711
6712   // Collect all outer faces by passing from one outer face to another via their links
6713   // and BTW find out if there are internal faces at all.
6714
6715   // checked links and links where outer boundary meets internal one
6716   set< SMESH_TLink > visitedLinks, seamLinks;
6717
6718   // links to treat with already visited faces sharing them
6719   list < TFaceLink > startLinks;
6720
6721   // load startLinks with the first outerFace
6722   startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6723   _outerFaces.insert( outerFace );
6724
6725   TIDSortedElemSet emptySet;
6726   while ( !startLinks.empty() )
6727   {
6728     const SMESH_TLink& link  = startLinks.front()._link;
6729     TIDSortedElemSet&  faces = startLinks.front()._faces;
6730
6731     outerFace = *faces.begin();
6732     // find other faces sharing the link
6733     const SMDS_MeshElement* f;
6734     while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6735       faces.insert( f );
6736
6737     // select another outer face among the found
6738     const SMDS_MeshElement* outerFace2 = 0;
6739     if ( faces.size() == 2 )
6740     {
6741       outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6742     }
6743     else if ( faces.size() > 2 )
6744     {
6745       seamLinks.insert( link );
6746
6747       // link direction within the outerFace
6748       gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()),
6749                    SMESH_TNodeXYZ( link.node2()));
6750       int i1 = outerFace->GetNodeIndex( link.node1() );
6751       int i2 = outerFace->GetNodeIndex( link.node2() );
6752       bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6753       if ( rev ) n1n2.Reverse();
6754       // outerFace normal
6755       gp_XYZ ofNorm, fNorm;
6756       if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6757       {
6758         // direction from the link inside outerFace
6759         gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6760         // sort all other faces by angle with the dirInOF
6761         map< double, const SMDS_MeshElement* > angle2Face;
6762         set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6763         for ( ; face != faces.end(); ++face )
6764         {
6765           if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6766             continue;
6767           gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6768           double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6769           if ( angle < 0 ) angle += 2. * M_PI;
6770           angle2Face.insert( make_pair( angle, *face ));
6771         }
6772         if ( !angle2Face.empty() )
6773           outerFace2 = angle2Face.begin()->second;
6774       }
6775     }
6776     // store the found outer face and add its links to continue seaching from
6777     if ( outerFace2 )
6778     {
6779       _outerFaces.insert( outerFace );
6780       int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6781       for ( int i = 0; i < nbNodes; ++i )
6782       {
6783         SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6784         if ( visitedLinks.insert( link2 ).second )
6785           startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6786       }
6787     }
6788     startLinks.pop_front();
6789   }
6790   _outerFacesFound = true;
6791
6792   if ( !seamLinks.empty() )
6793   {
6794     // There are internal boundaries touching the outher one,
6795     // find all faces of internal boundaries in order to find
6796     // faces of boundaries of holes, if any.
6797
6798   }
6799   else
6800   {
6801     _outerFaces.clear();
6802   }
6803 }
6804
6805 //=======================================================================
6806 /*!
6807  * \brief Find elements of given type where the given point is IN or ON.
6808  *        Returns nb of found elements and elements them-selves.
6809  *
6810  * 'ALL' type means elements of any type excluding nodes, balls and 0D elements
6811  */
6812 //=======================================================================
6813
6814 int SMESH_ElementSearcherImpl::
6815 FindElementsByPoint(const gp_Pnt&                      point,
6816                     SMDSAbs_ElementType                type,
6817                     vector< const SMDS_MeshElement* >& foundElements)
6818 {
6819   foundElements.clear();
6820
6821   double tolerance = getTolerance();
6822
6823   // =================================================================================
6824   if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement || type == SMDSAbs_Ball)
6825   {
6826     if ( !_nodeSearcher )
6827       _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6828
6829     const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6830     if ( !closeNode ) return foundElements.size();
6831
6832     if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance )
6833       return foundElements.size(); // to far from any node
6834
6835     if ( type == SMDSAbs_Node )
6836     {
6837       foundElements.push_back( closeNode );
6838     }
6839     else
6840     {
6841       SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( type );
6842       while ( elemIt->more() )
6843         foundElements.push_back( elemIt->next() );
6844     }
6845   }
6846   // =================================================================================
6847   else // elements more complex than 0D
6848   {
6849     if ( !_ebbTree || _elementType != type )
6850     {
6851       if ( _ebbTree ) delete _ebbTree;
6852       _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance );
6853     }
6854     TIDSortedElemSet suspectElems;
6855     _ebbTree->getElementsNearPoint( point, suspectElems );
6856     TIDSortedElemSet::iterator elem = suspectElems.begin();
6857     for ( ; elem != suspectElems.end(); ++elem )
6858       if ( !SMESH_MeshEditor::IsOut( *elem, point, tolerance ))
6859         foundElements.push_back( *elem );
6860   }
6861   return foundElements.size();
6862 }
6863
6864 //=======================================================================
6865 /*!
6866  * \brief Find an element of given type most close to the given point
6867  *
6868  * WARNING: Only face search is implemeneted so far
6869  */
6870 //=======================================================================
6871
6872 const SMDS_MeshElement*
6873 SMESH_ElementSearcherImpl::FindClosestTo( const gp_Pnt&       point,
6874                                           SMDSAbs_ElementType type )
6875 {
6876   const SMDS_MeshElement* closestElem = 0;
6877
6878   if ( type == SMDSAbs_Face )
6879   {
6880     if ( !_ebbTree || _elementType != type )
6881     {
6882       if ( _ebbTree ) delete _ebbTree;
6883       _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
6884     }
6885     TIDSortedElemSet suspectElems;
6886     _ebbTree->getElementsNearPoint( point, suspectElems );
6887
6888     if ( suspectElems.empty() && _ebbTree->maxSize() > 0 )
6889     {
6890       gp_Pnt boxCenter = 0.5 * ( _ebbTree->getBox()->CornerMin() +
6891                                  _ebbTree->getBox()->CornerMax() );
6892       double radius;
6893       if ( _ebbTree->getBox()->IsOut( point.XYZ() ))
6894         radius = point.Distance( boxCenter ) - 0.5 * _ebbTree->maxSize();
6895       else
6896         radius = _ebbTree->maxSize() / pow( 2., _ebbTree->getHeight()) / 2;
6897       while ( suspectElems.empty() )
6898       {
6899         _ebbTree->getElementsInSphere( point.XYZ(), radius, suspectElems );
6900         radius *= 1.1;
6901       }
6902     }
6903     double minDist = std::numeric_limits<double>::max();
6904     multimap< double, const SMDS_MeshElement* > dist2face;
6905     TIDSortedElemSet::iterator elem = suspectElems.begin();
6906     for ( ; elem != suspectElems.end(); ++elem )
6907     {
6908       double dist = SMESH_MeshEditor::GetDistance( dynamic_cast<const SMDS_MeshFace*>(*elem),
6909                                                    point );
6910       if ( dist < minDist + 1e-10)
6911       {
6912         minDist = dist;
6913         dist2face.insert( dist2face.begin(), make_pair( dist, *elem ));
6914       }
6915     }
6916     if ( !dist2face.empty() )
6917     {
6918       multimap< double, const SMDS_MeshElement* >::iterator d2f = dist2face.begin();
6919       closestElem = d2f->second;
6920       // if there are several elements at the same distance, select one
6921       // with GC closest to the point
6922       typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
6923       double minDistToGC = 0;
6924       for ( ++d2f; d2f != dist2face.end() && fabs( d2f->first - minDist ) < 1e-10; ++d2f )
6925       {
6926         if ( minDistToGC == 0 )
6927         {
6928           gp_XYZ gc(0,0,0);
6929           gc = accumulate( TXyzIterator(closestElem->nodesIterator()),
6930                            TXyzIterator(), gc ) / closestElem->NbNodes();
6931           minDistToGC = point.SquareDistance( gc );
6932         }
6933         gp_XYZ gc(0,0,0);
6934         gc = accumulate( TXyzIterator( d2f->second->nodesIterator()),
6935                          TXyzIterator(), gc ) / d2f->second->NbNodes();
6936         double d = point.SquareDistance( gc );
6937         if ( d < minDistToGC )
6938         {
6939           minDistToGC = d;
6940           closestElem = d2f->second;
6941         }
6942       }
6943       // cout << "FindClosestTo( " <<point.X()<<", "<<point.Y()<<", "<<point.Z()<<" ) FACE "
6944       //      <<closestElem->GetID() << " DIST " << minDist << endl;
6945     }
6946   }
6947   else
6948   {
6949     // NOT IMPLEMENTED SO FAR
6950   }
6951   return closestElem;
6952 }
6953
6954
6955 //================================================================================
6956 /*!
6957  * \brief Classify the given point in the closed 2D mesh
6958  */
6959 //================================================================================
6960
6961 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6962 {
6963   double tolerance = getTolerance();
6964   if ( !_ebbTree || _elementType != SMDSAbs_Face )
6965   {
6966     if ( _ebbTree ) delete _ebbTree;
6967     _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt );
6968   }
6969   // Algo: analyse transition of a line starting at the point through mesh boundary;
6970   // try three lines parallel to axis of the coordinate system and perform rough
6971   // analysis. If solution is not clear perform thorough analysis.
6972
6973   const int nbAxes = 3;
6974   gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6975   map< double, TInters >   paramOnLine2TInters[ nbAxes ];
6976   list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6977   multimap< int, int > nbInt2Axis; // to find the simplest case
6978   for ( int axis = 0; axis < nbAxes; ++axis )
6979   {
6980     gp_Ax1 lineAxis( point, axisDir[axis]);
6981     gp_Lin line    ( lineAxis );
6982
6983     TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
6984     _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
6985
6986     // Intersect faces with the line
6987
6988     map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6989     TIDSortedElemSet::iterator face = suspectFaces.begin();
6990     for ( ; face != suspectFaces.end(); ++face )
6991     {
6992       // get face plane
6993       gp_XYZ fNorm;
6994       if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
6995       gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm );
6996
6997       // perform intersection
6998       IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
6999       if ( !intersection.IsDone() )
7000         continue;
7001       if ( intersection.IsInQuadric() )
7002       {
7003         tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
7004       }
7005       else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
7006       {
7007         gp_Pnt intersectionPoint = intersection.Point(1);
7008         if ( !SMESH_MeshEditor::IsOut( *face, intersectionPoint, tolerance ))
7009           u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
7010       }
7011     }
7012     // Analyse intersections roughly
7013
7014     int nbInter = u2inters.size();
7015     if ( nbInter == 0 )
7016       return TopAbs_OUT;
7017
7018     double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
7019     if ( nbInter == 1 ) // not closed mesh
7020       return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7021
7022     if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
7023       return TopAbs_ON;
7024
7025     if ( (f<0) == (l<0) )
7026       return TopAbs_OUT;
7027
7028     int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
7029     int nbIntAfterPoint  = nbInter - nbIntBeforePoint;
7030     if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7031       return TopAbs_IN;
7032
7033     nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
7034
7035     if ( _outerFacesFound ) break; // pass to thorough analysis
7036
7037   } // three attempts - loop on CS axes
7038
7039   // Analyse intersections thoroughly.
7040   // We make two loops maximum, on the first one we only exclude touching intersections,
7041   // on the second, if situation is still unclear, we gather and use information on
7042   // position of faces (internal or outer). If faces position is already gathered,
7043   // we make the second loop right away.
7044
7045   for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
7046   {
7047     multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
7048     for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
7049     {
7050       int axis = nb_axis->second;
7051       map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
7052
7053       gp_Ax1 lineAxis( point, axisDir[axis]);
7054       gp_Lin line    ( lineAxis );
7055
7056       // add tangent intersections to u2inters
7057       double param;
7058       list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
7059       for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
7060         if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
7061           u2inters.insert(make_pair( param, *tgtInt ));
7062       tangentInters[ axis ].clear();
7063
7064       // Count intersections before and after the point excluding touching ones.
7065       // If hasPositionInfo we count intersections of outer boundary only
7066
7067       int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
7068       double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
7069       map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
7070       bool ok = ! u_int1->second._coincides;
7071       while ( ok && u_int1 != u2inters.end() )
7072       {
7073         double u = u_int1->first;
7074         bool touchingInt = false;
7075         if ( ++u_int2 != u2inters.end() )
7076         {
7077           // skip intersections at the same point (if the line passes through edge or node)
7078           int nbSamePnt = 0;
7079           while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
7080           {
7081             ++nbSamePnt;
7082             ++u_int2;
7083           }
7084
7085           // skip tangent intersections
7086           int nbTgt = 0;
7087           const SMDS_MeshElement* prevFace = u_int1->second._face;
7088           while ( ok && u_int2->second._coincides )
7089           {
7090             if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
7091               ok = false;
7092             else
7093             {
7094               nbTgt++;
7095               u_int2++;
7096               ok = ( u_int2 != u2inters.end() );
7097             }
7098           }
7099           if ( !ok ) break;
7100
7101           // skip intersections at the same point after tangent intersections
7102           if ( nbTgt > 0 )
7103           {
7104             double u2 = u_int2->first;
7105             ++u_int2;
7106             while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
7107             {
7108               ++nbSamePnt;
7109               ++u_int2;
7110             }
7111           }
7112           // decide if we skipped a touching intersection
7113           if ( nbSamePnt + nbTgt > 0 )
7114           {
7115             double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
7116             map< double, TInters >::iterator u_int = u_int1;
7117             for ( ; u_int != u_int2; ++u_int )
7118             {
7119               if ( u_int->second._coincides ) continue;
7120               double dot = u_int->second._faceNorm * line.Direction();
7121               if ( dot > maxDot ) maxDot = dot;
7122               if ( dot < minDot ) minDot = dot;
7123             }
7124             touchingInt = ( minDot*maxDot < 0 );
7125           }
7126         }
7127         if ( !touchingInt )
7128         {
7129           if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
7130           {
7131             if ( u < 0 )
7132               ++nbIntBeforePoint;
7133             else
7134               ++nbIntAfterPoint;
7135           }
7136           if ( u < f ) f = u;
7137           if ( u > l ) l = u;
7138         }
7139
7140         u_int1 = u_int2; // to next intersection
7141
7142       } // loop on intersections with one line
7143
7144       if ( ok )
7145       {
7146         if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
7147           return TopAbs_ON;
7148
7149         if ( nbIntBeforePoint == 0  || nbIntAfterPoint == 0)
7150           return TopAbs_OUT;
7151
7152         if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
7153           return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7154
7155         if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7156           return TopAbs_IN;
7157
7158         if ( (f<0) == (l<0) )
7159           return TopAbs_OUT;
7160
7161         if ( hasPositionInfo )
7162           return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
7163       }
7164     } // loop on intersections of the tree lines - thorough analysis
7165
7166     if ( !hasPositionInfo )
7167     {
7168       // gather info on faces position - is face in the outer boundary or not
7169       map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
7170       findOuterBoundary( u2inters.begin()->second._face );
7171     }
7172
7173   } // two attempts - with and w/o faces position info in the mesh
7174
7175   return TopAbs_UNKNOWN;
7176 }
7177
7178 //=======================================================================
7179 /*!
7180  * \brief Return elements possibly intersecting the line
7181  */
7182 //=======================================================================
7183
7184 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1&                      line,
7185                                                      SMDSAbs_ElementType                type,
7186                                                      vector< const SMDS_MeshElement* >& foundElems)
7187 {
7188   if ( !_ebbTree || _elementType != type )
7189   {
7190     if ( _ebbTree ) delete _ebbTree;
7191     _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
7192   }
7193   TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
7194   _ebbTree->getElementsNearLine( line, suspectFaces );
7195   foundElems.assign( suspectFaces.begin(), suspectFaces.end());
7196 }
7197
7198 //=======================================================================
7199 /*!
7200  * \brief Return SMESH_ElementSearcher
7201  */
7202 //=======================================================================
7203
7204 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
7205 {
7206   return new SMESH_ElementSearcherImpl( *GetMeshDS() );
7207 }
7208
7209 //=======================================================================
7210 /*!
7211  * \brief Return SMESH_ElementSearcher acting on a sub-set of elements
7212  */
7213 //=======================================================================
7214
7215 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt)
7216 {
7217   return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt );
7218 }
7219
7220 //=======================================================================
7221 /*!
7222  * \brief Return true if the point is IN or ON of the element
7223  */
7224 //=======================================================================
7225
7226 bool SMESH_MeshEditor::IsOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
7227 {
7228   if ( element->GetType() == SMDSAbs_Volume)
7229   {
7230     return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
7231   }
7232
7233   // get ordered nodes
7234
7235   vector< gp_XYZ > xyz;
7236   vector<const SMDS_MeshNode*> nodeList;
7237
7238   SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
7239   if ( element->IsQuadratic() ) {
7240     if (const SMDS_VtkFace* f=dynamic_cast<const SMDS_VtkFace*>(element))
7241       nodeIt = f->interlacedNodesElemIterator();
7242     else if (const SMDS_VtkEdge*  e =dynamic_cast<const SMDS_VtkEdge*>(element))
7243       nodeIt = e->interlacedNodesElemIterator();
7244   }
7245   while ( nodeIt->more() )
7246     {
7247       const SMDS_MeshNode* node = cast2Node( nodeIt->next() );
7248       xyz.push_back( SMESH_TNodeXYZ(node) );
7249       nodeList.push_back(node);
7250     }
7251
7252   int i, nbNodes = element->NbNodes();
7253
7254   if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
7255   {
7256     // compute face normal
7257     gp_Vec faceNorm(0,0,0);
7258     xyz.push_back( xyz.front() );
7259     nodeList.push_back( nodeList.front() );
7260     for ( i = 0; i < nbNodes; ++i )
7261     {
7262       gp_Vec edge1( xyz[i+1], xyz[i]);
7263       gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
7264       faceNorm += edge1 ^ edge2;
7265     }
7266     double normSize = faceNorm.Magnitude();
7267     if ( normSize <= tol )
7268     {
7269       // degenerated face: point is out if it is out of all face edges
7270       for ( i = 0; i < nbNodes; ++i )
7271       {
7272         SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] );
7273         if ( !IsOut( &edge, point, tol ))
7274           return false;
7275       }
7276       return true;
7277     }
7278     faceNorm /= normSize;
7279
7280     // check if the point lays on face plane
7281     gp_Vec n2p( xyz[0], point );
7282     if ( fabs( n2p * faceNorm ) > tol )
7283       return true; // not on face plane
7284
7285     // check if point is out of face boundary:
7286     // define it by closest transition of a ray point->infinity through face boundary
7287     // on the face plane.
7288     // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
7289     // to find intersections of the ray with the boundary.
7290     gp_Vec ray = n2p;
7291     gp_Vec plnNorm = ray ^ faceNorm;
7292     normSize = plnNorm.Magnitude();
7293     if ( normSize <= tol ) return false; // point coincides with the first node
7294     plnNorm /= normSize;
7295     // for each node of the face, compute its signed distance to the plane
7296     vector<double> dist( nbNodes + 1);
7297     for ( i = 0; i < nbNodes; ++i )
7298     {
7299       gp_Vec n2p( xyz[i], point );
7300       dist[i] = n2p * plnNorm;
7301     }
7302     dist.back() = dist.front();
7303     // find the closest intersection
7304     int    iClosest = -1;
7305     double rClosest, distClosest = 1e100;;
7306     gp_Pnt pClosest;
7307     for ( i = 0; i < nbNodes; ++i )
7308     {
7309       double r;
7310       if ( fabs( dist[i]) < tol )
7311         r = 0.;
7312       else if ( fabs( dist[i+1]) < tol )
7313         r = 1.;
7314       else if ( dist[i] * dist[i+1] < 0 )
7315         r = dist[i] / ( dist[i] - dist[i+1] );
7316       else
7317         continue; // no intersection
7318       gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
7319       gp_Vec p2int ( point, pInt);
7320       if ( p2int * ray > -tol ) // right half-space
7321       {
7322         double intDist = p2int.SquareMagnitude();
7323         if ( intDist < distClosest )
7324         {
7325           iClosest = i;
7326           rClosest = r;
7327           pClosest = pInt;
7328           distClosest = intDist;
7329         }
7330       }
7331     }
7332     if ( iClosest < 0 )
7333       return true; // no intesections - out
7334
7335     // analyse transition
7336     gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
7337     gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
7338     gp_Vec p2int ( point, pClosest );
7339     bool out = (edgeNorm * p2int) < -tol;
7340     if ( rClosest > 0. && rClosest < 1. ) // not node intersection
7341       return out;
7342
7343     // ray pass through a face node; analyze transition through an adjacent edge
7344     gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
7345     gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
7346     gp_Vec edgeAdjacent( p1, p2 );
7347     gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
7348     bool out2 = (edgeNorm2 * p2int) < -tol;
7349
7350     bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
7351     return covexCorner ? (out || out2) : (out && out2);
7352   }
7353   if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
7354   {
7355     // point is out of edge if it is NOT ON any straight part of edge
7356     // (we consider quadratic edge as being composed of two straight parts)
7357     for ( i = 1; i < nbNodes; ++i )
7358     {
7359       gp_Vec edge( xyz[i-1], xyz[i]);
7360       gp_Vec n1p ( xyz[i-1], point);
7361       double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
7362       if ( dist > tol )
7363         continue;
7364       gp_Vec n2p( xyz[i], point );
7365       if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
7366         continue;
7367       return false; // point is ON this part
7368     }
7369     return true;
7370   }
7371   // Node or 0D element -------------------------------------------------------------------------
7372   {
7373     gp_Vec n2p ( xyz[0], point );
7374     return n2p.Magnitude() <= tol;
7375   }
7376   return true;
7377 }
7378
7379 //=======================================================================
7380
7381 namespace
7382 {
7383   // Position of a point relative to a segment
7384   //            .           .
7385   //            .  LEFT     .
7386   //            .           .
7387   //  VERTEX 1  o----ON----->  VERTEX 2
7388   //            .           .
7389   //            .  RIGHT    .
7390   //            .           .
7391   enum PositionName { POS_LEFT = 1, POS_VERTEX = 2, POS_RIGHT = 4, //POS_ON = 8,
7392                       POS_ALL = POS_LEFT | POS_RIGHT | POS_VERTEX };
7393   struct PointPos
7394   {
7395     PositionName _name;
7396     int          _index; // index of vertex or segment
7397
7398     PointPos( PositionName n, int i=-1 ): _name(n), _index(i) {}
7399     bool operator < (const PointPos& other ) const
7400     {
7401       if ( _name == other._name )
7402         return  ( _index < 0 || other._index < 0 ) ? false : _index < other._index;
7403       return _name < other._name;
7404     }
7405   };
7406
7407   //================================================================================
7408   /*!
7409    * \brief Return of a point relative to a segment
7410    *  \param point2D      - the point to analyze position of
7411    *  \param xyVec        - end points of segments
7412    *  \param index0       - 0-based index of the first point of segment
7413    *  \param posToFindOut - flags of positions to detect
7414    *  \retval PointPos - point position
7415    */
7416   //================================================================================
7417
7418   PointPos getPointPosition( const gp_XY& point2D,
7419                              const gp_XY* segEnds,
7420                              const int    index0 = 0,
7421                              const int    posToFindOut = POS_ALL)
7422   {
7423     const gp_XY& p1 = segEnds[ index0   ];
7424     const gp_XY& p2 = segEnds[ index0+1 ];
7425     const gp_XY grad = p2 - p1;
7426
7427     if ( posToFindOut & POS_VERTEX )
7428     {
7429       // check if the point2D is at "vertex 1" zone
7430       gp_XY pp1[2] = { p1, gp_XY( p1.X() - grad.Y(),
7431                                   p1.Y() + grad.X() ) };
7432       if ( getPointPosition( point2D, pp1, 0, POS_LEFT|POS_RIGHT )._name == POS_LEFT )
7433         return PointPos( POS_VERTEX, index0 );
7434
7435       // check if the point2D is at "vertex 2" zone
7436       gp_XY pp2[2] = { p2, gp_XY( p2.X() - grad.Y(),
7437                                   p2.Y() + grad.X() ) };
7438       if ( getPointPosition( point2D, pp2, 0, POS_LEFT|POS_RIGHT )._name == POS_RIGHT )
7439         return PointPos( POS_VERTEX, index0 + 1);
7440     }
7441     double edgeEquation =
7442       ( point2D.X() - p1.X() ) * grad.Y() - ( point2D.Y() - p1.Y() ) * grad.X();
7443     return PointPos( edgeEquation < 0 ? POS_LEFT : POS_RIGHT, index0 );
7444   }
7445 }
7446
7447 //=======================================================================
7448 /*!
7449  * \brief Return minimal distance from a point to a face
7450  *
7451  * Currently we ignore non-planarity and 2nd order of face
7452  */
7453 //=======================================================================
7454
7455 double SMESH_MeshEditor::GetDistance( const SMDS_MeshFace* face,
7456                                       const gp_Pnt&        point )
7457 {
7458   double badDistance = -1;
7459   if ( !face ) return badDistance;
7460
7461   // coordinates of nodes (medium nodes, if any, ignored)
7462   typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
7463   vector<gp_XYZ> xyz( TXyzIterator( face->nodesIterator()), TXyzIterator() );
7464   xyz.resize( face->NbCornerNodes()+1 );
7465
7466   // transformation to get xyz[0] lies on the origin, xyz[1] lies on the Z axis,
7467   // and xyz[2] lies in the XZ plane. This is to pass to 2D space on XZ plane.
7468   gp_Trsf trsf;
7469   gp_Vec OZ ( xyz[0], xyz[1] );
7470   gp_Vec OX ( xyz[0], xyz[2] );
7471   if ( OZ.Magnitude() < std::numeric_limits<double>::min() )
7472   {
7473     if ( xyz.size() < 4 ) return badDistance;
7474     OZ = gp_Vec ( xyz[0], xyz[2] );
7475     OX = gp_Vec ( xyz[0], xyz[3] );
7476   }
7477   gp_Ax3 tgtCS;
7478   try {
7479     tgtCS = gp_Ax3( xyz[0], OZ, OX );
7480   }
7481   catch ( Standard_Failure ) {
7482     return badDistance;
7483   }
7484   trsf.SetTransformation( tgtCS );
7485
7486   // move all the nodes to 2D
7487   vector<gp_XY> xy( xyz.size() );
7488   for ( size_t i = 0;i < xyz.size()-1; ++i )
7489   {
7490     gp_XYZ p3d = xyz[i];
7491     trsf.Transforms( p3d );
7492     xy[i].SetCoord( p3d.X(), p3d.Z() );
7493   }
7494   xyz.back() = xyz.front();
7495   xy.back() = xy.front();
7496
7497   // // move the point in 2D
7498   gp_XYZ tmpPnt = point.XYZ();
7499   trsf.Transforms( tmpPnt );
7500   gp_XY point2D( tmpPnt.X(), tmpPnt.Z() );
7501
7502   // loop on segments of the face to analyze point position ralative to the face
7503   set< PointPos > pntPosSet;
7504   for ( size_t i = 1; i < xy.size(); ++i )
7505   {
7506     PointPos pos = getPointPosition( point2D, &xy[0], i-1 );
7507     pntPosSet.insert( pos );
7508   }
7509
7510   // compute distance
7511   PointPos pos = *pntPosSet.begin();
7512   // cout << "Face " << face->GetID() << " DIST: ";
7513   switch ( pos._name )
7514   {
7515   case POS_LEFT: {
7516     // point is most close to a segment
7517     gp_Vec p0p1( point, xyz[ pos._index ] );
7518     gp_Vec p1p2( xyz[ pos._index ], xyz[ pos._index+1 ]); // segment vector
7519     p1p2.Normalize();
7520     double projDist = p0p1 * p1p2; // distance projected to the segment
7521     gp_Vec projVec = p1p2 * projDist;
7522     gp_Vec distVec = p0p1 - projVec;
7523     // cout << distVec.Magnitude()  << ", SEG " << face->GetNode(pos._index)->GetID()
7524     //      << " - " << face->GetNodeWrap(pos._index+1)->GetID() << endl;
7525     return distVec.Magnitude();
7526   }
7527   case POS_RIGHT: {
7528     // point is inside the face
7529     double distToFacePlane = tmpPnt.Y();
7530     // cout << distToFacePlane << ", INSIDE " << endl;
7531     return Abs( distToFacePlane );
7532   }
7533   case POS_VERTEX: {
7534     // point is most close to a node
7535     gp_Vec distVec( point, xyz[ pos._index ]);
7536     // cout << distVec.Magnitude()  << " VERTEX " << face->GetNode(pos._index)->GetID() << endl;
7537     return distVec.Magnitude();
7538   }
7539   }
7540   return badDistance;
7541 }
7542
7543 //=======================================================================
7544 //function : SimplifyFace
7545 //purpose  :
7546 //=======================================================================
7547 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
7548                                     vector<const SMDS_MeshNode *>&      poly_nodes,
7549                                     vector<int>&                        quantities) const
7550 {
7551   int nbNodes = faceNodes.size();
7552
7553   if (nbNodes < 3)
7554     return 0;
7555
7556   set<const SMDS_MeshNode*> nodeSet;
7557
7558   // get simple seq of nodes
7559   //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7560   vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7561   int iSimple = 0, nbUnique = 0;
7562
7563   simpleNodes[iSimple++] = faceNodes[0];
7564   nbUnique++;
7565   for (int iCur = 1; iCur < nbNodes; iCur++) {
7566     if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7567       simpleNodes[iSimple++] = faceNodes[iCur];
7568       if (nodeSet.insert( faceNodes[iCur] ).second)
7569         nbUnique++;
7570     }
7571   }
7572   int nbSimple = iSimple;
7573   if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7574     nbSimple--;
7575     iSimple--;
7576   }
7577
7578   if (nbUnique < 3)
7579     return 0;
7580
7581   // separate loops
7582   int nbNew = 0;
7583   bool foundLoop = (nbSimple > nbUnique);
7584   while (foundLoop) {
7585     foundLoop = false;
7586     set<const SMDS_MeshNode*> loopSet;
7587     for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7588       const SMDS_MeshNode* n = simpleNodes[iSimple];
7589       if (!loopSet.insert( n ).second) {
7590         foundLoop = true;
7591
7592         // separate loop
7593         int iC = 0, curLast = iSimple;
7594         for (; iC < curLast; iC++) {
7595           if (simpleNodes[iC] == n) break;
7596         }
7597         int loopLen = curLast - iC;
7598         if (loopLen > 2) {
7599           // create sub-element
7600           nbNew++;
7601           quantities.push_back(loopLen);
7602           for (; iC < curLast; iC++) {
7603             poly_nodes.push_back(simpleNodes[iC]);
7604           }
7605         }
7606         // shift the rest nodes (place from the first loop position)
7607         for (iC = curLast + 1; iC < nbSimple; iC++) {
7608           simpleNodes[iC - loopLen] = simpleNodes[iC];
7609         }
7610         nbSimple -= loopLen;
7611         iSimple -= loopLen;
7612       }
7613     } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7614   } // while (foundLoop)
7615
7616   if (iSimple > 2) {
7617     nbNew++;
7618     quantities.push_back(iSimple);
7619     for (int i = 0; i < iSimple; i++)
7620       poly_nodes.push_back(simpleNodes[i]);
7621   }
7622
7623   return nbNew;
7624 }
7625
7626 //=======================================================================
7627 //function : MergeNodes
7628 //purpose  : In each group, the cdr of nodes are substituted by the first one
7629 //           in all elements.
7630 //=======================================================================
7631
7632 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7633 {
7634   MESSAGE("MergeNodes");
7635   myLastCreatedElems.Clear();
7636   myLastCreatedNodes.Clear();
7637
7638   SMESHDS_Mesh* aMesh = GetMeshDS();
7639
7640   TNodeNodeMap nodeNodeMap; // node to replace - new node
7641   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7642   list< int > rmElemIds, rmNodeIds;
7643
7644   // Fill nodeNodeMap and elems
7645
7646   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7647   for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7648     list<const SMDS_MeshNode*>& nodes = *grIt;
7649     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7650     const SMDS_MeshNode* nToKeep = *nIt;
7651     //MESSAGE("node to keep " << nToKeep->GetID());
7652     for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7653       const SMDS_MeshNode* nToRemove = *nIt;
7654       nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7655       if ( nToRemove != nToKeep ) {
7656         //MESSAGE("  node to remove " << nToRemove->GetID());
7657         rmNodeIds.push_back( nToRemove->GetID() );
7658         AddToSameGroups( nToKeep, nToRemove, aMesh );
7659       }
7660
7661       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7662       while ( invElemIt->more() ) {
7663         const SMDS_MeshElement* elem = invElemIt->next();
7664         elems.insert(elem);
7665       }
7666     }
7667   }
7668   // Change element nodes or remove an element
7669
7670   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7671   for ( ; eIt != elems.end(); eIt++ ) {
7672     const SMDS_MeshElement* elem = *eIt;
7673     //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7674     int nbNodes = elem->NbNodes();
7675     int aShapeId = FindShape( elem );
7676
7677     set<const SMDS_MeshNode*> nodeSet;
7678     vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7679     int iUnique = 0, iCur = 0, nbRepl = 0;
7680     vector<int> iRepl( nbNodes );
7681
7682     // get new seq of nodes
7683     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7684     while ( itN->more() ) {
7685       const SMDS_MeshNode* n =
7686         static_cast<const SMDS_MeshNode*>( itN->next() );
7687
7688       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7689       if ( nnIt != nodeNodeMap.end() ) { // n sticks
7690         n = (*nnIt).second;
7691         // BUG 0020185: begin
7692         {
7693           bool stopRecur = false;
7694           set<const SMDS_MeshNode*> nodesRecur;
7695           nodesRecur.insert(n);
7696           while (!stopRecur) {
7697             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7698             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7699               n = (*nnIt_i).second;
7700               if (!nodesRecur.insert(n).second) {
7701                 // error: recursive dependancy
7702                 stopRecur = true;
7703               }
7704             }
7705             else
7706               stopRecur = true;
7707           }
7708         }
7709         // BUG 0020185: end
7710       }
7711       curNodes[ iCur ] = n;
7712       bool isUnique = nodeSet.insert( n ).second;
7713       if ( isUnique )
7714         uniqueNodes[ iUnique++ ] = n;
7715       else
7716         iRepl[ nbRepl++ ] = iCur;
7717       iCur++;
7718     }
7719
7720     // Analyse element topology after replacement
7721
7722     bool isOk = true;
7723     int nbUniqueNodes = nodeSet.size();
7724     //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7725     if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7726       // Polygons and Polyhedral volumes
7727       if (elem->IsPoly()) {
7728
7729         if (elem->GetType() == SMDSAbs_Face) {
7730           // Polygon
7731           vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7732           int inode = 0;
7733           for (; inode < nbNodes; inode++) {
7734             face_nodes[inode] = curNodes[inode];
7735           }
7736
7737           vector<const SMDS_MeshNode *> polygons_nodes;
7738           vector<int> quantities;
7739           int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7740           if (nbNew > 0) {
7741             inode = 0;
7742             for (int iface = 0; iface < nbNew; iface++) {
7743               int nbNodes = quantities[iface];
7744               vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7745               for (int ii = 0; ii < nbNodes; ii++, inode++) {
7746                 poly_nodes[ii] = polygons_nodes[inode];
7747               }
7748               SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7749               myLastCreatedElems.Append(newElem);
7750               if (aShapeId)
7751                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7752             }
7753
7754             MESSAGE("ChangeElementNodes MergeNodes Polygon");
7755             //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7756             vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7757             int quid =0;
7758             if (nbNew > 0) quid = nbNew - 1;
7759             vector<int> newquant(quantities.begin()+quid, quantities.end());
7760             const SMDS_MeshElement* newElem = 0;
7761             newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7762             myLastCreatedElems.Append(newElem);
7763             if ( aShapeId && newElem )
7764               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7765             rmElemIds.push_back(elem->GetID());
7766           }
7767           else {
7768             rmElemIds.push_back(elem->GetID());
7769           }
7770
7771         }
7772         else if (elem->GetType() == SMDSAbs_Volume) {
7773           // Polyhedral volume
7774           if (nbUniqueNodes < 4) {
7775             rmElemIds.push_back(elem->GetID());
7776           }
7777           else {
7778             // each face has to be analyzed in order to check volume validity
7779             const SMDS_VtkVolume* aPolyedre =
7780               dynamic_cast<const SMDS_VtkVolume*>( elem );
7781             if (aPolyedre) {
7782               int nbFaces = aPolyedre->NbFaces();
7783
7784               vector<const SMDS_MeshNode *> poly_nodes;
7785               vector<int> quantities;
7786
7787               for (int iface = 1; iface <= nbFaces; iface++) {
7788                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7789                 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7790
7791                 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7792                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7793                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7794                   if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7795                     faceNode = (*nnIt).second;
7796                   }
7797                   faceNodes[inode - 1] = faceNode;
7798                 }
7799
7800                 SimplifyFace(faceNodes, poly_nodes, quantities);
7801               }
7802
7803               if (quantities.size() > 3) {
7804                 // to be done: remove coincident faces
7805               }
7806
7807               if (quantities.size() > 3)
7808                 {
7809                   MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7810                   //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7811                   const SMDS_MeshElement* newElem = 0;
7812                   newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7813                   myLastCreatedElems.Append(newElem);
7814                   if ( aShapeId && newElem )
7815                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
7816                   rmElemIds.push_back(elem->GetID());
7817                 }
7818             }
7819             else {
7820               rmElemIds.push_back(elem->GetID());
7821             }
7822           }
7823         }
7824         else {
7825         }
7826
7827         continue;
7828       } // poly element
7829
7830       // Regular elements
7831       // TODO not all the possible cases are solved. Find something more generic?
7832       switch ( nbNodes ) {
7833       case 2: ///////////////////////////////////// EDGE
7834         isOk = false; break;
7835       case 3: ///////////////////////////////////// TRIANGLE
7836         isOk = false; break;
7837       case 4:
7838         if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7839           isOk = false;
7840         else { //////////////////////////////////// QUADRANGLE
7841           if ( nbUniqueNodes < 3 )
7842             isOk = false;
7843           else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7844             isOk = false; // opposite nodes stick
7845           //MESSAGE("isOk " << isOk);
7846         }
7847         break;
7848       case 6: ///////////////////////////////////// PENTAHEDRON
7849         if ( nbUniqueNodes == 4 ) {
7850           // ---------------------------------> tetrahedron
7851           if (nbRepl == 3 &&
7852               iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7853             // all top nodes stick: reverse a bottom
7854             uniqueNodes[ 0 ] = curNodes [ 1 ];
7855             uniqueNodes[ 1 ] = curNodes [ 0 ];
7856           }
7857           else if (nbRepl == 3 &&
7858                    iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7859             // all bottom nodes stick: set a top before
7860             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7861             uniqueNodes[ 0 ] = curNodes [ 3 ];
7862             uniqueNodes[ 1 ] = curNodes [ 4 ];
7863             uniqueNodes[ 2 ] = curNodes [ 5 ];
7864           }
7865           else if (nbRepl == 4 &&
7866                    iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7867             // a lateral face turns into a line: reverse a bottom
7868             uniqueNodes[ 0 ] = curNodes [ 1 ];
7869             uniqueNodes[ 1 ] = curNodes [ 0 ];
7870           }
7871           else
7872             isOk = false;
7873         }
7874         else if ( nbUniqueNodes == 5 ) {
7875           // PENTAHEDRON --------------------> 2 tetrahedrons
7876           if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7877             // a bottom node sticks with a linked top one
7878             // 1.
7879             SMDS_MeshElement* newElem =
7880               aMesh->AddVolume(curNodes[ 3 ],
7881                                curNodes[ 4 ],
7882                                curNodes[ 5 ],
7883                                curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7884             myLastCreatedElems.Append(newElem);
7885             if ( aShapeId )
7886               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7887             // 2. : reverse a bottom
7888             uniqueNodes[ 0 ] = curNodes [ 1 ];
7889             uniqueNodes[ 1 ] = curNodes [ 0 ];
7890             nbUniqueNodes = 4;
7891           }
7892           else
7893             isOk = false;
7894         }
7895         else
7896           isOk = false;
7897         break;
7898       case 8: {
7899         if(elem->IsQuadratic()) { // Quadratic quadrangle
7900           //   1    5    2
7901           //    +---+---+
7902           //    |       |
7903           //    |       |
7904           //   4+       +6
7905           //    |       |
7906           //    |       |
7907           //    +---+---+
7908           //   0    7    3
7909           isOk = false;
7910           if(nbRepl==2) {
7911             MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7912           }
7913           if(nbRepl==3) {
7914             MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2]);
7915             nbUniqueNodes = 6;
7916             if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7917               uniqueNodes[0] = curNodes[0];
7918               uniqueNodes[1] = curNodes[2];
7919               uniqueNodes[2] = curNodes[3];
7920               uniqueNodes[3] = curNodes[5];
7921               uniqueNodes[4] = curNodes[6];
7922               uniqueNodes[5] = curNodes[7];
7923               isOk = true;
7924             }
7925             if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7926               uniqueNodes[0] = curNodes[0];
7927               uniqueNodes[1] = curNodes[1];
7928               uniqueNodes[2] = curNodes[2];
7929               uniqueNodes[3] = curNodes[4];
7930               uniqueNodes[4] = curNodes[5];
7931               uniqueNodes[5] = curNodes[6];
7932               isOk = true;
7933             }
7934             if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7935               uniqueNodes[0] = curNodes[1];
7936               uniqueNodes[1] = curNodes[2];
7937               uniqueNodes[2] = curNodes[3];
7938               uniqueNodes[3] = curNodes[5];
7939               uniqueNodes[4] = curNodes[6];
7940               uniqueNodes[5] = curNodes[0];
7941               isOk = true;
7942             }
7943             if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7944               uniqueNodes[0] = curNodes[0];
7945               uniqueNodes[1] = curNodes[1];
7946               uniqueNodes[2] = curNodes[3];
7947               uniqueNodes[3] = curNodes[4];
7948               uniqueNodes[4] = curNodes[6];
7949               uniqueNodes[5] = curNodes[7];
7950               isOk = true;
7951             }
7952             if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7953               uniqueNodes[0] = curNodes[0];
7954               uniqueNodes[1] = curNodes[2];
7955               uniqueNodes[2] = curNodes[3];
7956               uniqueNodes[3] = curNodes[1];
7957               uniqueNodes[4] = curNodes[6];
7958               uniqueNodes[5] = curNodes[7];
7959               isOk = true;
7960             }
7961             if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7962               uniqueNodes[0] = curNodes[0];
7963               uniqueNodes[1] = curNodes[1];
7964               uniqueNodes[2] = curNodes[2];
7965               uniqueNodes[3] = curNodes[4];
7966               uniqueNodes[4] = curNodes[5];
7967               uniqueNodes[5] = curNodes[7];
7968               isOk = true;
7969             }
7970             if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7971               uniqueNodes[0] = curNodes[0];
7972               uniqueNodes[1] = curNodes[1];
7973               uniqueNodes[2] = curNodes[3];
7974               uniqueNodes[3] = curNodes[4];
7975               uniqueNodes[4] = curNodes[2];
7976               uniqueNodes[5] = curNodes[7];
7977               isOk = true;
7978             }
7979             if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7980               uniqueNodes[0] = curNodes[0];
7981               uniqueNodes[1] = curNodes[1];
7982               uniqueNodes[2] = curNodes[2];
7983               uniqueNodes[3] = curNodes[4];
7984               uniqueNodes[4] = curNodes[5];
7985               uniqueNodes[5] = curNodes[3];
7986               isOk = true;
7987             }
7988           }
7989           if(nbRepl==4) {
7990             MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3]);
7991           }
7992           if(nbRepl==5) {
7993             MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7994           }
7995           break;
7996         }
7997         //////////////////////////////////// HEXAHEDRON
7998         isOk = false;
7999         SMDS_VolumeTool hexa (elem);
8000         hexa.SetExternalNormal();
8001         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
8002           //////////////////////// HEX ---> 1 tetrahedron
8003           for ( int iFace = 0; iFace < 6; iFace++ ) {
8004             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8005             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
8006                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
8007                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
8008               // one face turns into a point ...
8009               int iOppFace = hexa.GetOppFaceIndex( iFace );
8010               ind = hexa.GetFaceNodesIndices( iOppFace );
8011               int nbStick = 0;
8012               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
8013                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
8014                   nbStick++;
8015               }
8016               if ( nbStick == 1 ) {
8017                 // ... and the opposite one - into a triangle.
8018                 // set a top node
8019                 ind = hexa.GetFaceNodesIndices( iFace );
8020                 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
8021                 isOk = true;
8022               }
8023               break;
8024             }
8025           }
8026         }
8027         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
8028           //////////////////////// HEX ---> 1 prism
8029           int nbTria = 0, iTria[3];
8030           const int *ind; // indices of face nodes
8031           // look for triangular faces
8032           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
8033             ind = hexa.GetFaceNodesIndices( iFace );
8034             TIDSortedNodeSet faceNodes;
8035             for ( iCur = 0; iCur < 4; iCur++ )
8036               faceNodes.insert( curNodes[ind[iCur]] );
8037             if ( faceNodes.size() == 3 )
8038               iTria[ nbTria++ ] = iFace;
8039           }
8040           // check if triangles are opposite
8041           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
8042           {
8043             isOk = true;
8044             // set nodes of the bottom triangle
8045             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
8046             vector<int> indB;
8047             for ( iCur = 0; iCur < 4; iCur++ )
8048               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
8049                 indB.push_back( ind[iCur] );
8050             if ( !hexa.IsForward() )
8051               std::swap( indB[0], indB[2] );
8052             for ( iCur = 0; iCur < 3; iCur++ )
8053               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
8054             // set nodes of the top triangle
8055             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
8056             for ( iCur = 0; iCur < 3; ++iCur )
8057               for ( int j = 0; j < 4; ++j )
8058                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
8059                 {
8060                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
8061                   break;
8062                 }
8063           }
8064           break;
8065         }
8066         else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
8067           //////////////////// HEXAHEDRON ---> 2 tetrahedrons
8068           for ( int iFace = 0; iFace < 6; iFace++ ) {
8069             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8070             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
8071                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
8072                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
8073               // one face turns into a point ...
8074               int iOppFace = hexa.GetOppFaceIndex( iFace );
8075               ind = hexa.GetFaceNodesIndices( iOppFace );
8076               int nbStick = 0;
8077               iUnique = 2;  // reverse a tetrahedron 1 bottom
8078               for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
8079                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
8080                   nbStick++;
8081                 else if ( iUnique >= 0 )
8082                   uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
8083               }
8084               if ( nbStick == 0 ) {
8085                 // ... and the opposite one is a quadrangle
8086                 // set a top node
8087                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
8088                 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
8089                 nbUniqueNodes = 4;
8090                 // tetrahedron 2
8091                 SMDS_MeshElement* newElem =
8092                   aMesh->AddVolume(curNodes[ind[ 0 ]],
8093                                    curNodes[ind[ 3 ]],
8094                                    curNodes[ind[ 2 ]],
8095                                    curNodes[indTop[ 0 ]]);
8096                 myLastCreatedElems.Append(newElem);
8097                 if ( aShapeId )
8098                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
8099                 isOk = true;
8100               }
8101               break;
8102             }
8103           }
8104         }
8105         else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
8106           ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
8107           // find indices of quad and tri faces
8108           int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
8109           for ( iFace = 0; iFace < 6; iFace++ ) {
8110             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8111             nodeSet.clear();
8112             for ( iCur = 0; iCur < 4; iCur++ )
8113               nodeSet.insert( curNodes[ind[ iCur ]] );
8114             nbUniqueNodes = nodeSet.size();
8115             if ( nbUniqueNodes == 3 )
8116               iTriFace[ nbTri++ ] = iFace;
8117             else if ( nbUniqueNodes == 4 )
8118               iQuadFace[ nbQuad++ ] = iFace;
8119           }
8120           if (nbQuad == 2 && nbTri == 4 &&
8121               hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
8122             // 2 opposite quadrangles stuck with a diagonal;
8123             // sample groups of merged indices: (0-4)(2-6)
8124             // --------------------------------------------> 2 tetrahedrons
8125             const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
8126             const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
8127             int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
8128             if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
8129                 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
8130               // stuck with 0-2 diagonal
8131               i0  = ind1[ 3 ];
8132               i1d = ind1[ 0 ];
8133               i2  = ind1[ 1 ];
8134               i3d = ind1[ 2 ];
8135               i0t = ind2[ 1 ];
8136               i2t = ind2[ 3 ];
8137             }
8138             else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
8139                      curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
8140               // stuck with 1-3 diagonal
8141               i0  = ind1[ 0 ];
8142               i1d = ind1[ 1 ];
8143               i2  = ind1[ 2 ];
8144               i3d = ind1[ 3 ];
8145               i0t = ind2[ 0 ];
8146               i2t = ind2[ 1 ];
8147             }
8148             else {
8149               ASSERT(0);
8150             }
8151             // tetrahedron 1
8152             uniqueNodes[ 0 ] = curNodes [ i0 ];
8153             uniqueNodes[ 1 ] = curNodes [ i1d ];
8154             uniqueNodes[ 2 ] = curNodes [ i3d ];
8155             uniqueNodes[ 3 ] = curNodes [ i0t ];
8156             nbUniqueNodes = 4;
8157             // tetrahedron 2
8158             SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
8159                                                          curNodes[ i2 ],
8160                                                          curNodes[ i3d ],
8161                                                          curNodes[ i2t ]);
8162             myLastCreatedElems.Append(newElem);
8163             if ( aShapeId )
8164               aMesh->SetMeshElementOnShape( newElem, aShapeId );
8165             isOk = true;
8166           }
8167           else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
8168                    ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
8169             // --------------------------------------------> prism
8170             // find 2 opposite triangles
8171             nbUniqueNodes = 6;
8172             for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
8173               if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
8174                 // find indices of kept and replaced nodes
8175                 // and fill unique nodes of 2 opposite triangles
8176                 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
8177                 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
8178                 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
8179                 // fill unique nodes
8180                 iUnique = 0;
8181                 isOk = true;
8182                 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
8183                   const SMDS_MeshNode* n     = curNodes[ind1[ iCur ]];
8184                   const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
8185                   if ( n == nInit ) {
8186                     // iCur of a linked node of the opposite face (make normals co-directed):
8187                     int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
8188                     // check that correspondent corners of triangles are linked
8189                     if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
8190                       isOk = false;
8191                     else {
8192                       uniqueNodes[ iUnique ] = n;
8193                       uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
8194                       iUnique++;
8195                     }
8196                   }
8197                 }
8198                 break;
8199               }
8200             }
8201           }
8202         } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
8203         else
8204         {
8205           MESSAGE("MergeNodes() removes hexahedron "<< elem);
8206         }
8207         break;
8208       } // HEXAHEDRON
8209
8210       default:
8211         isOk = false;
8212       } // switch ( nbNodes )
8213
8214     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
8215
8216     if ( isOk ) { // the elem remains valid after sticking nodes
8217       if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
8218       {
8219         // Change nodes of polyedre
8220         const SMDS_VtkVolume* aPolyedre =
8221           dynamic_cast<const SMDS_VtkVolume*>( elem );
8222         if (aPolyedre) {
8223           int nbFaces = aPolyedre->NbFaces();
8224
8225           vector<const SMDS_MeshNode *> poly_nodes;
8226           vector<int> quantities (nbFaces);
8227
8228           for (int iface = 1; iface <= nbFaces; iface++) {
8229             int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
8230             quantities[iface - 1] = nbFaceNodes;
8231
8232             for (inode = 1; inode <= nbFaceNodes; inode++) {
8233               const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
8234
8235               TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
8236               if (nnIt != nodeNodeMap.end()) { // curNode sticks
8237                 curNode = (*nnIt).second;
8238               }
8239               poly_nodes.push_back(curNode);
8240             }
8241           }
8242           aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
8243         }
8244       }
8245       else // replace non-polyhedron elements
8246       {
8247         const SMDSAbs_ElementType etyp = elem->GetType();
8248         const int elemId               = elem->GetID();
8249         const bool isPoly              = (elem->GetEntityType() == SMDSEntity_Polygon);
8250         uniqueNodes.resize(nbUniqueNodes);
8251
8252         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
8253
8254         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
8255         SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
8256         if ( sm && newElem )
8257           sm->AddElement( newElem );
8258         if ( elem != newElem )
8259           ReplaceElemInGroups( elem, newElem, aMesh );
8260       }
8261     }
8262     else {
8263       // Remove invalid regular element or invalid polygon
8264       rmElemIds.push_back( elem->GetID() );
8265     }
8266
8267   } // loop on elements
8268
8269   // Remove bad elements, then equal nodes (order important)
8270
8271   Remove( rmElemIds, false );
8272   Remove( rmNodeIds, true );
8273
8274 }
8275
8276
8277 // ========================================================
8278 // class   : SortableElement
8279 // purpose : allow sorting elements basing on their nodes
8280 // ========================================================
8281 class SortableElement : public set <const SMDS_MeshElement*>
8282 {
8283 public:
8284
8285   SortableElement( const SMDS_MeshElement* theElem )
8286   {
8287     myElem = theElem;
8288     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
8289     while ( nodeIt->more() )
8290       this->insert( nodeIt->next() );
8291   }
8292
8293   const SMDS_MeshElement* Get() const
8294   { return myElem; }
8295
8296   void Set(const SMDS_MeshElement* e) const
8297   { myElem = e; }
8298
8299
8300 private:
8301   mutable const SMDS_MeshElement* myElem;
8302 };
8303
8304 //=======================================================================
8305 //function : FindEqualElements
8306 //purpose  : Return list of group of elements built on the same nodes.
8307 //           Search among theElements or in the whole mesh if theElements is empty
8308 //=======================================================================
8309
8310 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
8311                                          TListOfListOfElementsID & theGroupsOfElementsID)
8312 {
8313   myLastCreatedElems.Clear();
8314   myLastCreatedNodes.Clear();
8315
8316   typedef map< SortableElement, int > TMapOfNodeSet;
8317   typedef list<int> TGroupOfElems;
8318
8319   if ( theElements.empty() )
8320   { // get all elements in the mesh
8321     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
8322     while ( eIt->more() )
8323       theElements.insert( theElements.end(), eIt->next());
8324   }
8325
8326   vector< TGroupOfElems > arrayOfGroups;
8327   TGroupOfElems groupOfElems;
8328   TMapOfNodeSet mapOfNodeSet;
8329
8330   TIDSortedElemSet::iterator elemIt = theElements.begin();
8331   for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
8332     const SMDS_MeshElement* curElem = *elemIt;
8333     SortableElement SE(curElem);
8334     int ind = -1;
8335     // check uniqueness
8336     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8337     if( !(pp.second) ) {
8338       TMapOfNodeSet::iterator& itSE = pp.first;
8339       ind = (*itSE).second;
8340       arrayOfGroups[ind].push_back(curElem->GetID());
8341     }
8342     else {
8343       groupOfElems.clear();
8344       groupOfElems.push_back(curElem->GetID());
8345       arrayOfGroups.push_back(groupOfElems);
8346       i++;
8347     }
8348   }
8349
8350   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8351   for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
8352     groupOfElems = *groupIt;
8353     if ( groupOfElems.size() > 1 ) {
8354       groupOfElems.sort();
8355       theGroupsOfElementsID.push_back(groupOfElems);
8356     }
8357   }
8358 }
8359
8360 //=======================================================================
8361 //function : MergeElements
8362 //purpose  : In each given group, substitute all elements by the first one.
8363 //=======================================================================
8364
8365 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8366 {
8367   myLastCreatedElems.Clear();
8368   myLastCreatedNodes.Clear();
8369
8370   typedef list<int> TListOfIDs;
8371   TListOfIDs rmElemIds; // IDs of elems to remove
8372
8373   SMESHDS_Mesh* aMesh = GetMeshDS();
8374
8375   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8376   while ( groupsIt != theGroupsOfElementsID.end() ) {
8377     TListOfIDs& aGroupOfElemID = *groupsIt;
8378     aGroupOfElemID.sort();
8379     int elemIDToKeep = aGroupOfElemID.front();
8380     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8381     aGroupOfElemID.pop_front();
8382     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8383     while ( idIt != aGroupOfElemID.end() ) {
8384       int elemIDToRemove = *idIt;
8385       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8386       // add the kept element in groups of removed one (PAL15188)
8387       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8388       rmElemIds.push_back( elemIDToRemove );
8389       ++idIt;
8390     }
8391     ++groupsIt;
8392   }
8393
8394   Remove( rmElemIds, false );
8395 }
8396
8397 //=======================================================================
8398 //function : MergeEqualElements
8399 //purpose  : Remove all but one of elements built on the same nodes.
8400 //=======================================================================
8401
8402 void SMESH_MeshEditor::MergeEqualElements()
8403 {
8404   TIDSortedElemSet aMeshElements; /* empty input ==
8405                                      to merge equal elements in the whole mesh */
8406   TListOfListOfElementsID aGroupsOfElementsID;
8407   FindEqualElements(aMeshElements, aGroupsOfElementsID);
8408   MergeElements(aGroupsOfElementsID);
8409 }
8410
8411 //=======================================================================
8412 //function : FindFaceInSet
8413 //purpose  : Return a face having linked nodes n1 and n2 and which is
8414 //           - not in avoidSet,
8415 //           - in elemSet provided that !elemSet.empty()
8416 //           i1 and i2 optionally returns indices of n1 and n2
8417 //=======================================================================
8418
8419 const SMDS_MeshElement*
8420 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode*    n1,
8421                                 const SMDS_MeshNode*    n2,
8422                                 const TIDSortedElemSet& elemSet,
8423                                 const TIDSortedElemSet& avoidSet,
8424                                 int*                    n1ind,
8425                                 int*                    n2ind)
8426
8427 {
8428   int i1, i2;
8429   const SMDS_MeshElement* face = 0;
8430
8431   SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
8432   //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
8433   while ( invElemIt->more() && !face ) // loop on inverse faces of n1
8434   {
8435     //MESSAGE("in while ( invElemIt->more() && !face )");
8436     const SMDS_MeshElement* elem = invElemIt->next();
8437     if (avoidSet.count( elem ))
8438       continue;
8439     if ( !elemSet.empty() && !elemSet.count( elem ))
8440       continue;
8441     // index of n1
8442     i1 = elem->GetNodeIndex( n1 );
8443     // find a n2 linked to n1
8444     int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
8445     for ( int di = -1; di < 2 && !face; di += 2 )
8446     {
8447       i2 = (i1+di+nbN) % nbN;
8448       if ( elem->GetNode( i2 ) == n2 )
8449         face = elem;
8450     }
8451     if ( !face && elem->IsQuadratic())
8452     {
8453       // analysis for quadratic elements using all nodes
8454       const SMDS_VtkFace* F =
8455         dynamic_cast<const SMDS_VtkFace*>(elem);
8456       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8457       // use special nodes iterator
8458       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8459       const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
8460       for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
8461       {
8462         const SMDS_MeshNode* n = cast2Node( anIter->next() );
8463         if ( n1 == prevN && n2 == n )
8464         {
8465           face = elem;
8466         }
8467         else if ( n2 == prevN && n1 == n )
8468         {
8469           face = elem; swap( i1, i2 );
8470         }
8471         prevN = n;
8472       }
8473     }
8474   }
8475   if ( n1ind ) *n1ind = i1;
8476   if ( n2ind ) *n2ind = i2;
8477   return face;
8478 }
8479
8480 //=======================================================================
8481 //function : findAdjacentFace
8482 //purpose  :
8483 //=======================================================================
8484
8485 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8486                                                 const SMDS_MeshNode* n2,
8487                                                 const SMDS_MeshElement* elem)
8488 {
8489   TIDSortedElemSet elemSet, avoidSet;
8490   if ( elem )
8491     avoidSet.insert ( elem );
8492   return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
8493 }
8494
8495 //=======================================================================
8496 //function : FindFreeBorder
8497 //purpose  :
8498 //=======================================================================
8499
8500 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8501
8502 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
8503                                        const SMDS_MeshNode*             theSecondNode,
8504                                        const SMDS_MeshNode*             theLastNode,
8505                                        list< const SMDS_MeshNode* > &   theNodes,
8506                                        list< const SMDS_MeshElement* >& theFaces)
8507 {
8508   if ( !theFirstNode || !theSecondNode )
8509     return false;
8510   // find border face between theFirstNode and theSecondNode
8511   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8512   if ( !curElem )
8513     return false;
8514
8515   theFaces.push_back( curElem );
8516   theNodes.push_back( theFirstNode );
8517   theNodes.push_back( theSecondNode );
8518
8519   //vector<const SMDS_MeshNode*> nodes;
8520   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8521   TIDSortedElemSet foundElems;
8522   bool needTheLast = ( theLastNode != 0 );
8523
8524   while ( nStart != theLastNode ) {
8525     if ( nStart == theFirstNode )
8526       return !needTheLast;
8527
8528     // find all free border faces sharing form nStart
8529
8530     list< const SMDS_MeshElement* > curElemList;
8531     list< const SMDS_MeshNode* > nStartList;
8532     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8533     while ( invElemIt->more() ) {
8534       const SMDS_MeshElement* e = invElemIt->next();
8535       if ( e == curElem || foundElems.insert( e ).second ) {
8536         // get nodes
8537         int iNode = 0, nbNodes = e->NbNodes();
8538         //const SMDS_MeshNode* nodes[nbNodes+1];
8539         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8540
8541         if(e->IsQuadratic()) {
8542           const SMDS_VtkFace* F =
8543             dynamic_cast<const SMDS_VtkFace*>(e);
8544           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8545           // use special nodes iterator
8546           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8547           while( anIter->more() ) {
8548             nodes[ iNode++ ] = cast2Node(anIter->next());
8549           }
8550         }
8551         else {
8552           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8553           while ( nIt->more() )
8554             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8555         }
8556         nodes[ iNode ] = nodes[ 0 ];
8557         // check 2 links
8558         for ( iNode = 0; iNode < nbNodes; iNode++ )
8559           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8560                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8561               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8562           {
8563             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8564             curElemList.push_back( e );
8565           }
8566       }
8567     }
8568     // analyse the found
8569
8570     int nbNewBorders = curElemList.size();
8571     if ( nbNewBorders == 0 ) {
8572       // no free border furthermore
8573       return !needTheLast;
8574     }
8575     else if ( nbNewBorders == 1 ) {
8576       // one more element found
8577       nIgnore = nStart;
8578       nStart = nStartList.front();
8579       curElem = curElemList.front();
8580       theFaces.push_back( curElem );
8581       theNodes.push_back( nStart );
8582     }
8583     else {
8584       // several continuations found
8585       list< const SMDS_MeshElement* >::iterator curElemIt;
8586       list< const SMDS_MeshNode* >::iterator nStartIt;
8587       // check if one of them reached the last node
8588       if ( needTheLast ) {
8589         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8590              curElemIt!= curElemList.end();
8591              curElemIt++, nStartIt++ )
8592           if ( *nStartIt == theLastNode ) {
8593             theFaces.push_back( *curElemIt );
8594             theNodes.push_back( *nStartIt );
8595             return true;
8596           }
8597       }
8598       // find the best free border by the continuations
8599       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8600       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8601       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8602            curElemIt!= curElemList.end();
8603            curElemIt++, nStartIt++ )
8604       {
8605         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8606         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8607         // find one more free border
8608         if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8609           cNL->clear();
8610           cFL->clear();
8611         }
8612         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8613           // choice: clear a worse one
8614           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8615           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8616           contNodes[ iWorse ].clear();
8617           contFaces[ iWorse ].clear();
8618         }
8619       }
8620       if ( contNodes[0].empty() && contNodes[1].empty() )
8621         return false;
8622
8623       // append the best free border
8624       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8625       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8626       theNodes.pop_back(); // remove nIgnore
8627       theNodes.pop_back(); // remove nStart
8628       theFaces.pop_back(); // remove curElem
8629       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8630       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8631       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8632       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8633       return true;
8634
8635     } // several continuations found
8636   } // while ( nStart != theLastNode )
8637
8638   return true;
8639 }
8640
8641 //=======================================================================
8642 //function : CheckFreeBorderNodes
8643 //purpose  : Return true if the tree nodes are on a free border
8644 //=======================================================================
8645
8646 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8647                                             const SMDS_MeshNode* theNode2,
8648                                             const SMDS_MeshNode* theNode3)
8649 {
8650   list< const SMDS_MeshNode* > nodes;
8651   list< const SMDS_MeshElement* > faces;
8652   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8653 }
8654
8655 //=======================================================================
8656 //function : SewFreeBorder
8657 //purpose  :
8658 //=======================================================================
8659
8660 SMESH_MeshEditor::Sew_Error
8661 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8662                                  const SMDS_MeshNode* theBordSecondNode,
8663                                  const SMDS_MeshNode* theBordLastNode,
8664                                  const SMDS_MeshNode* theSideFirstNode,
8665                                  const SMDS_MeshNode* theSideSecondNode,
8666                                  const SMDS_MeshNode* theSideThirdNode,
8667                                  const bool           theSideIsFreeBorder,
8668                                  const bool           toCreatePolygons,
8669                                  const bool           toCreatePolyedrs)
8670 {
8671   myLastCreatedElems.Clear();
8672   myLastCreatedNodes.Clear();
8673
8674   MESSAGE("::SewFreeBorder()");
8675   Sew_Error aResult = SEW_OK;
8676
8677   // ====================================
8678   //    find side nodes and elements
8679   // ====================================
8680
8681   list< const SMDS_MeshNode* > nSide[ 2 ];
8682   list< const SMDS_MeshElement* > eSide[ 2 ];
8683   list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8684   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8685
8686   // Free border 1
8687   // --------------
8688   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8689                       nSide[0], eSide[0])) {
8690     MESSAGE(" Free Border 1 not found " );
8691     aResult = SEW_BORDER1_NOT_FOUND;
8692   }
8693   if (theSideIsFreeBorder) {
8694     // Free border 2
8695     // --------------
8696     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8697                         nSide[1], eSide[1])) {
8698       MESSAGE(" Free Border 2 not found " );
8699       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8700     }
8701   }
8702   if ( aResult != SEW_OK )
8703     return aResult;
8704
8705   if (!theSideIsFreeBorder) {
8706     // Side 2
8707     // --------------
8708
8709     // -------------------------------------------------------------------------
8710     // Algo:
8711     // 1. If nodes to merge are not coincident, move nodes of the free border
8712     //    from the coord sys defined by the direction from the first to last
8713     //    nodes of the border to the correspondent sys of the side 2
8714     // 2. On the side 2, find the links most co-directed with the correspondent
8715     //    links of the free border
8716     // -------------------------------------------------------------------------
8717
8718     // 1. Since sewing may break if there are volumes to split on the side 2,
8719     //    we wont move nodes but just compute new coordinates for them
8720     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8721     TNodeXYZMap nBordXYZ;
8722     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8723     list< const SMDS_MeshNode* >::iterator nBordIt;
8724
8725     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8726     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8727     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8728     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8729     double tol2 = 1.e-8;
8730     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8731     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8732       // Need node movement.
8733
8734       // find X and Z axes to create trsf
8735       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8736       gp_Vec X = Zs ^ Zb;
8737       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8738         // Zb || Zs
8739         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8740
8741       // coord systems
8742       gp_Ax3 toBordAx( Pb1, Zb, X );
8743       gp_Ax3 fromSideAx( Ps1, Zs, X );
8744       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8745       // set trsf
8746       gp_Trsf toBordSys, fromSide2Sys;
8747       toBordSys.SetTransformation( toBordAx );
8748       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8749       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8750
8751       // move
8752       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8753         const SMDS_MeshNode* n = *nBordIt;
8754         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8755         toBordSys.Transforms( xyz );
8756         fromSide2Sys.Transforms( xyz );
8757         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8758       }
8759     }
8760     else {
8761       // just insert nodes XYZ in the nBordXYZ map
8762       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8763         const SMDS_MeshNode* n = *nBordIt;
8764         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8765       }
8766     }
8767
8768     // 2. On the side 2, find the links most co-directed with the correspondent
8769     //    links of the free border
8770
8771     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8772     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8773     sideNodes.push_back( theSideFirstNode );
8774
8775     bool hasVolumes = false;
8776     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8777     set<long> foundSideLinkIDs, checkedLinkIDs;
8778     SMDS_VolumeTool volume;
8779     //const SMDS_MeshNode* faceNodes[ 4 ];
8780
8781     const SMDS_MeshNode*    sideNode;
8782     const SMDS_MeshElement* sideElem;
8783     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8784     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8785     nBordIt = bordNodes.begin();
8786     nBordIt++;
8787     // border node position and border link direction to compare with
8788     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8789     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8790     // choose next side node by link direction or by closeness to
8791     // the current border node:
8792     bool searchByDir = ( *nBordIt != theBordLastNode );
8793     do {
8794       // find the next node on the Side 2
8795       sideNode = 0;
8796       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8797       long linkID;
8798       checkedLinkIDs.clear();
8799       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8800
8801       // loop on inverse elements of current node (prevSideNode) on the Side 2
8802       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8803       while ( invElemIt->more() )
8804       {
8805         const SMDS_MeshElement* elem = invElemIt->next();
8806         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8807         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8808         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8809         bool isVolume = volume.Set( elem );
8810         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8811         if ( isVolume ) // --volume
8812           hasVolumes = true;
8813         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8814           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8815           if(elem->IsQuadratic()) {
8816             const SMDS_VtkFace* F =
8817               dynamic_cast<const SMDS_VtkFace*>(elem);
8818             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8819             // use special nodes iterator
8820             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8821             while( anIter->more() ) {
8822               nodes[ iNode ] = cast2Node(anIter->next());
8823               if ( nodes[ iNode++ ] == prevSideNode )
8824                 iPrevNode = iNode - 1;
8825             }
8826           }
8827           else {
8828             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8829             while ( nIt->more() ) {
8830               nodes[ iNode ] = cast2Node( nIt->next() );
8831               if ( nodes[ iNode++ ] == prevSideNode )
8832                 iPrevNode = iNode - 1;
8833             }
8834           }
8835           // there are 2 links to check
8836           nbNodes = 2;
8837         }
8838         else // --edge
8839           continue;
8840         // loop on links, to be precise, on the second node of links
8841         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8842           const SMDS_MeshNode* n = nodes[ iNode ];
8843           if ( isVolume ) {
8844             if ( !volume.IsLinked( n, prevSideNode ))
8845               continue;
8846           }
8847           else {
8848             if ( iNode ) // a node before prevSideNode
8849               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8850             else         // a node after prevSideNode
8851               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8852           }
8853           // check if this link was already used
8854           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8855           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8856           if (!isJustChecked &&
8857               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8858           {
8859             // test a link geometrically
8860             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8861             bool linkIsBetter = false;
8862             double dot = 0.0, dist = 0.0;
8863             if ( searchByDir ) { // choose most co-directed link
8864               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8865               linkIsBetter = ( dot > maxDot );
8866             }
8867             else { // choose link with the node closest to bordPos
8868               dist = ( nextXYZ - bordPos ).SquareModulus();
8869               linkIsBetter = ( dist < minDist );
8870             }
8871             if ( linkIsBetter ) {
8872               maxDot = dot;
8873               minDist = dist;
8874               linkID = iLink;
8875               sideNode = n;
8876               sideElem = elem;
8877             }
8878           }
8879         }
8880       } // loop on inverse elements of prevSideNode
8881
8882       if ( !sideNode ) {
8883         MESSAGE(" Cant find path by links of the Side 2 ");
8884         return SEW_BAD_SIDE_NODES;
8885       }
8886       sideNodes.push_back( sideNode );
8887       sideElems.push_back( sideElem );
8888       foundSideLinkIDs.insert ( linkID );
8889       prevSideNode = sideNode;
8890
8891       if ( *nBordIt == theBordLastNode )
8892         searchByDir = false;
8893       else {
8894         // find the next border link to compare with
8895         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8896         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8897         // move to next border node if sideNode is before forward border node (bordPos)
8898         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8899           prevBordNode = *nBordIt;
8900           nBordIt++;
8901           bordPos = nBordXYZ[ *nBordIt ];
8902           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8903           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8904         }
8905       }
8906     }
8907     while ( sideNode != theSideSecondNode );
8908
8909     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8910       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8911       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8912     }
8913   } // end nodes search on the side 2
8914
8915   // ============================
8916   // sew the border to the side 2
8917   // ============================
8918
8919   int nbNodes[]  = { nSide[0].size(), nSide[1].size() };
8920   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8921
8922   TListOfListOfNodes nodeGroupsToMerge;
8923   if ( nbNodes[0] == nbNodes[1] ||
8924        ( theSideIsFreeBorder && !theSideThirdNode)) {
8925
8926     // all nodes are to be merged
8927
8928     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8929          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8930          nIt[0]++, nIt[1]++ )
8931     {
8932       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8933       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8934       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8935     }
8936   }
8937   else {
8938
8939     // insert new nodes into the border and the side to get equal nb of segments
8940
8941     // get normalized parameters of nodes on the borders
8942     //double param[ 2 ][ maxNbNodes ];
8943     double* param[ 2 ];
8944     param[0] = new double [ maxNbNodes ];
8945     param[1] = new double [ maxNbNodes ];
8946     int iNode, iBord;
8947     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8948       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8949       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8950       const SMDS_MeshNode* nPrev = *nIt;
8951       double bordLength = 0;
8952       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8953         const SMDS_MeshNode* nCur = *nIt;
8954         gp_XYZ segment (nCur->X() - nPrev->X(),
8955                         nCur->Y() - nPrev->Y(),
8956                         nCur->Z() - nPrev->Z());
8957         double segmentLen = segment.Modulus();
8958         bordLength += segmentLen;
8959         param[ iBord ][ iNode ] = bordLength;
8960         nPrev = nCur;
8961       }
8962       // normalize within [0,1]
8963       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8964         param[ iBord ][ iNode ] /= bordLength;
8965       }
8966     }
8967
8968     // loop on border segments
8969     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8970     int i[ 2 ] = { 0, 0 };
8971     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8972     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8973
8974     TElemOfNodeListMap insertMap;
8975     TElemOfNodeListMap::iterator insertMapIt;
8976     // insertMap is
8977     // key:   elem to insert nodes into
8978     // value: 2 nodes to insert between + nodes to be inserted
8979     do {
8980       bool next[ 2 ] = { false, false };
8981
8982       // find min adjacent segment length after sewing
8983       double nextParam = 10., prevParam = 0;
8984       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8985         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8986           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8987         if ( i[ iBord ] > 0 )
8988           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8989       }
8990       double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8991       double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8992       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8993
8994       // choose to insert or to merge nodes
8995       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8996       if ( Abs( du ) <= minSegLen * 0.2 ) {
8997         // merge
8998         // ------
8999         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
9000         const SMDS_MeshNode* n0 = *nIt[0];
9001         const SMDS_MeshNode* n1 = *nIt[1];
9002         nodeGroupsToMerge.back().push_back( n1 );
9003         nodeGroupsToMerge.back().push_back( n0 );
9004         // position of node of the border changes due to merge
9005         param[ 0 ][ i[0] ] += du;
9006         // move n1 for the sake of elem shape evaluation during insertion.
9007         // n1 will be removed by MergeNodes() anyway
9008         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
9009         next[0] = next[1] = true;
9010       }
9011       else {
9012         // insert
9013         // ------
9014         int intoBord = ( du < 0 ) ? 0 : 1;
9015         const SMDS_MeshElement* elem = *eIt[ intoBord ];
9016         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
9017         const SMDS_MeshNode*    n2   = *nIt[ intoBord ];
9018         const SMDS_MeshNode*    nIns = *nIt[ 1 - intoBord ];
9019         if ( intoBord == 1 ) {
9020           // move node of the border to be on a link of elem of the side
9021           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
9022           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
9023           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
9024           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
9025           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
9026         }
9027         insertMapIt = insertMap.find( elem );
9028         bool notFound = ( insertMapIt == insertMap.end() );
9029         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
9030         if ( otherLink ) {
9031           // insert into another link of the same element:
9032           // 1. perform insertion into the other link of the elem
9033           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
9034           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
9035           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
9036           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
9037           // 2. perform insertion into the link of adjacent faces
9038           while (true) {
9039             const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
9040             if ( adjElem )
9041               InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
9042             else
9043               break;
9044           }
9045           if (toCreatePolyedrs) {
9046             // perform insertion into the links of adjacent volumes
9047             UpdateVolumes(n12, n22, nodeList);
9048           }
9049           // 3. find an element appeared on n1 and n2 after the insertion
9050           insertMap.erase( elem );
9051           elem = findAdjacentFace( n1, n2, 0 );
9052         }
9053         if ( notFound || otherLink ) {
9054           // add element and nodes of the side into the insertMap
9055           insertMapIt = insertMap.insert
9056             ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
9057           (*insertMapIt).second.push_back( n1 );
9058           (*insertMapIt).second.push_back( n2 );
9059         }
9060         // add node to be inserted into elem
9061         (*insertMapIt).second.push_back( nIns );
9062         next[ 1 - intoBord ] = true;
9063       }
9064
9065       // go to the next segment
9066       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
9067         if ( next[ iBord ] ) {
9068           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
9069             eIt[ iBord ]++;
9070           nPrev[ iBord ] = *nIt[ iBord ];
9071           nIt[ iBord ]++; i[ iBord ]++;
9072         }
9073       }
9074     }
9075     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
9076
9077     // perform insertion of nodes into elements
9078
9079     for (insertMapIt = insertMap.begin();
9080          insertMapIt != insertMap.end();
9081          insertMapIt++ )
9082     {
9083       const SMDS_MeshElement* elem = (*insertMapIt).first;
9084       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
9085       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
9086       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
9087
9088       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
9089
9090       if ( !theSideIsFreeBorder ) {
9091         // look for and insert nodes into the faces adjacent to elem
9092         while (true) {
9093           const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
9094           if ( adjElem )
9095             InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
9096           else
9097             break;
9098         }
9099       }
9100       if (toCreatePolyedrs) {
9101         // perform insertion into the links of adjacent volumes
9102         UpdateVolumes(n1, n2, nodeList);
9103       }
9104     }
9105
9106     delete param[0];
9107     delete param[1];
9108   } // end: insert new nodes
9109
9110   MergeNodes ( nodeGroupsToMerge );
9111
9112   return aResult;
9113 }
9114
9115 //=======================================================================
9116 //function : InsertNodesIntoLink
9117 //purpose  : insert theNodesToInsert into theFace between theBetweenNode1
9118 //           and theBetweenNode2 and split theElement
9119 //=======================================================================
9120
9121 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theFace,
9122                                            const SMDS_MeshNode*        theBetweenNode1,
9123                                            const SMDS_MeshNode*        theBetweenNode2,
9124                                            list<const SMDS_MeshNode*>& theNodesToInsert,
9125                                            const bool                  toCreatePoly)
9126 {
9127   if ( theFace->GetType() != SMDSAbs_Face ) return;
9128
9129   // find indices of 2 link nodes and of the rest nodes
9130   int iNode = 0, il1, il2, i3, i4;
9131   il1 = il2 = i3 = i4 = -1;
9132   //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
9133   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
9134
9135   if(theFace->IsQuadratic()) {
9136     const SMDS_VtkFace* F =
9137       dynamic_cast<const SMDS_VtkFace*>(theFace);
9138     if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9139     // use special nodes iterator
9140     SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9141     while( anIter->more() ) {
9142       const SMDS_MeshNode* n = cast2Node(anIter->next());
9143       if ( n == theBetweenNode1 )
9144         il1 = iNode;
9145       else if ( n == theBetweenNode2 )
9146         il2 = iNode;
9147       else if ( i3 < 0 )
9148         i3 = iNode;
9149       else
9150         i4 = iNode;
9151       nodes[ iNode++ ] = n;
9152     }
9153   }
9154   else {
9155     SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9156     while ( nodeIt->more() ) {
9157       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9158       if ( n == theBetweenNode1 )
9159         il1 = iNode;
9160       else if ( n == theBetweenNode2 )
9161         il2 = iNode;
9162       else if ( i3 < 0 )
9163         i3 = iNode;
9164       else
9165         i4 = iNode;
9166       nodes[ iNode++ ] = n;
9167     }
9168   }
9169   if ( il1 < 0 || il2 < 0 || i3 < 0 )
9170     return ;
9171
9172   // arrange link nodes to go one after another regarding the face orientation
9173   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
9174   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
9175   if ( reverse ) {
9176     iNode = il1;
9177     il1 = il2;
9178     il2 = iNode;
9179     aNodesToInsert.reverse();
9180   }
9181   // check that not link nodes of a quadrangles are in good order
9182   int nbFaceNodes = theFace->NbNodes();
9183   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
9184     iNode = i3;
9185     i3 = i4;
9186     i4 = iNode;
9187   }
9188
9189   if (toCreatePoly || theFace->IsPoly()) {
9190
9191     iNode = 0;
9192     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
9193
9194     // add nodes of face up to first node of link
9195     bool isFLN = false;
9196
9197     if(theFace->IsQuadratic()) {
9198       const SMDS_VtkFace* F =
9199         dynamic_cast<const SMDS_VtkFace*>(theFace);
9200       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9201       // use special nodes iterator
9202       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9203       while( anIter->more()  && !isFLN ) {
9204         const SMDS_MeshNode* n = cast2Node(anIter->next());
9205         poly_nodes[iNode++] = n;
9206         if (n == nodes[il1]) {
9207           isFLN = true;
9208         }
9209       }
9210       // add nodes to insert
9211       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9212       for (; nIt != aNodesToInsert.end(); nIt++) {
9213         poly_nodes[iNode++] = *nIt;
9214       }
9215       // add nodes of face starting from last node of link
9216       while ( anIter->more() ) {
9217         poly_nodes[iNode++] = cast2Node(anIter->next());
9218       }
9219     }
9220     else {
9221       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9222       while ( nodeIt->more() && !isFLN ) {
9223         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9224         poly_nodes[iNode++] = n;
9225         if (n == nodes[il1]) {
9226           isFLN = true;
9227         }
9228       }
9229       // add nodes to insert
9230       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9231       for (; nIt != aNodesToInsert.end(); nIt++) {
9232         poly_nodes[iNode++] = *nIt;
9233       }
9234       // add nodes of face starting from last node of link
9235       while ( nodeIt->more() ) {
9236         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9237         poly_nodes[iNode++] = n;
9238       }
9239     }
9240
9241     // edit or replace the face
9242     SMESHDS_Mesh *aMesh = GetMeshDS();
9243
9244     if (theFace->IsPoly()) {
9245       aMesh->ChangePolygonNodes(theFace, poly_nodes);
9246     }
9247     else {
9248       int aShapeId = FindShape( theFace );
9249
9250       SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
9251       myLastCreatedElems.Append(newElem);
9252       if ( aShapeId && newElem )
9253         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9254
9255       aMesh->RemoveElement(theFace);
9256     }
9257     return;
9258   }
9259
9260   SMESHDS_Mesh *aMesh = GetMeshDS();
9261   if( !theFace->IsQuadratic() ) {
9262
9263     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
9264     int nbLinkNodes = 2 + aNodesToInsert.size();
9265     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
9266     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
9267     linkNodes[ 0 ] = nodes[ il1 ];
9268     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
9269     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9270     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9271       linkNodes[ iNode++ ] = *nIt;
9272     }
9273     // decide how to split a quadrangle: compare possible variants
9274     // and choose which of splits to be a quadrangle
9275     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
9276     if ( nbFaceNodes == 3 ) {
9277       iBestQuad = nbSplits;
9278       i4 = i3;
9279     }
9280     else if ( nbFaceNodes == 4 ) {
9281       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
9282       double aBestRate = DBL_MAX;
9283       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
9284         i1 = 0; i2 = 1;
9285         double aBadRate = 0;
9286         // evaluate elements quality
9287         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
9288           if ( iSplit == iQuad ) {
9289             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
9290                                    linkNodes[ i2++ ],
9291                                    nodes[ i3 ],
9292                                    nodes[ i4 ]);
9293             aBadRate += getBadRate( &quad, aCrit );
9294           }
9295           else {
9296             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
9297                                    linkNodes[ i2++ ],
9298                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
9299             aBadRate += getBadRate( &tria, aCrit );
9300           }
9301         }
9302         // choice
9303         if ( aBadRate < aBestRate ) {
9304           iBestQuad = iQuad;
9305           aBestRate = aBadRate;
9306         }
9307       }
9308     }
9309
9310     // create new elements
9311     int aShapeId = FindShape( theFace );
9312
9313     i1 = 0; i2 = 1;
9314     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
9315       SMDS_MeshElement* newElem = 0;
9316       if ( iSplit == iBestQuad )
9317         newElem = aMesh->AddFace (linkNodes[ i1++ ],
9318                                   linkNodes[ i2++ ],
9319                                   nodes[ i3 ],
9320                                   nodes[ i4 ]);
9321       else
9322         newElem = aMesh->AddFace (linkNodes[ i1++ ],
9323                                   linkNodes[ i2++ ],
9324                                   nodes[ iSplit < iBestQuad ? i4 : i3 ]);
9325       myLastCreatedElems.Append(newElem);
9326       if ( aShapeId && newElem )
9327         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9328     }
9329
9330     // change nodes of theFace
9331     const SMDS_MeshNode* newNodes[ 4 ];
9332     newNodes[ 0 ] = linkNodes[ i1 ];
9333     newNodes[ 1 ] = linkNodes[ i2 ];
9334     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9335     newNodes[ 3 ] = nodes[ i4 ];
9336     //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
9337     const SMDS_MeshElement* newElem = 0;
9338     if (iSplit == iBestQuad)
9339       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
9340     else
9341       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
9342     myLastCreatedElems.Append(newElem);
9343     if ( aShapeId && newElem )
9344       aMesh->SetMeshElementOnShape( newElem, aShapeId );
9345 } // end if(!theFace->IsQuadratic())
9346   else { // theFace is quadratic
9347     // we have to split theFace on simple triangles and one simple quadrangle
9348     int tmp = il1/2;
9349     int nbshift = tmp*2;
9350     // shift nodes in nodes[] by nbshift
9351     int i,j;
9352     for(i=0; i<nbshift; i++) {
9353       const SMDS_MeshNode* n = nodes[0];
9354       for(j=0; j<nbFaceNodes-1; j++) {
9355         nodes[j] = nodes[j+1];
9356       }
9357       nodes[nbFaceNodes-1] = n;
9358     }
9359     il1 = il1 - nbshift;
9360     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9361     //   n0      n1     n2    n0      n1     n2
9362     //     +-----+-----+        +-----+-----+
9363     //      \         /         |           |
9364     //       \       /          |           |
9365     //      n5+     +n3       n7+           +n3
9366     //         \   /            |           |
9367     //          \ /             |           |
9368     //           +              +-----+-----+
9369     //           n4           n6      n5     n4
9370
9371     // create new elements
9372     int aShapeId = FindShape( theFace );
9373
9374     int n1,n2,n3;
9375     if(nbFaceNodes==6) { // quadratic triangle
9376       SMDS_MeshElement* newElem =
9377         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9378       myLastCreatedElems.Append(newElem);
9379       if ( aShapeId && newElem )
9380         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9381       if(theFace->IsMediumNode(nodes[il1])) {
9382         // create quadrangle
9383         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
9384         myLastCreatedElems.Append(newElem);
9385         if ( aShapeId && newElem )
9386           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9387         n1 = 1;
9388         n2 = 2;
9389         n3 = 3;
9390       }
9391       else {
9392         // create quadrangle
9393         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
9394         myLastCreatedElems.Append(newElem);
9395         if ( aShapeId && newElem )
9396           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9397         n1 = 0;
9398         n2 = 1;
9399         n3 = 5;
9400       }
9401     }
9402     else { // nbFaceNodes==8 - quadratic quadrangle
9403       SMDS_MeshElement* newElem =
9404         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9405       myLastCreatedElems.Append(newElem);
9406       if ( aShapeId && newElem )
9407         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9408       newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
9409       myLastCreatedElems.Append(newElem);
9410       if ( aShapeId && newElem )
9411         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9412       newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
9413       myLastCreatedElems.Append(newElem);
9414       if ( aShapeId && newElem )
9415         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9416       if(theFace->IsMediumNode(nodes[il1])) {
9417         // create quadrangle
9418         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
9419         myLastCreatedElems.Append(newElem);
9420         if ( aShapeId && newElem )
9421           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9422         n1 = 1;
9423         n2 = 2;
9424         n3 = 3;
9425       }
9426       else {
9427         // create quadrangle
9428         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
9429         myLastCreatedElems.Append(newElem);
9430         if ( aShapeId && newElem )
9431           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9432         n1 = 0;
9433         n2 = 1;
9434         n3 = 7;
9435       }
9436     }
9437     // create needed triangles using n1,n2,n3 and inserted nodes
9438     int nbn = 2 + aNodesToInsert.size();
9439     //const SMDS_MeshNode* aNodes[nbn];
9440     vector<const SMDS_MeshNode*> aNodes(nbn);
9441     aNodes[0] = nodes[n1];
9442     aNodes[nbn-1] = nodes[n2];
9443     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9444     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9445       aNodes[iNode++] = *nIt;
9446     }
9447     for(i=1; i<nbn; i++) {
9448       SMDS_MeshElement* newElem =
9449         aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
9450       myLastCreatedElems.Append(newElem);
9451       if ( aShapeId && newElem )
9452         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9453     }
9454   }
9455   // remove old face
9456   aMesh->RemoveElement(theFace);
9457 }
9458
9459 //=======================================================================
9460 //function : UpdateVolumes
9461 //purpose  :
9462 //=======================================================================
9463 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
9464                                       const SMDS_MeshNode*        theBetweenNode2,
9465                                       list<const SMDS_MeshNode*>& theNodesToInsert)
9466 {
9467   myLastCreatedElems.Clear();
9468   myLastCreatedNodes.Clear();
9469
9470   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9471   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9472     const SMDS_MeshElement* elem = invElemIt->next();
9473
9474     // check, if current volume has link theBetweenNode1 - theBetweenNode2
9475     SMDS_VolumeTool aVolume (elem);
9476     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9477       continue;
9478
9479     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9480     int iface, nbFaces = aVolume.NbFaces();
9481     vector<const SMDS_MeshNode *> poly_nodes;
9482     vector<int> quantities (nbFaces);
9483
9484     for (iface = 0; iface < nbFaces; iface++) {
9485       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9486       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9487       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9488
9489       for (int inode = 0; inode < nbFaceNodes; inode++) {
9490         poly_nodes.push_back(faceNodes[inode]);
9491
9492         if (nbInserted == 0) {
9493           if (faceNodes[inode] == theBetweenNode1) {
9494             if (faceNodes[inode + 1] == theBetweenNode2) {
9495               nbInserted = theNodesToInsert.size();
9496
9497               // add nodes to insert
9498               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9499               for (; nIt != theNodesToInsert.end(); nIt++) {
9500                 poly_nodes.push_back(*nIt);
9501               }
9502             }
9503           }
9504           else if (faceNodes[inode] == theBetweenNode2) {
9505             if (faceNodes[inode + 1] == theBetweenNode1) {
9506               nbInserted = theNodesToInsert.size();
9507
9508               // add nodes to insert in reversed order
9509               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9510               nIt--;
9511               for (; nIt != theNodesToInsert.begin(); nIt--) {
9512                 poly_nodes.push_back(*nIt);
9513               }
9514               poly_nodes.push_back(*nIt);
9515             }
9516           }
9517           else {
9518           }
9519         }
9520       }
9521       quantities[iface] = nbFaceNodes + nbInserted;
9522     }
9523
9524     // Replace or update the volume
9525     SMESHDS_Mesh *aMesh = GetMeshDS();
9526
9527     if (elem->IsPoly()) {
9528       aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
9529
9530     }
9531     else {
9532       int aShapeId = FindShape( elem );
9533
9534       SMDS_MeshElement* newElem =
9535         aMesh->AddPolyhedralVolume(poly_nodes, quantities);
9536       myLastCreatedElems.Append(newElem);
9537       if (aShapeId && newElem)
9538         aMesh->SetMeshElementOnShape(newElem, aShapeId);
9539
9540       aMesh->RemoveElement(elem);
9541     }
9542   }
9543 }
9544
9545 namespace
9546 {
9547   //================================================================================
9548   /*!
9549    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9550    */
9551   //================================================================================
9552
9553   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9554                            vector<const SMDS_MeshNode *> & nodes,
9555                            vector<int> &                   nbNodeInFaces )
9556   {
9557     nodes.clear();
9558     nbNodeInFaces.clear();
9559     SMDS_VolumeTool vTool ( elem );
9560     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9561     {
9562       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9563       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9564       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9565     }
9566   }
9567 }
9568
9569 //=======================================================================
9570 /*!
9571  * \brief Convert elements contained in a submesh to quadratic
9572  * \return int - nb of checked elements
9573  */
9574 //=======================================================================
9575
9576 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9577                                              SMESH_MesherHelper& theHelper,
9578                                              const bool          theForce3d)
9579 {
9580   int nbElem = 0;
9581   if( !theSm ) return nbElem;
9582
9583   vector<int> nbNodeInFaces;
9584   vector<const SMDS_MeshNode *> nodes;
9585   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9586   while(ElemItr->more())
9587   {
9588     nbElem++;
9589     const SMDS_MeshElement* elem = ElemItr->next();
9590     if( !elem || elem->IsQuadratic() ) continue;
9591
9592     // get elem data needed to re-create it
9593     //
9594     const int id                        = elem->GetID();
9595     const int nbNodes                   = elem->NbNodes();
9596     const SMDSAbs_ElementType aType     = elem->GetType();
9597     const SMDSAbs_EntityType  aGeomType = elem->GetEntityType();
9598     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9599     if ( aGeomType == SMDSEntity_Polyhedra )
9600       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9601     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9602       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9603
9604     // remove a linear element
9605     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9606
9607     const SMDS_MeshElement* NewElem = 0;
9608
9609     switch( aType )
9610     {
9611     case SMDSAbs_Edge :
9612       {
9613         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9614         break;
9615       }
9616     case SMDSAbs_Face :
9617       {
9618         switch(nbNodes)
9619         {
9620         case 3:
9621           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9622           break;
9623         case 4:
9624           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9625           break;
9626         default:
9627           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9628           continue;
9629         }
9630         break;
9631       }
9632     case SMDSAbs_Volume :
9633       {
9634         switch( aGeomType )
9635         {
9636         case SMDSEntity_Tetra:
9637           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9638           break;
9639         case SMDSEntity_Pyramid:
9640           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9641           break;
9642         case SMDSEntity_Penta:
9643           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9644           break;
9645         case SMDSEntity_Hexa:
9646           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9647                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9648           break;
9649         case SMDSEntity_Hexagonal_Prism:
9650         default:
9651           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9652         }
9653         break;
9654       }
9655     default :
9656       continue;
9657     }
9658     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9659     if( NewElem )
9660       theSm->AddElement( NewElem );
9661   }
9662   return nbElem;
9663 }
9664
9665 //=======================================================================
9666 //function : ConvertToQuadratic
9667 //purpose  :
9668 //=======================================================================
9669
9670 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d)
9671 {
9672   SMESHDS_Mesh* meshDS = GetMeshDS();
9673
9674   SMESH_MesherHelper aHelper(*myMesh);
9675   aHelper.SetIsQuadratic( true );
9676
9677   int nbCheckedElems = 0;
9678   if ( myMesh->HasShapeToMesh() )
9679   {
9680     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9681     {
9682       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9683       while ( smIt->more() ) {
9684         SMESH_subMesh* sm = smIt->next();
9685         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9686           aHelper.SetSubShape( sm->GetSubShape() );
9687           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9688         }
9689       }
9690     }
9691   }
9692   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9693   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9694   {
9695     SMESHDS_SubMesh *smDS = 0;
9696     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9697     while(aEdgeItr->more())
9698     {
9699       const SMDS_MeshEdge* edge = aEdgeItr->next();
9700       if(edge && !edge->IsQuadratic())
9701       {
9702         int id = edge->GetID();
9703         //MESSAGE("edge->GetID() " << id);
9704         const SMDS_MeshNode* n1 = edge->GetNode(0);
9705         const SMDS_MeshNode* n2 = edge->GetNode(1);
9706
9707         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9708
9709         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9710         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9711       }
9712     }
9713     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9714     while(aFaceItr->more())
9715     {
9716       const SMDS_MeshFace* face = aFaceItr->next();
9717       if(!face || face->IsQuadratic() ) continue;
9718
9719       const int id = face->GetID();
9720       const SMDSAbs_EntityType type = face->GetEntityType();
9721       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9722
9723       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9724
9725       SMDS_MeshFace * NewFace = 0;
9726       switch( type )
9727       {
9728       case SMDSEntity_Triangle:
9729         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9730         break;
9731       case SMDSEntity_Quadrangle:
9732         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9733         break;
9734       default:
9735         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9736       }
9737       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9738     }
9739     vector<int> nbNodeInFaces;
9740     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9741     while(aVolumeItr->more())
9742     {
9743       const SMDS_MeshVolume* volume = aVolumeItr->next();
9744       if(!volume || volume->IsQuadratic() ) continue;
9745
9746       const int id = volume->GetID();
9747       const SMDSAbs_EntityType type = volume->GetEntityType();
9748       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9749       if ( type == SMDSEntity_Polyhedra )
9750         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9751       else if ( type == SMDSEntity_Hexagonal_Prism )
9752         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9753
9754       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9755
9756       SMDS_MeshVolume * NewVolume = 0;
9757       switch ( type )
9758       {
9759       case SMDSEntity_Tetra:
9760         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9761         break;
9762       case SMDSEntity_Hexa:
9763         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9764                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9765         break;
9766       case SMDSEntity_Pyramid:
9767         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9768                                       nodes[3], nodes[4], id, theForce3d);
9769         break;
9770       case SMDSEntity_Penta:
9771         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9772                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9773         break;
9774       case SMDSEntity_Hexagonal_Prism:
9775       default:
9776         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9777       }
9778       ReplaceElemInGroups(volume, NewVolume, meshDS);
9779     }
9780   }
9781
9782   if ( !theForce3d )
9783   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9784     aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9785     aHelper.FixQuadraticElements(myError);
9786   }
9787 }
9788
9789 //================================================================================
9790 /*!
9791  * \brief Makes given elements quadratic
9792  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9793  *  \param theElements - elements to make quadratic
9794  */
9795 //================================================================================
9796
9797 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9798                                           TIDSortedElemSet& theElements)
9799 {
9800   if ( theElements.empty() ) return;
9801
9802   // we believe that all theElements are of the same type
9803   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9804
9805   // get all nodes shared by theElements
9806   TIDSortedNodeSet allNodes;
9807   TIDSortedElemSet::iterator eIt = theElements.begin();
9808   for ( ; eIt != theElements.end(); ++eIt )
9809     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9810
9811   // complete theElements with elements of lower dim whose all nodes are in allNodes
9812
9813   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9814   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9815   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9816   for ( ; nIt != allNodes.end(); ++nIt )
9817   {
9818     const SMDS_MeshNode* n = *nIt;
9819     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9820     while ( invIt->more() )
9821     {
9822       const SMDS_MeshElement* e = invIt->next();
9823       if ( e->IsQuadratic() )
9824       {
9825         quadAdjacentElems[ e->GetType() ].insert( e );
9826         continue;
9827       }
9828       if ( e->GetType() >= elemType )
9829       {
9830         continue; // same type of more complex linear element
9831       }
9832
9833       if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9834         continue; // e is already checked
9835
9836       // check nodes
9837       bool allIn = true;
9838       SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9839       while ( nodeIt->more() && allIn )
9840         allIn = allNodes.count( cast2Node( nodeIt->next() ));
9841       if ( allIn )
9842         theElements.insert(e );
9843     }
9844   }
9845
9846   SMESH_MesherHelper helper(*myMesh);
9847   helper.SetIsQuadratic( true );
9848
9849   // add links of quadratic adjacent elements to the helper
9850
9851   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9852     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9853           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9854     {
9855       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9856     }
9857   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9858     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9859           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9860     {
9861       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9862     }
9863   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9864     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9865           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9866     {
9867       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9868     }
9869
9870   // make quadratic elements instead of linear ones
9871
9872   SMESHDS_Mesh* meshDS = GetMeshDS();
9873   SMESHDS_SubMesh* smDS = 0;
9874   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9875   {
9876     const SMDS_MeshElement* elem = *eIt;
9877     if( elem->IsQuadratic() || elem->NbNodes() < 2 || elem->IsPoly() )
9878       continue;
9879
9880     const int id                   = elem->GetID();
9881     const SMDSAbs_ElementType type = elem->GetType();
9882     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9883
9884     if ( !smDS || !smDS->Contains( elem ))
9885       smDS = meshDS->MeshElements( elem->getshapeId() );
9886     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9887
9888     SMDS_MeshElement * newElem = 0;
9889     switch( nodes.size() )
9890     {
9891     case 4: // cases for most frequently used element types go first (for optimization)
9892       if ( type == SMDSAbs_Volume )
9893         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9894       else
9895         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9896       break;
9897     case 8:
9898       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9899                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9900       break;
9901     case 3:
9902       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9903       break;
9904     case 2:
9905       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9906       break;
9907     case 5:
9908       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9909                                  nodes[4], id, theForce3d);
9910       break;
9911     case 6:
9912       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9913                                  nodes[4], nodes[5], id, theForce3d);
9914       break;
9915     default:;
9916     }
9917     ReplaceElemInGroups( elem, newElem, meshDS);
9918     if( newElem && smDS )
9919       smDS->AddElement( newElem );
9920   }
9921
9922   if ( !theForce3d  && !getenv("NO_FixQuadraticElements"))
9923   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9924     helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9925     helper.FixQuadraticElements( myError );
9926   }
9927 }
9928
9929 //=======================================================================
9930 /*!
9931  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9932  * \return int - nb of checked elements
9933  */
9934 //=======================================================================
9935
9936 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9937                                      SMDS_ElemIteratorPtr theItr,
9938                                      const int            theShapeID)
9939 {
9940   int nbElem = 0;
9941   SMESHDS_Mesh* meshDS = GetMeshDS();
9942
9943   while( theItr->more() )
9944   {
9945     const SMDS_MeshElement* elem = theItr->next();
9946     nbElem++;
9947     if( elem && elem->IsQuadratic())
9948     {
9949       int id                    = elem->GetID();
9950       int nbCornerNodes         = elem->NbCornerNodes();
9951       SMDSAbs_ElementType aType = elem->GetType();
9952
9953       vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9954
9955       //remove a quadratic element
9956       if ( !theSm || !theSm->Contains( elem ))
9957         theSm = meshDS->MeshElements( elem->getshapeId() );
9958       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9959
9960       // remove medium nodes
9961       for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9962         if ( nodes[i]->NbInverseElements() == 0 )
9963           meshDS->RemoveFreeNode( nodes[i], theSm );
9964
9965       // add a linear element
9966       nodes.resize( nbCornerNodes );
9967       SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9968       ReplaceElemInGroups(elem, newElem, meshDS);
9969       if( theSm && newElem )
9970         theSm->AddElement( newElem );
9971     }
9972   }
9973   return nbElem;
9974 }
9975
9976 //=======================================================================
9977 //function : ConvertFromQuadratic
9978 //purpose  :
9979 //=======================================================================
9980
9981 bool SMESH_MeshEditor::ConvertFromQuadratic()
9982 {
9983   int nbCheckedElems = 0;
9984   if ( myMesh->HasShapeToMesh() )
9985   {
9986     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9987     {
9988       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9989       while ( smIt->more() ) {
9990         SMESH_subMesh* sm = smIt->next();
9991         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9992           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9993       }
9994     }
9995   }
9996
9997   int totalNbElems =
9998     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9999   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
10000   {
10001     SMESHDS_SubMesh *aSM = 0;
10002     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
10003   }
10004
10005   return true;
10006 }
10007
10008 namespace
10009 {
10010   //================================================================================
10011   /*!
10012    * \brief Return true if all medium nodes of the element are in the node set
10013    */
10014   //================================================================================
10015
10016   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
10017   {
10018     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
10019       if ( !nodeSet.count( elem->GetNode(i) ))
10020         return false;
10021     return true;
10022   }
10023 }
10024
10025 //================================================================================
10026 /*!
10027  * \brief Makes given elements linear
10028  */
10029 //================================================================================
10030
10031 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
10032 {
10033   if ( theElements.empty() ) return;
10034
10035   // collect IDs of medium nodes of theElements; some of these nodes will be removed
10036   set<int> mediumNodeIDs;
10037   TIDSortedElemSet::iterator eIt = theElements.begin();
10038   for ( ; eIt != theElements.end(); ++eIt )
10039   {
10040     const SMDS_MeshElement* e = *eIt;
10041     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
10042       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
10043   }
10044
10045   // replace given elements by linear ones
10046   typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
10047   SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
10048   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10049
10050   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
10051   // except those elements sharing medium nodes of quadratic element whose medium nodes
10052   // are not all in mediumNodeIDs
10053
10054   // get remaining medium nodes
10055   TIDSortedNodeSet mediumNodes;
10056   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
10057   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
10058     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
10059       mediumNodes.insert( mediumNodes.end(), n );
10060
10061   // find more quadratic elements to convert
10062   TIDSortedElemSet moreElemsToConvert;
10063   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
10064   for ( ; nIt != mediumNodes.end(); ++nIt )
10065   {
10066     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
10067     while ( invIt->more() )
10068     {
10069       const SMDS_MeshElement* e = invIt->next();
10070       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
10071       {
10072         // find a more complex element including e and
10073         // whose medium nodes are not in mediumNodes
10074         bool complexFound = false;
10075         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
10076         {
10077           SMDS_ElemIteratorPtr invIt2 =
10078             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
10079           while ( invIt2->more() )
10080           {
10081             const SMDS_MeshElement* eComplex = invIt2->next();
10082             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
10083             {
10084               int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
10085               if ( nbCommonNodes == e->NbNodes())
10086               {
10087                 complexFound = true;
10088                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
10089                 break;
10090               }
10091             }
10092           }
10093         }
10094         if ( !complexFound )
10095           moreElemsToConvert.insert( e );
10096       }
10097     }
10098   }
10099   elemIt = SMDS_ElemIteratorPtr
10100     (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
10101   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10102 }
10103
10104 //=======================================================================
10105 //function : SewSideElements
10106 //purpose  :
10107 //=======================================================================
10108
10109 SMESH_MeshEditor::Sew_Error
10110 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
10111                                    TIDSortedElemSet&    theSide2,
10112                                    const SMDS_MeshNode* theFirstNode1,
10113                                    const SMDS_MeshNode* theFirstNode2,
10114                                    const SMDS_MeshNode* theSecondNode1,
10115                                    const SMDS_MeshNode* theSecondNode2)
10116 {
10117   myLastCreatedElems.Clear();
10118   myLastCreatedNodes.Clear();
10119
10120   MESSAGE ("::::SewSideElements()");
10121   if ( theSide1.size() != theSide2.size() )
10122     return SEW_DIFF_NB_OF_ELEMENTS;
10123
10124   Sew_Error aResult = SEW_OK;
10125   // Algo:
10126   // 1. Build set of faces representing each side
10127   // 2. Find which nodes of the side 1 to merge with ones on the side 2
10128   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10129
10130   // =======================================================================
10131   // 1. Build set of faces representing each side:
10132   // =======================================================================
10133   // a. build set of nodes belonging to faces
10134   // b. complete set of faces: find missing faces whose nodes are in set of nodes
10135   // c. create temporary faces representing side of volumes if correspondent
10136   //    face does not exist
10137
10138   SMESHDS_Mesh* aMesh = GetMeshDS();
10139   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
10140   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
10141   TIDSortedElemSet             faceSet1, faceSet2;
10142   set<const SMDS_MeshElement*> volSet1,  volSet2;
10143   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
10144   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
10145   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
10146   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
10147   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
10148   int iSide, iFace, iNode;
10149
10150   list<const SMDS_MeshElement* > tempFaceList;
10151   for ( iSide = 0; iSide < 2; iSide++ ) {
10152     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
10153     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
10154     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
10155     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
10156     set<const SMDS_MeshElement*>::iterator vIt;
10157     TIDSortedElemSet::iterator eIt;
10158     set<const SMDS_MeshNode*>::iterator    nIt;
10159
10160     // check that given nodes belong to given elements
10161     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
10162     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
10163     int firstIndex = -1, secondIndex = -1;
10164     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10165       const SMDS_MeshElement* elem = *eIt;
10166       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
10167       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
10168       if ( firstIndex > -1 && secondIndex > -1 ) break;
10169     }
10170     if ( firstIndex < 0 || secondIndex < 0 ) {
10171       // we can simply return until temporary faces created
10172       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
10173     }
10174
10175     // -----------------------------------------------------------
10176     // 1a. Collect nodes of existing faces
10177     //     and build set of face nodes in order to detect missing
10178     //     faces corresponding to sides of volumes
10179     // -----------------------------------------------------------
10180
10181     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
10182
10183     // loop on the given element of a side
10184     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10185       //const SMDS_MeshElement* elem = *eIt;
10186       const SMDS_MeshElement* elem = *eIt;
10187       if ( elem->GetType() == SMDSAbs_Face ) {
10188         faceSet->insert( elem );
10189         set <const SMDS_MeshNode*> faceNodeSet;
10190         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10191         while ( nodeIt->more() ) {
10192           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10193           nodeSet->insert( n );
10194           faceNodeSet.insert( n );
10195         }
10196         setOfFaceNodeSet.insert( faceNodeSet );
10197       }
10198       else if ( elem->GetType() == SMDSAbs_Volume )
10199         volSet->insert( elem );
10200     }
10201     // ------------------------------------------------------------------------------
10202     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10203     // ------------------------------------------------------------------------------
10204
10205     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10206       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10207       while ( fIt->more() ) { // loop on faces sharing a node
10208         const SMDS_MeshElement* f = fIt->next();
10209         if ( faceSet->find( f ) == faceSet->end() ) {
10210           // check if all nodes are in nodeSet and
10211           // complete setOfFaceNodeSet if they are
10212           set <const SMDS_MeshNode*> faceNodeSet;
10213           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10214           bool allInSet = true;
10215           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10216             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10217             if ( nodeSet->find( n ) == nodeSet->end() )
10218               allInSet = false;
10219             else
10220               faceNodeSet.insert( n );
10221           }
10222           if ( allInSet ) {
10223             faceSet->insert( f );
10224             setOfFaceNodeSet.insert( faceNodeSet );
10225           }
10226         }
10227       }
10228     }
10229
10230     // -------------------------------------------------------------------------
10231     // 1c. Create temporary faces representing sides of volumes if correspondent
10232     //     face does not exist
10233     // -------------------------------------------------------------------------
10234
10235     if ( !volSet->empty() ) {
10236       //int nodeSetSize = nodeSet->size();
10237
10238       // loop on given volumes
10239       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10240         SMDS_VolumeTool vol (*vIt);
10241         // loop on volume faces: find free faces
10242         // --------------------------------------
10243         list<const SMDS_MeshElement* > freeFaceList;
10244         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10245           if ( !vol.IsFreeFace( iFace ))
10246             continue;
10247           // check if there is already a face with same nodes in a face set
10248           const SMDS_MeshElement* aFreeFace = 0;
10249           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10250           int nbNodes = vol.NbFaceNodes( iFace );
10251           set <const SMDS_MeshNode*> faceNodeSet;
10252           vol.GetFaceNodes( iFace, faceNodeSet );
10253           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10254           if ( isNewFace ) {
10255             // no such a face is given but it still can exist, check it
10256             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10257             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10258           }
10259           if ( !aFreeFace ) {
10260             // create a temporary face
10261             if ( nbNodes == 3 ) {
10262               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10263               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10264             }
10265             else if ( nbNodes == 4 ) {
10266               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10267               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10268             }
10269             else {
10270               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10271               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10272               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10273             }
10274             if ( aFreeFace )
10275               tempFaceList.push_back( aFreeFace );
10276           }
10277
10278           if ( aFreeFace )
10279             freeFaceList.push_back( aFreeFace );
10280
10281         } // loop on faces of a volume
10282
10283         // choose one of several free faces of a volume
10284         // --------------------------------------------
10285         if ( freeFaceList.size() > 1 ) {
10286           // choose a face having max nb of nodes shared by other elems of a side
10287           int maxNbNodes = -1;
10288           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10289           while ( fIt != freeFaceList.end() ) { // loop on free faces
10290             int nbSharedNodes = 0;
10291             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10292             while ( nodeIt->more() ) { // loop on free face nodes
10293               const SMDS_MeshNode* n =
10294                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10295               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10296               while ( invElemIt->more() ) {
10297                 const SMDS_MeshElement* e = invElemIt->next();
10298                 nbSharedNodes += faceSet->count( e );
10299                 nbSharedNodes += elemSet->count( e );
10300               }
10301             }
10302             if ( nbSharedNodes > maxNbNodes ) {
10303               maxNbNodes = nbSharedNodes;
10304               freeFaceList.erase( freeFaceList.begin(), fIt++ );
10305             }
10306             else if ( nbSharedNodes == maxNbNodes ) {
10307               fIt++;
10308             }
10309             else {
10310               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10311             }
10312           }
10313           if ( freeFaceList.size() > 1 )
10314           {
10315             // could not choose one face, use another way
10316             // choose a face most close to the bary center of the opposite side
10317             gp_XYZ aBC( 0., 0., 0. );
10318             set <const SMDS_MeshNode*> addedNodes;
10319             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10320             eIt = elemSet2->begin();
10321             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10322               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10323               while ( nodeIt->more() ) { // loop on free face nodes
10324                 const SMDS_MeshNode* n =
10325                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10326                 if ( addedNodes.insert( n ).second )
10327                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10328               }
10329             }
10330             aBC /= addedNodes.size();
10331             double minDist = DBL_MAX;
10332             fIt = freeFaceList.begin();
10333             while ( fIt != freeFaceList.end() ) { // loop on free faces
10334               double dist = 0;
10335               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10336               while ( nodeIt->more() ) { // loop on free face nodes
10337                 const SMDS_MeshNode* n =
10338                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10339                 gp_XYZ p( n->X(),n->Y(),n->Z() );
10340                 dist += ( aBC - p ).SquareModulus();
10341               }
10342               if ( dist < minDist ) {
10343                 minDist = dist;
10344                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10345               }
10346               else
10347                 fIt = freeFaceList.erase( fIt++ );
10348             }
10349           }
10350         } // choose one of several free faces of a volume
10351
10352         if ( freeFaceList.size() == 1 ) {
10353           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10354           faceSet->insert( aFreeFace );
10355           // complete a node set with nodes of a found free face
10356           //           for ( iNode = 0; iNode < ; iNode++ )
10357           //             nodeSet->insert( fNodes[ iNode ] );
10358         }
10359
10360       } // loop on volumes of a side
10361
10362       //       // complete a set of faces if new nodes in a nodeSet appeared
10363       //       // ----------------------------------------------------------
10364       //       if ( nodeSetSize != nodeSet->size() ) {
10365       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10366       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10367       //           while ( fIt->more() ) { // loop on faces sharing a node
10368       //             const SMDS_MeshElement* f = fIt->next();
10369       //             if ( faceSet->find( f ) == faceSet->end() ) {
10370       //               // check if all nodes are in nodeSet and
10371       //               // complete setOfFaceNodeSet if they are
10372       //               set <const SMDS_MeshNode*> faceNodeSet;
10373       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10374       //               bool allInSet = true;
10375       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10376       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10377       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10378       //                   allInSet = false;
10379       //                 else
10380       //                   faceNodeSet.insert( n );
10381       //               }
10382       //               if ( allInSet ) {
10383       //                 faceSet->insert( f );
10384       //                 setOfFaceNodeSet.insert( faceNodeSet );
10385       //               }
10386       //             }
10387       //           }
10388       //         }
10389       //       }
10390     } // Create temporary faces, if there are volumes given
10391   } // loop on sides
10392
10393   if ( faceSet1.size() != faceSet2.size() ) {
10394     // delete temporary faces: they are in reverseElements of actual nodes
10395 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10396 //    while ( tmpFaceIt->more() )
10397 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10398 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10399 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10400 //      aMesh->RemoveElement(*tmpFaceIt);
10401     MESSAGE("Diff nb of faces");
10402     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10403   }
10404
10405   // ============================================================
10406   // 2. Find nodes to merge:
10407   //              bind a node to remove to a node to put instead
10408   // ============================================================
10409
10410   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10411   if ( theFirstNode1 != theFirstNode2 )
10412     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10413   if ( theSecondNode1 != theSecondNode2 )
10414     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10415
10416   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10417   set< long > linkIdSet; // links to process
10418   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10419
10420   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10421   list< NLink > linkList[2];
10422   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10423   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10424   // loop on links in linkList; find faces by links and append links
10425   // of the found faces to linkList
10426   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10427   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10428   {
10429     NLink link[] = { *linkIt[0], *linkIt[1] };
10430     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10431     if ( !linkIdSet.count( linkID ) )
10432       continue;
10433
10434     // by links, find faces in the face sets,
10435     // and find indices of link nodes in the found faces;
10436     // in a face set, there is only one or no face sharing a link
10437     // ---------------------------------------------------------------
10438
10439     const SMDS_MeshElement* face[] = { 0, 0 };
10440     vector<const SMDS_MeshNode*> fnodes[2];
10441     int iLinkNode[2][2];
10442     TIDSortedElemSet avoidSet;
10443     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10444       const SMDS_MeshNode* n1 = link[iSide].first;
10445       const SMDS_MeshNode* n2 = link[iSide].second;
10446       //cout << "Side " << iSide << " ";
10447       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10448       // find a face by two link nodes
10449       face[ iSide ] = FindFaceInSet( n1, n2, *faceSetPtr[ iSide ], avoidSet,
10450                                      &iLinkNode[iSide][0], &iLinkNode[iSide][1] );
10451       if ( face[ iSide ])
10452       {
10453         //cout << " F " << face[ iSide]->GetID() <<endl;
10454         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10455         // put face nodes to fnodes
10456         if ( face[ iSide ]->IsQuadratic() )
10457         {
10458           // use interlaced nodes iterator
10459           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10460           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10461           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10462           while ( nIter->more() )
10463             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10464         }
10465         else
10466         {
10467           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10468                                   face[ iSide ]->end_nodes() );
10469         }
10470         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10471       }
10472     }
10473
10474     // check similarity of elements of the sides
10475     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10476       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10477       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10478         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10479       }
10480       else {
10481         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10482       }
10483       break; // do not return because it's necessary to remove tmp faces
10484     }
10485
10486     // set nodes to merge
10487     // -------------------
10488
10489     if ( face[0] && face[1] )  {
10490       const int nbNodes = face[0]->NbNodes();
10491       if ( nbNodes != face[1]->NbNodes() ) {
10492         MESSAGE("Diff nb of face nodes");
10493         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10494         break; // do not return because it s necessary to remove tmp faces
10495       }
10496       bool reverse[] = { false, false }; // order of nodes in the link
10497       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10498         // analyse link orientation in faces
10499         int i1 = iLinkNode[ iSide ][ 0 ];
10500         int i2 = iLinkNode[ iSide ][ 1 ];
10501         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10502       }
10503       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10504       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10505       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10506       {
10507         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10508                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10509       }
10510
10511       // add other links of the faces to linkList
10512       // -----------------------------------------
10513
10514       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10515         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10516         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10517         if ( !iter_isnew.second ) { // already in a set: no need to process
10518           linkIdSet.erase( iter_isnew.first );
10519         }
10520         else // new in set == encountered for the first time: add
10521         {
10522           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10523           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10524           linkList[0].push_back ( NLink( n1, n2 ));
10525           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10526         }
10527       }
10528     } // 2 faces found
10529
10530     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10531       break;
10532
10533   } // loop on link lists
10534
10535   if ( aResult == SEW_OK &&
10536        ( //linkIt[0] != linkList[0].end() ||
10537          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10538     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10539              " " << (faceSetPtr[1]->empty()));
10540     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10541   }
10542
10543   // ====================================================================
10544   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10545   // ====================================================================
10546
10547   // delete temporary faces
10548 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10549 //  while ( tmpFaceIt->more() )
10550 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10551   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10552   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10553     aMesh->RemoveElement(*tmpFaceIt);
10554
10555   if ( aResult != SEW_OK)
10556     return aResult;
10557
10558   list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10559   // loop on nodes replacement map
10560   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10561   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10562     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10563       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10564       nodeIDsToRemove.push_back( nToRemove->GetID() );
10565       // loop on elements sharing nToRemove
10566       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10567       while ( invElemIt->more() ) {
10568         const SMDS_MeshElement* e = invElemIt->next();
10569         // get a new suite of nodes: make replacement
10570         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10571         vector< const SMDS_MeshNode*> nodes( nbNodes );
10572         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10573         while ( nIt->more() ) {
10574           const SMDS_MeshNode* n =
10575             static_cast<const SMDS_MeshNode*>( nIt->next() );
10576           nnIt = nReplaceMap.find( n );
10577           if ( nnIt != nReplaceMap.end() ) {
10578             nbReplaced++;
10579             n = (*nnIt).second;
10580           }
10581           nodes[ i++ ] = n;
10582         }
10583         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10584         //         elemIDsToRemove.push_back( e->GetID() );
10585         //       else
10586         if ( nbReplaced )
10587           {
10588             SMDSAbs_ElementType etyp = e->GetType();
10589             SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10590             if (newElem)
10591               {
10592                 myLastCreatedElems.Append(newElem);
10593                 AddToSameGroups(newElem, e, aMesh);
10594                 int aShapeId = e->getshapeId();
10595                 if ( aShapeId )
10596                   {
10597                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
10598                   }
10599               }
10600             aMesh->RemoveElement(e);
10601           }
10602       }
10603     }
10604
10605   Remove( nodeIDsToRemove, true );
10606
10607   return aResult;
10608 }
10609
10610 //================================================================================
10611 /*!
10612  * \brief Find corresponding nodes in two sets of faces
10613  * \param theSide1 - first face set
10614  * \param theSide2 - second first face
10615  * \param theFirstNode1 - a boundary node of set 1
10616  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10617  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10618  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10619  * \param nReplaceMap - output map of corresponding nodes
10620  * \return bool  - is a success or not
10621  */
10622 //================================================================================
10623
10624 #ifdef _DEBUG_
10625 //#define DEBUG_MATCHING_NODES
10626 #endif
10627
10628 SMESH_MeshEditor::Sew_Error
10629 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10630                                     set<const SMDS_MeshElement*>& theSide2,
10631                                     const SMDS_MeshNode*          theFirstNode1,
10632                                     const SMDS_MeshNode*          theFirstNode2,
10633                                     const SMDS_MeshNode*          theSecondNode1,
10634                                     const SMDS_MeshNode*          theSecondNode2,
10635                                     TNodeNodeMap &                nReplaceMap)
10636 {
10637   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10638
10639   nReplaceMap.clear();
10640   if ( theFirstNode1 != theFirstNode2 )
10641     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10642   if ( theSecondNode1 != theSecondNode2 )
10643     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10644
10645   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10646   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10647
10648   list< NLink > linkList[2];
10649   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10650   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10651
10652   // loop on links in linkList; find faces by links and append links
10653   // of the found faces to linkList
10654   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10655   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10656     NLink link[] = { *linkIt[0], *linkIt[1] };
10657     if ( linkSet.find( link[0] ) == linkSet.end() )
10658       continue;
10659
10660     // by links, find faces in the face sets,
10661     // and find indices of link nodes in the found faces;
10662     // in a face set, there is only one or no face sharing a link
10663     // ---------------------------------------------------------------
10664
10665     const SMDS_MeshElement* face[] = { 0, 0 };
10666     list<const SMDS_MeshNode*> notLinkNodes[2];
10667     //bool reverse[] = { false, false }; // order of notLinkNodes
10668     int nbNodes[2];
10669     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10670     {
10671       const SMDS_MeshNode* n1 = link[iSide].first;
10672       const SMDS_MeshNode* n2 = link[iSide].second;
10673       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10674       set< const SMDS_MeshElement* > facesOfNode1;
10675       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10676       {
10677         // during a loop of the first node, we find all faces around n1,
10678         // during a loop of the second node, we find one face sharing both n1 and n2
10679         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10680         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10681         while ( fIt->more() ) { // loop on faces sharing a node
10682           const SMDS_MeshElement* f = fIt->next();
10683           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10684               ! facesOfNode1.insert( f ).second ) // f encounters twice
10685           {
10686             if ( face[ iSide ] ) {
10687               MESSAGE( "2 faces per link " );
10688               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10689             }
10690             face[ iSide ] = f;
10691             faceSet->erase( f );
10692
10693             // get not link nodes
10694             int nbN = f->NbNodes();
10695             if ( f->IsQuadratic() )
10696               nbN /= 2;
10697             nbNodes[ iSide ] = nbN;
10698             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10699             int i1 = f->GetNodeIndex( n1 );
10700             int i2 = f->GetNodeIndex( n2 );
10701             int iEnd = nbN, iBeg = -1, iDelta = 1;
10702             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10703             if ( reverse ) {
10704               std::swap( iEnd, iBeg ); iDelta = -1;
10705             }
10706             int i = i2;
10707             while ( true ) {
10708               i += iDelta;
10709               if ( i == iEnd ) i = iBeg + iDelta;
10710               if ( i == i1 ) break;
10711               nodes.push_back ( f->GetNode( i ) );
10712             }
10713           }
10714         }
10715       }
10716     }
10717     // check similarity of elements of the sides
10718     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10719       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10720       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10721         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10722       }
10723       else {
10724         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10725       }
10726     }
10727
10728     // set nodes to merge
10729     // -------------------
10730
10731     if ( face[0] && face[1] )  {
10732       if ( nbNodes[0] != nbNodes[1] ) {
10733         MESSAGE("Diff nb of face nodes");
10734         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10735       }
10736 #ifdef DEBUG_MATCHING_NODES
10737       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10738                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10739                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10740 #endif
10741       int nbN = nbNodes[0];
10742       {
10743         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10744         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10745         for ( int i = 0 ; i < nbN - 2; ++i ) {
10746 #ifdef DEBUG_MATCHING_NODES
10747           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10748 #endif
10749           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10750         }
10751       }
10752
10753       // add other links of the face 1 to linkList
10754       // -----------------------------------------
10755
10756       const SMDS_MeshElement* f0 = face[0];
10757       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10758       for ( int i = 0; i < nbN; i++ )
10759       {
10760         const SMDS_MeshNode* n2 = f0->GetNode( i );
10761         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10762           linkSet.insert( SMESH_TLink( n1, n2 ));
10763         if ( !iter_isnew.second ) { // already in a set: no need to process
10764           linkSet.erase( iter_isnew.first );
10765         }
10766         else // new in set == encountered for the first time: add
10767         {
10768 #ifdef DEBUG_MATCHING_NODES
10769           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10770                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10771 #endif
10772           linkList[0].push_back ( NLink( n1, n2 ));
10773           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10774         }
10775         n1 = n2;
10776       }
10777     } // 2 faces found
10778   } // loop on link lists
10779
10780   return SEW_OK;
10781 }
10782
10783 //================================================================================
10784 /*!
10785   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10786   \param theElems - the list of elements (edges or faces) to be replicated
10787   The nodes for duplication could be found from these elements
10788   \param theNodesNot - list of nodes to NOT replicate
10789   \param theAffectedElems - the list of elements (cells and edges) to which the
10790   replicated nodes should be associated to.
10791   \return TRUE if operation has been completed successfully, FALSE otherwise
10792 */
10793 //================================================================================
10794
10795 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10796                                     const TIDSortedElemSet& theNodesNot,
10797                                     const TIDSortedElemSet& theAffectedElems )
10798 {
10799   myLastCreatedElems.Clear();
10800   myLastCreatedNodes.Clear();
10801
10802   if ( theElems.size() == 0 )
10803     return false;
10804
10805   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10806   if ( !aMeshDS )
10807     return false;
10808
10809   bool res = false;
10810   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10811   // duplicate elements and nodes
10812   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10813   // replce nodes by duplications
10814   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10815   return res;
10816 }
10817
10818 //================================================================================
10819 /*!
10820   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10821   \param theMeshDS - mesh instance
10822   \param theElems - the elements replicated or modified (nodes should be changed)
10823   \param theNodesNot - nodes to NOT replicate
10824   \param theNodeNodeMap - relation of old node to new created node
10825   \param theIsDoubleElem - flag os to replicate element or modify
10826   \return TRUE if operation has been completed successfully, FALSE otherwise
10827 */
10828 //================================================================================
10829
10830 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh*     theMeshDS,
10831                                     const TIDSortedElemSet& theElems,
10832                                     const TIDSortedElemSet& theNodesNot,
10833                                     std::map< const SMDS_MeshNode*,
10834                                     const SMDS_MeshNode* >& theNodeNodeMap,
10835                                     const bool theIsDoubleElem )
10836 {
10837   MESSAGE("doubleNodes");
10838   // iterate on through element and duplicate them (by nodes duplication)
10839   bool res = false;
10840   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10841   for ( ;  elemItr != theElems.end(); ++elemItr )
10842   {
10843     const SMDS_MeshElement* anElem = *elemItr;
10844     if (!anElem)
10845       continue;
10846
10847     bool isDuplicate = false;
10848     // duplicate nodes to duplicate element
10849     std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10850     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10851     int ind = 0;
10852     while ( anIter->more() )
10853     {
10854
10855       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10856       SMDS_MeshNode* aNewNode = aCurrNode;
10857       if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10858         aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10859       else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10860       {
10861         // duplicate node
10862         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10863         theNodeNodeMap[ aCurrNode ] = aNewNode;
10864         myLastCreatedNodes.Append( aNewNode );
10865       }
10866       isDuplicate |= (aCurrNode != aNewNode);
10867       newNodes[ ind++ ] = aNewNode;
10868     }
10869     if ( !isDuplicate )
10870       continue;
10871
10872     if ( theIsDoubleElem )
10873       AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10874     else
10875       {
10876       MESSAGE("ChangeElementNodes");
10877       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10878       }
10879     res = true;
10880   }
10881   return res;
10882 }
10883
10884 //================================================================================
10885 /*!
10886   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10887   \param theNodes - identifiers of nodes to be doubled
10888   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10889          nodes. If list of element identifiers is empty then nodes are doubled but
10890          they not assigned to elements
10891   \return TRUE if operation has been completed successfully, FALSE otherwise
10892 */
10893 //================================================================================
10894
10895 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10896                                     const std::list< int >& theListOfModifiedElems )
10897 {
10898   MESSAGE("DoubleNodes");
10899   myLastCreatedElems.Clear();
10900   myLastCreatedNodes.Clear();
10901
10902   if ( theListOfNodes.size() == 0 )
10903     return false;
10904
10905   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10906   if ( !aMeshDS )
10907     return false;
10908
10909   // iterate through nodes and duplicate them
10910
10911   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10912
10913   std::list< int >::const_iterator aNodeIter;
10914   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10915   {
10916     int aCurr = *aNodeIter;
10917     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10918     if ( !aNode )
10919       continue;
10920
10921     // duplicate node
10922
10923     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10924     if ( aNewNode )
10925     {
10926       anOldNodeToNewNode[ aNode ] = aNewNode;
10927       myLastCreatedNodes.Append( aNewNode );
10928     }
10929   }
10930
10931   // Create map of new nodes for modified elements
10932
10933   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10934
10935   std::list< int >::const_iterator anElemIter;
10936   for ( anElemIter = theListOfModifiedElems.begin();
10937         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10938   {
10939     int aCurr = *anElemIter;
10940     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10941     if ( !anElem )
10942       continue;
10943
10944     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10945
10946     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10947     int ind = 0;
10948     while ( anIter->more() )
10949     {
10950       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10951       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10952       {
10953         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10954         aNodeArr[ ind++ ] = aNewNode;
10955       }
10956       else
10957         aNodeArr[ ind++ ] = aCurrNode;
10958     }
10959     anElemToNodes[ anElem ] = aNodeArr;
10960   }
10961
10962   // Change nodes of elements
10963
10964   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10965     anElemToNodesIter = anElemToNodes.begin();
10966   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10967   {
10968     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10969     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10970     if ( anElem )
10971       {
10972       MESSAGE("ChangeElementNodes");
10973       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10974       }
10975   }
10976
10977   return true;
10978 }
10979
10980 namespace {
10981
10982   //================================================================================
10983   /*!
10984   \brief Check if element located inside shape
10985   \return TRUE if IN or ON shape, FALSE otherwise
10986   */
10987   //================================================================================
10988
10989   template<class Classifier>
10990   bool isInside(const SMDS_MeshElement* theElem,
10991                 Classifier&             theClassifier,
10992                 const double            theTol)
10993   {
10994     gp_XYZ centerXYZ (0, 0, 0);
10995     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10996     while (aNodeItr->more())
10997       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10998
10999     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11000     theClassifier.Perform(aPnt, theTol);
11001     TopAbs_State aState = theClassifier.State();
11002     return (aState == TopAbs_IN || aState == TopAbs_ON );
11003   }
11004
11005   //================================================================================
11006   /*!
11007    * \brief Classifier of the 3D point on the TopoDS_Face
11008    *        with interaface suitable for isInside()
11009    */
11010   //================================================================================
11011
11012   struct _FaceClassifier
11013   {
11014     Extrema_ExtPS       _extremum;
11015     BRepAdaptor_Surface _surface;
11016     TopAbs_State        _state;
11017
11018     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11019     {
11020       _extremum.Initialize( _surface,
11021                             _surface.FirstUParameter(), _surface.LastUParameter(),
11022                             _surface.FirstVParameter(), _surface.LastVParameter(),
11023                             _surface.Tolerance(), _surface.Tolerance() );
11024     }
11025     void Perform(const gp_Pnt& aPnt, double theTol)
11026     {
11027       _state = TopAbs_OUT;
11028       _extremum.Perform(aPnt);
11029       if ( _extremum.IsDone() )
11030         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11031 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
11032           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11033 #else
11034           _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11035 #endif
11036     }
11037     TopAbs_State State() const
11038     {
11039       return _state;
11040     }
11041   };
11042 }
11043
11044 //================================================================================
11045 /*!
11046   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed.
11047   This method is the first step of DoubleNodeElemGroupsInRegion.
11048   \param theElems - list of groups of elements (edges or faces) to be replicated
11049   \param theNodesNot - list of groups of nodes not to replicated
11050   \param theShape - shape to detect affected elements (element which geometric center
11051          located on or inside shape).
11052          The replicated nodes should be associated to affected elements.
11053   \return groups of affected elements
11054   \sa DoubleNodeElemGroupsInRegion()
11055  */
11056 //================================================================================
11057
11058 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11059                                                    const TIDSortedElemSet& theNodesNot,
11060                                                    const TopoDS_Shape&     theShape,
11061                                                    TIDSortedElemSet&       theAffectedElems)
11062 {
11063   if ( theShape.IsNull() )
11064     return false;
11065
11066   const double aTol = Precision::Confusion();
11067   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11068   auto_ptr<_FaceClassifier>              aFaceClassifier;
11069   if ( theShape.ShapeType() == TopAbs_SOLID )
11070   {
11071     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11072     bsc3d->PerformInfinitePoint(aTol);
11073   }
11074   else if (theShape.ShapeType() == TopAbs_FACE )
11075   {
11076     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11077   }
11078
11079   // iterates on indicated elements and get elements by back references from their nodes
11080   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11081   for ( ;  elemItr != theElems.end(); ++elemItr )
11082   {
11083     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11084     if (!anElem)
11085       continue;
11086
11087     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11088     while ( nodeItr->more() )
11089     {
11090       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11091       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11092         continue;
11093       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11094       while ( backElemItr->more() )
11095       {
11096         const SMDS_MeshElement* curElem = backElemItr->next();
11097         if ( curElem && theElems.find(curElem) == theElems.end() &&
11098              ( bsc3d.get() ?
11099                isInside( curElem, *bsc3d, aTol ) :
11100                isInside( curElem, *aFaceClassifier, aTol )))
11101           theAffectedElems.insert( curElem );
11102       }
11103     }
11104   }
11105   return true;
11106 }
11107
11108 //================================================================================
11109 /*!
11110   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11111   \param theElems - group of of elements (edges or faces) to be replicated
11112   \param theNodesNot - group of nodes not to replicate
11113   \param theShape - shape to detect affected elements (element which geometric center
11114   located on or inside shape).
11115   The replicated nodes should be associated to affected elements.
11116   \return TRUE if operation has been completed successfully, FALSE otherwise
11117 */
11118 //================================================================================
11119
11120 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11121                                             const TIDSortedElemSet& theNodesNot,
11122                                             const TopoDS_Shape&     theShape )
11123 {
11124   if ( theShape.IsNull() )
11125     return false;
11126
11127   const double aTol = Precision::Confusion();
11128   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11129   auto_ptr<_FaceClassifier>              aFaceClassifier;
11130   if ( theShape.ShapeType() == TopAbs_SOLID )
11131   {
11132     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11133     bsc3d->PerformInfinitePoint(aTol);
11134   }
11135   else if (theShape.ShapeType() == TopAbs_FACE )
11136   {
11137     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11138   }
11139
11140   // iterates on indicated elements and get elements by back references from their nodes
11141   TIDSortedElemSet anAffected;
11142   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11143   for ( ;  elemItr != theElems.end(); ++elemItr )
11144   {
11145     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11146     if (!anElem)
11147       continue;
11148
11149     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11150     while ( nodeItr->more() )
11151     {
11152       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11153       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11154         continue;
11155       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11156       while ( backElemItr->more() )
11157       {
11158         const SMDS_MeshElement* curElem = backElemItr->next();
11159         if ( curElem && theElems.find(curElem) == theElems.end() &&
11160              ( bsc3d.get() ?
11161                isInside( curElem, *bsc3d, aTol ) :
11162                isInside( curElem, *aFaceClassifier, aTol )))
11163           anAffected.insert( curElem );
11164       }
11165     }
11166   }
11167   return DoubleNodes( theElems, theNodesNot, anAffected );
11168 }
11169
11170 /*!
11171  *  \brief compute an oriented angle between two planes defined by four points.
11172  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11173  *  @param p0 base of the rotation axe
11174  *  @param p1 extremity of the rotation axe
11175  *  @param g1 belongs to the first plane
11176  *  @param g2 belongs to the second plane
11177  */
11178 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11179 {
11180 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11181 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11182 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11183 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11184   gp_Vec vref(p0, p1);
11185   gp_Vec v1(p0, g1);
11186   gp_Vec v2(p0, g2);
11187   gp_Vec n1 = vref.Crossed(v1);
11188   gp_Vec n2 = vref.Crossed(v2);
11189   return n2.AngleWithRef(n1, vref);
11190 }
11191
11192 /*!
11193  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11194  * The list of groups must describe a partition of the mesh volumes.
11195  * The nodes of the internal faces at the boundaries of the groups are doubled.
11196  * In option, the internal faces are replaced by flat elements.
11197  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11198  * The flat elements are stored in groups of volumes.
11199  * @param theElems - list of groups of volumes, where a group of volume is a set of
11200  * SMDS_MeshElements sorted by Id.
11201  * @param createJointElems - if TRUE, create the elements
11202  * @return TRUE if operation has been completed successfully, FALSE otherwise
11203  */
11204 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11205                                                      bool createJointElems)
11206 {
11207   MESSAGE("----------------------------------------------");
11208   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11209   MESSAGE("----------------------------------------------");
11210
11211   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11212   meshDS->BuildDownWardConnectivity(true);
11213   CHRONO(50);
11214   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11215
11216   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11217   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11218   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11219
11220   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11221   std::map<int,int>celldom; // cell vtkId --> domain
11222   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11223   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11224   faceDomains.clear();
11225   celldom.clear();
11226   cellDomains.clear();
11227   nodeDomains.clear();
11228   std::map<int,int> emptyMap;
11229   std::set<int> emptySet;
11230   emptyMap.clear();
11231
11232   for (int idom = 0; idom < theElems.size(); idom++)
11233     {
11234
11235       // --- build a map (face to duplicate --> volume to modify)
11236       //     with all the faces shared by 2 domains (group of elements)
11237       //     and corresponding volume of this domain, for each shared face.
11238       //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11239
11240       //MESSAGE("Domain " << idom);
11241       const TIDSortedElemSet& domain = theElems[idom];
11242       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11243       for (; elemItr != domain.end(); ++elemItr)
11244         {
11245           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11246           if (!anElem)
11247             continue;
11248           int vtkId = anElem->getVtkId();
11249           //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11250           int neighborsVtkIds[NBMAXNEIGHBORS];
11251           int downIds[NBMAXNEIGHBORS];
11252           unsigned char downTypes[NBMAXNEIGHBORS];
11253           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11254           for (int n = 0; n < nbNeighbors; n++)
11255             {
11256               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11257               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11258               if (! domain.count(elem)) // neighbor is in another domain : face is shared
11259                 {
11260                   DownIdType face(downIds[n], downTypes[n]);
11261                   if (!faceDomains.count(face))
11262                     faceDomains[face] = emptyMap; // create an empty entry for face
11263                   if (!faceDomains[face].count(idom))
11264                     {
11265                       faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11266                       celldom[vtkId] = idom;
11267                       //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11268                     }
11269                 }
11270             }
11271         }
11272     }
11273
11274   //MESSAGE("Number of shared faces " << faceDomains.size());
11275   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11276
11277   // --- explore the shared faces domain by domain,
11278   //     explore the nodes of the face and see if they belong to a cell in the domain,
11279   //     which has only a node or an edge on the border (not a shared face)
11280
11281   for (int idomain = 0; idomain < theElems.size(); idomain++)
11282     {
11283       //MESSAGE("Domain " << idomain);
11284       const TIDSortedElemSet& domain = theElems[idomain];
11285       itface = faceDomains.begin();
11286       for (; itface != faceDomains.end(); ++itface)
11287         {
11288           std::map<int, int> domvol = itface->second;
11289           if (!domvol.count(idomain))
11290             continue;
11291           DownIdType face = itface->first;
11292           //MESSAGE(" --- face " << face.cellId);
11293           std::set<int> oldNodes;
11294           oldNodes.clear();
11295           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11296           std::set<int>::iterator itn = oldNodes.begin();
11297           for (; itn != oldNodes.end(); ++itn)
11298             {
11299               int oldId = *itn;
11300               //MESSAGE("     node " << oldId);
11301               vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11302               for (int i=0; i<l.ncells; i++)
11303                 {
11304                   int vtkId = l.cells[i];
11305                   const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11306                   if (!domain.count(anElem))
11307                     continue;
11308                   int vtkType = grid->GetCellType(vtkId);
11309                   int downId = grid->CellIdToDownId(vtkId);
11310                   if (downId < 0)
11311                     {
11312                       MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11313                       continue; // not OK at this stage of the algorithm:
11314                                 //no cells created after BuildDownWardConnectivity
11315                     }
11316                   DownIdType aCell(downId, vtkType);
11317                   if (!cellDomains.count(aCell))
11318                     cellDomains[aCell] = emptyMap; // create an empty entry for cell
11319                   cellDomains[aCell][idomain] = vtkId;
11320                   celldom[vtkId] = idomain;
11321                   //MESSAGE("       cell " << vtkId << " domain " << idomain);
11322                 }
11323             }
11324         }
11325     }
11326
11327   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11328   //     for each shared face, get the nodes
11329   //     for each node, for each domain of the face, create a clone of the node
11330
11331   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11332   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11333   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11334
11335   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11336   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11337   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11338
11339   for (int idomain = 0; idomain < theElems.size(); idomain++)
11340     {
11341       itface = faceDomains.begin();
11342       for (; itface != faceDomains.end(); ++itface)
11343         {
11344           std::map<int, int> domvol = itface->second;
11345           if (!domvol.count(idomain))
11346             continue;
11347           DownIdType face = itface->first;
11348           //MESSAGE(" --- face " << face.cellId);
11349           std::set<int> oldNodes;
11350           oldNodes.clear();
11351           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11352           std::set<int>::iterator itn = oldNodes.begin();
11353           for (; itn != oldNodes.end(); ++itn)
11354             {
11355               int oldId = *itn;
11356               //MESSAGE("-+-+-a node " << oldId);
11357               if (!nodeDomains.count(oldId))
11358                 nodeDomains[oldId] = emptyMap; // create an empty entry for node
11359               if (nodeDomains[oldId].empty())
11360                 {
11361                   nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11362                   //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11363                 }
11364               std::map<int, int>::iterator itdom = domvol.begin();
11365               for (; itdom != domvol.end(); ++itdom)
11366                 {
11367                   int idom = itdom->first;
11368                   //MESSAGE("         domain " << idom);
11369                   if (!nodeDomains[oldId].count(idom)) // --- node to clone
11370                     {
11371                       if (nodeDomains[oldId].size() >= 2) // a multiple node
11372                         {
11373                           vector<int> orderedDoms;
11374                           //MESSAGE("multiple node " << oldId);
11375                           if (mutipleNodes.count(oldId))
11376                             orderedDoms = mutipleNodes[oldId];
11377                           else
11378                             {
11379                               map<int,int>::iterator it = nodeDomains[oldId].begin();
11380                               for (; it != nodeDomains[oldId].end(); ++it)
11381                                 orderedDoms.push_back(it->first);
11382                             }
11383                           orderedDoms.push_back(idom); // TODO order ==> push_front or back
11384                           //stringstream txt;
11385                           //for (int i=0; i<orderedDoms.size(); i++)
11386                           //  txt << orderedDoms[i] << " ";
11387                           //MESSAGE("orderedDoms " << txt.str());
11388                           mutipleNodes[oldId] = orderedDoms;
11389                         }
11390                       double *coords = grid->GetPoint(oldId);
11391                       SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11392                       int newId = newNode->getVtkId();
11393                       nodeDomains[oldId][idom] = newId; // cloned node for other domains
11394                       //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11395                     }
11396                 }
11397             }
11398         }
11399     }
11400
11401   for (int idomain = 0; idomain < theElems.size(); idomain++)
11402     {
11403       itface = faceDomains.begin();
11404       for (; itface != faceDomains.end(); ++itface)
11405         {
11406           std::map<int, int> domvol = itface->second;
11407           if (!domvol.count(idomain))
11408             continue;
11409           DownIdType face = itface->first;
11410           //MESSAGE(" --- face " << face.cellId);
11411           std::set<int> oldNodes;
11412           oldNodes.clear();
11413           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11414           int nbMultipleNodes = 0;
11415           std::set<int>::iterator itn = oldNodes.begin();
11416           for (; itn != oldNodes.end(); ++itn)
11417             {
11418               int oldId = *itn;
11419               if (mutipleNodes.count(oldId))
11420                 nbMultipleNodes++;
11421             }
11422           if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11423             {
11424               //MESSAGE("multiple Nodes detected on a shared face");
11425               int downId = itface->first.cellId;
11426               unsigned char cellType = itface->first.cellType;
11427               // --- shared edge or shared face ?
11428               if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11429                 {
11430                   int nodes[3];
11431                   int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11432                   for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11433                     if (mutipleNodes.count(nodes[i]))
11434                       if (!mutipleNodesToFace.count(nodes[i]))
11435                         mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11436                 }
11437               else // shared face (between two volumes)
11438                 {
11439                   int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11440                   const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11441                   const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11442                   for (int ie =0; ie < nbEdges; ie++)
11443                     {
11444                       int nodes[3];
11445                       int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11446                       if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11447                         {
11448                           vector<int> vn0 = mutipleNodes[nodes[0]];
11449                           vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11450                           vector<int> doms;
11451                           for (int i0 = 0; i0 < vn0.size(); i0++)
11452                             for (int i1 = 0; i1 < vn1.size(); i1++)
11453                               if (vn0[i0] == vn1[i1])
11454                                 doms.push_back(vn0[i0]);
11455                           if (doms.size() >2)
11456                             {
11457                               //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11458                               double *coords = grid->GetPoint(nodes[0]);
11459                               gp_Pnt p0(coords[0], coords[1], coords[2]);
11460                               coords = grid->GetPoint(nodes[nbNodes - 1]);
11461                               gp_Pnt p1(coords[0], coords[1], coords[2]);
11462                               gp_Pnt gref;
11463                               int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11464                               map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11465                               map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11466                               int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11467                               for (int id=0; id < doms.size(); id++)
11468                                 {
11469                                   int idom = doms[id];
11470                                   for (int ivol=0; ivol<nbvol; ivol++)
11471                                     {
11472                                       int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11473                                       SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11474                                       if (theElems[idom].count(elem))
11475                                         {
11476                                           SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11477                                           domvol[idom] = svol;
11478                                           //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11479                                           double values[3];
11480                                           vtkIdType npts = 0;
11481                                           vtkIdType* pts = 0;
11482                                           grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11483                                           SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11484                                           if (id ==0)
11485                                             {
11486                                               gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11487                                               angleDom[idom] = 0;
11488                                             }
11489                                           else
11490                                             {
11491                                               gp_Pnt g(values[0], values[1], values[2]);
11492                                               angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11493                                               //MESSAGE("  angle=" << angleDom[idom]);
11494                                             }
11495                                           break;
11496                                         }
11497                                     }
11498                                 }
11499                               map<double, int> sortedDom; // sort domains by angle
11500                               for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11501                                 sortedDom[ia->second] = ia->first;
11502                               vector<int> vnodes;
11503                               vector<int> vdom;
11504                               for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11505                                 {
11506                                   vdom.push_back(ib->second);
11507                                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11508                                 }
11509                               for (int ino = 0; ino < nbNodes; ino++)
11510                                 vnodes.push_back(nodes[ino]);
11511                               edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11512                             }
11513                         }
11514                     }
11515                 }
11516             }
11517         }
11518     }
11519
11520   // --- iterate on shared faces (volumes to modify, face to extrude)
11521   //     get node id's of the face (id SMDS = id VTK)
11522   //     create flat element with old and new nodes if requested
11523
11524   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11525   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11526
11527   std::map<int, std::map<long,int> > nodeQuadDomains;
11528   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11529
11530   if (createJointElems)
11531     {
11532       int idg;
11533       string joints2DName = "joints2D";
11534       mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11535       SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11536       string joints3DName = "joints3D";
11537       mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11538       SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11539
11540       itface = faceDomains.begin();
11541       for (; itface != faceDomains.end(); ++itface)
11542         {
11543           DownIdType face = itface->first;
11544           std::set<int> oldNodes;
11545           std::set<int>::iterator itn;
11546           oldNodes.clear();
11547           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11548
11549           std::map<int, int> domvol = itface->second;
11550           std::map<int, int>::iterator itdom = domvol.begin();
11551           int dom1 = itdom->first;
11552           int vtkVolId = itdom->second;
11553           itdom++;
11554           int dom2 = itdom->first;
11555           SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11556                                                              nodeQuadDomains);
11557           stringstream grpname;
11558           grpname << "j_";
11559           if (dom1 < dom2)
11560             grpname << dom1 << "_" << dom2;
11561           else
11562             grpname << dom2 << "_" << dom1;
11563           string namegrp = grpname.str();
11564           if (!mapOfJunctionGroups.count(namegrp))
11565             mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11566           SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11567           if (sgrp)
11568             sgrp->Add(vol->GetID());
11569           if (vol->GetType() == SMDSAbs_Volume)
11570             joints3DGrp->Add(vol->GetID());
11571           else if (vol->GetType() == SMDSAbs_Face)
11572             joints2DGrp->Add(vol->GetID());
11573         }
11574     }
11575
11576   // --- create volumes on multiple domain intersection if requested
11577   //     iterate on mutipleNodesToFace
11578   //     iterate on edgesMultiDomains
11579
11580   if (createJointElems)
11581     {
11582       // --- iterate on mutipleNodesToFace
11583
11584       std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11585       for (; itn != mutipleNodesToFace.end(); ++itn)
11586         {
11587           int node = itn->first;
11588           vector<int> orderDom = itn->second;
11589           vector<vtkIdType> orderedNodes;
11590           for (int idom = 0; idom <orderDom.size(); idom++)
11591             orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11592             SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11593
11594             stringstream grpname;
11595             grpname << "m2j_";
11596             grpname << 0 << "_" << 0;
11597             int idg;
11598             string namegrp = grpname.str();
11599             if (!mapOfJunctionGroups.count(namegrp))
11600               mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11601             SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11602             if (sgrp)
11603               sgrp->Add(face->GetID());
11604         }
11605
11606       // --- iterate on edgesMultiDomains
11607
11608       std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11609       for (; ite != edgesMultiDomains.end(); ++ite)
11610         {
11611           vector<int> nodes = ite->first;
11612           vector<int> orderDom = ite->second;
11613           vector<vtkIdType> orderedNodes;
11614           if (nodes.size() == 2)
11615             {
11616               //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11617               for (int ino=0; ino < nodes.size(); ino++)
11618                 if (orderDom.size() == 3)
11619                   for (int idom = 0; idom <orderDom.size(); idom++)
11620                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11621                 else
11622                   for (int idom = orderDom.size()-1; idom >=0; idom--)
11623                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11624               SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11625
11626               int idg;
11627               string namegrp = "jointsMultiples";
11628               if (!mapOfJunctionGroups.count(namegrp))
11629                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11630               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11631               if (sgrp)
11632                 sgrp->Add(vol->GetID());
11633             }
11634           else
11635             {
11636               INFOS("Quadratic multiple joints not implemented");
11637               // TODO quadratic nodes
11638             }
11639         }
11640     }
11641
11642   // --- list the explicit faces and edges of the mesh that need to be modified,
11643   //     i.e. faces and edges built with one or more duplicated nodes.
11644   //     associate these faces or edges to their corresponding domain.
11645   //     only the first domain found is kept when a face or edge is shared
11646
11647   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11648   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11649   faceOrEdgeDom.clear();
11650   feDom.clear();
11651
11652   for (int idomain = 0; idomain < theElems.size(); idomain++)
11653     {
11654       std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11655       for (; itnod != nodeDomains.end(); ++itnod)
11656         {
11657           int oldId = itnod->first;
11658           //MESSAGE("     node " << oldId);
11659           vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11660           for (int i = 0; i < l.ncells; i++)
11661             {
11662               int vtkId = l.cells[i];
11663               int vtkType = grid->GetCellType(vtkId);
11664               int downId = grid->CellIdToDownId(vtkId);
11665               if (downId < 0)
11666                 continue; // new cells: not to be modified
11667               DownIdType aCell(downId, vtkType);
11668               int volParents[1000];
11669               int nbvol = grid->GetParentVolumes(volParents, vtkId);
11670               for (int j = 0; j < nbvol; j++)
11671                 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11672                   if (!feDom.count(vtkId))
11673                     {
11674                       feDom[vtkId] = idomain;
11675                       faceOrEdgeDom[aCell] = emptyMap;
11676                       faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11677                       //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11678                       //        << " type " << vtkType << " downId " << downId);
11679                     }
11680             }
11681         }
11682     }
11683
11684   // --- iterate on shared faces (volumes to modify, face to extrude)
11685   //     get node id's of the face
11686   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11687
11688   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11689   for (int m=0; m<3; m++)
11690     {
11691       std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11692       itface = (*amap).begin();
11693       for (; itface != (*amap).end(); ++itface)
11694         {
11695           DownIdType face = itface->first;
11696           std::set<int> oldNodes;
11697           std::set<int>::iterator itn;
11698           oldNodes.clear();
11699           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11700           //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11701           std::map<int, int> localClonedNodeIds;
11702
11703           std::map<int, int> domvol = itface->second;
11704           std::map<int, int>::iterator itdom = domvol.begin();
11705           for (; itdom != domvol.end(); ++itdom)
11706             {
11707               int idom = itdom->first;
11708               int vtkVolId = itdom->second;
11709               //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11710               localClonedNodeIds.clear();
11711               for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11712                 {
11713                   int oldId = *itn;
11714                   if (nodeDomains[oldId].count(idom))
11715                     {
11716                       localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11717                       //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11718                     }
11719                 }
11720               meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11721             }
11722         }
11723     }
11724
11725   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11726   grid->BuildLinks();
11727
11728   CHRONOSTOP(50);
11729   counters::stats();
11730   return true;
11731 }
11732
11733 /*!
11734  * \brief Double nodes on some external faces and create flat elements.
11735  * Flat elements are mainly used by some types of mechanic calculations.
11736  *
11737  * Each group of the list must be constituted of faces.
11738  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11739  * @param theElems - list of groups of faces, where a group of faces is a set of
11740  * SMDS_MeshElements sorted by Id.
11741  * @return TRUE if operation has been completed successfully, FALSE otherwise
11742  */
11743 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11744 {
11745   MESSAGE("-------------------------------------------------");
11746   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11747   MESSAGE("-------------------------------------------------");
11748
11749   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11750
11751   // --- For each group of faces
11752   //     duplicate the nodes, create a flat element based on the face
11753   //     replace the nodes of the faces by their clones
11754
11755   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11756   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11757   clonedNodes.clear();
11758   intermediateNodes.clear();
11759   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11760   mapOfJunctionGroups.clear();
11761
11762   for (int idom = 0; idom < theElems.size(); idom++)
11763     {
11764       const TIDSortedElemSet& domain = theElems[idom];
11765       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11766       for (; elemItr != domain.end(); ++elemItr)
11767         {
11768           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11769           SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11770           if (!aFace)
11771             continue;
11772           // MESSAGE("aFace=" << aFace->GetID());
11773           bool isQuad = aFace->IsQuadratic();
11774           vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11775
11776           // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11777
11778           SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11779           while (nodeIt->more())
11780             {
11781               const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11782               bool isMedium = isQuad && (aFace->IsMediumNode(node));
11783               if (isMedium)
11784                 ln2.push_back(node);
11785               else
11786                 ln0.push_back(node);
11787
11788               const SMDS_MeshNode* clone = 0;
11789               if (!clonedNodes.count(node))
11790                 {
11791                   clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11792                   clonedNodes[node] = clone;
11793                 }
11794               else
11795                 clone = clonedNodes[node];
11796
11797               if (isMedium)
11798                 ln3.push_back(clone);
11799               else
11800                 ln1.push_back(clone);
11801
11802               const SMDS_MeshNode* inter = 0;
11803               if (isQuad && (!isMedium))
11804                 {
11805                   if (!intermediateNodes.count(node))
11806                     {
11807                       inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11808                       intermediateNodes[node] = inter;
11809                     }
11810                   else
11811                     inter = intermediateNodes[node];
11812                   ln4.push_back(inter);
11813                 }
11814             }
11815
11816           // --- extrude the face
11817
11818           vector<const SMDS_MeshNode*> ln;
11819           SMDS_MeshVolume* vol = 0;
11820           vtkIdType aType = aFace->GetVtkType();
11821           switch (aType)
11822           {
11823             case VTK_TRIANGLE:
11824               vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11825               // MESSAGE("vol prism " << vol->GetID());
11826               ln.push_back(ln1[0]);
11827               ln.push_back(ln1[1]);
11828               ln.push_back(ln1[2]);
11829               break;
11830             case VTK_QUAD:
11831               vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11832               // MESSAGE("vol hexa " << vol->GetID());
11833               ln.push_back(ln1[0]);
11834               ln.push_back(ln1[1]);
11835               ln.push_back(ln1[2]);
11836               ln.push_back(ln1[3]);
11837               break;
11838             case VTK_QUADRATIC_TRIANGLE:
11839               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11840                                       ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11841               // MESSAGE("vol quad prism " << vol->GetID());
11842               ln.push_back(ln1[0]);
11843               ln.push_back(ln1[1]);
11844               ln.push_back(ln1[2]);
11845               ln.push_back(ln3[0]);
11846               ln.push_back(ln3[1]);
11847               ln.push_back(ln3[2]);
11848               break;
11849             case VTK_QUADRATIC_QUAD:
11850 //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11851 //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11852 //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11853               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11854                                       ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11855                                       ln4[0], ln4[1], ln4[2], ln4[3]);
11856               // MESSAGE("vol quad hexa " << vol->GetID());
11857               ln.push_back(ln1[0]);
11858               ln.push_back(ln1[1]);
11859               ln.push_back(ln1[2]);
11860               ln.push_back(ln1[3]);
11861               ln.push_back(ln3[0]);
11862               ln.push_back(ln3[1]);
11863               ln.push_back(ln3[2]);
11864               ln.push_back(ln3[3]);
11865               break;
11866             case VTK_POLYGON:
11867               break;
11868             default:
11869               break;
11870           }
11871
11872           if (vol)
11873             {
11874               stringstream grpname;
11875               grpname << "jf_";
11876               grpname << idom;
11877               int idg;
11878               string namegrp = grpname.str();
11879               if (!mapOfJunctionGroups.count(namegrp))
11880                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11881               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11882               if (sgrp)
11883                 sgrp->Add(vol->GetID());
11884             }
11885
11886           // --- modify the face
11887
11888           aFace->ChangeNodes(&ln[0], ln.size());
11889         }
11890     }
11891   return true;
11892 }
11893
11894 /*!
11895  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11896  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11897  *  groups of faces to remove inside the object, (idem edges).
11898  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11899  */
11900 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11901                                       const TopoDS_Shape& theShape,
11902                                       SMESH_NodeSearcher* theNodeSearcher,
11903                                       const char* groupName,
11904                                       std::vector<double>&   nodesCoords,
11905                                       std::vector<std::vector<int> >& listOfListOfNodes)
11906 {
11907   MESSAGE("--------------------------------");
11908   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11909   MESSAGE("--------------------------------");
11910
11911   // --- zone of volumes to remove is given :
11912   //     1 either by a geom shape (one or more vertices) and a radius,
11913   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11914   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11915   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11916   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11917   //     defined by it's name.
11918
11919   SMESHDS_GroupBase* groupDS = 0;
11920   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11921   while ( groupIt->more() )
11922     {
11923       groupDS = 0;
11924       SMESH_Group * group = groupIt->next();
11925       if ( !group ) continue;
11926       groupDS = group->GetGroupDS();
11927       if ( !groupDS || groupDS->IsEmpty() ) continue;
11928       std::string grpName = group->GetName();
11929       //MESSAGE("grpName=" << grpName);
11930       if (grpName == groupName)
11931         break;
11932       else
11933         groupDS = 0;
11934     }
11935
11936   bool isNodeGroup = false;
11937   bool isNodeCoords = false;
11938   if (groupDS)
11939     {
11940       if (groupDS->GetType() != SMDSAbs_Node)
11941         return;
11942       isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11943     }
11944
11945   if (nodesCoords.size() > 0)
11946     isNodeCoords = true; // a list o nodes given by their coordinates
11947   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11948
11949   // --- define groups to build
11950
11951   int idg; // --- group of SMDS volumes
11952   string grpvName = groupName;
11953   grpvName += "_vol";
11954   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11955   if (!grp)
11956     {
11957       MESSAGE("group not created " << grpvName);
11958       return;
11959     }
11960   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11961
11962   int idgs; // --- group of SMDS faces on the skin
11963   string grpsName = groupName;
11964   grpsName += "_skin";
11965   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11966   if (!grps)
11967     {
11968       MESSAGE("group not created " << grpsName);
11969       return;
11970     }
11971   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11972
11973   int idgi; // --- group of SMDS faces internal (several shapes)
11974   string grpiName = groupName;
11975   grpiName += "_internalFaces";
11976   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11977   if (!grpi)
11978     {
11979       MESSAGE("group not created " << grpiName);
11980       return;
11981     }
11982   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11983
11984   int idgei; // --- group of SMDS faces internal (several shapes)
11985   string grpeiName = groupName;
11986   grpeiName += "_internalEdges";
11987   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11988   if (!grpei)
11989     {
11990       MESSAGE("group not created " << grpeiName);
11991       return;
11992     }
11993   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11994
11995   // --- build downward connectivity
11996
11997   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11998   meshDS->BuildDownWardConnectivity(true);
11999   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12000
12001   // --- set of volumes detected inside
12002
12003   std::set<int> setOfInsideVol;
12004   std::set<int> setOfVolToCheck;
12005
12006   std::vector<gp_Pnt> gpnts;
12007   gpnts.clear();
12008
12009   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12010     {
12011       MESSAGE("group of nodes provided");
12012       SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12013       while ( elemIt->more() )
12014         {
12015           const SMDS_MeshElement* elem = elemIt->next();
12016           if (!elem)
12017             continue;
12018           const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12019           if (!node)
12020             continue;
12021           SMDS_MeshElement* vol = 0;
12022           SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12023           while (volItr->more())
12024             {
12025               vol = (SMDS_MeshElement*)volItr->next();
12026               setOfInsideVol.insert(vol->getVtkId());
12027               sgrp->Add(vol->GetID());
12028             }
12029         }
12030     }
12031   else if (isNodeCoords)
12032     {
12033       MESSAGE("list of nodes coordinates provided");
12034       int i = 0;
12035       int k = 0;
12036       while (i < nodesCoords.size()-2)
12037         {
12038           double x = nodesCoords[i++];
12039           double y = nodesCoords[i++];
12040           double z = nodesCoords[i++];
12041           gp_Pnt p = gp_Pnt(x, y ,z);
12042           gpnts.push_back(p);
12043           MESSAGE("TopoDS_Vertex " << k++ << " " << p.X() << " " << p.Y() << " " << p.Z());
12044         }
12045     }
12046   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12047     {
12048       MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12049       TopTools_IndexedMapOfShape vertexMap;
12050       TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12051       gp_Pnt p = gp_Pnt(0,0,0);
12052       if (vertexMap.Extent() < 1)
12053         return;
12054
12055       for ( int i = 1; i <= vertexMap.Extent(); ++i )
12056         {
12057           const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12058           p = BRep_Tool::Pnt(vertex);
12059           gpnts.push_back(p);
12060           MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12061         }
12062     }
12063
12064   if (gpnts.size() > 0)
12065     {
12066       int nodeId = 0;
12067       const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12068       if (startNode)
12069         nodeId = startNode->GetID();
12070       MESSAGE("nodeId " << nodeId);
12071
12072       double radius2 = radius*radius;
12073       MESSAGE("radius2 " << radius2);
12074
12075       // --- volumes on start node
12076
12077       setOfVolToCheck.clear();
12078       SMDS_MeshElement* startVol = 0;
12079       SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12080       while (volItr->more())
12081         {
12082           startVol = (SMDS_MeshElement*)volItr->next();
12083           setOfVolToCheck.insert(startVol->getVtkId());
12084         }
12085       if (setOfVolToCheck.empty())
12086         {
12087           MESSAGE("No volumes found");
12088           return;
12089         }
12090
12091       // --- starting with central volumes then their neighbors, check if they are inside
12092       //     or outside the domain, until no more new neighbor volume is inside.
12093       //     Fill the group of inside volumes
12094
12095       std::map<int, double> mapOfNodeDistance2;
12096       mapOfNodeDistance2.clear();
12097       std::set<int> setOfOutsideVol;
12098       while (!setOfVolToCheck.empty())
12099         {
12100           std::set<int>::iterator it = setOfVolToCheck.begin();
12101           int vtkId = *it;
12102           MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12103           bool volInside = false;
12104           vtkIdType npts = 0;
12105           vtkIdType* pts = 0;
12106           grid->GetCellPoints(vtkId, npts, pts);
12107           for (int i=0; i<npts; i++)
12108             {
12109               double distance2 = 0;
12110               if (mapOfNodeDistance2.count(pts[i]))
12111                 {
12112                   distance2 = mapOfNodeDistance2[pts[i]];
12113                   MESSAGE("point " << pts[i] << " distance2 " << distance2);
12114                 }
12115               else
12116                 {
12117                   double *coords = grid->GetPoint(pts[i]);
12118                   gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12119                   distance2 = 1.E40;
12120                   for (int j=0; j<gpnts.size(); j++)
12121                     {
12122                       double d2 = aPoint.SquareDistance(gpnts[j]);
12123                       if (d2 < distance2)
12124                         {
12125                           distance2 = d2;
12126                           if (distance2 < radius2)
12127                             break;
12128                         }
12129                     }
12130                   mapOfNodeDistance2[pts[i]] = distance2;
12131                   MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12132                 }
12133               if (distance2 < radius2)
12134                 {
12135                   volInside = true; // one or more nodes inside the domain
12136                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12137                   break;
12138                 }
12139             }
12140           if (volInside)
12141             {
12142               setOfInsideVol.insert(vtkId);
12143               MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12144               int neighborsVtkIds[NBMAXNEIGHBORS];
12145               int downIds[NBMAXNEIGHBORS];
12146               unsigned char downTypes[NBMAXNEIGHBORS];
12147               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12148               for (int n = 0; n < nbNeighbors; n++)
12149                 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12150                   setOfVolToCheck.insert(neighborsVtkIds[n]);
12151             }
12152           else
12153             {
12154               setOfOutsideVol.insert(vtkId);
12155               MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12156             }
12157           setOfVolToCheck.erase(vtkId);
12158         }
12159     }
12160
12161   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12162   //     If yes, add the volume to the inside set
12163
12164   bool addedInside = true;
12165   std::set<int> setOfVolToReCheck;
12166   while (addedInside)
12167     {
12168       MESSAGE(" --------------------------- re check");
12169       addedInside = false;
12170       std::set<int>::iterator itv = setOfInsideVol.begin();
12171       for (; itv != setOfInsideVol.end(); ++itv)
12172         {
12173           int vtkId = *itv;
12174           int neighborsVtkIds[NBMAXNEIGHBORS];
12175           int downIds[NBMAXNEIGHBORS];
12176           unsigned char downTypes[NBMAXNEIGHBORS];
12177           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12178           for (int n = 0; n < nbNeighbors; n++)
12179             if (!setOfInsideVol.count(neighborsVtkIds[n]))
12180               setOfVolToReCheck.insert(neighborsVtkIds[n]);
12181         }
12182       setOfVolToCheck = setOfVolToReCheck;
12183       setOfVolToReCheck.clear();
12184       while  (!setOfVolToCheck.empty())
12185         {
12186           std::set<int>::iterator it = setOfVolToCheck.begin();
12187           int vtkId = *it;
12188           if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12189             {
12190               MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12191               int countInside = 0;
12192               int neighborsVtkIds[NBMAXNEIGHBORS];
12193               int downIds[NBMAXNEIGHBORS];
12194               unsigned char downTypes[NBMAXNEIGHBORS];
12195               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12196               for (int n = 0; n < nbNeighbors; n++)
12197                 if (setOfInsideVol.count(neighborsVtkIds[n]))
12198                   countInside++;
12199               MESSAGE("countInside " << countInside);
12200               if (countInside > 1)
12201                 {
12202                   MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12203                   setOfInsideVol.insert(vtkId);
12204                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12205                   addedInside = true;
12206                 }
12207               else
12208                 setOfVolToReCheck.insert(vtkId);
12209             }
12210           setOfVolToCheck.erase(vtkId);
12211         }
12212     }
12213
12214   // --- map of Downward faces at the boundary, inside the global volume
12215   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12216   //     fill group of SMDS faces inside the volume (when several volume shapes)
12217   //     fill group of SMDS faces on the skin of the global volume (if skin)
12218
12219   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12220   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12221   std::set<int>::iterator it = setOfInsideVol.begin();
12222   for (; it != setOfInsideVol.end(); ++it)
12223     {
12224       int vtkId = *it;
12225       //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12226       int neighborsVtkIds[NBMAXNEIGHBORS];
12227       int downIds[NBMAXNEIGHBORS];
12228       unsigned char downTypes[NBMAXNEIGHBORS];
12229       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12230       for (int n = 0; n < nbNeighbors; n++)
12231         {
12232           int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12233           if (neighborDim == 3)
12234             {
12235               if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12236                 {
12237                   DownIdType face(downIds[n], downTypes[n]);
12238                   boundaryFaces[face] = vtkId;
12239                 }
12240               // if the face between to volumes is in the mesh, get it (internal face between shapes)
12241               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12242               if (vtkFaceId >= 0)
12243                 {
12244                   sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12245                   // find also the smds edges on this face
12246                   int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12247                   const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12248                   const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12249                   for (int i = 0; i < nbEdges; i++)
12250                     {
12251                       int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12252                       if (vtkEdgeId >= 0)
12253                         sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12254                     }
12255                 }
12256             }
12257           else if (neighborDim == 2) // skin of the volume
12258             {
12259               DownIdType face(downIds[n], downTypes[n]);
12260               skinFaces[face] = vtkId;
12261               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12262               if (vtkFaceId >= 0)
12263                 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12264             }
12265         }
12266     }
12267
12268   // --- identify the edges constituting the wire of each subshape on the skin
12269   //     define polylines with the nodes of edges, equivalent to wires
12270   //     project polylines on subshapes, and partition, to get geom faces
12271
12272   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12273   std::set<int> emptySet;
12274   emptySet.clear();
12275   std::set<int> shapeIds;
12276
12277   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12278   while (itelem->more())
12279     {
12280       const SMDS_MeshElement *elem = itelem->next();
12281       int shapeId = elem->getshapeId();
12282       int vtkId = elem->getVtkId();
12283       if (!shapeIdToVtkIdSet.count(shapeId))
12284         {
12285           shapeIdToVtkIdSet[shapeId] = emptySet;
12286           shapeIds.insert(shapeId);
12287         }
12288       shapeIdToVtkIdSet[shapeId].insert(vtkId);
12289     }
12290
12291   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12292   std::set<DownIdType, DownIdCompare> emptyEdges;
12293   emptyEdges.clear();
12294
12295   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12296   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12297     {
12298       int shapeId = itShape->first;
12299       MESSAGE(" --- Shape ID --- "<< shapeId);
12300       shapeIdToEdges[shapeId] = emptyEdges;
12301
12302       std::vector<int> nodesEdges;
12303
12304       std::set<int>::iterator its = itShape->second.begin();
12305       for (; its != itShape->second.end(); ++its)
12306         {
12307           int vtkId = *its;
12308           MESSAGE("     " << vtkId);
12309           int neighborsVtkIds[NBMAXNEIGHBORS];
12310           int downIds[NBMAXNEIGHBORS];
12311           unsigned char downTypes[NBMAXNEIGHBORS];
12312           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12313           for (int n = 0; n < nbNeighbors; n++)
12314             {
12315               if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12316                 continue;
12317               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12318               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12319               if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12320                 {
12321                   DownIdType edge(downIds[n], downTypes[n]);
12322                   if (!shapeIdToEdges[shapeId].count(edge))
12323                     {
12324                       shapeIdToEdges[shapeId].insert(edge);
12325                       int vtkNodeId[3];
12326                       int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12327                       nodesEdges.push_back(vtkNodeId[0]);
12328                       nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12329                       MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12330                     }
12331                 }
12332             }
12333         }
12334
12335       std::list<int> order;
12336       order.clear();
12337       if (nodesEdges.size() > 0)
12338         {
12339           order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12340           nodesEdges[0] = -1;
12341           order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
12342           nodesEdges[1] = -1; // do not reuse this edge
12343           bool found = true;
12344           while (found)
12345             {
12346               int nodeTofind = order.back(); // try first to push back
12347               int i = 0;
12348               for (i = 0; i<nodesEdges.size(); i++)
12349                 if (nodesEdges[i] == nodeTofind)
12350                   break;
12351               if (i == nodesEdges.size())
12352                 found = false; // no follower found on back
12353               else
12354                 {
12355                   if (i%2) // odd ==> use the previous one
12356                     if (nodesEdges[i-1] < 0)
12357                       found = false;
12358                     else
12359                       {
12360                         order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
12361                         nodesEdges[i-1] = -1;
12362                       }
12363                   else // even ==> use the next one
12364                     if (nodesEdges[i+1] < 0)
12365                       found = false;
12366                     else
12367                       {
12368                         order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
12369                         nodesEdges[i+1] = -1;
12370                       }
12371                 }
12372               if (found)
12373                 continue;
12374               // try to push front
12375               found = true;
12376               nodeTofind = order.front(); // try to push front
12377               for (i = 0; i<nodesEdges.size(); i++)
12378                 if (nodesEdges[i] == nodeTofind)
12379                   break;
12380               if (i == nodesEdges.size())
12381                 {
12382                   found = false; // no predecessor found on front
12383                   continue;
12384                 }
12385               if (i%2) // odd ==> use the previous one
12386                 if (nodesEdges[i-1] < 0)
12387                   found = false;
12388                 else
12389                   {
12390                     order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
12391                     nodesEdges[i-1] = -1;
12392                   }
12393               else // even ==> use the next one
12394                 if (nodesEdges[i+1] < 0)
12395                   found = false;
12396                 else
12397                   {
12398                     order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
12399                     nodesEdges[i+1] = -1;
12400                   }
12401             }
12402         }
12403
12404
12405       std::vector<int> nodes;
12406       nodes.push_back(shapeId);
12407       std::list<int>::iterator itl = order.begin();
12408       for (; itl != order.end(); itl++)
12409         {
12410           nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12411           MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12412         }
12413       listOfListOfNodes.push_back(nodes);
12414     }
12415
12416   //     partition geom faces with blocFissure
12417   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12418   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12419
12420   return;
12421 }
12422
12423
12424 //================================================================================
12425 /*!
12426  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12427  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12428  * \return TRUE if operation has been completed successfully, FALSE otherwise
12429  */
12430 //================================================================================
12431
12432 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12433 {
12434   // iterates on volume elements and detect all free faces on them
12435   SMESHDS_Mesh* aMesh = GetMeshDS();
12436   if (!aMesh)
12437     return false;
12438   //bool res = false;
12439   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12440   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12441   while(vIt->more())
12442   {
12443     const SMDS_MeshVolume* volume = vIt->next();
12444     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12445     vTool.SetExternalNormal();
12446     //const bool isPoly = volume->IsPoly();
12447     const int iQuad = volume->IsQuadratic();
12448     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12449     {
12450       if (!vTool.IsFreeFace(iface))
12451         continue;
12452       nbFree++;
12453       vector<const SMDS_MeshNode *> nodes;
12454       int nbFaceNodes = vTool.NbFaceNodes(iface);
12455       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12456       int inode = 0;
12457       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12458         nodes.push_back(faceNodes[inode]);
12459       if (iQuad) { // add medium nodes
12460         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12461           nodes.push_back(faceNodes[inode]);
12462         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12463           nodes.push_back(faceNodes[8]);
12464       }
12465       // add new face based on volume nodes
12466       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12467         nbExisted++;
12468         continue; // face already exsist
12469       }
12470       AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12471       nbCreated++;
12472     }
12473   }
12474   return ( nbFree==(nbExisted+nbCreated) );
12475 }
12476
12477 namespace
12478 {
12479   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12480   {
12481     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12482       return n;
12483     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12484   }
12485 }
12486 //================================================================================
12487 /*!
12488  * \brief Creates missing boundary elements
12489  *  \param elements - elements whose boundary is to be checked
12490  *  \param dimension - defines type of boundary elements to create
12491  *  \param group - a group to store created boundary elements in
12492  *  \param targetMesh - a mesh to store created boundary elements in
12493  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12494  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12495  *                                boundary elements will be copied into the targetMesh
12496  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12497  *                                boundary elements will be added into the new group
12498  *  \param aroundElements - if true, elements will be created on boundary of given
12499  *                          elements else, on boundary of the whole mesh.
12500  * \return nb of added boundary elements
12501  */
12502 //================================================================================
12503
12504 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12505                                        Bnd_Dimension           dimension,
12506                                        SMESH_Group*            group/*=0*/,
12507                                        SMESH_Mesh*             targetMesh/*=0*/,
12508                                        bool                    toCopyElements/*=false*/,
12509                                        bool                    toCopyExistingBoundary/*=false*/,
12510                                        bool                    toAddExistingBondary/*= false*/,
12511                                        bool                    aroundElements/*= false*/)
12512 {
12513   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12514   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12515   // hope that all elements are of the same type, do not check them all
12516   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12517     throw SALOME_Exception(LOCALIZED("wrong element type"));
12518
12519   if ( !targetMesh )
12520     toCopyElements = toCopyExistingBoundary = false;
12521
12522   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12523   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12524   int nbAddedBnd = 0;
12525
12526   // editor adding present bnd elements and optionally holding elements to add to the group
12527   SMESH_MeshEditor* presentEditor;
12528   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12529   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12530
12531   SMESH_MesherHelper helper( *myMesh );
12532   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12533   SMDS_VolumeTool vTool;
12534   TIDSortedElemSet avoidSet;
12535   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12536   int inode;
12537
12538   typedef vector<const SMDS_MeshNode*> TConnectivity;
12539
12540   SMDS_ElemIteratorPtr eIt;
12541   if (elements.empty())
12542     eIt = aMesh->elementsIterator(elemType);
12543   else
12544     eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12545
12546   while (eIt->more())
12547   {
12548     const SMDS_MeshElement* elem = eIt->next();
12549     const int iQuad = elem->IsQuadratic();
12550
12551     // ------------------------------------------------------------------------------------
12552     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12553     // ------------------------------------------------------------------------------------
12554     vector<const SMDS_MeshElement*> presentBndElems;
12555     vector<TConnectivity>           missingBndElems;
12556     TConnectivity nodes;
12557     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12558     {
12559       vTool.SetExternalNormal();
12560       const SMDS_MeshElement* otherVol = 0;
12561       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12562       {
12563         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12564              ( !aroundElements || elements.count( otherVol )))
12565           continue;
12566         const int nbFaceNodes = vTool.NbFaceNodes(iface);
12567         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12568         if ( missType == SMDSAbs_Edge ) // boundary edges
12569         {
12570           nodes.resize( 2+iQuad );
12571           for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12572           {
12573             for ( int j = 0; j < nodes.size(); ++j )
12574               nodes[j] =nn[i+j];
12575             if ( const SMDS_MeshElement* edge =
12576                  aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12577               presentBndElems.push_back( edge );
12578             else
12579               missingBndElems.push_back( nodes );
12580           }
12581         }
12582         else // boundary face
12583         {
12584           nodes.clear();
12585           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12586             nodes.push_back( nn[inode] );
12587           if (iQuad) // add medium nodes
12588             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12589               nodes.push_back( nn[inode] );
12590           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12591           if ( iCenter > 0 )
12592             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12593
12594           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12595                                                                SMDSAbs_Face, /*noMedium=*/false ))
12596             presentBndElems.push_back( f );
12597           else
12598             missingBndElems.push_back( nodes );
12599
12600           if ( targetMesh != myMesh )
12601           {
12602             // add 1D elements on face boundary to be added to a new mesh
12603             const SMDS_MeshElement* edge;
12604             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12605             {
12606               if ( iQuad )
12607                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12608               else
12609                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12610               if ( edge && avoidSet.insert( edge ).second )
12611                 presentBndElems.push_back( edge );
12612             }
12613           }
12614         }
12615       }
12616     }
12617     else                     // elem is a face ------------------------------------------
12618     {
12619       avoidSet.clear(), avoidSet.insert( elem );
12620       int nbNodes = elem->NbCornerNodes();
12621       nodes.resize( 2 /*+ iQuad*/);
12622       for ( int i = 0; i < nbNodes; i++ )
12623       {
12624         nodes[0] = elem->GetNode(i);
12625         nodes[1] = elem->GetNode((i+1)%nbNodes);
12626         if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12627           continue; // not free link
12628
12629         //if ( iQuad )
12630         //nodes[2] = elem->GetNode( i + nbNodes );
12631         if ( const SMDS_MeshElement* edge =
12632              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
12633           presentBndElems.push_back( edge );
12634         else
12635           missingBndElems.push_back( nodes );
12636       }
12637     }
12638
12639     // ---------------------------------
12640     // 2. Add missing boundary elements
12641     // ---------------------------------
12642     if ( targetMesh != myMesh )
12643       // instead of making a map of nodes in this mesh and targetMesh,
12644       // we create nodes with same IDs.
12645       for ( int i = 0; i < missingBndElems.size(); ++i )
12646       {
12647         TConnectivity& srcNodes = missingBndElems[i];
12648         TConnectivity  nodes( srcNodes.size() );
12649         for ( inode = 0; inode < nodes.size(); ++inode )
12650           nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12651         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12652                                                                    missType,
12653                                                                    /*noMedium=*/false))
12654           continue;
12655         tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12656         ++nbAddedBnd;
12657       }
12658     else
12659       for ( int i = 0; i < missingBndElems.size(); ++i )
12660       {
12661         TConnectivity& nodes = missingBndElems[i];
12662         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12663                                                                    missType,
12664                                                                    /*noMedium=*/false))
12665           continue;
12666         SMDS_MeshElement* elem =
12667           tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12668         ++nbAddedBnd;
12669
12670         // try to set a new element to a shape
12671         if ( myMesh->HasShapeToMesh() )
12672         {
12673           bool ok = true;
12674           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12675           const int nbN = nodes.size() / (iQuad+1 );
12676           for ( inode = 0; inode < nbN && ok; ++inode )
12677           {
12678             pair<int, TopAbs_ShapeEnum> i_stype =
12679               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12680             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12681               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12682           }
12683           if ( ok && mediumShapes.size() > 1 )
12684           {
12685             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12686             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12687             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12688             {
12689               if (( ok = ( stype_i->first != stype_i_0.first )))
12690                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12691                                         aMesh->IndexToShape( stype_i_0.second ));
12692             }
12693           }
12694           if ( ok && mediumShapes.begin()->first == missShapeType )
12695             aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12696         }
12697       }
12698
12699     // ----------------------------------
12700     // 3. Copy present boundary elements
12701     // ----------------------------------
12702     if ( toCopyExistingBoundary )
12703       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12704       {
12705         const SMDS_MeshElement* e = presentBndElems[i];
12706         TConnectivity nodes( e->NbNodes() );
12707         for ( inode = 0; inode < nodes.size(); ++inode )
12708           nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12709         presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12710       }
12711     else // store present elements to add them to a group
12712       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12713       {
12714         presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12715       }
12716
12717   } // loop on given elements
12718
12719   // ---------------------------------------------
12720   // 4. Fill group with boundary elements
12721   // ---------------------------------------------
12722   if ( group )
12723   {
12724     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12725       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12726         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12727   }
12728   tgtEditor.myLastCreatedElems.Clear();
12729   tgtEditor2.myLastCreatedElems.Clear();
12730
12731   // -----------------------
12732   // 5. Copy given elements
12733   // -----------------------
12734   if ( toCopyElements && targetMesh != myMesh )
12735   {
12736     if (elements.empty())
12737       eIt = aMesh->elementsIterator(elemType);
12738     else
12739       eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12740     while (eIt->more())
12741     {
12742       const SMDS_MeshElement* elem = eIt->next();
12743       TConnectivity nodes( elem->NbNodes() );
12744       for ( inode = 0; inode < nodes.size(); ++inode )
12745         nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12746       tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12747
12748       tgtEditor.myLastCreatedElems.Clear();
12749     }
12750   }
12751   return nbAddedBnd;
12752 }