Salome HOME
0021542: EDF 1699 SMESH: Reorient a group of faces
[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 <Standard_Failure.hxx>
101 #include <Standard_ErrorHandler.hxx>
102
103 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
104
105 using namespace std;
106 using namespace SMESH::Controls;
107
108 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshNode*> >    TElemOfNodeListMap;
109 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshElement*> > TElemOfElemListMap;
110
111 typedef SMDS_SetIterator< SMDS_pElement, TIDSortedElemSet::const_iterator> TSetIterator;
112
113 //=======================================================================
114 //function : SMESH_MeshEditor
115 //purpose  :
116 //=======================================================================
117
118 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
119   :myMesh( theMesh ) // theMesh may be NULL
120 {
121 }
122
123 //================================================================================
124 /*!
125  * \brief Clears myLastCreatedNodes and myLastCreatedElems
126  */
127 //================================================================================
128
129 void SMESH_MeshEditor::CrearLastCreated()
130 {
131   myLastCreatedNodes.Clear();
132   myLastCreatedElems.Clear();
133 }
134
135
136 //=======================================================================
137 /*!
138  * \brief Add element
139  */
140 //=======================================================================
141
142 SMDS_MeshElement*
143 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
144                              const SMDSAbs_ElementType            type,
145                              const bool                           isPoly,
146                              const int                            ID,
147                              const double                         ballDiameter)
148 {
149   //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
150   SMDS_MeshElement* e = 0;
151   int nbnode = node.size();
152   SMESHDS_Mesh* mesh = GetMeshDS();
153   switch ( type ) {
154   case SMDSAbs_Face:
155     if ( !isPoly ) {
156       if      (nbnode == 3) {
157         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
158         else           e = mesh->AddFace      (node[0], node[1], node[2] );
159       }
160       else if (nbnode == 4) {
161         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
162         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
163       }
164       else if (nbnode == 6) {
165         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
166                                                node[4], node[5], ID);
167         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
168                                                node[4], node[5] );
169       }
170       else if (nbnode == 8) {
171         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
172                                                node[4], node[5], node[6], node[7], ID);
173         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
174                                                node[4], node[5], node[6], node[7] );
175       }
176       else if (nbnode == 9) {
177         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
178                                                node[4], node[5], node[6], node[7], node[8], ID);
179         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
180                                                node[4], node[5], node[6], node[7], node[8] );
181       }
182     } else {
183       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
184       else           e = mesh->AddPolygonalFace      (node    );
185     }
186     break;
187
188   case SMDSAbs_Volume:
189     if ( !isPoly ) {
190       if      (nbnode == 4) {
191         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
192         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
193       }
194       else if (nbnode == 5) {
195         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
196                                                  node[4], ID);
197         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
198                                                  node[4] );
199       }
200       else if (nbnode == 6) {
201         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
202                                                  node[4], node[5], ID);
203         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
204                                                  node[4], node[5] );
205       }
206       else if (nbnode == 8) {
207         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
208                                                  node[4], node[5], node[6], node[7], ID);
209         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
210                                                  node[4], node[5], node[6], node[7] );
211       }
212       else if (nbnode == 10) {
213         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
214                                                  node[4], node[5], node[6], node[7],
215                                                  node[8], node[9], ID);
216         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
217                                                  node[4], node[5], node[6], node[7],
218                                                  node[8], node[9] );
219       }
220       else if (nbnode == 12) {
221         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
222                                                  node[4], node[5], node[6], node[7],
223                                                  node[8], node[9], node[10], node[11], ID);
224         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
225                                                  node[4], node[5], node[6], node[7],
226                                                  node[8], node[9], node[10], node[11] );
227       }
228       else if (nbnode == 13) {
229         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
230                                                  node[4], node[5], node[6], node[7],
231                                                  node[8], node[9], node[10],node[11],
232                                                  node[12],ID);
233         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
234                                                  node[4], node[5], node[6], node[7],
235                                                  node[8], node[9], node[10],node[11],
236                                                  node[12] );
237       }
238       else if (nbnode == 15) {
239         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
240                                                  node[4], node[5], node[6], node[7],
241                                                  node[8], node[9], node[10],node[11],
242                                                  node[12],node[13],node[14],ID);
243         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
244                                                  node[4], node[5], node[6], node[7],
245                                                  node[8], node[9], node[10],node[11],
246                                                  node[12],node[13],node[14] );
247       }
248       else if (nbnode == 20) {
249         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
250                                                  node[4], node[5], node[6], node[7],
251                                                  node[8], node[9], node[10],node[11],
252                                                  node[12],node[13],node[14],node[15],
253                                                  node[16],node[17],node[18],node[19],ID);
254         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
255                                                  node[4], node[5], node[6], node[7],
256                                                  node[8], node[9], node[10],node[11],
257                                                  node[12],node[13],node[14],node[15],
258                                                  node[16],node[17],node[18],node[19] );
259       }
260       else if (nbnode == 27) {
261         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
262                                                  node[4], node[5], node[6], node[7],
263                                                  node[8], node[9], node[10],node[11],
264                                                  node[12],node[13],node[14],node[15],
265                                                  node[16],node[17],node[18],node[19],
266                                                  node[20],node[21],node[22],node[23],
267                                                  node[24],node[25],node[26], ID);
268         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
269                                                  node[4], node[5], node[6], node[7],
270                                                  node[8], node[9], node[10],node[11],
271                                                  node[12],node[13],node[14],node[15],
272                                                  node[16],node[17],node[18],node[19],
273                                                  node[20],node[21],node[22],node[23],
274                                                  node[24],node[25],node[26] );
275       }
276     }
277     break;
278
279   case SMDSAbs_Edge:
280     if ( nbnode == 2 ) {
281       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
282       else           e = mesh->AddEdge      (node[0], node[1] );
283     }
284     else if ( nbnode == 3 ) {
285       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
286       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
287     }
288     break;
289
290   case SMDSAbs_0DElement:
291     if ( nbnode == 1 ) {
292       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
293       else           e = mesh->Add0DElement      (node[0] );
294     }
295     break;
296
297   case SMDSAbs_Node:
298     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
299     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z());
300     break;
301
302   case SMDSAbs_Ball:
303     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
304     else           e = mesh->AddBall      (node[0], ballDiameter);
305     break;
306
307   default:;
308   }
309   if ( e ) myLastCreatedElems.Append( e );
310   return e;
311 }
312
313 //=======================================================================
314 /*!
315  * \brief Add element
316  */
317 //=======================================================================
318
319 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> &       nodeIDs,
320                                                const SMDSAbs_ElementType type,
321                                                const bool                isPoly,
322                                                const int                 ID)
323 {
324   vector<const SMDS_MeshNode*> nodes;
325   nodes.reserve( nodeIDs.size() );
326   vector<int>::const_iterator id = nodeIDs.begin();
327   while ( id != nodeIDs.end() ) {
328     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
329       nodes.push_back( node );
330     else
331       return 0;
332   }
333   return AddElement( nodes, type, isPoly, ID );
334 }
335
336 //=======================================================================
337 //function : Remove
338 //purpose  : Remove a node or an element.
339 //           Modify a compute state of sub-meshes which become empty
340 //=======================================================================
341
342 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
343                               const bool         isNodes )
344 {
345   myLastCreatedElems.Clear();
346   myLastCreatedNodes.Clear();
347
348   SMESHDS_Mesh* aMesh = GetMeshDS();
349   set< SMESH_subMesh *> smmap;
350
351   int removed = 0;
352   list<int>::const_iterator it = theIDs.begin();
353   for ( ; it != theIDs.end(); it++ ) {
354     const SMDS_MeshElement * elem;
355     if ( isNodes )
356       elem = aMesh->FindNode( *it );
357     else
358       elem = aMesh->FindElement( *it );
359     if ( !elem )
360       continue;
361
362     // Notify VERTEX sub-meshes about modification
363     if ( isNodes ) {
364       const SMDS_MeshNode* node = cast2Node( elem );
365       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
366         if ( int aShapeID = node->getshapeId() )
367           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
368             smmap.insert( sm );
369     }
370     // Find sub-meshes to notify about modification
371     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
372     //     while ( nodeIt->more() ) {
373     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
374     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
375     //       if ( aPosition.get() ) {
376     //         if ( int aShapeID = aPosition->GetShapeId() ) {
377     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
378     //             smmap.insert( sm );
379     //         }
380     //       }
381     //     }
382
383     // Do remove
384     if ( isNodes )
385       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
386     else
387       aMesh->RemoveElement( elem );
388     removed++;
389   }
390
391   // Notify sub-meshes about modification
392   if ( !smmap.empty() ) {
393     set< SMESH_subMesh *>::iterator smIt;
394     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
395       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
396   }
397
398   //   // Check if the whole mesh becomes empty
399   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
400   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
401
402   return removed;
403 }
404
405 //================================================================================
406 /*!
407  * \brief Create 0D elements on all nodes of the given object except those
408  *        nodes on which a 0D element already exists.
409  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
410  *                    the all mesh is treated
411  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
412  */
413 //================================================================================
414
415 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
416                                                    TIDSortedElemSet&       all0DElems )
417 {
418   typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::const_iterator> TSetIterator;
419   SMDS_ElemIteratorPtr elemIt;
420   if ( elements.empty() )
421     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
422   else
423     elemIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
424
425   while ( elemIt->more() )
426   {
427     const SMDS_MeshElement* e = elemIt->next();
428     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
429     while ( nodeIt->more() )
430     {
431       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
432       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
433       if ( it0D->more() )
434         all0DElems.insert( it0D->next() );
435       else {
436         myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
437         all0DElems.insert( myLastCreatedElems.Last() );
438       }
439     }
440   }
441 }
442
443 //=======================================================================
444 //function : FindShape
445 //purpose  : Return an index of the shape theElem is on
446 //           or zero if a shape not found
447 //=======================================================================
448
449 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
450 {
451   myLastCreatedElems.Clear();
452   myLastCreatedNodes.Clear();
453
454   SMESHDS_Mesh * aMesh = GetMeshDS();
455   if ( aMesh->ShapeToMesh().IsNull() )
456     return 0;
457
458   int aShapeID = theElem->getshapeId();
459   if ( aShapeID < 1 )
460     return 0;
461
462   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
463     if ( sm->Contains( theElem ))
464       return aShapeID;
465
466   if ( theElem->GetType() == SMDSAbs_Node ) {
467     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
468   }
469   else {
470     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
471   }
472
473   TopoDS_Shape aShape; // the shape a node of theElem is on
474   if ( theElem->GetType() != SMDSAbs_Node )
475   {
476     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
477     while ( nodeIt->more() ) {
478       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
479       if ((aShapeID = node->getshapeId()) > 0) {
480         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
481           if ( sm->Contains( theElem ))
482             return aShapeID;
483           if ( aShape.IsNull() )
484             aShape = aMesh->IndexToShape( aShapeID );
485         }
486       }
487     }
488   }
489
490   // None of nodes is on a proper shape,
491   // find the shape among ancestors of aShape on which a node is
492   if ( !aShape.IsNull() ) {
493     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
494     for ( ; ancIt.More(); ancIt.Next() ) {
495       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
496       if ( sm && sm->Contains( theElem ))
497         return aMesh->ShapeToIndex( ancIt.Value() );
498     }
499   }
500   else
501   {
502     const map<int,SMESHDS_SubMesh*>& id2sm = GetMeshDS()->SubMeshes();
503     map<int,SMESHDS_SubMesh*>::const_iterator id_sm = id2sm.begin();
504     for ( ; id_sm != id2sm.end(); ++id_sm )
505       if ( id_sm->second->Contains( theElem ))
506         return id_sm->first;
507   }
508
509   //MESSAGE ("::FindShape() - SHAPE NOT FOUND")
510   return 0;
511 }
512
513 //=======================================================================
514 //function : IsMedium
515 //purpose  :
516 //=======================================================================
517
518 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
519                                 const SMDSAbs_ElementType typeToCheck)
520 {
521   bool isMedium = false;
522   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
523   while (it->more() && !isMedium ) {
524     const SMDS_MeshElement* elem = it->next();
525     isMedium = elem->IsMediumNode(node);
526   }
527   return isMedium;
528 }
529
530 //=======================================================================
531 //function : ShiftNodesQuadTria
532 //purpose  : auxilary
533 //           Shift nodes in the array corresponded to quadratic triangle
534 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
535 //=======================================================================
536 static void ShiftNodesQuadTria(const SMDS_MeshNode* aNodes[])
537 {
538   const SMDS_MeshNode* nd1 = aNodes[0];
539   aNodes[0] = aNodes[1];
540   aNodes[1] = aNodes[2];
541   aNodes[2] = nd1;
542   const SMDS_MeshNode* nd2 = aNodes[3];
543   aNodes[3] = aNodes[4];
544   aNodes[4] = aNodes[5];
545   aNodes[5] = nd2;
546 }
547
548 //=======================================================================
549 //function : edgeConnectivity
550 //purpose  : auxilary
551 //           return number of the edges connected with the theNode.
552 //           if theEdges has connections with the other type of the
553 //           elements, return -1
554 //=======================================================================
555 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
556 {
557   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
558   int nb=0;
559   while(elemIt->more()) {
560     elemIt->next();
561     nb++;
562   }
563   return nb;
564 }
565
566
567 //=======================================================================
568 //function : GetNodesFromTwoTria
569 //purpose  : auxilary
570 //           Shift nodes in the array corresponded to quadratic triangle
571 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
572 //=======================================================================
573 static bool GetNodesFromTwoTria(const SMDS_MeshElement * theTria1,
574                                 const SMDS_MeshElement * theTria2,
575                                 const SMDS_MeshNode* N1[],
576                                 const SMDS_MeshNode* N2[])
577 {
578   SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
579   int i=0;
580   while(i<6) {
581     N1[i] = static_cast<const SMDS_MeshNode*>( it->next() );
582     i++;
583   }
584   if(it->more()) return false;
585   it = theTria2->nodesIterator();
586   i=0;
587   while(i<6) {
588     N2[i] = static_cast<const SMDS_MeshNode*>( it->next() );
589     i++;
590   }
591   if(it->more()) return false;
592
593   int sames[3] = {-1,-1,-1};
594   int nbsames = 0;
595   int j;
596   for(i=0; i<3; i++) {
597     for(j=0; j<3; j++) {
598       if(N1[i]==N2[j]) {
599         sames[i] = j;
600         nbsames++;
601         break;
602       }
603     }
604   }
605   if(nbsames!=2) return false;
606   if(sames[0]>-1) {
607     ShiftNodesQuadTria(N1);
608     if(sames[1]>-1) {
609       ShiftNodesQuadTria(N1);
610     }
611   }
612   i = sames[0] + sames[1] + sames[2];
613   for(; i<2; i++) {
614     ShiftNodesQuadTria(N2);
615   }
616   // now we receive following N1 and N2 (using numeration as above image)
617   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
618   // i.e. first nodes from both arrays determ new diagonal
619   return true;
620 }
621
622 //=======================================================================
623 //function : InverseDiag
624 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
625 //           but having other common link.
626 //           Return False if args are improper
627 //=======================================================================
628
629 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
630                                     const SMDS_MeshElement * theTria2 )
631 {
632   MESSAGE("InverseDiag");
633   myLastCreatedElems.Clear();
634   myLastCreatedNodes.Clear();
635
636   if (!theTria1 || !theTria2)
637     return false;
638
639   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
640   if (!F1) return false;
641   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
642   if (!F2) return false;
643   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
644       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
645
646     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
647     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
648     //    |/ |                                         | \|
649     //  B +--+ 2                                     B +--+ 2
650
651     // put nodes in array and find out indices of the same ones
652     const SMDS_MeshNode* aNodes [6];
653     int sameInd [] = { 0, 0, 0, 0, 0, 0 };
654     int i = 0;
655     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
656     while ( it->more() ) {
657       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
658
659       if ( i > 2 ) // theTria2
660         // find same node of theTria1
661         for ( int j = 0; j < 3; j++ )
662           if ( aNodes[ i ] == aNodes[ j ]) {
663             sameInd[ j ] = i;
664             sameInd[ i ] = j;
665             break;
666           }
667       // next
668       i++;
669       if ( i == 3 ) {
670         if ( it->more() )
671           return false; // theTria1 is not a triangle
672         it = theTria2->nodesIterator();
673       }
674       if ( i == 6 && it->more() )
675         return false; // theTria2 is not a triangle
676     }
677
678     // find indices of 1,2 and of A,B in theTria1
679     int iA = 0, iB = 0, i1 = 0, i2 = 0;
680     for ( i = 0; i < 6; i++ ) {
681       if ( sameInd [ i ] == 0 ) {
682         if ( i < 3 ) i1 = i;
683         else         i2 = i;
684       }
685       else if (i < 3) {
686         if ( iA ) iB = i;
687         else      iA = i;
688       }
689     }
690     // nodes 1 and 2 should not be the same
691     if ( aNodes[ i1 ] == aNodes[ i2 ] )
692       return false;
693
694     // theTria1: A->2
695     aNodes[ iA ] = aNodes[ i2 ];
696     // theTria2: B->1
697     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
698
699     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
700     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
701
702     return true;
703
704   } // end if(F1 && F2)
705
706   // check case of quadratic faces
707   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle)
708     return false;
709   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle)
710     return false;
711
712   //       5
713   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
714   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
715   //    |   / |
716   //  7 +  +  + 6
717   //    | /9  |
718   //    |/    |
719   //  4 +--+--+ 3
720   //       8
721
722   const SMDS_MeshNode* N1 [6];
723   const SMDS_MeshNode* N2 [6];
724   if(!GetNodesFromTwoTria(theTria1,theTria2,N1,N2))
725     return false;
726   // now we receive following N1 and N2 (using numeration as above image)
727   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
728   // i.e. first nodes from both arrays determ new diagonal
729
730   const SMDS_MeshNode* N1new [6];
731   const SMDS_MeshNode* N2new [6];
732   N1new[0] = N1[0];
733   N1new[1] = N2[0];
734   N1new[2] = N2[1];
735   N1new[3] = N1[4];
736   N1new[4] = N2[3];
737   N1new[5] = N1[5];
738   N2new[0] = N1[0];
739   N2new[1] = N1[1];
740   N2new[2] = N2[0];
741   N2new[3] = N1[3];
742   N2new[4] = N2[5];
743   N2new[5] = N1[4];
744   // replaces nodes in faces
745   GetMeshDS()->ChangeElementNodes( theTria1, N1new, 6 );
746   GetMeshDS()->ChangeElementNodes( theTria2, N2new, 6 );
747
748   return true;
749 }
750
751 //=======================================================================
752 //function : findTriangles
753 //purpose  : find triangles sharing theNode1-theNode2 link
754 //=======================================================================
755
756 static bool findTriangles(const SMDS_MeshNode *    theNode1,
757                           const SMDS_MeshNode *    theNode2,
758                           const SMDS_MeshElement*& theTria1,
759                           const SMDS_MeshElement*& theTria2)
760 {
761   if ( !theNode1 || !theNode2 ) return false;
762
763   theTria1 = theTria2 = 0;
764
765   set< const SMDS_MeshElement* > emap;
766   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
767   while (it->more()) {
768     const SMDS_MeshElement* elem = it->next();
769     if ( elem->NbNodes() == 3 )
770       emap.insert( elem );
771   }
772   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
773   while (it->more()) {
774     const SMDS_MeshElement* elem = it->next();
775     if ( emap.find( elem ) != emap.end() ) {
776       if ( theTria1 ) {
777         // theTria1 must be element with minimum ID
778         if( theTria1->GetID() < elem->GetID() ) {
779           theTria2 = elem;
780         }
781         else {
782           theTria2 = theTria1;
783           theTria1 = elem;
784         }
785         break;
786       }
787       else {
788         theTria1 = elem;
789       }
790     }
791   }
792   return ( theTria1 && theTria2 );
793 }
794
795 //=======================================================================
796 //function : InverseDiag
797 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
798 //           with ones built on the same 4 nodes but having other common link.
799 //           Return false if proper faces not found
800 //=======================================================================
801
802 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
803                                     const SMDS_MeshNode * theNode2)
804 {
805   myLastCreatedElems.Clear();
806   myLastCreatedNodes.Clear();
807
808   MESSAGE( "::InverseDiag()" );
809
810   const SMDS_MeshElement *tr1, *tr2;
811   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
812     return false;
813
814   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
815   if (!F1) return false;
816   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
817   if (!F2) return false;
818   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
819       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
820
821     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
822     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
823     //    |/ |                                    | \|
824     //  B +--+ 2                                B +--+ 2
825
826     // put nodes in array
827     // and find indices of 1,2 and of A in tr1 and of B in tr2
828     int i, iA1 = 0, i1 = 0;
829     const SMDS_MeshNode* aNodes1 [3];
830     SMDS_ElemIteratorPtr it;
831     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
832       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
833       if ( aNodes1[ i ] == theNode1 )
834         iA1 = i; // node A in tr1
835       else if ( aNodes1[ i ] != theNode2 )
836         i1 = i;  // node 1
837     }
838     int iB2 = 0, i2 = 0;
839     const SMDS_MeshNode* aNodes2 [3];
840     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
841       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
842       if ( aNodes2[ i ] == theNode2 )
843         iB2 = i; // node B in tr2
844       else if ( aNodes2[ i ] != theNode1 )
845         i2 = i;  // node 2
846     }
847
848     // nodes 1 and 2 should not be the same
849     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
850       return false;
851
852     // tr1: A->2
853     aNodes1[ iA1 ] = aNodes2[ i2 ];
854     // tr2: B->1
855     aNodes2[ iB2 ] = aNodes1[ i1 ];
856
857     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
858     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
859
860     return true;
861   }
862
863   // check case of quadratic faces
864   return InverseDiag(tr1,tr2);
865 }
866
867 //=======================================================================
868 //function : getQuadrangleNodes
869 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
870 //           fusion of triangles tr1 and tr2 having shared link on
871 //           theNode1 and theNode2
872 //=======================================================================
873
874 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
875                         const SMDS_MeshNode *    theNode1,
876                         const SMDS_MeshNode *    theNode2,
877                         const SMDS_MeshElement * tr1,
878                         const SMDS_MeshElement * tr2 )
879 {
880   if( tr1->NbNodes() != tr2->NbNodes() )
881     return false;
882   // find the 4-th node to insert into tr1
883   const SMDS_MeshNode* n4 = 0;
884   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
885   int i=0;
886   while ( !n4 && i<3 ) {
887     const SMDS_MeshNode * n = cast2Node( it->next() );
888     i++;
889     bool isDiag = ( n == theNode1 || n == theNode2 );
890     if ( !isDiag )
891       n4 = n;
892   }
893   // Make an array of nodes to be in a quadrangle
894   int iNode = 0, iFirstDiag = -1;
895   it = tr1->nodesIterator();
896   i=0;
897   while ( i<3 ) {
898     const SMDS_MeshNode * n = cast2Node( it->next() );
899     i++;
900     bool isDiag = ( n == theNode1 || n == theNode2 );
901     if ( isDiag ) {
902       if ( iFirstDiag < 0 )
903         iFirstDiag = iNode;
904       else if ( iNode - iFirstDiag == 1 )
905         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
906     }
907     else if ( n == n4 ) {
908       return false; // tr1 and tr2 should not have all the same nodes
909     }
910     theQuadNodes[ iNode++ ] = n;
911   }
912   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
913     theQuadNodes[ iNode ] = n4;
914
915   return true;
916 }
917
918 //=======================================================================
919 //function : DeleteDiag
920 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
921 //           with a quadrangle built on the same 4 nodes.
922 //           Return false if proper faces not found
923 //=======================================================================
924
925 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
926                                    const SMDS_MeshNode * theNode2)
927 {
928   myLastCreatedElems.Clear();
929   myLastCreatedNodes.Clear();
930
931   MESSAGE( "::DeleteDiag()" );
932
933   const SMDS_MeshElement *tr1, *tr2;
934   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
935     return false;
936
937   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
938   if (!F1) return false;
939   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
940   if (!F2) return false;
941   SMESHDS_Mesh * aMesh = GetMeshDS();
942
943   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
944       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
945
946     const SMDS_MeshNode* aNodes [ 4 ];
947     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
948       return false;
949
950     const SMDS_MeshElement* newElem = 0;
951     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
952     myLastCreatedElems.Append(newElem);
953     AddToSameGroups( newElem, tr1, aMesh );
954     int aShapeId = tr1->getshapeId();
955     if ( aShapeId )
956       {
957         aMesh->SetMeshElementOnShape( newElem, aShapeId );
958       }
959     aMesh->RemoveElement( tr1 );
960     aMesh->RemoveElement( tr2 );
961
962     return true;
963   }
964
965   // check case of quadratic faces
966   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
967     return false;
968   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
969     return false;
970
971   //       5
972   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
973   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
974   //    |   / |
975   //  7 +  +  + 6
976   //    | /9  |
977   //    |/    |
978   //  4 +--+--+ 3
979   //       8
980
981   const SMDS_MeshNode* N1 [6];
982   const SMDS_MeshNode* N2 [6];
983   if(!GetNodesFromTwoTria(tr1,tr2,N1,N2))
984     return false;
985   // now we receive following N1 and N2 (using numeration as above image)
986   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
987   // i.e. first nodes from both arrays determ new diagonal
988
989   const SMDS_MeshNode* aNodes[8];
990   aNodes[0] = N1[0];
991   aNodes[1] = N1[1];
992   aNodes[2] = N2[0];
993   aNodes[3] = N2[1];
994   aNodes[4] = N1[3];
995   aNodes[5] = N2[5];
996   aNodes[6] = N2[3];
997   aNodes[7] = N1[5];
998
999   const SMDS_MeshElement* newElem = 0;
1000   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1001                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1002   myLastCreatedElems.Append(newElem);
1003   AddToSameGroups( newElem, tr1, aMesh );
1004   int aShapeId = tr1->getshapeId();
1005   if ( aShapeId )
1006     {
1007       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1008     }
1009   aMesh->RemoveElement( tr1 );
1010   aMesh->RemoveElement( tr2 );
1011
1012   // remove middle node (9)
1013   GetMeshDS()->RemoveNode( N1[4] );
1014
1015   return true;
1016 }
1017
1018 //=======================================================================
1019 //function : Reorient
1020 //purpose  : Reverse theElement orientation
1021 //=======================================================================
1022
1023 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1024 {
1025   MESSAGE("Reorient");
1026   myLastCreatedElems.Clear();
1027   myLastCreatedNodes.Clear();
1028
1029   if (!theElem)
1030     return false;
1031   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1032   if ( !it || !it->more() )
1033     return false;
1034
1035   switch ( theElem->GetType() ) {
1036
1037   case SMDSAbs_Edge:
1038   case SMDSAbs_Face: {
1039     if(!theElem->IsQuadratic()) {
1040       int i = theElem->NbNodes();
1041       vector<const SMDS_MeshNode*> aNodes( i );
1042       while ( it->more() )
1043         aNodes[ --i ]= static_cast<const SMDS_MeshNode*>( it->next() );
1044       return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], theElem->NbNodes() );
1045     }
1046     else {
1047       // quadratic elements
1048       if(theElem->GetType()==SMDSAbs_Edge) {
1049         vector<const SMDS_MeshNode*> aNodes(3);
1050         aNodes[1]= static_cast<const SMDS_MeshNode*>( it->next() );
1051         aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1052         aNodes[2]= static_cast<const SMDS_MeshNode*>( it->next() );
1053         return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], 3 );
1054       }
1055       else {
1056         int nbn = theElem->NbNodes();
1057         vector<const SMDS_MeshNode*> aNodes(nbn);
1058         aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1059         int i=1;
1060         for(; i<nbn/2; i++) {
1061           aNodes[nbn/2-i]= static_cast<const SMDS_MeshNode*>( it->next() );
1062         }
1063         for(i=0; i<nbn/2; i++) {
1064           aNodes[nbn-i-1]= static_cast<const SMDS_MeshNode*>( it->next() );
1065         }
1066         return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], nbn );
1067       }
1068     }
1069   }
1070   case SMDSAbs_Volume: {
1071     if (theElem->IsPoly()) {
1072       // TODO reorient vtk polyhedron
1073       MESSAGE("reorient vtk polyhedron ?");
1074       const SMDS_VtkVolume* aPolyedre =
1075         dynamic_cast<const SMDS_VtkVolume*>( theElem );
1076       if (!aPolyedre) {
1077         MESSAGE("Warning: bad volumic element");
1078         return false;
1079       }
1080
1081       int nbFaces = aPolyedre->NbFaces();
1082       vector<const SMDS_MeshNode *> poly_nodes;
1083       vector<int> quantities (nbFaces);
1084
1085       // reverse each face of the polyedre
1086       for (int iface = 1; iface <= nbFaces; iface++) {
1087         int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1088         quantities[iface - 1] = nbFaceNodes;
1089
1090         for (inode = nbFaceNodes; inode >= 1; inode--) {
1091           const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1092           poly_nodes.push_back(curNode);
1093         }
1094       }
1095
1096       return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1097
1098     }
1099     else {
1100       SMDS_VolumeTool vTool;
1101       if ( !vTool.Set( theElem ))
1102         return false;
1103       vTool.Inverse();
1104       MESSAGE("ChangeElementNodes reorient: check vTool.Inverse");
1105       return GetMeshDS()->ChangeElementNodes( theElem, vTool.GetNodes(), vTool.NbNodes() );
1106     }
1107   }
1108   default:;
1109   }
1110
1111   return false;
1112 }
1113
1114 //================================================================================
1115 /*!
1116  * \brief Reorient faces.
1117  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1118  * \param theDirection - desired direction of normal of \a theFace
1119  * \param theFace - one of \a theFaces that sould be oriented according to
1120  *        \a theDirection and whose orientation defines orientation of other faces
1121  * \return number of reoriented faces.
1122  */
1123 //================================================================================
1124
1125 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1126                                   const gp_Dir&            theDirection,
1127                                   const SMDS_MeshElement * theFace)
1128 {
1129   int nbReori = 0;
1130   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1131
1132   if ( theFaces.empty() )
1133   {
1134     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1135     while ( fIt->more() )
1136       theFaces.insert( theFaces.end(), fIt->next() );
1137   }
1138
1139   // orient theFace according to theDirection
1140   gp_XYZ normal;
1141   SMESH_Algo::FaceNormal( theFace, normal, /*normalized=*/false );
1142   if ( normal * theDirection.XYZ() < 0 )
1143     nbReori += Reorient( theFace );
1144
1145   // Orient other faces
1146
1147   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1148   TIDSortedElemSet avoidSet;
1149   set< SMESH_TLink > checkedLinks;
1150   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1151
1152   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1153     theFaces.erase( theFace );
1154   startFaces.insert( theFace );
1155
1156   int nodeInd1, nodeInd2;
1157   const SMDS_MeshElement*           otherFace;
1158   vector< const SMDS_MeshElement* > facesNearLink;
1159   vector< std::pair< int, int > >   nodeIndsOfFace;
1160
1161   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1162   while ( !startFaces.empty() )
1163   {
1164     startFace = startFaces.begin();
1165     theFace = *startFace;
1166     startFaces.erase( startFace );
1167     if ( !visitedFaces.insert( theFace ).second )
1168       continue;
1169
1170     avoidSet.clear();
1171     avoidSet.insert(theFace);
1172
1173     NLink link( theFace->GetNode( 0 ), 0 );
1174
1175     const int nbNodes = theFace->NbCornerNodes();
1176     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1177     {
1178       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1179       linkIt_isNew = checkedLinks.insert( link );
1180       if ( !linkIt_isNew.second )
1181       {
1182         // link has already been checked and won't be encountered more
1183         // if the group (theFaces) is manifold
1184         //checkedLinks.erase( linkIt_isNew.first );
1185       }
1186       else
1187       {
1188         facesNearLink.clear();
1189         nodeIndsOfFace.clear();
1190         while (( otherFace = FindFaceInSet( link.first, link.second,
1191                                             theFaces, avoidSet, &nodeInd1, &nodeInd2 )))
1192           if ( otherFace != theFace)
1193           {
1194             facesNearLink.push_back( otherFace );
1195             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1196             avoidSet.insert( otherFace );
1197           }
1198         if ( facesNearLink.size() > 1 )
1199         {
1200           // NON-MANIFOLD mesh shell !
1201           // select a face most co-directed with theFace,
1202           // other faces won't be visited this time
1203           gp_XYZ NF, NOF;
1204           SMESH_Algo::FaceNormal( theFace, NF, /*normalized=*/false );
1205           double proj, maxProj = -1;
1206           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1207             SMESH_Algo::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1208             if (( proj = Abs( NF * NOF )) > maxProj ) {
1209               maxProj = proj;
1210               otherFace = facesNearLink[i];
1211               nodeInd1  = nodeIndsOfFace[i].first;
1212               nodeInd2  = nodeIndsOfFace[i].second;
1213             }
1214           }
1215           // not to visit rejected faces
1216           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1217             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1218               visitedFaces.insert( facesNearLink[i] );
1219         }
1220         else if ( facesNearLink.size() == 1 )
1221         {
1222           otherFace = facesNearLink[0];
1223           nodeInd1  = nodeIndsOfFace.back().first;
1224           nodeInd2  = nodeIndsOfFace.back().second;
1225         }
1226         if ( otherFace && otherFace != theFace)
1227         {
1228           // link must be reverse in otherFace if orientation ot otherFace
1229           // is same as that of theFace
1230           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1231           {
1232             nbReori += Reorient( otherFace );
1233           }
1234           startFaces.insert( otherFace );
1235         }
1236       }
1237       std::swap( link.first, link.second ); // reverse the link
1238     }
1239   }
1240   return nbReori;
1241 }
1242
1243 //=======================================================================
1244 //function : getBadRate
1245 //purpose  :
1246 //=======================================================================
1247
1248 static double getBadRate (const SMDS_MeshElement*               theElem,
1249                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1250 {
1251   SMESH::Controls::TSequenceOfXYZ P;
1252   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1253     return 1e100;
1254   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1255   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1256 }
1257
1258 //=======================================================================
1259 //function : QuadToTri
1260 //purpose  : Cut quadrangles into triangles.
1261 //           theCrit is used to select a diagonal to cut
1262 //=======================================================================
1263
1264 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1265                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1266 {
1267   myLastCreatedElems.Clear();
1268   myLastCreatedNodes.Clear();
1269
1270   MESSAGE( "::QuadToTri()" );
1271
1272   if ( !theCrit.get() )
1273     return false;
1274
1275   SMESHDS_Mesh * aMesh = GetMeshDS();
1276
1277   Handle(Geom_Surface) surface;
1278   SMESH_MesherHelper   helper( *GetMesh() );
1279
1280   TIDSortedElemSet::iterator itElem;
1281   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1282     const SMDS_MeshElement* elem = *itElem;
1283     if ( !elem || elem->GetType() != SMDSAbs_Face )
1284       continue;
1285     if ( elem->NbCornerNodes() != 4 )
1286       continue;
1287
1288     // retrieve element nodes
1289     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1290
1291     // compare two sets of possible triangles
1292     double aBadRate1, aBadRate2; // to what extent a set is bad
1293     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1294     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1295     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1296
1297     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1298     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1299     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1300
1301     int aShapeId = FindShape( elem );
1302     const SMDS_MeshElement* newElem1 = 0;
1303     const SMDS_MeshElement* newElem2 = 0;
1304
1305     if( !elem->IsQuadratic() ) {
1306
1307       // split liner quadrangle
1308       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1309       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1310       if ( aBadRate1 <= aBadRate2 ) {
1311         // tr1 + tr2 is better
1312         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1313         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1314       }
1315       else {
1316         // tr3 + tr4 is better
1317         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1318         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1319       }
1320     }
1321     else {
1322
1323       // split quadratic quadrangle
1324
1325       // get surface elem is on
1326       if ( aShapeId != helper.GetSubShapeID() ) {
1327         surface.Nullify();
1328         TopoDS_Shape shape;
1329         if ( aShapeId > 0 )
1330           shape = aMesh->IndexToShape( aShapeId );
1331         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1332           TopoDS_Face face = TopoDS::Face( shape );
1333           surface = BRep_Tool::Surface( face );
1334           if ( !surface.IsNull() )
1335             helper.SetSubShape( shape );
1336         }
1337       }
1338       // find middle point for (0,1,2,3)
1339       // and create a node in this point;
1340       const SMDS_MeshNode* newN = 0;
1341       if ( aNodes.size() == 9 )
1342       {
1343         // SMDSEntity_BiQuad_Quadrangle
1344         newN = aNodes.back();
1345       }
1346       else
1347       {
1348         gp_XYZ p( 0,0,0 );
1349         if ( surface.IsNull() )
1350         {
1351           for ( int i = 0; i < 4; i++ )
1352             p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1353           p /= 4;
1354         }
1355         else
1356         {
1357           const SMDS_MeshNode* inFaceNode = 0;
1358           if ( helper.GetNodeUVneedInFaceNode() )
1359             for ( size_t i = 0; i < aNodes.size() && !inFaceNode; ++i )
1360               if ( aNodes[ i ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1361                 inFaceNode = aNodes[ i ];
1362
1363           TopoDS_Face face = TopoDS::Face( helper.GetSubShape() );
1364           gp_XY uv( 0,0 );
1365           for ( int i = 0; i < 4; i++ )
1366             uv += helper.GetNodeUV( face, aNodes[i], inFaceNode );
1367           uv /= 4.;
1368           p = surface->Value( uv.X(), uv.Y() ).XYZ();
1369         }
1370         newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1371         myLastCreatedNodes.Append(newN);
1372       }
1373       // create a new element
1374       if ( aBadRate1 <= aBadRate2 ) {
1375         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1376                                   aNodes[6], aNodes[7], newN );
1377         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
1378                                   newN,      aNodes[4], aNodes[5] );
1379       }
1380       else {
1381         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1382                                   aNodes[7], aNodes[4], newN );
1383         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
1384                                   newN,      aNodes[5], aNodes[6] );
1385       }
1386     } // quadratic case
1387
1388     // care of a new element
1389
1390     myLastCreatedElems.Append(newElem1);
1391     myLastCreatedElems.Append(newElem2);
1392     AddToSameGroups( newElem1, elem, aMesh );
1393     AddToSameGroups( newElem2, elem, aMesh );
1394
1395     // put a new triangle on the same shape
1396     if ( aShapeId )
1397       {
1398         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1399         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1400       }
1401     aMesh->RemoveElement( elem );
1402   }
1403   return true;
1404 }
1405
1406 //=======================================================================
1407 //function : BestSplit
1408 //purpose  : Find better diagonal for cutting.
1409 //=======================================================================
1410
1411 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1412                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1413 {
1414   myLastCreatedElems.Clear();
1415   myLastCreatedNodes.Clear();
1416
1417   if (!theCrit.get())
1418     return -1;
1419
1420   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1421     return -1;
1422
1423   if( theQuad->NbNodes()==4 ||
1424       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1425
1426     // retrieve element nodes
1427     const SMDS_MeshNode* aNodes [4];
1428     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1429     int i = 0;
1430     //while (itN->more())
1431     while (i<4) {
1432       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1433     }
1434     // compare two sets of possible triangles
1435     double aBadRate1, aBadRate2; // to what extent a set is bad
1436     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1437     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1438     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1439
1440     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1441     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1442     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1443     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1444     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1445     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1446       return 1; // diagonal 1-3
1447
1448     return 2; // diagonal 2-4
1449   }
1450   return -1;
1451 }
1452
1453 namespace
1454 {
1455   // Methods of splitting volumes into tetra
1456
1457   const int theHexTo5_1[5*4+1] =
1458     {
1459       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1460     };
1461   const int theHexTo5_2[5*4+1] =
1462     {
1463       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1464     };
1465   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1466
1467   const int theHexTo6_1[6*4+1] =
1468     {
1469       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
1470     };
1471   const int theHexTo6_2[6*4+1] =
1472     {
1473       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
1474     };
1475   const int theHexTo6_3[6*4+1] =
1476     {
1477       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
1478     };
1479   const int theHexTo6_4[6*4+1] =
1480     {
1481       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
1482     };
1483   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1484
1485   const int thePyraTo2_1[2*4+1] =
1486     {
1487       0, 1, 2, 4,    0, 2, 3, 4,   -1
1488     };
1489   const int thePyraTo2_2[2*4+1] =
1490     {
1491       1, 2, 3, 4,    1, 3, 0, 4,   -1
1492     };
1493   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1494
1495   const int thePentaTo3_1[3*4+1] =
1496     {
1497       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1498     };
1499   const int thePentaTo3_2[3*4+1] =
1500     {
1501       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1502     };
1503   const int thePentaTo3_3[3*4+1] =
1504     {
1505       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1506     };
1507   const int thePentaTo3_4[3*4+1] =
1508     {
1509       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1510     };
1511   const int thePentaTo3_5[3*4+1] =
1512     {
1513       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1514     };
1515   const int thePentaTo3_6[3*4+1] =
1516     {
1517       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1518     };
1519   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1520                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1521
1522   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1523   {
1524     int _n1, _n2, _n3;
1525     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1526     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1527     bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1528   };
1529   struct TSplitMethod
1530   {
1531     int        _nbTetra;
1532     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1533     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1534     bool       _ownConn;      //!< to delete _connectivity in destructor
1535     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1536
1537     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1538       : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1539     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1540     bool hasFacet( const TTriangleFacet& facet ) const
1541     {
1542       const int* tetConn = _connectivity;
1543       for ( ; tetConn[0] >= 0; tetConn += 4 )
1544         if (( facet.contains( tetConn[0] ) +
1545               facet.contains( tetConn[1] ) +
1546               facet.contains( tetConn[2] ) +
1547               facet.contains( tetConn[3] )) == 3 )
1548           return true;
1549       return false;
1550     }
1551   };
1552
1553   //=======================================================================
1554   /*!
1555    * \brief return TSplitMethod for the given element
1556    */
1557   //=======================================================================
1558
1559   TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1560   {
1561     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1562
1563     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1564     // an edge and a face barycenter; tertaherdons are based on triangles and
1565     // a volume barycenter
1566     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1567
1568     // Find out how adjacent volumes are split
1569
1570     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1571     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1572     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1573     {
1574       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1575       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1576       if ( nbNodes < 4 ) continue;
1577
1578       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1579       const int* nInd = vol.GetFaceNodesIndices( iF );
1580       if ( nbNodes == 4 )
1581       {
1582         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1583         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1584         if      ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1585         else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1586       }
1587       else
1588       {
1589         int iCom = 0; // common node of triangle faces to split into
1590         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1591         {
1592           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1593                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1594                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1595           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1596                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1597                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1598           if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1599           {
1600             triaSplits.push_back( t012 );
1601             triaSplits.push_back( t023 );
1602             break;
1603           }
1604         }
1605       }
1606       if ( !triaSplits.empty() )
1607         hasAdjacentSplits = true;
1608     }
1609
1610     // Among variants of split method select one compliant with adjacent volumes
1611
1612     TSplitMethod method;
1613     if ( !vol.Element()->IsPoly() && !is24TetMode )
1614     {
1615       int nbVariants = 2, nbTet = 0;
1616       const int** connVariants = 0;
1617       switch ( vol.Element()->GetEntityType() )
1618       {
1619       case SMDSEntity_Hexa:
1620       case SMDSEntity_Quad_Hexa:
1621       case SMDSEntity_TriQuad_Hexa:
1622         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1623           connVariants = theHexTo5, nbTet = 5;
1624         else
1625           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1626         break;
1627       case SMDSEntity_Pyramid:
1628       case SMDSEntity_Quad_Pyramid:
1629         connVariants = thePyraTo2;  nbTet = 2;
1630         break;
1631       case SMDSEntity_Penta:
1632       case SMDSEntity_Quad_Penta:
1633         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1634         break;
1635       default:
1636         nbVariants = 0;
1637       }
1638       for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1639       {
1640         // check method compliancy with adjacent tetras,
1641         // all found splits must be among facets of tetras described by this method
1642         method = TSplitMethod( nbTet, connVariants[variant] );
1643         if ( hasAdjacentSplits && method._nbTetra > 0 )
1644         {
1645           bool facetCreated = true;
1646           for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1647           {
1648             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1649             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1650               facetCreated = method.hasFacet( *facet );
1651           }
1652           if ( !facetCreated )
1653             method = TSplitMethod(0); // incompatible method
1654         }
1655       }
1656     }
1657     if ( method._nbTetra < 1 )
1658     {
1659       // No standard method is applicable, use a generic solution:
1660       // each facet of a volume is split into triangles and
1661       // each of triangles and a volume barycenter form a tetrahedron.
1662
1663       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1664
1665       int* connectivity = new int[ maxTetConnSize + 1 ];
1666       method._connectivity = connectivity;
1667       method._ownConn = true;
1668       method._baryNode = !isHex27; // to create central node or not
1669
1670       int connSize = 0;
1671       int baryCenInd = vol.NbNodes() - int( isHex27 );
1672       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1673       {
1674         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1675         const int*   nInd = vol.GetFaceNodesIndices( iF );
1676         // find common node of triangle facets of tetra to create
1677         int iCommon = 0; // index in linear numeration
1678         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1679         if ( !triaSplits.empty() )
1680         {
1681           // by found facets
1682           const TTriangleFacet* facet = &triaSplits.front();
1683           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1684             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1685                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1686               break;
1687         }
1688         else if ( nbNodes > 3 && !is24TetMode )
1689         {
1690           // find the best method of splitting into triangles by aspect ratio
1691           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1692           map< double, int > badness2iCommon;
1693           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1694           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1695           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1696           {
1697             double badness = 0;
1698             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1699             {
1700               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1701                                       nodes[ iQ*((iLast-1)%nbNodes)],
1702                                       nodes[ iQ*((iLast  )%nbNodes)]);
1703               badness += getBadRate( &tria, aspectRatio );
1704             }
1705             badness2iCommon.insert( make_pair( badness, iCommon ));
1706           }
1707           // use iCommon with lowest badness
1708           iCommon = badness2iCommon.begin()->second;
1709         }
1710         if ( iCommon >= nbNodes )
1711           iCommon = 0; // something wrong
1712
1713         // fill connectivity of tetrahedra based on a current face
1714         int nbTet = nbNodes - 2;
1715         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1716         {
1717           int faceBaryCenInd;
1718           if ( isHex27 )
1719           {
1720             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1721             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1722           }
1723           else
1724           {
1725             method._faceBaryNode[ iF ] = 0;
1726             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1727           }
1728           nbTet = nbNodes;
1729           for ( int i = 0; i < nbTet; ++i )
1730           {
1731             int i1 = i, i2 = (i+1) % nbNodes;
1732             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1733             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1734             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1735             connectivity[ connSize++ ] = faceBaryCenInd;
1736             connectivity[ connSize++ ] = baryCenInd;
1737           }
1738         }
1739         else
1740         {
1741           for ( int i = 0; i < nbTet; ++i )
1742           {
1743             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1744             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1745             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1746             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1747             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1748             connectivity[ connSize++ ] = baryCenInd;
1749           }
1750         }
1751         method._nbTetra += nbTet;
1752
1753       } // loop on volume faces
1754
1755       connectivity[ connSize++ ] = -1;
1756
1757     } // end of generic solution
1758
1759     return method;
1760   }
1761   //================================================================================
1762   /*!
1763    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1764    */
1765   //================================================================================
1766
1767   bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1768   {
1769     // find the tetrahedron including the three nodes of facet
1770     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1771     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1772     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1773     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1774     while ( volIt1->more() )
1775     {
1776       const SMDS_MeshElement* v = volIt1->next();
1777       SMDSAbs_EntityType type = v->GetEntityType();
1778       if ( type != SMDSEntity_Tetra && type != SMDSEntity_Quad_Tetra )
1779         continue;
1780       if ( type == SMDSEntity_Quad_Tetra && v->GetNodeIndex( n1 ) > 3 )
1781         continue; // medium node not allowed
1782       const int ind2 = v->GetNodeIndex( n2 );
1783       if ( ind2 < 0 || 3 < ind2 )
1784         continue;
1785       const int ind3 = v->GetNodeIndex( n3 );
1786       if ( ind3 < 0 || 3 < ind3 )
1787         continue;
1788       return true;
1789     }
1790     return false;
1791   }
1792
1793   //=======================================================================
1794   /*!
1795    * \brief A key of a face of volume
1796    */
1797   //=======================================================================
1798
1799   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
1800   {
1801     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1802     {
1803       TIDSortedNodeSet sortedNodes;
1804       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1805       int nbNodes = vol.NbFaceNodes( iF );
1806       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1807       for ( int i = 0; i < nbNodes; i += iQ )
1808         sortedNodes.insert( fNodes[i] );
1809       TIDSortedNodeSet::iterator n = sortedNodes.begin();
1810       first.first   = (*(n++))->GetID();
1811       first.second  = (*(n++))->GetID();
1812       second.first  = (*(n++))->GetID();
1813       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
1814     }
1815   };
1816 } // namespace
1817
1818 //=======================================================================
1819 //function : SplitVolumesIntoTetra
1820 //purpose  : Split volume elements into tetrahedra.
1821 //=======================================================================
1822
1823 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1824                                               const int                theMethodFlags)
1825 {
1826   // std-like iterator on coordinates of nodes of mesh element
1827   typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1828   NXyzIterator xyzEnd;
1829
1830   SMDS_VolumeTool    volTool;
1831   SMESH_MesherHelper helper( *GetMesh());
1832
1833   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1834   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1835
1836   SMESH_SequenceOfElemPtr newNodes, newElems;
1837
1838   // map face of volume to it's baricenrtic node
1839   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1840   double bc[3];
1841
1842   TIDSortedElemSet::const_iterator elem = theElems.begin();
1843   for ( ; elem != theElems.end(); ++elem )
1844   {
1845     if ( (*elem)->GetType() != SMDSAbs_Volume )
1846       continue;
1847     SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1848     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
1849       continue;
1850
1851     if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange...
1852
1853     TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1854     if ( splitMethod._nbTetra < 1 ) continue;
1855
1856     // find submesh to add new tetras to
1857     if ( !subMesh || !subMesh->Contains( *elem ))
1858     {
1859       int shapeID = FindShape( *elem );
1860       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1861       subMesh = GetMeshDS()->MeshElements( shapeID );
1862     }
1863     int iQ;
1864     if ( (*elem)->IsQuadratic() )
1865     {
1866       iQ = 2;
1867       // add quadratic links to the helper
1868       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1869       {
1870         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1871         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
1872         for ( int iN = 0; iN < nbN; iN += iQ )
1873           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
1874       }
1875       helper.SetIsQuadratic( true );
1876     }
1877     else
1878     {
1879       iQ = 1;
1880       helper.SetIsQuadratic( false );
1881     }
1882     vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1883     helper.SetElementsOnShape( true );
1884     if ( splitMethod._baryNode )
1885     {
1886       // make a node at barycenter
1887       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1888       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1889       nodes.push_back( gcNode );
1890       newNodes.Append( gcNode );
1891     }
1892     if ( !splitMethod._faceBaryNode.empty() )
1893     {
1894       // make or find baricentric nodes of faces
1895       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1896       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1897       {
1898         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1899           volFace2BaryNode.insert
1900           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
1901         if ( !f_n->second )
1902         {
1903           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1904           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1905         }
1906         nodes.push_back( iF_n->second = f_n->second );
1907       }
1908     }
1909
1910     // make tetras
1911     vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1912     const int* tetConn = splitMethod._connectivity;
1913     for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1914       newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1915                                                        nodes[ tetConn[1] ],
1916                                                        nodes[ tetConn[2] ],
1917                                                        nodes[ tetConn[3] ]));
1918
1919     ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
1920
1921     // Split faces on sides of the split volume
1922
1923     const SMDS_MeshNode** volNodes = volTool.GetNodes();
1924     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1925     {
1926       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
1927       if ( nbNodes < 4 ) continue;
1928
1929       // find an existing face
1930       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
1931                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
1932       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
1933                                                                        /*noMedium=*/false))
1934       {
1935         // make triangles
1936         helper.SetElementsOnShape( false );
1937         vector< const SMDS_MeshElement* > triangles;
1938
1939         // find submesh to add new triangles in
1940         if ( !fSubMesh || !fSubMesh->Contains( face ))
1941         {
1942           int shapeID = FindShape( face );
1943           fSubMesh = GetMeshDS()->MeshElements( shapeID );
1944         }
1945         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
1946         if ( iF_n != splitMethod._faceBaryNode.end() )
1947         {
1948           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
1949           {
1950             const SMDS_MeshNode* n1 = fNodes[iN];
1951             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
1952             const SMDS_MeshNode *n3 = iF_n->second;
1953             if ( !volTool.IsFaceExternal( iF ))
1954               swap( n2, n3 );
1955             triangles.push_back( helper.AddFace( n1,n2,n3 ));
1956
1957             if ( fSubMesh && n3->getshapeId() < 1 )
1958               fSubMesh->AddNode( n3 );
1959           }
1960         }
1961         else
1962         {
1963           // among possible triangles create ones discribed by split method
1964           const int* nInd = volTool.GetFaceNodesIndices( iF );
1965           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1966           int iCom = 0; // common node of triangle faces to split into
1967           list< TTriangleFacet > facets;
1968           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
1969           {
1970             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
1971                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
1972                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
1973             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
1974                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
1975                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
1976             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
1977             {
1978               facets.push_back( t012 );
1979               facets.push_back( t023 );
1980               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
1981                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
1982                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
1983                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
1984               break;
1985             }
1986           }
1987           list< TTriangleFacet >::iterator facet = facets.begin();
1988           for ( ; facet != facets.end(); ++facet )
1989           {
1990             if ( !volTool.IsFaceExternal( iF ))
1991               swap( facet->_n2, facet->_n3 );
1992             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
1993                                                  volNodes[ facet->_n2 ],
1994                                                  volNodes[ facet->_n3 ]));
1995           }
1996         }
1997         for ( int i = 0; i < triangles.size(); ++i )
1998         {
1999           if ( !triangles[i] ) continue;
2000           if ( fSubMesh )
2001             fSubMesh->AddElement( triangles[i]);
2002           newElems.Append( triangles[i] );
2003         }
2004         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2005         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2006       }
2007
2008     } // loop on volume faces to split them into triangles
2009
2010     GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
2011
2012     if ( geomType == SMDSEntity_TriQuad_Hexa )
2013     {
2014       // remove medium nodes that could become free
2015       for ( int i = 20; i < volTool.NbNodes(); ++i )
2016         if ( volNodes[i]->NbInverseElements() == 0 )
2017           GetMeshDS()->RemoveNode( volNodes[i] );
2018     }
2019   } // loop on volumes to split
2020
2021   myLastCreatedNodes = newNodes;
2022   myLastCreatedElems = newElems;
2023 }
2024
2025 //=======================================================================
2026 //function : AddToSameGroups
2027 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2028 //=======================================================================
2029
2030 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2031                                         const SMDS_MeshElement* elemInGroups,
2032                                         SMESHDS_Mesh *          aMesh)
2033 {
2034   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2035   if (!groups.empty()) {
2036     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2037     for ( ; grIt != groups.end(); grIt++ ) {
2038       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2039       if ( group && group->Contains( elemInGroups ))
2040         group->SMDSGroup().Add( elemToAdd );
2041     }
2042   }
2043 }
2044
2045
2046 //=======================================================================
2047 //function : RemoveElemFromGroups
2048 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2049 //=======================================================================
2050 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2051                                              SMESHDS_Mesh *          aMesh)
2052 {
2053   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2054   if (!groups.empty())
2055   {
2056     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2057     for (; GrIt != groups.end(); GrIt++)
2058     {
2059       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2060       if (!grp || grp->IsEmpty()) continue;
2061       grp->SMDSGroup().Remove(removeelem);
2062     }
2063   }
2064 }
2065
2066 //================================================================================
2067 /*!
2068  * \brief Replace elemToRm by elemToAdd in the all groups
2069  */
2070 //================================================================================
2071
2072 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2073                                             const SMDS_MeshElement* elemToAdd,
2074                                             SMESHDS_Mesh *          aMesh)
2075 {
2076   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2077   if (!groups.empty()) {
2078     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2079     for ( ; grIt != groups.end(); grIt++ ) {
2080       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2081       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2082         group->SMDSGroup().Add( elemToAdd );
2083     }
2084   }
2085 }
2086
2087 //================================================================================
2088 /*!
2089  * \brief Replace elemToRm by elemToAdd in the all groups
2090  */
2091 //================================================================================
2092
2093 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2094                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2095                                             SMESHDS_Mesh *                         aMesh)
2096 {
2097   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2098   if (!groups.empty())
2099   {
2100     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2101     for ( ; grIt != groups.end(); grIt++ ) {
2102       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2103       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2104         for ( int i = 0; i < elemToAdd.size(); ++i )
2105           group->SMDSGroup().Add( elemToAdd[ i ] );
2106     }
2107   }
2108 }
2109
2110 //=======================================================================
2111 //function : QuadToTri
2112 //purpose  : Cut quadrangles into triangles.
2113 //           theCrit is used to select a diagonal to cut
2114 //=======================================================================
2115
2116 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2117                                   const bool         the13Diag)
2118 {
2119   myLastCreatedElems.Clear();
2120   myLastCreatedNodes.Clear();
2121
2122   MESSAGE( "::QuadToTri()" );
2123
2124   SMESHDS_Mesh * aMesh = GetMeshDS();
2125
2126   Handle(Geom_Surface) surface;
2127   SMESH_MesherHelper   helper( *GetMesh() );
2128
2129   TIDSortedElemSet::iterator itElem;
2130   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2131     const SMDS_MeshElement* elem = *itElem;
2132     if ( !elem || elem->GetType() != SMDSAbs_Face )
2133       continue;
2134     bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2135     if(!isquad) continue;
2136
2137     if(elem->NbNodes()==4) {
2138       // retrieve element nodes
2139       const SMDS_MeshNode* aNodes [4];
2140       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2141       int i = 0;
2142       while ( itN->more() )
2143         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2144
2145       int aShapeId = FindShape( elem );
2146       const SMDS_MeshElement* newElem1 = 0;
2147       const SMDS_MeshElement* newElem2 = 0;
2148       if ( the13Diag ) {
2149         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2150         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2151       }
2152       else {
2153         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2154         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2155       }
2156       myLastCreatedElems.Append(newElem1);
2157       myLastCreatedElems.Append(newElem2);
2158       // put a new triangle on the same shape and add to the same groups
2159       if ( aShapeId )
2160         {
2161           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2162           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2163         }
2164       AddToSameGroups( newElem1, elem, aMesh );
2165       AddToSameGroups( newElem2, elem, aMesh );
2166       //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2167       aMesh->RemoveElement( elem );
2168     }
2169
2170     // Quadratic quadrangle
2171
2172     if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2173
2174       // get surface elem is on
2175       int aShapeId = FindShape( elem );
2176       if ( aShapeId != helper.GetSubShapeID() ) {
2177         surface.Nullify();
2178         TopoDS_Shape shape;
2179         if ( aShapeId > 0 )
2180           shape = aMesh->IndexToShape( aShapeId );
2181         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2182           TopoDS_Face face = TopoDS::Face( shape );
2183           surface = BRep_Tool::Surface( face );
2184           if ( !surface.IsNull() )
2185             helper.SetSubShape( shape );
2186         }
2187       }
2188
2189       const SMDS_MeshNode* aNodes [8];
2190       const SMDS_MeshNode* inFaceNode = 0;
2191       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2192       int i = 0;
2193       while ( itN->more() ) {
2194         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2195         if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2196              aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2197         {
2198           inFaceNode = aNodes[ i-1 ];
2199         }
2200       }
2201
2202       // find middle point for (0,1,2,3)
2203       // and create a node in this point;
2204       gp_XYZ p( 0,0,0 );
2205       if ( surface.IsNull() ) {
2206         for(i=0; i<4; i++)
2207           p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2208         p /= 4;
2209       }
2210       else {
2211         TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2212         gp_XY uv( 0,0 );
2213         for(i=0; i<4; i++)
2214           uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2215         uv /= 4.;
2216         p = surface->Value( uv.X(), uv.Y() ).XYZ();
2217       }
2218       const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2219       myLastCreatedNodes.Append(newN);
2220
2221       // create a new element
2222       const SMDS_MeshElement* newElem1 = 0;
2223       const SMDS_MeshElement* newElem2 = 0;
2224       if ( the13Diag ) {
2225         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2226                                   aNodes[6], aNodes[7], newN );
2227         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2228                                   newN,      aNodes[4], aNodes[5] );
2229       }
2230       else {
2231         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2232                                   aNodes[7], aNodes[4], newN );
2233         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2234                                   newN,      aNodes[5], aNodes[6] );
2235       }
2236       myLastCreatedElems.Append(newElem1);
2237       myLastCreatedElems.Append(newElem2);
2238       // put a new triangle on the same shape and add to the same groups
2239       if ( aShapeId )
2240         {
2241           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2242           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2243         }
2244       AddToSameGroups( newElem1, elem, aMesh );
2245       AddToSameGroups( newElem2, elem, aMesh );
2246       aMesh->RemoveElement( elem );
2247     }
2248   }
2249
2250   return true;
2251 }
2252
2253 //=======================================================================
2254 //function : getAngle
2255 //purpose  :
2256 //=======================================================================
2257
2258 double getAngle(const SMDS_MeshElement * tr1,
2259                 const SMDS_MeshElement * tr2,
2260                 const SMDS_MeshNode *    n1,
2261                 const SMDS_MeshNode *    n2)
2262 {
2263   double angle = 2. * M_PI; // bad angle
2264
2265   // get normals
2266   SMESH::Controls::TSequenceOfXYZ P1, P2;
2267   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2268        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2269     return angle;
2270   gp_Vec N1,N2;
2271   if(!tr1->IsQuadratic())
2272     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2273   else
2274     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2275   if ( N1.SquareMagnitude() <= gp::Resolution() )
2276     return angle;
2277   if(!tr2->IsQuadratic())
2278     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2279   else
2280     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2281   if ( N2.SquareMagnitude() <= gp::Resolution() )
2282     return angle;
2283
2284   // find the first diagonal node n1 in the triangles:
2285   // take in account a diagonal link orientation
2286   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2287   for ( int t = 0; t < 2; t++ ) {
2288     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2289     int i = 0, iDiag = -1;
2290     while ( it->more()) {
2291       const SMDS_MeshElement *n = it->next();
2292       if ( n == n1 || n == n2 ) {
2293         if ( iDiag < 0)
2294           iDiag = i;
2295         else {
2296           if ( i - iDiag == 1 )
2297             nFirst[ t ] = ( n == n1 ? n2 : n1 );
2298           else
2299             nFirst[ t ] = n;
2300           break;
2301         }
2302       }
2303       i++;
2304     }
2305   }
2306   if ( nFirst[ 0 ] == nFirst[ 1 ] )
2307     N2.Reverse();
2308
2309   angle = N1.Angle( N2 );
2310   //SCRUTE( angle );
2311   return angle;
2312 }
2313
2314 // =================================================
2315 // class generating a unique ID for a pair of nodes
2316 // and able to return nodes by that ID
2317 // =================================================
2318 class LinkID_Gen {
2319 public:
2320
2321   LinkID_Gen( const SMESHDS_Mesh* theMesh )
2322     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2323   {}
2324
2325   long GetLinkID (const SMDS_MeshNode * n1,
2326                   const SMDS_MeshNode * n2) const
2327   {
2328     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2329   }
2330
2331   bool GetNodes (const long             theLinkID,
2332                  const SMDS_MeshNode* & theNode1,
2333                  const SMDS_MeshNode* & theNode2) const
2334   {
2335     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2336     if ( !theNode1 ) return false;
2337     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2338     if ( !theNode2 ) return false;
2339     return true;
2340   }
2341
2342 private:
2343   LinkID_Gen();
2344   const SMESHDS_Mesh* myMesh;
2345   long                myMaxID;
2346 };
2347
2348
2349 //=======================================================================
2350 //function : TriToQuad
2351 //purpose  : Fuse neighbour triangles into quadrangles.
2352 //           theCrit is used to select a neighbour to fuse with.
2353 //           theMaxAngle is a max angle between element normals at which
2354 //           fusion is still performed.
2355 //=======================================================================
2356
2357 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
2358                                   SMESH::Controls::NumericalFunctorPtr theCrit,
2359                                   const double                         theMaxAngle)
2360 {
2361   myLastCreatedElems.Clear();
2362   myLastCreatedNodes.Clear();
2363
2364   MESSAGE( "::TriToQuad()" );
2365
2366   if ( !theCrit.get() )
2367     return false;
2368
2369   SMESHDS_Mesh * aMesh = GetMeshDS();
2370
2371   // Prepare data for algo: build
2372   // 1. map of elements with their linkIDs
2373   // 2. map of linkIDs with their elements
2374
2375   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2376   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2377   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
2378   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2379
2380   TIDSortedElemSet::iterator itElem;
2381   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2382     const SMDS_MeshElement* elem = *itElem;
2383     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2384     bool IsTria = elem->NbNodes()==3 || (elem->NbNodes()==6 && elem->IsQuadratic());
2385     if(!IsTria) continue;
2386
2387     // retrieve element nodes
2388     const SMDS_MeshNode* aNodes [4];
2389     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2390     int i = 0;
2391     while ( i<3 )
2392       aNodes[ i++ ] = cast2Node( itN->next() );
2393     aNodes[ 3 ] = aNodes[ 0 ];
2394
2395     // fill maps
2396     for ( i = 0; i < 3; i++ ) {
2397       SMESH_TLink link( aNodes[i], aNodes[i+1] );
2398       // check if elements sharing a link can be fused
2399       itLE = mapLi_listEl.find( link );
2400       if ( itLE != mapLi_listEl.end() ) {
2401         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2402           continue;
2403         const SMDS_MeshElement* elem2 = (*itLE).second.front();
2404         //if ( FindShape( elem ) != FindShape( elem2 ))
2405         //  continue; // do not fuse triangles laying on different shapes
2406         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2407           continue; // avoid making badly shaped quads
2408         (*itLE).second.push_back( elem );
2409       }
2410       else {
2411         mapLi_listEl[ link ].push_back( elem );
2412       }
2413       mapEl_setLi [ elem ].insert( link );
2414     }
2415   }
2416   // Clean the maps from the links shared by a sole element, ie
2417   // links to which only one element is bound in mapLi_listEl
2418
2419   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2420     int nbElems = (*itLE).second.size();
2421     if ( nbElems < 2  ) {
2422       const SMDS_MeshElement* elem = (*itLE).second.front();
2423       SMESH_TLink link = (*itLE).first;
2424       mapEl_setLi[ elem ].erase( link );
2425       if ( mapEl_setLi[ elem ].empty() )
2426         mapEl_setLi.erase( elem );
2427     }
2428   }
2429
2430   // Algo: fuse triangles into quadrangles
2431
2432   while ( ! mapEl_setLi.empty() ) {
2433     // Look for the start element:
2434     // the element having the least nb of shared links
2435     const SMDS_MeshElement* startElem = 0;
2436     int minNbLinks = 4;
2437     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2438       int nbLinks = (*itEL).second.size();
2439       if ( nbLinks < minNbLinks ) {
2440         startElem = (*itEL).first;
2441         minNbLinks = nbLinks;
2442         if ( minNbLinks == 1 )
2443           break;
2444       }
2445     }
2446
2447     // search elements to fuse starting from startElem or links of elements
2448     // fused earlyer - startLinks
2449     list< SMESH_TLink > startLinks;
2450     while ( startElem || !startLinks.empty() ) {
2451       while ( !startElem && !startLinks.empty() ) {
2452         // Get an element to start, by a link
2453         SMESH_TLink linkId = startLinks.front();
2454         startLinks.pop_front();
2455         itLE = mapLi_listEl.find( linkId );
2456         if ( itLE != mapLi_listEl.end() ) {
2457           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2458           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2459           for ( ; itE != listElem.end() ; itE++ )
2460             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2461               startElem = (*itE);
2462           mapLi_listEl.erase( itLE );
2463         }
2464       }
2465
2466       if ( startElem ) {
2467         // Get candidates to be fused
2468         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2469         const SMESH_TLink *link12, *link13;
2470         startElem = 0;
2471         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2472         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2473         ASSERT( !setLi.empty() );
2474         set< SMESH_TLink >::iterator itLi;
2475         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2476         {
2477           const SMESH_TLink & link = (*itLi);
2478           itLE = mapLi_listEl.find( link );
2479           if ( itLE == mapLi_listEl.end() )
2480             continue;
2481
2482           const SMDS_MeshElement* elem = (*itLE).second.front();
2483           if ( elem == tr1 )
2484             elem = (*itLE).second.back();
2485           mapLi_listEl.erase( itLE );
2486           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2487             continue;
2488           if ( tr2 ) {
2489             tr3 = elem;
2490             link13 = &link;
2491           }
2492           else {
2493             tr2 = elem;
2494             link12 = &link;
2495           }
2496
2497           // add other links of elem to list of links to re-start from
2498           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2499           set< SMESH_TLink >::iterator it;
2500           for ( it = links.begin(); it != links.end(); it++ ) {
2501             const SMESH_TLink& link2 = (*it);
2502             if ( link2 != link )
2503               startLinks.push_back( link2 );
2504           }
2505         }
2506
2507         // Get nodes of possible quadrangles
2508         const SMDS_MeshNode *n12 [4], *n13 [4];
2509         bool Ok12 = false, Ok13 = false;
2510         const SMDS_MeshNode *linkNode1, *linkNode2;
2511         if(tr2) {
2512           linkNode1 = link12->first;
2513           linkNode2 = link12->second;
2514           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2515             Ok12 = true;
2516         }
2517         if(tr3) {
2518           linkNode1 = link13->first;
2519           linkNode2 = link13->second;
2520           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2521             Ok13 = true;
2522         }
2523
2524         // Choose a pair to fuse
2525         if ( Ok12 && Ok13 ) {
2526           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2527           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2528           double aBadRate12 = getBadRate( &quad12, theCrit );
2529           double aBadRate13 = getBadRate( &quad13, theCrit );
2530           if (  aBadRate13 < aBadRate12 )
2531             Ok12 = false;
2532           else
2533             Ok13 = false;
2534         }
2535
2536         // Make quadrangles
2537         // and remove fused elems and removed links from the maps
2538         mapEl_setLi.erase( tr1 );
2539         if ( Ok12 ) {
2540           mapEl_setLi.erase( tr2 );
2541           mapLi_listEl.erase( *link12 );
2542           if(tr1->NbNodes()==3) {
2543             const SMDS_MeshElement* newElem = 0;
2544             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2545             myLastCreatedElems.Append(newElem);
2546             AddToSameGroups( newElem, tr1, aMesh );
2547             int aShapeId = tr1->getshapeId();
2548             if ( aShapeId )
2549               {
2550                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2551               }
2552             aMesh->RemoveElement( tr1 );
2553             aMesh->RemoveElement( tr2 );
2554           }
2555           else {
2556             const SMDS_MeshNode* N1 [6];
2557             const SMDS_MeshNode* N2 [6];
2558             GetNodesFromTwoTria(tr1,tr2,N1,N2);
2559             // now we receive following N1 and N2 (using numeration as above image)
2560             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
2561             // i.e. first nodes from both arrays determ new diagonal
2562             const SMDS_MeshNode* aNodes[8];
2563             aNodes[0] = N1[0];
2564             aNodes[1] = N1[1];
2565             aNodes[2] = N2[0];
2566             aNodes[3] = N2[1];
2567             aNodes[4] = N1[3];
2568             aNodes[5] = N2[5];
2569             aNodes[6] = N2[3];
2570             aNodes[7] = N1[5];
2571             const SMDS_MeshElement* newElem = 0;
2572             newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2573                                      aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2574             myLastCreatedElems.Append(newElem);
2575             AddToSameGroups( newElem, tr1, aMesh );
2576             int aShapeId = tr1->getshapeId();
2577             if ( aShapeId )
2578               {
2579                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2580               }
2581             aMesh->RemoveElement( tr1 );
2582             aMesh->RemoveElement( tr2 );
2583             // remove middle node (9)
2584             GetMeshDS()->RemoveNode( N1[4] );
2585           }
2586         }
2587         else if ( Ok13 ) {
2588           mapEl_setLi.erase( tr3 );
2589           mapLi_listEl.erase( *link13 );
2590           if(tr1->NbNodes()==3) {
2591             const SMDS_MeshElement* newElem = 0;
2592             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2593             myLastCreatedElems.Append(newElem);
2594             AddToSameGroups( newElem, tr1, aMesh );
2595             int aShapeId = tr1->getshapeId();
2596             if ( aShapeId )
2597               {
2598                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2599               }
2600             aMesh->RemoveElement( tr1 );
2601             aMesh->RemoveElement( tr3 );
2602           }
2603           else {
2604             const SMDS_MeshNode* N1 [6];
2605             const SMDS_MeshNode* N2 [6];
2606             GetNodesFromTwoTria(tr1,tr3,N1,N2);
2607             // now we receive following N1 and N2 (using numeration as above image)
2608             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
2609             // i.e. first nodes from both arrays determ new diagonal
2610             const SMDS_MeshNode* aNodes[8];
2611             aNodes[0] = N1[0];
2612             aNodes[1] = N1[1];
2613             aNodes[2] = N2[0];
2614             aNodes[3] = N2[1];
2615             aNodes[4] = N1[3];
2616             aNodes[5] = N2[5];
2617             aNodes[6] = N2[3];
2618             aNodes[7] = N1[5];
2619             const SMDS_MeshElement* newElem = 0;
2620             newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2621                                      aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2622             myLastCreatedElems.Append(newElem);
2623             AddToSameGroups( newElem, tr1, aMesh );
2624             int aShapeId = tr1->getshapeId();
2625             if ( aShapeId )
2626               {
2627                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2628               }
2629             aMesh->RemoveElement( tr1 );
2630             aMesh->RemoveElement( tr3 );
2631             // remove middle node (9)
2632             GetMeshDS()->RemoveNode( N1[4] );
2633           }
2634         }
2635
2636         // Next element to fuse: the rejected one
2637         if ( tr3 )
2638           startElem = Ok12 ? tr3 : tr2;
2639
2640       } // if ( startElem )
2641     } // while ( startElem || !startLinks.empty() )
2642   } // while ( ! mapEl_setLi.empty() )
2643
2644   return true;
2645 }
2646
2647
2648 /*#define DUMPSO(txt) \
2649 //  cout << txt << endl;
2650 //=============================================================================
2651 //
2652 //
2653 //
2654 //=============================================================================
2655 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2656 {
2657 if ( i1 == i2 )
2658 return;
2659 int tmp = idNodes[ i1 ];
2660 idNodes[ i1 ] = idNodes[ i2 ];
2661 idNodes[ i2 ] = tmp;
2662 gp_Pnt Ptmp = P[ i1 ];
2663 P[ i1 ] = P[ i2 ];
2664 P[ i2 ] = Ptmp;
2665 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2666 }
2667
2668 //=======================================================================
2669 //function : SortQuadNodes
2670 //purpose  : Set 4 nodes of a quadrangle face in a good order.
2671 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
2672 //           1 or 2 else 0.
2673 //=======================================================================
2674
2675 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2676 int               idNodes[] )
2677 {
2678   gp_Pnt P[4];
2679   int i;
2680   for ( i = 0; i < 4; i++ ) {
2681     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2682     if ( !n ) return 0;
2683     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2684   }
2685
2686   gp_Vec V1(P[0], P[1]);
2687   gp_Vec V2(P[0], P[2]);
2688   gp_Vec V3(P[0], P[3]);
2689
2690   gp_Vec Cross1 = V1 ^ V2;
2691   gp_Vec Cross2 = V2 ^ V3;
2692
2693   i = 0;
2694   if (Cross1.Dot(Cross2) < 0)
2695   {
2696     Cross1 = V2 ^ V1;
2697     Cross2 = V1 ^ V3;
2698
2699     if (Cross1.Dot(Cross2) < 0)
2700       i = 2;
2701     else
2702       i = 1;
2703     swap ( i, i + 1, idNodes, P );
2704
2705     //     for ( int ii = 0; ii < 4; ii++ ) {
2706     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2707     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2708     //     }
2709   }
2710   return i;
2711 }
2712
2713 //=======================================================================
2714 //function : SortHexaNodes
2715 //purpose  : Set 8 nodes of a hexahedron in a good order.
2716 //           Return success status
2717 //=======================================================================
2718
2719 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2720                                       int               idNodes[] )
2721 {
2722   gp_Pnt P[8];
2723   int i;
2724   DUMPSO( "INPUT: ========================================");
2725   for ( i = 0; i < 8; i++ ) {
2726     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2727     if ( !n ) return false;
2728     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2729     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2730   }
2731   DUMPSO( "========================================");
2732
2733
2734   set<int> faceNodes;  // ids of bottom face nodes, to be found
2735   set<int> checkedId1; // ids of tried 2-nd nodes
2736   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2737   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
2738   int iMin, iLoop1 = 0;
2739
2740   // Loop to try the 2-nd nodes
2741
2742   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2743   {
2744     // Find not checked 2-nd node
2745     for ( i = 1; i < 8; i++ )
2746       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2747         int id1 = idNodes[i];
2748         swap ( 1, i, idNodes, P );
2749         checkedId1.insert ( id1 );
2750         break;
2751       }
2752
2753     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2754     // ie that all but meybe one (id3 which is on the same face) nodes
2755     // lay on the same side from the triangle plane.
2756
2757     bool manyInPlane = false; // more than 4 nodes lay in plane
2758     int iLoop2 = 0;
2759     while ( ++iLoop2 < 6 ) {
2760
2761       // get 1-2-3 plane coeffs
2762       Standard_Real A, B, C, D;
2763       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2764       if ( N.SquareMagnitude() > gp::Resolution() )
2765       {
2766         gp_Pln pln ( P[0], N );
2767         pln.Coefficients( A, B, C, D );
2768
2769         // find the node (iMin) closest to pln
2770         Standard_Real dist[ 8 ], minDist = DBL_MAX;
2771         set<int> idInPln;
2772         for ( i = 3; i < 8; i++ ) {
2773           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2774           if ( fabs( dist[i] ) < minDist ) {
2775             minDist = fabs( dist[i] );
2776             iMin = i;
2777           }
2778           if ( fabs( dist[i] ) <= tol )
2779             idInPln.insert( idNodes[i] );
2780         }
2781
2782         // there should not be more than 4 nodes in bottom plane
2783         if ( idInPln.size() > 1 )
2784         {
2785           DUMPSO( "### idInPln.size() = " << idInPln.size());
2786           // idInPlane does not contain the first 3 nodes
2787           if ( manyInPlane || idInPln.size() == 5)
2788             return false; // all nodes in one plane
2789           manyInPlane = true;
2790
2791           // set the 1-st node to be not in plane
2792           for ( i = 3; i < 8; i++ ) {
2793             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2794               DUMPSO( "### Reset 0-th node");
2795               swap( 0, i, idNodes, P );
2796               break;
2797             }
2798           }
2799
2800           // reset to re-check second nodes
2801           leastDist = DBL_MAX;
2802           faceNodes.clear();
2803           checkedId1.clear();
2804           iLoop1 = 0;
2805           break; // from iLoop2;
2806         }
2807
2808         // check that the other 4 nodes are on the same side
2809         bool sameSide = true;
2810         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2811         for ( i = 3; sameSide && i < 8; i++ ) {
2812           if ( i != iMin )
2813             sameSide = ( isNeg == dist[i] <= 0.);
2814         }
2815
2816         // keep best solution
2817         if ( sameSide && minDist < leastDist ) {
2818           leastDist = minDist;
2819           faceNodes.clear();
2820           faceNodes.insert( idNodes[ 1 ] );
2821           faceNodes.insert( idNodes[ 2 ] );
2822           faceNodes.insert( idNodes[ iMin ] );
2823           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2824                   << " leastDist = " << leastDist);
2825           if ( leastDist <= DBL_MIN )
2826             break;
2827         }
2828       }
2829
2830       // set next 3-d node to check
2831       int iNext = 2 + iLoop2;
2832       if ( iNext < 8 ) {
2833         DUMPSO( "Try 2-nd");
2834         swap ( 2, iNext, idNodes, P );
2835       }
2836     } // while ( iLoop2 < 6 )
2837   } // iLoop1
2838
2839   if ( faceNodes.empty() ) return false;
2840
2841   // Put the faceNodes in proper places
2842   for ( i = 4; i < 8; i++ ) {
2843     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2844       // find a place to put
2845       int iTo = 1;
2846       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2847         iTo++;
2848       DUMPSO( "Set faceNodes");
2849       swap ( iTo, i, idNodes, P );
2850     }
2851   }
2852
2853
2854   // Set nodes of the found bottom face in good order
2855   DUMPSO( " Found bottom face: ");
2856   i = SortQuadNodes( theMesh, idNodes );
2857   if ( i ) {
2858     gp_Pnt Ptmp = P[ i ];
2859     P[ i ] = P[ i+1 ];
2860     P[ i+1 ] = Ptmp;
2861   }
2862   //   else
2863   //     for ( int ii = 0; ii < 4; ii++ ) {
2864   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2865   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2866   //    }
2867
2868   // Gravity center of the top and bottom faces
2869   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2870   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2871
2872   // Get direction from the bottom to the top face
2873   gp_Vec upDir ( aGCb, aGCt );
2874   Standard_Real upDirSize = upDir.Magnitude();
2875   if ( upDirSize <= gp::Resolution() ) return false;
2876   upDir / upDirSize;
2877
2878   // Assure that the bottom face normal points up
2879   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2880   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2881   if ( Nb.Dot( upDir ) < 0 ) {
2882     DUMPSO( "Reverse bottom face");
2883     swap( 1, 3, idNodes, P );
2884   }
2885
2886   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2887   Standard_Real minDist = DBL_MAX;
2888   for ( i = 4; i < 8; i++ ) {
2889     // projection of P[i] to the plane defined by P[0] and upDir
2890     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2891     Standard_Real sqDist = P[0].SquareDistance( Pp );
2892     if ( sqDist < minDist ) {
2893       minDist = sqDist;
2894       iMin = i;
2895     }
2896   }
2897   DUMPSO( "Set 4-th");
2898   swap ( 4, iMin, idNodes, P );
2899
2900   // Set nodes of the top face in good order
2901   DUMPSO( "Sort top face");
2902   i = SortQuadNodes( theMesh, &idNodes[4] );
2903   if ( i ) {
2904     i += 4;
2905     gp_Pnt Ptmp = P[ i ];
2906     P[ i ] = P[ i+1 ];
2907     P[ i+1 ] = Ptmp;
2908   }
2909
2910   // Assure that direction of the top face normal is from the bottom face
2911   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
2912   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
2913   if ( Nt.Dot( upDir ) < 0 ) {
2914     DUMPSO( "Reverse top face");
2915     swap( 5, 7, idNodes, P );
2916   }
2917
2918   //   DUMPSO( "OUTPUT: ========================================");
2919   //   for ( i = 0; i < 8; i++ ) {
2920   //     float *p = ugrid->GetPoint(idNodes[i]);
2921   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
2922   //   }
2923
2924   return true;
2925 }*/
2926
2927 //================================================================================
2928 /*!
2929  * \brief Return nodes linked to the given one
2930  * \param theNode - the node
2931  * \param linkedNodes - the found nodes
2932  * \param type - the type of elements to check
2933  *
2934  * Medium nodes are ignored
2935  */
2936 //================================================================================
2937
2938 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
2939                                        TIDSortedElemSet &   linkedNodes,
2940                                        SMDSAbs_ElementType  type )
2941 {
2942   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
2943   while ( elemIt->more() )
2944   {
2945     const SMDS_MeshElement* elem = elemIt->next();
2946     if(elem->GetType() == SMDSAbs_0DElement)
2947       continue;
2948
2949     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
2950     if ( elem->GetType() == SMDSAbs_Volume )
2951     {
2952       SMDS_VolumeTool vol( elem );
2953       while ( nodeIt->more() ) {
2954         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2955         if ( theNode != n && vol.IsLinked( theNode, n ))
2956           linkedNodes.insert( n );
2957       }
2958     }
2959     else
2960     {
2961       for ( int i = 0; nodeIt->more(); ++i ) {
2962         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2963         if ( n == theNode ) {
2964           int iBefore = i - 1;
2965           int iAfter  = i + 1;
2966           if ( elem->IsQuadratic() ) {
2967             int nb = elem->NbNodes() / 2;
2968             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
2969             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
2970           }
2971           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
2972           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
2973         }
2974       }
2975     }
2976   }
2977 }
2978
2979 //=======================================================================
2980 //function : laplacianSmooth
2981 //purpose  : pulls theNode toward the center of surrounding nodes directly
2982 //           connected to that node along an element edge
2983 //=======================================================================
2984
2985 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
2986                      const Handle(Geom_Surface)&          theSurface,
2987                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2988 {
2989   // find surrounding nodes
2990
2991   TIDSortedElemSet nodeSet;
2992   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
2993
2994   // compute new coodrs
2995
2996   double coord[] = { 0., 0., 0. };
2997   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
2998   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
2999     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3000     if ( theSurface.IsNull() ) { // smooth in 3D
3001       coord[0] += node->X();
3002       coord[1] += node->Y();
3003       coord[2] += node->Z();
3004     }
3005     else { // smooth in 2D
3006       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3007       gp_XY* uv = theUVMap[ node ];
3008       coord[0] += uv->X();
3009       coord[1] += uv->Y();
3010     }
3011   }
3012   int nbNodes = nodeSet.size();
3013   if ( !nbNodes )
3014     return;
3015   coord[0] /= nbNodes;
3016   coord[1] /= nbNodes;
3017
3018   if ( !theSurface.IsNull() ) {
3019     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3020     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3021     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3022     coord[0] = p3d.X();
3023     coord[1] = p3d.Y();
3024     coord[2] = p3d.Z();
3025   }
3026   else
3027     coord[2] /= nbNodes;
3028
3029   // move node
3030
3031   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3032 }
3033
3034 //=======================================================================
3035 //function : centroidalSmooth
3036 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3037 //           surrounding elements
3038 //=======================================================================
3039
3040 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3041                       const Handle(Geom_Surface)&          theSurface,
3042                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3043 {
3044   gp_XYZ aNewXYZ(0.,0.,0.);
3045   SMESH::Controls::Area anAreaFunc;
3046   double totalArea = 0.;
3047   int nbElems = 0;
3048
3049   // compute new XYZ
3050
3051   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3052   while ( elemIt->more() )
3053   {
3054     const SMDS_MeshElement* elem = elemIt->next();
3055     nbElems++;
3056
3057     gp_XYZ elemCenter(0.,0.,0.);
3058     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3059     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3060     int nn = elem->NbNodes();
3061     if(elem->IsQuadratic()) nn = nn/2;
3062     int i=0;
3063     //while ( itN->more() ) {
3064     while ( i<nn ) {
3065       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3066       i++;
3067       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3068       aNodePoints.push_back( aP );
3069       if ( !theSurface.IsNull() ) { // smooth in 2D
3070         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3071         gp_XY* uv = theUVMap[ aNode ];
3072         aP.SetCoord( uv->X(), uv->Y(), 0. );
3073       }
3074       elemCenter += aP;
3075     }
3076     double elemArea = anAreaFunc.GetValue( aNodePoints );
3077     totalArea += elemArea;
3078     elemCenter /= nn;
3079     aNewXYZ += elemCenter * elemArea;
3080   }
3081   aNewXYZ /= totalArea;
3082   if ( !theSurface.IsNull() ) {
3083     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3084     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3085   }
3086
3087   // move node
3088
3089   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3090 }
3091
3092 //=======================================================================
3093 //function : getClosestUV
3094 //purpose  : return UV of closest projection
3095 //=======================================================================
3096
3097 static bool getClosestUV (Extrema_GenExtPS& projector,
3098                           const gp_Pnt&     point,
3099                           gp_XY &           result)
3100 {
3101   projector.Perform( point );
3102   if ( projector.IsDone() ) {
3103     double u, v, minVal = DBL_MAX;
3104     for ( int i = projector.NbExt(); i > 0; i-- )
3105 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3106       if ( projector.SquareDistance( i ) < minVal ) {
3107         minVal = projector.SquareDistance( i );
3108 #else
3109       if ( projector.Value( i ) < minVal ) {
3110         minVal = projector.Value( i );
3111 #endif
3112         projector.Point( i ).Parameter( u, v );
3113       }
3114     result.SetCoord( u, v );
3115     return true;
3116   }
3117   return false;
3118 }
3119
3120 //=======================================================================
3121 //function : Smooth
3122 //purpose  : Smooth theElements during theNbIterations or until a worst
3123 //           element has aspect ratio <= theTgtAspectRatio.
3124 //           Aspect Ratio varies in range [1.0, inf].
3125 //           If theElements is empty, the whole mesh is smoothed.
3126 //           theFixedNodes contains additionally fixed nodes. Nodes built
3127 //           on edges and boundary nodes are always fixed.
3128 //=======================================================================
3129
3130 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3131                                set<const SMDS_MeshNode*> & theFixedNodes,
3132                                const SmoothMethod          theSmoothMethod,
3133                                const int                   theNbIterations,
3134                                double                      theTgtAspectRatio,
3135                                const bool                  the2D)
3136 {
3137   myLastCreatedElems.Clear();
3138   myLastCreatedNodes.Clear();
3139
3140   MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3141
3142   if ( theTgtAspectRatio < 1.0 )
3143     theTgtAspectRatio = 1.0;
3144
3145   const double disttol = 1.e-16;
3146
3147   SMESH::Controls::AspectRatio aQualityFunc;
3148
3149   SMESHDS_Mesh* aMesh = GetMeshDS();
3150
3151   if ( theElems.empty() ) {
3152     // add all faces to theElems
3153     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3154     while ( fIt->more() ) {
3155       const SMDS_MeshElement* face = fIt->next();
3156       theElems.insert( theElems.end(), face );
3157     }
3158   }
3159   // get all face ids theElems are on
3160   set< int > faceIdSet;
3161   TIDSortedElemSet::iterator itElem;
3162   if ( the2D )
3163     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3164       int fId = FindShape( *itElem );
3165       // check that corresponding submesh exists and a shape is face
3166       if (fId &&
3167           faceIdSet.find( fId ) == faceIdSet.end() &&
3168           aMesh->MeshElements( fId )) {
3169         TopoDS_Shape F = aMesh->IndexToShape( fId );
3170         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3171           faceIdSet.insert( fId );
3172       }
3173     }
3174   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3175
3176   // ===============================================
3177   // smooth elements on each TopoDS_Face separately
3178   // ===============================================
3179
3180   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3181   for ( ; fId != faceIdSet.rend(); ++fId ) {
3182     // get face surface and submesh
3183     Handle(Geom_Surface) surface;
3184     SMESHDS_SubMesh* faceSubMesh = 0;
3185     TopoDS_Face face;
3186     double fToler2 = 0, f,l;
3187     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3188     bool isUPeriodic = false, isVPeriodic = false;
3189     if ( *fId ) {
3190       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3191       surface = BRep_Tool::Surface( face );
3192       faceSubMesh = aMesh->MeshElements( *fId );
3193       fToler2 = BRep_Tool::Tolerance( face );
3194       fToler2 *= fToler2 * 10.;
3195       isUPeriodic = surface->IsUPeriodic();
3196       if ( isUPeriodic )
3197         surface->UPeriod();
3198       isVPeriodic = surface->IsVPeriodic();
3199       if ( isVPeriodic )
3200         surface->VPeriod();
3201       surface->Bounds( u1, u2, v1, v2 );
3202     }
3203     // ---------------------------------------------------------
3204     // for elements on a face, find movable and fixed nodes and
3205     // compute UV for them
3206     // ---------------------------------------------------------
3207     bool checkBoundaryNodes = false;
3208     bool isQuadratic = false;
3209     set<const SMDS_MeshNode*> setMovableNodes;
3210     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3211     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3212     list< const SMDS_MeshElement* > elemsOnFace;
3213
3214     Extrema_GenExtPS projector;
3215     GeomAdaptor_Surface surfAdaptor;
3216     if ( !surface.IsNull() ) {
3217       surfAdaptor.Load( surface );
3218       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3219     }
3220     int nbElemOnFace = 0;
3221     itElem = theElems.begin();
3222     // loop on not yet smoothed elements: look for elems on a face
3223     while ( itElem != theElems.end() ) {
3224       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3225         break; // all elements found
3226
3227       const SMDS_MeshElement* elem = *itElem;
3228       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3229            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3230         ++itElem;
3231         continue;
3232       }
3233       elemsOnFace.push_back( elem );
3234       theElems.erase( itElem++ );
3235       nbElemOnFace++;
3236
3237       if ( !isQuadratic )
3238         isQuadratic = elem->IsQuadratic();
3239
3240       // get movable nodes of elem
3241       const SMDS_MeshNode* node;
3242       SMDS_TypeOfPosition posType;
3243       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3244       int nn = 0, nbn =  elem->NbNodes();
3245       if(elem->IsQuadratic())
3246         nbn = nbn/2;
3247       while ( nn++ < nbn ) {
3248         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3249         const SMDS_PositionPtr& pos = node->GetPosition();
3250         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3251         if (posType != SMDS_TOP_EDGE &&
3252             posType != SMDS_TOP_VERTEX &&
3253             theFixedNodes.find( node ) == theFixedNodes.end())
3254         {
3255           // check if all faces around the node are on faceSubMesh
3256           // because a node on edge may be bound to face
3257           SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3258           bool all = true;
3259           if ( faceSubMesh ) {
3260             while ( eIt->more() && all ) {
3261               const SMDS_MeshElement* e = eIt->next();
3262               all = faceSubMesh->Contains( e );
3263             }
3264           }
3265           if ( all )
3266             setMovableNodes.insert( node );
3267           else
3268             checkBoundaryNodes = true;
3269         }
3270         if ( posType == SMDS_TOP_3DSPACE )
3271           checkBoundaryNodes = true;
3272       }
3273
3274       if ( surface.IsNull() )
3275         continue;
3276
3277       // get nodes to check UV
3278       list< const SMDS_MeshNode* > uvCheckNodes;
3279       itN = elem->nodesIterator();
3280       nn = 0; nbn =  elem->NbNodes();
3281       if(elem->IsQuadratic())
3282         nbn = nbn/2;
3283       while ( nn++ < nbn ) {
3284         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3285         if ( uvMap.find( node ) == uvMap.end() )
3286           uvCheckNodes.push_back( node );
3287         // add nodes of elems sharing node
3288         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3289         //         while ( eIt->more() ) {
3290         //           const SMDS_MeshElement* e = eIt->next();
3291         //           if ( e != elem ) {
3292         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3293         //             while ( nIt->more() ) {
3294         //               const SMDS_MeshNode* n =
3295         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3296         //               if ( uvMap.find( n ) == uvMap.end() )
3297         //                 uvCheckNodes.push_back( n );
3298         //             }
3299         //           }
3300         //         }
3301       }
3302       // check UV on face
3303       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3304       for ( ; n != uvCheckNodes.end(); ++n ) {
3305         node = *n;
3306         gp_XY uv( 0, 0 );
3307         const SMDS_PositionPtr& pos = node->GetPosition();
3308         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3309         // get existing UV
3310         switch ( posType ) {
3311         case SMDS_TOP_FACE: {
3312           SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3313           uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3314           break;
3315         }
3316         case SMDS_TOP_EDGE: {
3317           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3318           Handle(Geom2d_Curve) pcurve;
3319           if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3320             pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3321           if ( !pcurve.IsNull() ) {
3322             double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3323             uv = pcurve->Value( u ).XY();
3324           }
3325           break;
3326         }
3327         case SMDS_TOP_VERTEX: {
3328           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3329           if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3330             uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3331           break;
3332         }
3333         default:;
3334         }
3335         // check existing UV
3336         bool project = true;
3337         gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3338         double dist1 = DBL_MAX, dist2 = 0;
3339         if ( posType != SMDS_TOP_3DSPACE ) {
3340           dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3341           project = dist1 > fToler2;
3342         }
3343         if ( project ) { // compute new UV
3344           gp_XY newUV;
3345           if ( !getClosestUV( projector, pNode, newUV )) {
3346             MESSAGE("Node Projection Failed " << node);
3347           }
3348           else {
3349             if ( isUPeriodic )
3350               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3351             if ( isVPeriodic )
3352               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3353             // check new UV
3354             if ( posType != SMDS_TOP_3DSPACE )
3355               dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3356             if ( dist2 < dist1 )
3357               uv = newUV;
3358           }
3359         }
3360         // store UV in the map
3361         listUV.push_back( uv );
3362         uvMap.insert( make_pair( node, &listUV.back() ));
3363       }
3364     } // loop on not yet smoothed elements
3365
3366     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3367       checkBoundaryNodes = true;
3368
3369     // fix nodes on mesh boundary
3370
3371     if ( checkBoundaryNodes ) {
3372       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3373       map< SMESH_TLink, int >::iterator link_nb;
3374       // put all elements links to linkNbMap
3375       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3376       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3377         const SMDS_MeshElement* elem = (*elemIt);
3378         int nbn =  elem->NbCornerNodes();
3379         // loop on elem links: insert them in linkNbMap
3380         for ( int iN = 0; iN < nbn; ++iN ) {
3381           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3382           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3383           SMESH_TLink link( n1, n2 );
3384           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3385           link_nb->second++;
3386         }
3387       }
3388       // remove nodes that are in links encountered only once from setMovableNodes
3389       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3390         if ( link_nb->second == 1 ) {
3391           setMovableNodes.erase( link_nb->first.node1() );
3392           setMovableNodes.erase( link_nb->first.node2() );
3393         }
3394       }
3395     }
3396
3397     // -----------------------------------------------------
3398     // for nodes on seam edge, compute one more UV ( uvMap2 );
3399     // find movable nodes linked to nodes on seam and which
3400     // are to be smoothed using the second UV ( uvMap2 )
3401     // -----------------------------------------------------
3402
3403     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3404     if ( !surface.IsNull() ) {
3405       TopExp_Explorer eExp( face, TopAbs_EDGE );
3406       for ( ; eExp.More(); eExp.Next() ) {
3407         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3408         if ( !BRep_Tool::IsClosed( edge, face ))
3409           continue;
3410         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3411         if ( !sm ) continue;
3412         // find out which parameter varies for a node on seam
3413         double f,l;
3414         gp_Pnt2d uv1, uv2;
3415         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3416         if ( pcurve.IsNull() ) continue;
3417         uv1 = pcurve->Value( f );
3418         edge.Reverse();
3419         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3420         if ( pcurve.IsNull() ) continue;
3421         uv2 = pcurve->Value( f );
3422         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3423         // assure uv1 < uv2
3424         if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3425           gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3426         }
3427         // get nodes on seam and its vertices
3428         list< const SMDS_MeshNode* > seamNodes;
3429         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3430         while ( nSeamIt->more() ) {
3431           const SMDS_MeshNode* node = nSeamIt->next();
3432           if ( !isQuadratic || !IsMedium( node ))
3433             seamNodes.push_back( node );
3434         }
3435         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3436         for ( ; vExp.More(); vExp.Next() ) {
3437           sm = aMesh->MeshElements( vExp.Current() );
3438           if ( sm ) {
3439             nSeamIt = sm->GetNodes();
3440             while ( nSeamIt->more() )
3441               seamNodes.push_back( nSeamIt->next() );
3442           }
3443         }
3444         // loop on nodes on seam
3445         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3446         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3447           const SMDS_MeshNode* nSeam = *noSeIt;
3448           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3449           if ( n_uv == uvMap.end() )
3450             continue;
3451           // set the first UV
3452           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3453           // set the second UV
3454           listUV.push_back( *n_uv->second );
3455           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3456           if ( uvMap2.empty() )
3457             uvMap2 = uvMap; // copy the uvMap contents
3458           uvMap2[ nSeam ] = &listUV.back();
3459
3460           // collect movable nodes linked to ones on seam in nodesNearSeam
3461           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3462           while ( eIt->more() ) {
3463             const SMDS_MeshElement* e = eIt->next();
3464             int nbUseMap1 = 0, nbUseMap2 = 0;
3465             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3466             int nn = 0, nbn =  e->NbNodes();
3467             if(e->IsQuadratic()) nbn = nbn/2;
3468             while ( nn++ < nbn )
3469             {
3470               const SMDS_MeshNode* n =
3471                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3472               if (n == nSeam ||
3473                   setMovableNodes.find( n ) == setMovableNodes.end() )
3474                 continue;
3475               // add only nodes being closer to uv2 than to uv1
3476               gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3477                            0.5 * ( n->Y() + nSeam->Y() ),
3478                            0.5 * ( n->Z() + nSeam->Z() ));
3479               gp_XY uv;
3480               getClosestUV( projector, pMid, uv );
3481               if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3482                 nodesNearSeam.insert( n );
3483                 nbUseMap2++;
3484               }
3485               else
3486                 nbUseMap1++;
3487             }
3488             // for centroidalSmooth all element nodes must
3489             // be on one side of a seam
3490             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3491               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3492               nn = 0;
3493               while ( nn++ < nbn ) {
3494                 const SMDS_MeshNode* n =
3495                   static_cast<const SMDS_MeshNode*>( nIt->next() );
3496                 setMovableNodes.erase( n );
3497               }
3498             }
3499           }
3500         } // loop on nodes on seam
3501       } // loop on edge of a face
3502     } // if ( !face.IsNull() )
3503
3504     if ( setMovableNodes.empty() ) {
3505       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3506       continue; // goto next face
3507     }
3508
3509     // -------------
3510     // SMOOTHING //
3511     // -------------
3512
3513     int it = -1;
3514     double maxRatio = -1., maxDisplacement = -1.;
3515     set<const SMDS_MeshNode*>::iterator nodeToMove;
3516     for ( it = 0; it < theNbIterations; it++ ) {
3517       maxDisplacement = 0.;
3518       nodeToMove = setMovableNodes.begin();
3519       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3520         const SMDS_MeshNode* node = (*nodeToMove);
3521         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3522
3523         // smooth
3524         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3525         if ( theSmoothMethod == LAPLACIAN )
3526           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3527         else
3528           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3529
3530         // node displacement
3531         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3532         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3533         if ( aDispl > maxDisplacement )
3534           maxDisplacement = aDispl;
3535       }
3536       // no node movement => exit
3537       //if ( maxDisplacement < 1.e-16 ) {
3538       if ( maxDisplacement < disttol ) {
3539         MESSAGE("-- no node movement --");
3540         break;
3541       }
3542
3543       // check elements quality
3544       maxRatio  = 0;
3545       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3546       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3547         const SMDS_MeshElement* elem = (*elemIt);
3548         if ( !elem || elem->GetType() != SMDSAbs_Face )
3549           continue;
3550         SMESH::Controls::TSequenceOfXYZ aPoints;
3551         if ( aQualityFunc.GetPoints( elem, aPoints )) {
3552           double aValue = aQualityFunc.GetValue( aPoints );
3553           if ( aValue > maxRatio )
3554             maxRatio = aValue;
3555         }
3556       }
3557       if ( maxRatio <= theTgtAspectRatio ) {
3558         MESSAGE("-- quality achived --");
3559         break;
3560       }
3561       if (it+1 == theNbIterations) {
3562         MESSAGE("-- Iteration limit exceeded --");
3563       }
3564     } // smoothing iterations
3565
3566     MESSAGE(" Face id: " << *fId <<
3567             " Nb iterstions: " << it <<
3568             " Displacement: " << maxDisplacement <<
3569             " Aspect Ratio " << maxRatio);
3570
3571     // ---------------------------------------
3572     // new nodes positions are computed,
3573     // record movement in DS and set new UV
3574     // ---------------------------------------
3575     nodeToMove = setMovableNodes.begin();
3576     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3577       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3578       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3579       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3580       if ( node_uv != uvMap.end() ) {
3581         gp_XY* uv = node_uv->second;
3582         node->SetPosition
3583           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3584       }
3585     }
3586
3587     // move medium nodes of quadratic elements
3588     if ( isQuadratic )
3589     {
3590       SMESH_MesherHelper helper( *GetMesh() );
3591       if ( !face.IsNull() )
3592         helper.SetSubShape( face );
3593       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3594       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3595         const SMDS_VtkFace* QF =
3596           dynamic_cast<const SMDS_VtkFace*> (*elemIt);
3597         if(QF && QF->IsQuadratic()) {
3598           vector<const SMDS_MeshNode*> Ns;
3599           Ns.reserve(QF->NbNodes()+1);
3600           SMDS_ElemIteratorPtr anIter = QF->interlacedNodesElemIterator();
3601           while ( anIter->more() )
3602             Ns.push_back( cast2Node(anIter->next()) );
3603           Ns.push_back( Ns[0] );
3604           double x, y, z;
3605           for(int i=0; i<QF->NbNodes(); i=i+2) {
3606             if ( !surface.IsNull() ) {
3607               gp_XY uv1 = helper.GetNodeUV( face, Ns[i], Ns[i+2] );
3608               gp_XY uv2 = helper.GetNodeUV( face, Ns[i+2], Ns[i] );
3609               gp_XY uv = ( uv1 + uv2 ) / 2.;
3610               gp_Pnt xyz = surface->Value( uv.X(), uv.Y() );
3611               x = xyz.X(); y = xyz.Y(); z = xyz.Z();
3612             }
3613             else {
3614               x = (Ns[i]->X() + Ns[i+2]->X())/2;
3615               y = (Ns[i]->Y() + Ns[i+2]->Y())/2;
3616               z = (Ns[i]->Z() + Ns[i+2]->Z())/2;
3617             }
3618             if( fabs( Ns[i+1]->X() - x ) > disttol ||
3619                 fabs( Ns[i+1]->Y() - y ) > disttol ||
3620                 fabs( Ns[i+1]->Z() - z ) > disttol ) {
3621               // we have to move i+1 node
3622               aMesh->MoveNode( Ns[i+1], x, y, z );
3623             }
3624           }
3625         }
3626       }
3627     }
3628
3629   } // loop on face ids
3630
3631 }
3632
3633 //=======================================================================
3634 //function : isReverse
3635 //purpose  : Return true if normal of prevNodes is not co-directied with
3636 //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3637 //           iNotSame is where prevNodes and nextNodes are different.
3638 //           If result is true then future volume orientation is OK
3639 //=======================================================================
3640
3641 static bool isReverse(const SMDS_MeshElement*             face,
3642                       const vector<const SMDS_MeshNode*>& prevNodes,
3643                       const vector<const SMDS_MeshNode*>& nextNodes,
3644                       const int                           iNotSame)
3645 {
3646
3647   SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
3648   SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
3649   gp_XYZ extrDir( pN - pP ), faceNorm;
3650   SMESH_Algo::FaceNormal( face, faceNorm, /*normalized=*/false );
3651
3652   return faceNorm * extrDir < 0.0;
3653 }
3654
3655 //=======================================================================
3656 /*!
3657  * \brief Create elements by sweeping an element
3658  * \param elem - element to sweep
3659  * \param newNodesItVec - nodes generated from each node of the element
3660  * \param newElems - generated elements
3661  * \param nbSteps - number of sweeping steps
3662  * \param srcElements - to append elem for each generated element
3663  */
3664 //=======================================================================
3665
3666 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
3667                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3668                                     list<const SMDS_MeshElement*>&        newElems,
3669                                     const int                             nbSteps,
3670                                     SMESH_SequenceOfElemPtr&              srcElements)
3671 {
3672   //MESSAGE("sweepElement " << nbSteps);
3673   SMESHDS_Mesh* aMesh = GetMeshDS();
3674
3675   const int           nbNodes = elem->NbNodes();
3676   const int         nbCorners = elem->NbCornerNodes();
3677   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
3678                                                           polyhedron creation !!! */
3679   // Loop on elem nodes:
3680   // find new nodes and detect same nodes indices
3681   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3682   vector<const SMDS_MeshNode*> prevNod( nbNodes );
3683   vector<const SMDS_MeshNode*> nextNod( nbNodes );
3684   vector<const SMDS_MeshNode*> midlNod( nbNodes );
3685
3686   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
3687   vector<int> sames(nbNodes);
3688   vector<bool> isSingleNode(nbNodes);
3689
3690   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3691     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
3692     const SMDS_MeshNode*                         node = nnIt->first;
3693     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3694     if ( listNewNodes.empty() )
3695       return;
3696
3697     itNN   [ iNode ] = listNewNodes.begin();
3698     prevNod[ iNode ] = node;
3699     nextNod[ iNode ] = listNewNodes.front();
3700
3701     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
3702                                                              corner node of linear */
3703     if ( prevNod[ iNode ] != nextNod [ iNode ])
3704       nbDouble += !isSingleNode[iNode];
3705
3706     if( iNode < nbCorners ) { // check corners only
3707       if ( prevNod[ iNode ] == nextNod [ iNode ])
3708         sames[nbSame++] = iNode;
3709       else
3710         iNotSameNode = iNode;
3711     }
3712   }
3713
3714   if ( nbSame == nbNodes || nbSame > 2) {
3715     MESSAGE( " Too many same nodes of element " << elem->GetID() );
3716     return;
3717   }
3718
3719   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
3720   {
3721     // fix nodes order to have bottom normal external
3722     if ( baseType == SMDSEntity_Polygon )
3723     {
3724       std::reverse( itNN.begin(), itNN.end() );
3725       std::reverse( prevNod.begin(), prevNod.end() );
3726       std::reverse( midlNod.begin(), midlNod.end() );
3727       std::reverse( nextNod.begin(), nextNod.end() );
3728       std::reverse( isSingleNode.begin(), isSingleNode.end() );
3729     }
3730     else
3731     {
3732       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
3733       SMDS_MeshCell::applyInterlace( ind, itNN );
3734       SMDS_MeshCell::applyInterlace( ind, prevNod );
3735       SMDS_MeshCell::applyInterlace( ind, nextNod );
3736       SMDS_MeshCell::applyInterlace( ind, midlNod );
3737       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3738       if ( nbSame > 0 )
3739       {
3740         sames[nbSame] = iNotSameNode;
3741         for ( int j = 0; j <= nbSame; ++j )
3742           for ( size_t i = 0; i < ind.size(); ++i )
3743             if ( ind[i] == sames[j] )
3744             {
3745               sames[j] = i;
3746               break;
3747             }
3748         iNotSameNode = sames[nbSame];
3749       }
3750     }
3751   }
3752
3753   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3754   if ( nbSame > 0 ) {
3755     iSameNode    = sames[ nbSame-1 ];
3756     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
3757     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
3758     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
3759   }
3760
3761   // make new elements
3762   for (int iStep = 0; iStep < nbSteps; iStep++ )
3763   {
3764     // get next nodes
3765     for ( iNode = 0; iNode < nbNodes; iNode++ )
3766     {
3767       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
3768       nextNod[ iNode ] = *itNN[ iNode ]++;
3769     }
3770
3771     SMDS_MeshElement* aNewElem = 0;
3772     /*if(!elem->IsPoly())*/ {
3773       switch ( baseType ) {
3774       case SMDSEntity_0D:
3775       case SMDSEntity_Node: { // sweep NODE
3776         if ( nbSame == 0 ) {
3777           if ( isSingleNode[0] )
3778             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3779           else
3780             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3781         }
3782         else
3783           return;
3784         break;
3785       }
3786       case SMDSEntity_Edge: { // sweep EDGE
3787         if ( nbDouble == 0 )
3788         {
3789           if ( nbSame == 0 ) // ---> quadrangle
3790             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3791                                       nextNod[ 1 ], nextNod[ 0 ] );
3792           else               // ---> triangle
3793             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3794                                       nextNod[ iNotSameNode ] );
3795         }
3796         else                 // ---> polygon
3797         {
3798           vector<const SMDS_MeshNode*> poly_nodes;
3799           poly_nodes.push_back( prevNod[0] );
3800           poly_nodes.push_back( prevNod[1] );
3801           if ( prevNod[1] != nextNod[1] )
3802           {
3803             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
3804             poly_nodes.push_back( nextNod[1] );
3805           }
3806           if ( prevNod[0] != nextNod[0] )
3807           {
3808             poly_nodes.push_back( nextNod[0] );
3809             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
3810           }
3811           switch ( poly_nodes.size() ) {
3812           case 3:
3813             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
3814             break;
3815           case 4:
3816             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
3817                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
3818             break;
3819           default:
3820             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
3821           }
3822         }
3823         break;
3824       }
3825       case SMDSEntity_Triangle: // TRIANGLE --->
3826         {
3827           if ( nbDouble > 0 ) break;
3828           if ( nbSame == 0 )       // ---> pentahedron
3829             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3830                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
3831
3832           else if ( nbSame == 1 )  // ---> pyramid
3833             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3834                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
3835                                          nextNod[ iSameNode ]);
3836
3837           else // 2 same nodes:       ---> tetrahedron
3838             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3839                                          nextNod[ iNotSameNode ]);
3840           break;
3841         }
3842       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
3843         {
3844           if ( nbSame == 2 )
3845             return;
3846           if ( nbDouble+nbSame == 2 )
3847           {
3848             if(nbSame==0) {      // ---> quadratic quadrangle
3849               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3850                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
3851             }
3852             else { //(nbSame==1) // ---> quadratic triangle
3853               if(sames[0]==2) {
3854                 return; // medium node on axis
3855               }
3856               else if(sames[0]==0)
3857                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3858                                           nextNod[2], midlNod[1], prevNod[2]);
3859               else // sames[0]==1
3860                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3861                                           midlNod[0], nextNod[2], prevNod[2]);
3862             }
3863           }
3864           else if ( nbDouble == 3 )
3865           {
3866             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
3867               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3868                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
3869             }
3870           }
3871           else
3872             return;
3873           break;
3874         }
3875       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
3876         if ( nbDouble > 0 ) break;
3877
3878         if ( nbSame == 0 )       // ---> hexahedron
3879           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
3880                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
3881
3882         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
3883           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3884                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
3885                                        nextNod[ iSameNode ]);
3886           newElems.push_back( aNewElem );
3887           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
3888                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3889                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
3890         }
3891         else if ( nbSame == 2 ) { // ---> pentahedron
3892           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3893             // iBeforeSame is same too
3894             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3895                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
3896                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
3897           else
3898             // iAfterSame is same too
3899             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
3900                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3901                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
3902         }
3903         break;
3904       }
3905       case SMDSEntity_Quad_Triangle: { // sweep Quadratic TRIANGLE --->
3906         if ( nbDouble+nbSame != 3 ) break;
3907         if(nbSame==0) {
3908           // --->  pentahedron with 15 nodes
3909           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3910                                        nextNod[0], nextNod[1], nextNod[2],
3911                                        prevNod[3], prevNod[4], prevNod[5],
3912                                        nextNod[3], nextNod[4], nextNod[5],
3913                                        midlNod[0], midlNod[1], midlNod[2]);
3914         }
3915         else if(nbSame==1) {
3916           // --->  2d order pyramid of 13 nodes
3917           int apex = iSameNode;
3918           int i0 = ( apex + 1 ) % nbCorners;
3919           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
3920           int i0a = apex + 3;
3921           int i1a = i1 + 3;
3922           int i01 = i0 + 3;
3923           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
3924                                       nextNod[i0], nextNod[i1], prevNod[apex],
3925                                       prevNod[i01], midlNod[i0],
3926                                       nextNod[i01], midlNod[i1],
3927                                       prevNod[i1a], prevNod[i0a],
3928                                       nextNod[i0a], nextNod[i1a]);
3929         }
3930         else if(nbSame==2) {
3931           // --->  2d order tetrahedron of 10 nodes
3932           int n1 = iNotSameNode;
3933           int n2 = ( n1 + 1             ) % nbCorners;
3934           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
3935           int n12 = n1 + 3;
3936           int n23 = n2 + 3;
3937           int n31 = n3 + 3;
3938           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
3939                                        prevNod[n12], prevNod[n23], prevNod[n31],
3940                                        midlNod[n1], nextNod[n12], nextNod[n31]);
3941         }
3942         break;
3943       }
3944       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
3945         if( nbSame == 0 ) {
3946           if ( nbDouble != 4 ) break;
3947           // --->  hexahedron with 20 nodes
3948           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3949                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3950                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3951                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3952                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
3953         }
3954         else if(nbSame==1) {
3955           // ---> pyramid + pentahedron - can not be created since it is needed
3956           // additional middle node at the center of face
3957           INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
3958           return;
3959         }
3960         else if( nbSame == 2 ) {
3961           if ( nbDouble != 2 ) break;
3962           // --->  2d order Pentahedron with 15 nodes
3963           int n1,n2,n4,n5;
3964           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
3965             // iBeforeSame is same too
3966             n1 = iBeforeSame;
3967             n2 = iOpposSame;
3968             n4 = iSameNode;
3969             n5 = iAfterSame;
3970           }
3971           else {
3972             // iAfterSame is same too
3973             n1 = iSameNode;
3974             n2 = iBeforeSame;
3975             n4 = iAfterSame;
3976             n5 = iOpposSame;
3977           }
3978           int n12 = n2 + 4;
3979           int n45 = n4 + 4;
3980           int n14 = n1 + 4;
3981           int n25 = n5 + 4;
3982           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
3983                                        prevNod[n4], prevNod[n5], nextNod[n5],
3984                                        prevNod[n12], midlNod[n2], nextNod[n12],
3985                                        prevNod[n45], midlNod[n5], nextNod[n45],
3986                                        prevNod[n14], prevNod[n25], nextNod[n25]);
3987         }
3988         break;
3989       }
3990       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
3991
3992         if( nbSame == 0 && nbDouble == 9 ) {
3993           // --->  tri-quadratic hexahedron with 27 nodes
3994           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3995                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3996                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3997                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3998                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
3999                                        prevNod[8], // bottom center
4000                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4001                                        nextNod[8], // top center
4002                                        midlNod[8]);// elem center
4003         }
4004         else
4005         {
4006           return;
4007         }
4008         break;
4009       }
4010       case SMDSEntity_Polygon: { // sweep POLYGON
4011
4012         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4013           // --->  hexagonal prism
4014           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4015                                        prevNod[3], prevNod[4], prevNod[5],
4016                                        nextNod[0], nextNod[1], nextNod[2],
4017                                        nextNod[3], nextNod[4], nextNod[5]);
4018         }
4019         break;
4020       }
4021       case SMDSEntity_Ball:
4022         return;
4023
4024       default:
4025         break;
4026       }
4027     }
4028
4029     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4030     {
4031       if ( baseType != SMDSEntity_Polygon )
4032       {
4033         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4034         SMDS_MeshCell::applyInterlace( ind, prevNod );
4035         SMDS_MeshCell::applyInterlace( ind, nextNod );
4036         SMDS_MeshCell::applyInterlace( ind, midlNod );
4037         SMDS_MeshCell::applyInterlace( ind, itNN );
4038         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4039         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4040       }
4041       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4042       vector<int> quantities (nbNodes + 2);
4043       polyedre_nodes.clear();
4044       quantities.clear();
4045
4046       // bottom of prism
4047       for (int inode = 0; inode < nbNodes; inode++)
4048         polyedre_nodes.push_back( prevNod[inode] );
4049       quantities.push_back( nbNodes );
4050
4051       // top of prism
4052       polyedre_nodes.push_back( nextNod[0] );
4053       for (int inode = nbNodes; inode-1; --inode )
4054         polyedre_nodes.push_back( nextNod[inode-1] );
4055       quantities.push_back( nbNodes );
4056
4057       // side faces
4058       for (int iface = 0; iface < nbNodes; iface++)
4059       {
4060         const int prevNbNodes = polyedre_nodes.size();
4061         int inextface = (iface+1) % nbNodes;
4062         polyedre_nodes.push_back( prevNod[inextface] );
4063         polyedre_nodes.push_back( prevNod[iface] );
4064         if ( prevNod[iface] != nextNod[iface] )
4065         {
4066           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4067           polyedre_nodes.push_back( nextNod[iface] );
4068         }
4069         if ( prevNod[inextface] != nextNod[inextface] )
4070         {
4071           polyedre_nodes.push_back( nextNod[inextface] );
4072           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4073         }
4074         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4075         if ( nbFaceNodes > 2 )
4076           quantities.push_back( nbFaceNodes );
4077         else // degenerated face
4078           polyedre_nodes.resize( prevNbNodes );
4079       }
4080       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4081     }
4082
4083     if ( aNewElem ) {
4084       newElems.push_back( aNewElem );
4085       myLastCreatedElems.Append(aNewElem);
4086       srcElements.Append( elem );
4087     }
4088
4089     // set new prev nodes
4090     for ( iNode = 0; iNode < nbNodes; iNode++ )
4091       prevNod[ iNode ] = nextNod[ iNode ];
4092
4093   } // for steps
4094 }
4095
4096 //=======================================================================
4097 /*!
4098  * \brief Create 1D and 2D elements around swept elements
4099  * \param mapNewNodes - source nodes and ones generated from them
4100  * \param newElemsMap - source elements and ones generated from them
4101  * \param elemNewNodesMap - nodes generated from each node of each element
4102  * \param elemSet - all swept elements
4103  * \param nbSteps - number of sweeping steps
4104  * \param srcElements - to append elem for each generated element
4105  */
4106 //=======================================================================
4107
4108 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4109                                   TElemOfElemListMap &     newElemsMap,
4110                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4111                                   TIDSortedElemSet&        elemSet,
4112                                   const int                nbSteps,
4113                                   SMESH_SequenceOfElemPtr& srcElements)
4114 {
4115   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4116   SMESHDS_Mesh* aMesh = GetMeshDS();
4117
4118   // Find nodes belonging to only one initial element - sweep them to get edges.
4119
4120   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4121   for ( ; nList != mapNewNodes.end(); nList++ )
4122   {
4123     const SMDS_MeshNode* node =
4124       static_cast<const SMDS_MeshNode*>( nList->first );
4125     if ( newElemsMap.count( node ))
4126       continue; // node was extruded into edge
4127     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4128     int nbInitElems = 0;
4129     const SMDS_MeshElement* el = 0;
4130     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4131     while ( eIt->more() && nbInitElems < 2 ) {
4132       el = eIt->next();
4133       SMDSAbs_ElementType type = el->GetType();
4134       if ( type == SMDSAbs_Volume || type < highType ) continue;
4135       if ( type > highType ) {
4136         nbInitElems = 0;
4137         highType = type;
4138       }
4139       nbInitElems += elemSet.count(el);
4140     }
4141     if ( nbInitElems < 2 ) {
4142       bool NotCreateEdge = el && el->IsMediumNode(node);
4143       if(!NotCreateEdge) {
4144         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4145         list<const SMDS_MeshElement*> newEdges;
4146         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4147       }
4148     }
4149   }
4150
4151   // Make a ceiling for each element ie an equal element of last new nodes.
4152   // Find free links of faces - make edges and sweep them into faces.
4153
4154   TElemOfElemListMap::iterator   itElem      = newElemsMap.begin();
4155   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4156   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4157   {
4158     const SMDS_MeshElement* elem = itElem->first;
4159     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4160
4161     if(itElem->second.size()==0) continue;
4162
4163     const bool isQuadratic = elem->IsQuadratic();
4164
4165     if ( elem->GetType() == SMDSAbs_Edge ) {
4166       // create a ceiling edge
4167       if ( !isQuadratic ) {
4168         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4169                                vecNewNodes[ 1 ]->second.back())) {
4170           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4171                                                    vecNewNodes[ 1 ]->second.back()));
4172           srcElements.Append( myLastCreatedElems.Last() );
4173         }
4174       }
4175       else {
4176         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4177                                vecNewNodes[ 1 ]->second.back(),
4178                                vecNewNodes[ 2 ]->second.back())) {
4179           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4180                                                    vecNewNodes[ 1 ]->second.back(),
4181                                                    vecNewNodes[ 2 ]->second.back()));
4182           srcElements.Append( myLastCreatedElems.Last() );
4183         }
4184       }
4185     }
4186     if ( elem->GetType() != SMDSAbs_Face )
4187       continue;
4188
4189     bool hasFreeLinks = false;
4190
4191     TIDSortedElemSet avoidSet;
4192     avoidSet.insert( elem );
4193
4194     set<const SMDS_MeshNode*> aFaceLastNodes;
4195     int iNode, nbNodes = vecNewNodes.size();
4196     if ( !isQuadratic ) {
4197       // loop on the face nodes
4198       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4199         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4200         // look for free links of the face
4201         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4202         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4203         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4204         // check if a link is free
4205         if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4206           hasFreeLinks = true;
4207           // make an edge and a ceiling for a new edge
4208           if ( !aMesh->FindEdge( n1, n2 )) {
4209             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // free link edge
4210             srcElements.Append( myLastCreatedElems.Last() );
4211           }
4212           n1 = vecNewNodes[ iNode ]->second.back();
4213           n2 = vecNewNodes[ iNext ]->second.back();
4214           if ( !aMesh->FindEdge( n1, n2 )) {
4215             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // ceiling edge
4216             srcElements.Append( myLastCreatedElems.Last() );
4217           }
4218         }
4219       }
4220     }
4221     else { // elem is quadratic face
4222       int nbn = nbNodes/2;
4223       for ( iNode = 0; iNode < nbn; iNode++ ) {
4224         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4225         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4226         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4227         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4228         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4229         // check if a link is free
4230         if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4231              ! SMESH_MeshEditor::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4232              ! SMESH_MeshEditor::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4233           hasFreeLinks = true;
4234           // make an edge and a ceiling for a new edge
4235           // find medium node
4236           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4237             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4238             srcElements.Append( myLastCreatedElems.Last() );
4239           }
4240           n1 = vecNewNodes[ iNode ]->second.back();
4241           n2 = vecNewNodes[ iNext ]->second.back();
4242           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4243           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4244             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4245             srcElements.Append( myLastCreatedElems.Last() );
4246           }
4247         }
4248       }
4249       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4250         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4251       }
4252     }
4253
4254     // sweep free links into faces
4255
4256     if ( hasFreeLinks )  {
4257       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4258       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4259
4260       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4261       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4262         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4263         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4264       }
4265       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4266         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4267         std::advance( v, volNb );
4268         // find indices of free faces of a volume and their source edges
4269         list< int > freeInd;
4270         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4271         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4272         int iF, nbF = vTool.NbFaces();
4273         for ( iF = 0; iF < nbF; iF ++ ) {
4274           if (vTool.IsFreeFace( iF ) &&
4275               vTool.GetFaceNodes( iF, faceNodeSet ) &&
4276               initNodeSet != faceNodeSet) // except an initial face
4277           {
4278             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4279               continue;
4280             freeInd.push_back( iF );
4281             // find source edge of a free face iF
4282             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4283             commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4284             std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4285                                    initNodeSet.begin(), initNodeSet.end(),
4286                                    commonNodes.begin());
4287             if ( (*v)->IsQuadratic() )
4288               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4289             else
4290               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4291 #ifdef _DEBUG_
4292             if ( !srcEdges.back() )
4293             {
4294               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4295                    << iF << " of volume #" << vTool.ID() << endl;
4296             }
4297 #endif
4298           }
4299         }
4300         if ( freeInd.empty() )
4301           continue;
4302
4303         // create faces for all steps;
4304         // if such a face has been already created by sweep of edge,
4305         // assure that its orientation is OK
4306         for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4307           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4308           vTool.SetExternalNormal();
4309           const int nextShift = vTool.IsForward() ? +1 : -1;
4310           list< int >::iterator ind = freeInd.begin();
4311           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4312           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4313           {
4314             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4315             int nbn = vTool.NbFaceNodes( *ind );
4316             const SMDS_MeshElement * f = 0;
4317             if ( nbn == 3 )              ///// triangle
4318             {
4319               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4320               if ( !f ||
4321                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4322               {
4323                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4324                                                      nodes[ 1 ],
4325                                                      nodes[ 1 + nextShift ] };
4326                 if ( f )
4327                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4328                 else
4329                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4330                                                             newOrder[ 2 ] ));
4331               }
4332             }
4333             else if ( nbn == 4 )       ///// quadrangle
4334             {
4335               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4336               if ( !f ||
4337                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4338               {
4339                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4340                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4341                 if ( f )
4342                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4343                 else
4344                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4345                                                             newOrder[ 2 ], newOrder[ 3 ]));
4346               }
4347             }
4348             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4349             {
4350               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4351               if ( !f ||
4352                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4353               {
4354                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4355                                                      nodes[2],
4356                                                      nodes[2 + 2*nextShift],
4357                                                      nodes[3 - 2*nextShift],
4358                                                      nodes[3],
4359                                                      nodes[3 + 2*nextShift]};
4360                 if ( f )
4361                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4362                 else
4363                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4364                                                             newOrder[ 1 ],
4365                                                             newOrder[ 2 ],
4366                                                             newOrder[ 3 ],
4367                                                             newOrder[ 4 ],
4368                                                             newOrder[ 5 ] ));
4369               }
4370             }
4371             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4372             {
4373               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4374                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4375               if ( !f ||
4376                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4377               {
4378                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4379                                                      nodes[4 - 2*nextShift],
4380                                                      nodes[4],
4381                                                      nodes[4 + 2*nextShift],
4382                                                      nodes[1],
4383                                                      nodes[5 - 2*nextShift],
4384                                                      nodes[5],
4385                                                      nodes[5 + 2*nextShift] };
4386                 if ( f )
4387                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4388                 else
4389                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4390                                                            newOrder[ 2 ], newOrder[ 3 ],
4391                                                            newOrder[ 4 ], newOrder[ 5 ],
4392                                                            newOrder[ 6 ], newOrder[ 7 ]));
4393               }
4394             }
4395             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4396             {
4397               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4398                                       SMDSAbs_Face, /*noMedium=*/false);
4399               if ( !f ||
4400                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4401               {
4402                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4403                                                      nodes[4 - 2*nextShift],
4404                                                      nodes[4],
4405                                                      nodes[4 + 2*nextShift],
4406                                                      nodes[1],
4407                                                      nodes[5 - 2*nextShift],
4408                                                      nodes[5],
4409                                                      nodes[5 + 2*nextShift],
4410                                                      nodes[8] };
4411                 if ( f )
4412                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4413                 else
4414                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4415                                                            newOrder[ 2 ], newOrder[ 3 ],
4416                                                            newOrder[ 4 ], newOrder[ 5 ],
4417                                                            newOrder[ 6 ], newOrder[ 7 ],
4418                                                            newOrder[ 8 ]));
4419               }
4420             }
4421             else  //////// polygon
4422             {
4423               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4424               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4425               if ( !f ||
4426                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4427               {
4428                 if ( !vTool.IsForward() )
4429                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4430                 if ( f )
4431                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4432                 else
4433                   AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4434               }
4435             }
4436
4437             while ( srcElements.Length() < myLastCreatedElems.Length() )
4438               srcElements.Append( *srcEdge );
4439
4440           }  // loop on free faces
4441
4442           // go to the next volume
4443           iVol = 0;
4444           while ( iVol++ < nbVolumesByStep ) v++;
4445
4446         } // loop on steps
4447       } // loop on volumes of one step
4448     } // sweep free links into faces
4449
4450     // Make a ceiling face with a normal external to a volume
4451
4452     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4453
4454     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4455     if ( iF >= 0 ) {
4456       lastVol.SetExternalNormal();
4457       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4458       int nbn = lastVol.NbFaceNodes( iF );
4459       if ( nbn == 3 ) {
4460         if (!hasFreeLinks ||
4461             !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
4462           myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
4463       }
4464       else if ( nbn == 4 )
4465       {
4466         if (!hasFreeLinks ||
4467             !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
4468           myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]));
4469       }
4470       else if ( nbn == 6 && isQuadratic )
4471       {
4472         if (!hasFreeLinks ||
4473             !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5]) )
4474           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
4475                                                    nodes[1], nodes[3], nodes[5]));
4476       }
4477       else if ( nbn == 8 && isQuadratic )
4478       {
4479         if (!hasFreeLinks ||
4480             !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
4481                              nodes[1], nodes[3], nodes[5], nodes[7]) )
4482           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4483                                                    nodes[1], nodes[3], nodes[5], nodes[7]));
4484       }
4485       else if ( nbn == 9 && isQuadratic )
4486       {
4487         if (!hasFreeLinks ||
4488             !aMesh->FindElement(vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4489                                 SMDSAbs_Face, /*noMedium=*/false) )
4490           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4491                                                    nodes[1], nodes[3], nodes[5], nodes[7],
4492                                                    nodes[8]));
4493       }
4494       else {
4495         vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes + nbn );
4496         if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
4497           myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4498       }
4499
4500       while ( srcElements.Length() < myLastCreatedElems.Length() )
4501         srcElements.Append( myLastCreatedElems.Last() );
4502     }
4503   } // loop on swept elements
4504 }
4505
4506 //=======================================================================
4507 //function : RotationSweep
4508 //purpose  :
4509 //=======================================================================
4510
4511 SMESH_MeshEditor::PGroupIDs
4512 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4513                                 const gp_Ax1&      theAxis,
4514                                 const double       theAngle,
4515                                 const int          theNbSteps,
4516                                 const double       theTol,
4517                                 const bool         theMakeGroups,
4518                                 const bool         theMakeWalls)
4519 {
4520   myLastCreatedElems.Clear();
4521   myLastCreatedNodes.Clear();
4522
4523   // source elements for each generated one
4524   SMESH_SequenceOfElemPtr srcElems, srcNodes;
4525
4526   MESSAGE( "RotationSweep()");
4527   gp_Trsf aTrsf;
4528   aTrsf.SetRotation( theAxis, theAngle );
4529   gp_Trsf aTrsf2;
4530   aTrsf2.SetRotation( theAxis, theAngle/2. );
4531
4532   gp_Lin aLine( theAxis );
4533   double aSqTol = theTol * theTol;
4534
4535   SMESHDS_Mesh* aMesh = GetMeshDS();
4536
4537   TNodeOfNodeListMap mapNewNodes;
4538   TElemOfVecOfNnlmiMap mapElemNewNodes;
4539   TElemOfElemListMap newElemsMap;
4540
4541   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4542                                      myMesh->NbFaces(ORDER_QUADRATIC) +
4543                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
4544   // loop on theElems
4545   TIDSortedElemSet::iterator itElem;
4546   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4547     const SMDS_MeshElement* elem = *itElem;
4548     if ( !elem || elem->GetType() == SMDSAbs_Volume )
4549       continue;
4550     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4551     newNodesItVec.reserve( elem->NbNodes() );
4552
4553     // loop on elem nodes
4554     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4555     while ( itN->more() )
4556     {
4557       // check if a node has been already sweeped
4558       const SMDS_MeshNode* node = cast2Node( itN->next() );
4559
4560       gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4561       double coord[3];
4562       aXYZ.Coord( coord[0], coord[1], coord[2] );
4563       bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4564
4565       TNodeOfNodeListMapItr nIt =
4566         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4567       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4568       if ( listNewNodes.empty() )
4569       {
4570         // check if we are to create medium nodes between corner ones
4571         bool needMediumNodes = false;
4572         if ( isQuadraticMesh )
4573         {
4574           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4575           while (it->more() && !needMediumNodes )
4576           {
4577             const SMDS_MeshElement* invElem = it->next();
4578             if ( invElem != elem && !theElems.count( invElem )) continue;
4579             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4580             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4581               needMediumNodes = true;
4582           }
4583         }
4584
4585         // make new nodes
4586         const SMDS_MeshNode * newNode = node;
4587         for ( int i = 0; i < theNbSteps; i++ ) {
4588           if ( !isOnAxis ) {
4589             if ( needMediumNodes )  // create a medium node
4590             {
4591               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4592               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4593               myLastCreatedNodes.Append(newNode);
4594               srcNodes.Append( node );
4595               listNewNodes.push_back( newNode );
4596               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4597             }
4598             else {
4599               aTrsf.Transforms( coord[0], coord[1], coord[2] );
4600             }
4601             // create a corner node
4602             newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4603             myLastCreatedNodes.Append(newNode);
4604             srcNodes.Append( node );
4605             listNewNodes.push_back( newNode );
4606           }
4607           else {
4608             listNewNodes.push_back( newNode );
4609             // if ( needMediumNodes )
4610             //   listNewNodes.push_back( newNode );
4611           }
4612         }
4613       }
4614       newNodesItVec.push_back( nIt );
4615     }
4616     // make new elements
4617     sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4618   }
4619
4620   if ( theMakeWalls )
4621     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4622
4623   PGroupIDs newGroupIDs;
4624   if ( theMakeGroups )
4625     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4626
4627   return newGroupIDs;
4628 }
4629
4630
4631 //=======================================================================
4632 //function : CreateNode
4633 //purpose  :
4634 //=======================================================================
4635 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4636                                                   const double y,
4637                                                   const double z,
4638                                                   const double tolnode,
4639                                                   SMESH_SequenceOfNode& aNodes)
4640 {
4641   // myLastCreatedElems.Clear();
4642   // myLastCreatedNodes.Clear();
4643
4644   gp_Pnt P1(x,y,z);
4645   SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4646
4647   // try to search in sequence of existing nodes
4648   // if aNodes.Length()>0 we 'nave to use given sequence
4649   // else - use all nodes of mesh
4650   if(aNodes.Length()>0) {
4651     int i;
4652     for(i=1; i<=aNodes.Length(); i++) {
4653       gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4654       if(P1.Distance(P2)<tolnode)
4655         return aNodes.Value(i);
4656     }
4657   }
4658   else {
4659     SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4660     while(itn->more()) {
4661       const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4662       gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4663       if(P1.Distance(P2)<tolnode)
4664         return aN;
4665     }
4666   }
4667
4668   // create new node and return it
4669   const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4670   //myLastCreatedNodes.Append(NewNode);
4671   return NewNode;
4672 }
4673
4674
4675 //=======================================================================
4676 //function : ExtrusionSweep
4677 //purpose  :
4678 //=======================================================================
4679
4680 SMESH_MeshEditor::PGroupIDs
4681 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &  theElems,
4682                                   const gp_Vec&       theStep,
4683                                   const int           theNbSteps,
4684                                   TElemOfElemListMap& newElemsMap,
4685                                   const bool          theMakeGroups,
4686                                   const int           theFlags,
4687                                   const double        theTolerance)
4688 {
4689   ExtrusParam aParams;
4690   aParams.myDir = gp_Dir(theStep);
4691   aParams.myNodes.Clear();
4692   aParams.mySteps = new TColStd_HSequenceOfReal;
4693   int i;
4694   for(i=1; i<=theNbSteps; i++)
4695     aParams.mySteps->Append(theStep.Magnitude());
4696
4697   return
4698     ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4699 }
4700
4701
4702 //=======================================================================
4703 //function : ExtrusionSweep
4704 //purpose  :
4705 //=======================================================================
4706
4707 SMESH_MeshEditor::PGroupIDs
4708 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &  theElems,
4709                                   ExtrusParam&        theParams,
4710                                   TElemOfElemListMap& newElemsMap,
4711                                   const bool          theMakeGroups,
4712                                   const int           theFlags,
4713                                   const double        theTolerance)
4714 {
4715   myLastCreatedElems.Clear();
4716   myLastCreatedNodes.Clear();
4717
4718   // source elements for each generated one
4719   SMESH_SequenceOfElemPtr srcElems, srcNodes;
4720
4721   SMESHDS_Mesh* aMesh = GetMeshDS();
4722
4723   int nbsteps = theParams.mySteps->Length();
4724
4725   TNodeOfNodeListMap mapNewNodes;
4726   //TNodeOfNodeVecMap mapNewNodes;
4727   TElemOfVecOfNnlmiMap mapElemNewNodes;
4728   //TElemOfVecOfMapNodesMap mapElemNewNodes;
4729
4730   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4731                                      myMesh->NbFaces(ORDER_QUADRATIC) +
4732                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
4733   // loop on theElems
4734   TIDSortedElemSet::iterator itElem;
4735   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4736     // check element type
4737     const SMDS_MeshElement* elem = *itElem;
4738     if ( !elem  || elem->GetType() == SMDSAbs_Volume )
4739       continue;
4740
4741     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4742     newNodesItVec.reserve( elem->NbNodes() );
4743
4744     // loop on elem nodes
4745     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4746     while ( itN->more() )
4747     {
4748       // check if a node has been already sweeped
4749       const SMDS_MeshNode* node = cast2Node( itN->next() );
4750       TNodeOfNodeListMap::iterator nIt =
4751         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4752       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4753       if ( listNewNodes.empty() )
4754       {
4755         // make new nodes
4756
4757         // check if we are to create medium nodes between corner ones
4758         bool needMediumNodes = false;
4759         if ( isQuadraticMesh )
4760         {
4761           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4762           while (it->more() && !needMediumNodes )
4763           {
4764             const SMDS_MeshElement* invElem = it->next();
4765             if ( invElem != elem && !theElems.count( invElem )) continue;
4766             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4767             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4768               needMediumNodes = true;
4769           }
4770         }
4771
4772         double coord[] = { node->X(), node->Y(), node->Z() };
4773         for ( int i = 0; i < nbsteps; i++ )
4774         {
4775           if ( needMediumNodes ) // create a medium node
4776           {
4777             double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4778             double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4779             double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4780             if( theFlags & EXTRUSION_FLAG_SEW ) {
4781               const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4782                                                          theTolerance, theParams.myNodes);
4783               listNewNodes.push_back( newNode );
4784             }
4785             else {
4786               const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4787               myLastCreatedNodes.Append(newNode);
4788               srcNodes.Append( node );
4789               listNewNodes.push_back( newNode );
4790             }
4791           }
4792           // create a corner node
4793           coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4794           coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4795           coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4796           if( theFlags & EXTRUSION_FLAG_SEW ) {
4797             const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4798                                                        theTolerance, theParams.myNodes);
4799             listNewNodes.push_back( newNode );
4800           }
4801           else {
4802             const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4803             myLastCreatedNodes.Append(newNode);
4804             srcNodes.Append( node );
4805             listNewNodes.push_back( newNode );
4806           }
4807         }
4808       }
4809       newNodesItVec.push_back( nIt );
4810     }
4811     // make new elements
4812     sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4813   }
4814
4815   if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4816     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4817   }
4818   PGroupIDs newGroupIDs;
4819   if ( theMakeGroups )
4820     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4821
4822   return newGroupIDs;
4823 }
4824
4825 //=======================================================================
4826 //function : ExtrusionAlongTrack
4827 //purpose  :
4828 //=======================================================================
4829 SMESH_MeshEditor::Extrusion_Error
4830 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
4831                                        SMESH_subMesh*       theTrack,
4832                                        const SMDS_MeshNode* theN1,
4833                                        const bool           theHasAngles,
4834                                        list<double>&        theAngles,
4835                                        const bool           theLinearVariation,
4836                                        const bool           theHasRefPoint,
4837                                        const gp_Pnt&        theRefPoint,
4838                                        const bool           theMakeGroups)
4839 {
4840   MESSAGE("ExtrusionAlongTrack");
4841   myLastCreatedElems.Clear();
4842   myLastCreatedNodes.Clear();
4843
4844   int aNbE;
4845   std::list<double> aPrms;
4846   TIDSortedElemSet::iterator itElem;
4847
4848   gp_XYZ aGC;
4849   TopoDS_Edge aTrackEdge;
4850   TopoDS_Vertex aV1, aV2;
4851
4852   SMDS_ElemIteratorPtr aItE;
4853   SMDS_NodeIteratorPtr aItN;
4854   SMDSAbs_ElementType aTypeE;
4855
4856   TNodeOfNodeListMap mapNewNodes;
4857
4858   // 1. Check data
4859   aNbE = theElements.size();
4860   // nothing to do
4861   if ( !aNbE )
4862     return EXTR_NO_ELEMENTS;
4863
4864   // 1.1 Track Pattern
4865   ASSERT( theTrack );
4866
4867   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4868
4869   aItE = pSubMeshDS->GetElements();
4870   while ( aItE->more() ) {
4871     const SMDS_MeshElement* pE = aItE->next();
4872     aTypeE = pE->GetType();
4873     // Pattern must contain links only
4874     if ( aTypeE != SMDSAbs_Edge )
4875       return EXTR_PATH_NOT_EDGE;
4876   }
4877
4878   list<SMESH_MeshEditor_PathPoint> fullList;
4879
4880   const TopoDS_Shape& aS = theTrack->GetSubShape();
4881   // Sub-shape for the Pattern must be an Edge or Wire
4882   if( aS.ShapeType() == TopAbs_EDGE ) {
4883     aTrackEdge = TopoDS::Edge( aS );
4884     // the Edge must not be degenerated
4885     if ( BRep_Tool::Degenerated( aTrackEdge ) )
4886       return EXTR_BAD_PATH_SHAPE;
4887     TopExp::Vertices( aTrackEdge, aV1, aV2 );
4888     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4889     const SMDS_MeshNode* aN1 = aItN->next();
4890     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4891     const SMDS_MeshNode* aN2 = aItN->next();
4892     // starting node must be aN1 or aN2
4893     if ( !( aN1 == theN1 || aN2 == theN1 ) )
4894       return EXTR_BAD_STARTING_NODE;
4895     aItN = pSubMeshDS->GetNodes();
4896     while ( aItN->more() ) {
4897       const SMDS_MeshNode* pNode = aItN->next();
4898       const SMDS_EdgePosition* pEPos =
4899         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4900       double aT = pEPos->GetUParameter();
4901       aPrms.push_back( aT );
4902     }
4903     //Extrusion_Error err =
4904     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4905   } else if( aS.ShapeType() == TopAbs_WIRE ) {
4906     list< SMESH_subMesh* > LSM;
4907     TopTools_SequenceOfShape Edges;
4908     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4909     while(itSM->more()) {
4910       SMESH_subMesh* SM = itSM->next();
4911       LSM.push_back(SM);
4912       const TopoDS_Shape& aS = SM->GetSubShape();
4913       Edges.Append(aS);
4914     }
4915     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4916     int startNid = theN1->GetID();
4917     TColStd_MapOfInteger UsedNums;
4918
4919     int NbEdges = Edges.Length();
4920     int i = 1;
4921     for(; i<=NbEdges; i++) {
4922       int k = 0;
4923       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4924       for(; itLSM!=LSM.end(); itLSM++) {
4925         k++;
4926         if(UsedNums.Contains(k)) continue;
4927         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4928         SMESH_subMesh* locTrack = *itLSM;
4929         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4930         TopExp::Vertices( aTrackEdge, aV1, aV2 );
4931         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4932         const SMDS_MeshNode* aN1 = aItN->next();
4933         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4934         const SMDS_MeshNode* aN2 = aItN->next();
4935         // starting node must be aN1 or aN2
4936         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4937         // 2. Collect parameters on the track edge
4938         aPrms.clear();
4939         aItN = locMeshDS->GetNodes();
4940         while ( aItN->more() ) {
4941           const SMDS_MeshNode* pNode = aItN->next();
4942           const SMDS_EdgePosition* pEPos =
4943             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4944           double aT = pEPos->GetUParameter();
4945           aPrms.push_back( aT );
4946         }
4947         list<SMESH_MeshEditor_PathPoint> LPP;
4948         //Extrusion_Error err =
4949         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4950         LLPPs.push_back(LPP);
4951         UsedNums.Add(k);
4952         // update startN for search following egde
4953         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4954         else startNid = aN1->GetID();
4955         break;
4956       }
4957     }
4958     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4959     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4960     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4961     for(; itPP!=firstList.end(); itPP++) {
4962       fullList.push_back( *itPP );
4963     }
4964     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4965     fullList.pop_back();
4966     itLLPP++;
4967     for(; itLLPP!=LLPPs.end(); itLLPP++) {
4968       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4969       itPP = currList.begin();
4970       SMESH_MeshEditor_PathPoint PP2 = currList.front();
4971       gp_Dir D1 = PP1.Tangent();
4972       gp_Dir D2 = PP2.Tangent();
4973       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4974                            (D1.Z()+D2.Z())/2 ) );
4975       PP1.SetTangent(Dnew);
4976       fullList.push_back(PP1);
4977       itPP++;
4978       for(; itPP!=firstList.end(); itPP++) {
4979         fullList.push_back( *itPP );
4980       }
4981       PP1 = fullList.back();
4982       fullList.pop_back();
4983     }
4984     // if wire not closed
4985     fullList.push_back(PP1);
4986     // else ???
4987   }
4988   else {
4989     return EXTR_BAD_PATH_SHAPE;
4990   }
4991
4992   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4993                           theHasRefPoint, theRefPoint, theMakeGroups);
4994 }
4995
4996
4997 //=======================================================================
4998 //function : ExtrusionAlongTrack
4999 //purpose  :
5000 //=======================================================================
5001 SMESH_MeshEditor::Extrusion_Error
5002 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
5003                                        SMESH_Mesh*          theTrack,
5004                                        const SMDS_MeshNode* theN1,
5005                                        const bool           theHasAngles,
5006                                        list<double>&        theAngles,
5007                                        const bool           theLinearVariation,
5008                                        const bool           theHasRefPoint,
5009                                        const gp_Pnt&        theRefPoint,
5010                                        const bool           theMakeGroups)
5011 {
5012   myLastCreatedElems.Clear();
5013   myLastCreatedNodes.Clear();
5014
5015   int aNbE;
5016   std::list<double> aPrms;
5017   TIDSortedElemSet::iterator itElem;
5018
5019   gp_XYZ aGC;
5020   TopoDS_Edge aTrackEdge;
5021   TopoDS_Vertex aV1, aV2;
5022
5023   SMDS_ElemIteratorPtr aItE;
5024   SMDS_NodeIteratorPtr aItN;
5025   SMDSAbs_ElementType aTypeE;
5026
5027   TNodeOfNodeListMap mapNewNodes;
5028
5029   // 1. Check data
5030   aNbE = theElements.size();
5031   // nothing to do
5032   if ( !aNbE )
5033     return EXTR_NO_ELEMENTS;
5034
5035   // 1.1 Track Pattern
5036   ASSERT( theTrack );
5037
5038   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5039
5040   aItE = pMeshDS->elementsIterator();
5041   while ( aItE->more() ) {
5042     const SMDS_MeshElement* pE = aItE->next();
5043     aTypeE = pE->GetType();
5044     // Pattern must contain links only
5045     if ( aTypeE != SMDSAbs_Edge )
5046       return EXTR_PATH_NOT_EDGE;
5047   }
5048
5049   list<SMESH_MeshEditor_PathPoint> fullList;
5050
5051   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5052
5053   if( aS == SMESH_Mesh::PseudoShape() ) {
5054     //Mesh without shape
5055     const SMDS_MeshNode* currentNode = NULL;
5056     const SMDS_MeshNode* prevNode = theN1;
5057     std::vector<const SMDS_MeshNode*> aNodesList;
5058     aNodesList.push_back(theN1);
5059     int nbEdges = 0, conn=0;
5060     const SMDS_MeshElement* prevElem = NULL;
5061     const SMDS_MeshElement* currentElem = NULL;
5062     int totalNbEdges = theTrack->NbEdges();
5063     SMDS_ElemIteratorPtr nIt;
5064
5065     //check start node
5066     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5067       return EXTR_BAD_STARTING_NODE;
5068     }
5069
5070     conn = nbEdgeConnectivity(theN1);
5071     if(conn > 2)
5072       return EXTR_PATH_NOT_EDGE;
5073
5074     aItE = theN1->GetInverseElementIterator();
5075     prevElem = aItE->next();
5076     currentElem = prevElem;
5077     //Get all nodes
5078     if(totalNbEdges == 1 ) {
5079       nIt = currentElem->nodesIterator();
5080       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5081       if(currentNode == prevNode)
5082         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5083       aNodesList.push_back(currentNode);
5084     } else {
5085       nIt = currentElem->nodesIterator();
5086       while( nIt->more() ) {
5087         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5088         if(currentNode == prevNode)
5089           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5090         aNodesList.push_back(currentNode);
5091
5092         //case of the closed mesh
5093         if(currentNode == theN1) {
5094           nbEdges++;
5095           break;
5096         }
5097
5098         conn = nbEdgeConnectivity(currentNode);
5099         if(conn > 2) {
5100           return EXTR_PATH_NOT_EDGE;
5101         }else if( conn == 1 && nbEdges > 0 ) {
5102           //End of the path
5103           nbEdges++;
5104           break;
5105         }else {
5106           prevNode = currentNode;
5107           aItE = currentNode->GetInverseElementIterator();
5108           currentElem = aItE->next();
5109           if( currentElem  == prevElem)
5110             currentElem = aItE->next();
5111           nIt = currentElem->nodesIterator();
5112           prevElem = currentElem;
5113           nbEdges++;
5114         }
5115       }
5116     }
5117
5118     if(nbEdges != totalNbEdges)
5119       return EXTR_PATH_NOT_EDGE;
5120
5121     TopTools_SequenceOfShape Edges;
5122     double x1,x2,y1,y2,z1,z2;
5123     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5124     int startNid = theN1->GetID();
5125     for(int i = 1; i < aNodesList.size(); i++) {
5126       x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5127       y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5128       z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5129       TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5130       list<SMESH_MeshEditor_PathPoint> LPP;
5131       aPrms.clear();
5132       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5133       LLPPs.push_back(LPP);
5134       if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5135       else startNid = aNodesList[i-1]->GetID();
5136
5137     }
5138
5139     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5140     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5141     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5142     for(; itPP!=firstList.end(); itPP++) {
5143       fullList.push_back( *itPP );
5144     }
5145
5146     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5147     SMESH_MeshEditor_PathPoint PP2;
5148     fullList.pop_back();
5149     itLLPP++;
5150     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5151       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5152       itPP = currList.begin();
5153       PP2 = currList.front();
5154       gp_Dir D1 = PP1.Tangent();
5155       gp_Dir D2 = PP2.Tangent();
5156       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5157                            (D1.Z()+D2.Z())/2 ) );
5158       PP1.SetTangent(Dnew);
5159       fullList.push_back(PP1);
5160       itPP++;
5161       for(; itPP!=currList.end(); itPP++) {
5162         fullList.push_back( *itPP );
5163       }
5164       PP1 = fullList.back();
5165       fullList.pop_back();
5166     }
5167     fullList.push_back(PP1);
5168
5169   } // Sub-shape for the Pattern must be an Edge or Wire
5170   else if( aS.ShapeType() == TopAbs_EDGE ) {
5171     aTrackEdge = TopoDS::Edge( aS );
5172     // the Edge must not be degenerated
5173     if ( BRep_Tool::Degenerated( aTrackEdge ) )
5174       return EXTR_BAD_PATH_SHAPE;
5175     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5176     aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5177     const SMDS_MeshNode* aN1 = aItN->next();
5178     aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5179     const SMDS_MeshNode* aN2 = aItN->next();
5180     // starting node must be aN1 or aN2
5181     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5182       return EXTR_BAD_STARTING_NODE;
5183     aItN = pMeshDS->nodesIterator();
5184     while ( aItN->more() ) {
5185       const SMDS_MeshNode* pNode = aItN->next();
5186       if( pNode==aN1 || pNode==aN2 ) continue;
5187       const SMDS_EdgePosition* pEPos =
5188         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5189       double aT = pEPos->GetUParameter();
5190       aPrms.push_back( aT );
5191     }
5192     //Extrusion_Error err =
5193     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5194   }
5195   else if( aS.ShapeType() == TopAbs_WIRE ) {
5196     list< SMESH_subMesh* > LSM;
5197     TopTools_SequenceOfShape Edges;
5198     TopExp_Explorer eExp(aS, TopAbs_EDGE);
5199     for(; eExp.More(); eExp.Next()) {
5200       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5201       if( BRep_Tool::Degenerated(E) ) continue;
5202       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5203       if(SM) {
5204         LSM.push_back(SM);
5205         Edges.Append(E);
5206       }
5207     }
5208     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5209     int startNid = theN1->GetID();
5210     TColStd_MapOfInteger UsedNums;
5211     int NbEdges = Edges.Length();
5212     int i = 1;
5213     for(; i<=NbEdges; i++) {
5214       int k = 0;
5215       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5216       for(; itLSM!=LSM.end(); itLSM++) {
5217         k++;
5218         if(UsedNums.Contains(k)) continue;
5219         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5220         SMESH_subMesh* locTrack = *itLSM;
5221         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5222         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5223         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5224         const SMDS_MeshNode* aN1 = aItN->next();
5225         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5226         const SMDS_MeshNode* aN2 = aItN->next();
5227         // starting node must be aN1 or aN2
5228         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5229         // 2. Collect parameters on the track edge
5230         aPrms.clear();
5231         aItN = locMeshDS->GetNodes();
5232         while ( aItN->more() ) {
5233           const SMDS_MeshNode* pNode = aItN->next();
5234           const SMDS_EdgePosition* pEPos =
5235             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5236           double aT = pEPos->GetUParameter();
5237           aPrms.push_back( aT );
5238         }
5239         list<SMESH_MeshEditor_PathPoint> LPP;
5240         //Extrusion_Error err =
5241         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5242         LLPPs.push_back(LPP);
5243         UsedNums.Add(k);
5244         // update startN for search following egde
5245         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5246         else startNid = aN1->GetID();
5247         break;
5248       }
5249     }
5250     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5251     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5252     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5253     for(; itPP!=firstList.end(); itPP++) {
5254       fullList.push_back( *itPP );
5255     }
5256     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5257     fullList.pop_back();
5258     itLLPP++;
5259     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5260       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5261       itPP = currList.begin();
5262       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5263       gp_Dir D1 = PP1.Tangent();
5264       gp_Dir D2 = PP2.Tangent();
5265       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5266                            (D1.Z()+D2.Z())/2 ) );
5267       PP1.SetTangent(Dnew);
5268       fullList.push_back(PP1);
5269       itPP++;
5270       for(; itPP!=currList.end(); itPP++) {
5271         fullList.push_back( *itPP );
5272       }
5273       PP1 = fullList.back();
5274       fullList.pop_back();
5275     }
5276     // if wire not closed
5277     fullList.push_back(PP1);
5278     // else ???
5279   }
5280   else {
5281     return EXTR_BAD_PATH_SHAPE;
5282   }
5283
5284   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5285                           theHasRefPoint, theRefPoint, theMakeGroups);
5286 }
5287
5288
5289 //=======================================================================
5290 //function : MakeEdgePathPoints
5291 //purpose  : auxilary for ExtrusionAlongTrack
5292 //=======================================================================
5293 SMESH_MeshEditor::Extrusion_Error
5294 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5295                                      const TopoDS_Edge& aTrackEdge,
5296                                      bool FirstIsStart,
5297                                      list<SMESH_MeshEditor_PathPoint>& LPP)
5298 {
5299   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5300   aTolVec=1.e-7;
5301   aTolVec2=aTolVec*aTolVec;
5302   double aT1, aT2;
5303   TopoDS_Vertex aV1, aV2;
5304   TopExp::Vertices( aTrackEdge, aV1, aV2 );
5305   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5306   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5307   // 2. Collect parameters on the track edge
5308   aPrms.push_front( aT1 );
5309   aPrms.push_back( aT2 );
5310   // sort parameters
5311   aPrms.sort();
5312   if( FirstIsStart ) {
5313     if ( aT1 > aT2 ) {
5314       aPrms.reverse();
5315     }
5316   }
5317   else {
5318     if ( aT2 > aT1 ) {
5319       aPrms.reverse();
5320     }
5321   }
5322   // 3. Path Points
5323   SMESH_MeshEditor_PathPoint aPP;
5324   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5325   std::list<double>::iterator aItD = aPrms.begin();
5326   for(; aItD != aPrms.end(); ++aItD) {
5327     double aT = *aItD;
5328     gp_Pnt aP3D;
5329     gp_Vec aVec;
5330     aC3D->D1( aT, aP3D, aVec );
5331     aL2 = aVec.SquareMagnitude();
5332     if ( aL2 < aTolVec2 )
5333       return EXTR_CANT_GET_TANGENT;
5334     gp_Dir aTgt( aVec );
5335     aPP.SetPnt( aP3D );
5336     aPP.SetTangent( aTgt );
5337     aPP.SetParameter( aT );
5338     LPP.push_back(aPP);
5339   }
5340   return EXTR_OK;
5341 }
5342
5343
5344 //=======================================================================
5345 //function : MakeExtrElements
5346 //purpose  : auxilary for ExtrusionAlongTrack
5347 //=======================================================================
5348 SMESH_MeshEditor::Extrusion_Error
5349 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet&  theElements,
5350                                    list<SMESH_MeshEditor_PathPoint>& fullList,
5351                                    const bool theHasAngles,
5352                                    list<double>& theAngles,
5353                                    const bool theLinearVariation,
5354                                    const bool theHasRefPoint,
5355                                    const gp_Pnt& theRefPoint,
5356                                    const bool theMakeGroups)
5357 {
5358   MESSAGE("MakeExtrElements");
5359   //cout<<"MakeExtrElements  fullList.size() = "<<fullList.size()<<endl;
5360   int aNbTP = fullList.size();
5361   vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5362   // Angles
5363   if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5364     LinearAngleVariation(aNbTP-1, theAngles);
5365   }
5366   vector<double> aAngles( aNbTP );
5367   int j = 0;
5368   for(; j<aNbTP; ++j) {
5369     aAngles[j] = 0.;
5370   }
5371   if ( theHasAngles ) {
5372     double anAngle;;
5373     std::list<double>::iterator aItD = theAngles.begin();
5374     for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5375       anAngle = *aItD;
5376       aAngles[j] = anAngle;
5377     }
5378   }
5379   // fill vector of path points with angles
5380   //aPPs.resize(fullList.size());
5381   j = -1;
5382   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5383   for(; itPP!=fullList.end(); itPP++) {
5384     j++;
5385     SMESH_MeshEditor_PathPoint PP = *itPP;
5386     PP.SetAngle(aAngles[j]);
5387     aPPs[j] = PP;
5388   }
5389
5390   TNodeOfNodeListMap mapNewNodes;
5391   TElemOfVecOfNnlmiMap mapElemNewNodes;
5392   TElemOfElemListMap newElemsMap;
5393   TIDSortedElemSet::iterator itElem;
5394   double aX, aY, aZ;
5395   int aNb;
5396   SMDSAbs_ElementType aTypeE;
5397   // source elements for each generated one
5398   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5399
5400   // 3. Center of rotation aV0
5401   gp_Pnt aV0 = theRefPoint;
5402   gp_XYZ aGC;
5403   if ( !theHasRefPoint ) {
5404     aNb = 0;
5405     aGC.SetCoord( 0.,0.,0. );
5406
5407     itElem = theElements.begin();
5408     for ( ; itElem != theElements.end(); itElem++ ) {
5409       const SMDS_MeshElement* elem = *itElem;
5410
5411       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5412       while ( itN->more() ) {
5413         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5414         aX = node->X();
5415         aY = node->Y();
5416         aZ = node->Z();
5417
5418         if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5419           list<const SMDS_MeshNode*> aLNx;
5420           mapNewNodes[node] = aLNx;
5421           //
5422           gp_XYZ aXYZ( aX, aY, aZ );
5423           aGC += aXYZ;
5424           ++aNb;
5425         }
5426       }
5427     }
5428     aGC /= aNb;
5429     aV0.SetXYZ( aGC );
5430   } // if (!theHasRefPoint) {
5431   mapNewNodes.clear();
5432
5433   // 4. Processing the elements
5434   SMESHDS_Mesh* aMesh = GetMeshDS();
5435
5436   for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5437     // check element type
5438     const SMDS_MeshElement* elem = *itElem;
5439     aTypeE = elem->GetType();
5440     if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5441       continue;
5442
5443     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5444     newNodesItVec.reserve( elem->NbNodes() );
5445
5446     // loop on elem nodes
5447     int nodeIndex = -1;
5448     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5449     while ( itN->more() )
5450     {
5451       ++nodeIndex;
5452       // check if a node has been already processed
5453       const SMDS_MeshNode* node =
5454         static_cast<const SMDS_MeshNode*>( itN->next() );
5455       TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5456       if ( nIt == mapNewNodes.end() ) {
5457         nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5458         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5459
5460         // make new nodes
5461         aX = node->X();  aY = node->Y(); aZ = node->Z();
5462
5463         Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5464         gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5465         gp_Ax1 anAx1, anAxT1T0;
5466         gp_Dir aDT1x, aDT0x, aDT1T0;
5467
5468         aTolAng=1.e-4;
5469
5470         aV0x = aV0;
5471         aPN0.SetCoord(aX, aY, aZ);
5472
5473         const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5474         aP0x = aPP0.Pnt();
5475         aDT0x= aPP0.Tangent();
5476         //cout<<"j = 0   PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5477
5478         for ( j = 1; j < aNbTP; ++j ) {
5479           const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5480           aP1x = aPP1.Pnt();
5481           aDT1x = aPP1.Tangent();
5482           aAngle1x = aPP1.Angle();
5483
5484           gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5485           // Translation
5486           gp_Vec aV01x( aP0x, aP1x );
5487           aTrsf.SetTranslation( aV01x );
5488
5489           // traslated point
5490           aV1x = aV0x.Transformed( aTrsf );
5491           aPN1 = aPN0.Transformed( aTrsf );
5492
5493           // rotation 1 [ T1,T0 ]
5494           aAngleT1T0=-aDT1x.Angle( aDT0x );
5495           if (fabs(aAngleT1T0) > aTolAng) {
5496             aDT1T0=aDT1x^aDT0x;
5497             anAxT1T0.SetLocation( aV1x );
5498             anAxT1T0.SetDirection( aDT1T0 );
5499             aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5500
5501             aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5502           }
5503
5504           // rotation 2
5505           if ( theHasAngles ) {
5506             anAx1.SetLocation( aV1x );
5507             anAx1.SetDirection( aDT1x );
5508             aTrsfRot.SetRotation( anAx1, aAngle1x );
5509
5510             aPN1 = aPN1.Transformed( aTrsfRot );
5511           }
5512
5513           // make new node
5514           //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5515           if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5516             // create additional node
5517             double x = ( aPN1.X() + aPN0.X() )/2.;
5518             double y = ( aPN1.Y() + aPN0.Y() )/2.;
5519             double z = ( aPN1.Z() + aPN0.Z() )/2.;
5520             const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5521             myLastCreatedNodes.Append(newNode);
5522             srcNodes.Append( node );
5523             listNewNodes.push_back( newNode );
5524           }
5525           aX = aPN1.X();
5526           aY = aPN1.Y();
5527           aZ = aPN1.Z();
5528           const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5529           myLastCreatedNodes.Append(newNode);
5530           srcNodes.Append( node );
5531           listNewNodes.push_back( newNode );
5532
5533           aPN0 = aPN1;
5534           aP0x = aP1x;
5535           aV0x = aV1x;
5536           aDT0x = aDT1x;
5537         }
5538       }
5539
5540       else {
5541         // if current elem is quadratic and current node is not medium
5542         // we have to check - may be it is needed to insert additional nodes
5543         if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5544           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5545           if(listNewNodes.size()==aNbTP-1) {
5546             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5547             gp_XYZ P(node->X(), node->Y(), node->Z());
5548             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5549             int i;
5550             for(i=0; i<aNbTP-1; i++) {
5551               const SMDS_MeshNode* N = *it;
5552               double x = ( N->X() + P.X() )/2.;
5553               double y = ( N->Y() + P.Y() )/2.;
5554               double z = ( N->Z() + P.Z() )/2.;
5555               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5556               srcNodes.Append( node );
5557               myLastCreatedNodes.Append(newN);
5558               aNodes[2*i] = newN;
5559               aNodes[2*i+1] = N;
5560               P = gp_XYZ(N->X(),N->Y(),N->Z());
5561             }
5562             listNewNodes.clear();
5563             for(i=0; i<2*(aNbTP-1); i++) {
5564               listNewNodes.push_back(aNodes[i]);
5565             }
5566           }
5567         }
5568       }
5569
5570       newNodesItVec.push_back( nIt );
5571     }
5572     // make new elements
5573     //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5574     //              newNodesItVec[0]->second.size(), myLastCreatedElems );
5575     sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5576   }
5577
5578   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5579
5580   if ( theMakeGroups )
5581     generateGroups( srcNodes, srcElems, "extruded");
5582
5583   return EXTR_OK;
5584 }
5585
5586
5587 //=======================================================================
5588 //function : LinearAngleVariation
5589 //purpose  : auxilary for ExtrusionAlongTrack
5590 //=======================================================================
5591 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5592                                             list<double>& Angles)
5593 {
5594   int nbAngles = Angles.size();
5595   if( nbSteps > nbAngles ) {
5596     vector<double> theAngles(nbAngles);
5597     list<double>::iterator it = Angles.begin();
5598     int i = -1;
5599     for(; it!=Angles.end(); it++) {
5600       i++;
5601       theAngles[i] = (*it);
5602     }
5603     list<double> res;
5604     double rAn2St = double( nbAngles ) / double( nbSteps );
5605     double angPrev = 0, angle;
5606     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5607       double angCur = rAn2St * ( iSt+1 );
5608       double angCurFloor  = floor( angCur );
5609       double angPrevFloor = floor( angPrev );
5610       if ( angPrevFloor == angCurFloor )
5611         angle = rAn2St * theAngles[ int( angCurFloor ) ];
5612       else {
5613         int iP = int( angPrevFloor );
5614         double angPrevCeil = ceil(angPrev);
5615         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5616
5617         int iC = int( angCurFloor );
5618         if ( iC < nbAngles )
5619           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5620
5621         iP = int( angPrevCeil );
5622         while ( iC-- > iP )
5623           angle += theAngles[ iC ];
5624       }
5625       res.push_back(angle);
5626       angPrev = angCur;
5627     }
5628     Angles.clear();
5629     it = res.begin();
5630     for(; it!=res.end(); it++)
5631       Angles.push_back( *it );
5632   }
5633 }
5634
5635
5636 //================================================================================
5637 /*!
5638  * \brief Move or copy theElements applying theTrsf to their nodes
5639  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5640  *  \param theTrsf - transformation to apply
5641  *  \param theCopy - if true, create translated copies of theElems
5642  *  \param theMakeGroups - if true and theCopy, create translated groups
5643  *  \param theTargetMesh - mesh to copy translated elements into
5644  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5645  */
5646 //================================================================================
5647
5648 SMESH_MeshEditor::PGroupIDs
5649 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5650                              const gp_Trsf&     theTrsf,
5651                              const bool         theCopy,
5652                              const bool         theMakeGroups,
5653                              SMESH_Mesh*        theTargetMesh)
5654 {
5655   myLastCreatedElems.Clear();
5656   myLastCreatedNodes.Clear();
5657
5658   bool needReverse = false;
5659   string groupPostfix;
5660   switch ( theTrsf.Form() ) {
5661   case gp_PntMirror:
5662     MESSAGE("gp_PntMirror");
5663     needReverse = true;
5664     groupPostfix = "mirrored";
5665     break;
5666   case gp_Ax1Mirror:
5667     MESSAGE("gp_Ax1Mirror");
5668     groupPostfix = "mirrored";
5669     break;
5670   case gp_Ax2Mirror:
5671     MESSAGE("gp_Ax2Mirror");
5672     needReverse = true;
5673     groupPostfix = "mirrored";
5674     break;
5675   case gp_Rotation:
5676     MESSAGE("gp_Rotation");
5677     groupPostfix = "rotated";
5678     break;
5679   case gp_Translation:
5680     MESSAGE("gp_Translation");
5681     groupPostfix = "translated";
5682     break;
5683   case gp_Scale:
5684     MESSAGE("gp_Scale");
5685     groupPostfix = "scaled";
5686     break;
5687   case gp_CompoundTrsf: // different scale by axis
5688     MESSAGE("gp_CompoundTrsf");
5689     groupPostfix = "scaled";
5690     break;
5691   default:
5692     MESSAGE("default");
5693     needReverse = false;
5694     groupPostfix = "transformed";
5695   }
5696
5697   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5698   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5699   SMESHDS_Mesh* aMesh    = GetMeshDS();
5700
5701
5702   // map old node to new one
5703   TNodeNodeMap nodeMap;
5704
5705   // elements sharing moved nodes; those of them which have all
5706   // nodes mirrored but are not in theElems are to be reversed
5707   TIDSortedElemSet inverseElemSet;
5708
5709   // source elements for each generated one
5710   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5711
5712   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5713   TIDSortedElemSet orphanNode;
5714
5715   if ( theElems.empty() ) // transform the whole mesh
5716   {
5717     // add all elements
5718     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5719     while ( eIt->more() ) theElems.insert( eIt->next() );
5720     // add orphan nodes
5721     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5722     while ( nIt->more() )
5723     {
5724       const SMDS_MeshNode* node = nIt->next();
5725       if ( node->NbInverseElements() == 0)
5726         orphanNode.insert( node );
5727     }
5728   }
5729
5730   // loop on elements to transform nodes : first orphan nodes then elems
5731   TIDSortedElemSet::iterator itElem;
5732   TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5733   for (int i=0; i<2; i++)
5734   for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5735     const SMDS_MeshElement* elem = *itElem;
5736     if ( !elem )
5737       continue;
5738
5739     // loop on elem nodes
5740     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5741     while ( itN->more() ) {
5742
5743       const SMDS_MeshNode* node = cast2Node( itN->next() );
5744       // check if a node has been already transformed
5745       pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5746         nodeMap.insert( make_pair ( node, node ));
5747       if ( !n2n_isnew.second )
5748         continue;
5749
5750       double coord[3];
5751       coord[0] = node->X();
5752       coord[1] = node->Y();
5753       coord[2] = node->Z();
5754       theTrsf.Transforms( coord[0], coord[1], coord[2] );
5755       if ( theTargetMesh ) {
5756         const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5757         n2n_isnew.first->second = newNode;
5758         myLastCreatedNodes.Append(newNode);
5759         srcNodes.Append( node );
5760       }
5761       else if ( theCopy ) {
5762         const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5763         n2n_isnew.first->second = newNode;
5764         myLastCreatedNodes.Append(newNode);
5765         srcNodes.Append( node );
5766       }
5767       else {
5768         aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5769         // node position on shape becomes invalid
5770         const_cast< SMDS_MeshNode* > ( node )->SetPosition
5771           ( SMDS_SpacePosition::originSpacePosition() );
5772       }
5773
5774       // keep inverse elements
5775       if ( !theCopy && !theTargetMesh && needReverse ) {
5776         SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5777         while ( invElemIt->more() ) {
5778           const SMDS_MeshElement* iel = invElemIt->next();
5779           inverseElemSet.insert( iel );
5780         }
5781       }
5782     }
5783   }
5784
5785   // either create new elements or reverse mirrored ones
5786   if ( !theCopy && !needReverse && !theTargetMesh )
5787     return PGroupIDs();
5788
5789   TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5790   for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5791     theElems.insert( *invElemIt );
5792
5793   // Replicate or reverse elements
5794
5795   std::vector<int> iForw;
5796   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5797   {
5798     const SMDS_MeshElement* elem = *itElem;
5799     if ( !elem ) continue;
5800
5801     SMDSAbs_GeometryType geomType = elem->GetGeomType();
5802     int                  nbNodes  = elem->NbNodes();
5803     if ( geomType == SMDSGeom_NONE ) continue; // node
5804
5805     switch ( geomType ) {
5806
5807     case SMDSGeom_POLYGON:  // ---------------------- polygon
5808       {
5809         vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5810         int iNode = 0;
5811         SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5812         while (itN->more()) {
5813           const SMDS_MeshNode* node =
5814             static_cast<const SMDS_MeshNode*>(itN->next());
5815           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5816           if (nodeMapIt == nodeMap.end())
5817             break; // not all nodes transformed
5818           if (needReverse) {
5819             // reverse mirrored faces and volumes
5820             poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5821           } else {
5822             poly_nodes[iNode] = (*nodeMapIt).second;
5823           }
5824           iNode++;
5825         }
5826         if ( iNode != nbNodes )
5827           continue; // not all nodes transformed
5828
5829         if ( theTargetMesh ) {
5830           myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5831           srcElems.Append( elem );
5832         }
5833         else if ( theCopy ) {
5834           myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5835           srcElems.Append( elem );
5836         }
5837         else {
5838           aMesh->ChangePolygonNodes(elem, poly_nodes);
5839         }
5840       }
5841       break;
5842
5843     case SMDSGeom_POLYHEDRA:  // ------------------ polyhedral volume
5844       {
5845         const SMDS_VtkVolume* aPolyedre =
5846           dynamic_cast<const SMDS_VtkVolume*>( elem );
5847         if (!aPolyedre) {
5848           MESSAGE("Warning: bad volumic element");
5849           continue;
5850         }
5851
5852         vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5853         vector<int> quantities; quantities.reserve( nbNodes );
5854
5855         bool allTransformed = true;
5856         int nbFaces = aPolyedre->NbFaces();
5857         for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5858           int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5859           for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5860             const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5861             TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5862             if (nodeMapIt == nodeMap.end()) {
5863               allTransformed = false; // not all nodes transformed
5864             } else {
5865               poly_nodes.push_back((*nodeMapIt).second);
5866             }
5867             if ( needReverse && allTransformed )
5868               std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5869           }
5870           quantities.push_back(nbFaceNodes);
5871         }
5872         if ( !allTransformed )
5873           continue; // not all nodes transformed
5874
5875         if ( theTargetMesh ) {
5876           myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5877           srcElems.Append( elem );
5878         }
5879         else if ( theCopy ) {
5880           myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5881           srcElems.Append( elem );
5882         }
5883         else {
5884           aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5885         }
5886       }
5887       break;
5888
5889     case SMDSGeom_BALL: // -------------------- Ball
5890       {
5891         if ( !theCopy && !theTargetMesh ) continue;
5892
5893         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
5894         if (nodeMapIt == nodeMap.end())
5895           continue; // not all nodes transformed
5896
5897         double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
5898         if ( theTargetMesh ) {
5899           myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
5900           srcElems.Append( elem );
5901         }
5902         else {
5903           myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
5904           srcElems.Append( elem );
5905         }
5906       }
5907       break;
5908
5909     default: // ----------------------- Regular elements
5910
5911       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
5912       const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
5913       const std::vector<int>& i = needReverse ? iRev : iForw;
5914
5915       // find transformed nodes
5916       vector<const SMDS_MeshNode*> nodes(nbNodes);
5917       int iNode = 0;
5918       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5919       while ( itN->more() ) {
5920         const SMDS_MeshNode* node =
5921           static_cast<const SMDS_MeshNode*>( itN->next() );
5922         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5923         if ( nodeMapIt == nodeMap.end() )
5924           break; // not all nodes transformed
5925         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5926       }
5927       if ( iNode != nbNodes )
5928         continue; // not all nodes transformed
5929
5930       if ( theTargetMesh ) {
5931         if ( SMDS_MeshElement* copy =
5932              targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5933           myLastCreatedElems.Append( copy );
5934           srcElems.Append( elem );
5935         }
5936       }
5937       else if ( theCopy ) {
5938         if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5939           srcElems.Append( elem );
5940       }
5941       else {
5942         // reverse element as it was reversed by transformation
5943         if ( nbNodes > 2 )
5944           aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5945       }
5946     } // switch ( geomType )
5947
5948   } // loop on elements
5949
5950   PGroupIDs newGroupIDs;
5951
5952   if ( ( theMakeGroups && theCopy ) ||
5953        ( theMakeGroups && theTargetMesh ) )
5954     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5955
5956   return newGroupIDs;
5957 }
5958
5959 //=======================================================================
5960 /*!
5961  * \brief Create groups of elements made during transformation
5962  * \param nodeGens - nodes making corresponding myLastCreatedNodes
5963  * \param elemGens - elements making corresponding myLastCreatedElems
5964  * \param postfix - to append to names of new groups
5965  */
5966 //=======================================================================
5967
5968 SMESH_MeshEditor::PGroupIDs
5969 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5970                                  const SMESH_SequenceOfElemPtr& elemGens,
5971                                  const std::string&             postfix,
5972                                  SMESH_Mesh*                    targetMesh)
5973 {
5974   PGroupIDs newGroupIDs( new list<int> );
5975   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
5976
5977   // Sort existing groups by types and collect their names
5978
5979   // to store an old group and a generated new one
5980   typedef pair< SMESHDS_GroupBase*, SMESHDS_Group* > TOldNewGroup;
5981   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
5982   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
5983   // group names
5984   set< string > groupNames;
5985
5986   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
5987   if ( !groupIt->more() ) return newGroupIDs;
5988
5989   int newGroupID = mesh->GetGroupIds().back()+1;
5990   while ( groupIt->more() )
5991   {
5992     SMESH_Group * group = groupIt->next();
5993     if ( !group ) continue;
5994     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
5995     if ( !groupDS || groupDS->IsEmpty() ) continue;
5996     groupNames.insert( group->GetName() );
5997     groupDS->SetStoreName( group->GetName() );
5998     SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(),
5999                                                  groupDS->GetType() );
6000     groupsByType[ groupDS->GetType() ].push_back( make_pair( groupDS, newGroup ));
6001     orderedOldNewGroups.push_back( & groupsByType[ groupDS->GetType() ].back() );
6002   }
6003
6004   // Loop on nodes and elements to add them in new groups
6005
6006   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6007   {
6008     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6009     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6010     if ( gens.Length() != elems.Length() )
6011       throw SALOME_Exception(LOCALIZED("invalid args"));
6012
6013     // loop on created elements
6014     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6015     {
6016       const SMDS_MeshElement* sourceElem = gens( iElem );
6017       if ( !sourceElem ) {
6018         MESSAGE("generateGroups(): NULL source element");
6019         continue;
6020       }
6021       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6022       if ( groupsOldNew.empty() ) { // no groups of this type at all
6023         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6024           ++iElem; // skip all elements made by sourceElem
6025         continue;
6026       }
6027       // collect all elements made by sourceElem
6028       list< const SMDS_MeshElement* > resultElems;
6029       if ( const SMDS_MeshElement* resElem = elems( iElem ))
6030         if ( resElem != sourceElem )
6031           resultElems.push_back( resElem );
6032       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6033         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6034           if ( resElem != sourceElem )
6035             resultElems.push_back( resElem );
6036
6037       // add resultElems to groups made by ones the sourceElem belongs to
6038       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6039       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6040       {
6041         SMESHDS_GroupBase* oldGroup = gOldNew->first;
6042         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6043         {
6044           // fill in a new group
6045           SMDS_MeshGroup & newGroup = gOldNew->second->SMDSGroup();
6046           list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6047           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6048             newGroup.Add( *resElemIt );
6049         }
6050       }
6051     } // loop on created elements
6052   }// loop on nodes and elements
6053
6054   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6055
6056   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6057   {
6058     SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->first;
6059     SMESHDS_Group*     newGroupDS = orderedOldNewGroups[i]->second;
6060     if ( newGroupDS->IsEmpty() )
6061     {
6062       mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6063     }
6064     else
6065     {
6066       // make a name
6067       string name = oldGroupDS->GetStoreName();
6068       if ( !targetMesh ) {
6069         name += "_";
6070         name += postfix;
6071         int nb = 1;
6072         while ( !groupNames.insert( name ).second ) // name exists
6073           name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << postfix << "_" << nb++;
6074       }
6075       newGroupDS->SetStoreName( name.c_str() );
6076
6077       // make a SMESH_Groups
6078       mesh->AddGroup( newGroupDS );
6079       newGroupIDs->push_back( newGroupDS->GetID() );
6080
6081       // set group type
6082       newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6083     }
6084   }
6085
6086   return newGroupIDs;
6087 }
6088
6089 //================================================================================
6090 /*!
6091  * \brief Return list of group of nodes close to each other within theTolerance
6092  *        Search among theNodes or in the whole mesh if theNodes is empty using
6093  *        an Octree algorithm
6094  */
6095 //================================================================================
6096
6097 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6098                                             const double         theTolerance,
6099                                             TListOfListOfNodes & theGroupsOfNodes)
6100 {
6101   myLastCreatedElems.Clear();
6102   myLastCreatedNodes.Clear();
6103
6104   if ( theNodes.empty() )
6105   { // get all nodes in the mesh
6106     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6107     while ( nIt->more() )
6108       theNodes.insert( theNodes.end(),nIt->next());
6109   }
6110
6111   SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6112 }
6113
6114
6115 //=======================================================================
6116 /*!
6117  * \brief Implementation of search for the node closest to point
6118  */
6119 //=======================================================================
6120
6121 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
6122 {
6123   //---------------------------------------------------------------------
6124   /*!
6125    * \brief Constructor
6126    */
6127   SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
6128   {
6129     myMesh = ( SMESHDS_Mesh* ) theMesh;
6130
6131     TIDSortedNodeSet nodes;
6132     if ( theMesh ) {
6133       SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
6134       while ( nIt->more() )
6135         nodes.insert( nodes.end(), nIt->next() );
6136     }
6137     myOctreeNode = new SMESH_OctreeNode(nodes) ;
6138
6139     // get max size of a leaf box
6140     SMESH_OctreeNode* tree = myOctreeNode;
6141     while ( !tree->isLeaf() )
6142     {
6143       SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6144       if ( cIt->more() )
6145         tree = cIt->next();
6146     }
6147     myHalfLeafSize = tree->maxSize() / 2.;
6148   }
6149
6150   //---------------------------------------------------------------------
6151   /*!
6152    * \brief Move node and update myOctreeNode accordingly
6153    */
6154   void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
6155   {
6156     myOctreeNode->UpdateByMoveNode( node, toPnt );
6157     myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
6158   }
6159
6160   //---------------------------------------------------------------------
6161   /*!
6162    * \brief Do it's job
6163    */
6164   const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
6165   {
6166     map<double, const SMDS_MeshNode*> dist2Nodes;
6167     myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize );
6168     if ( !dist2Nodes.empty() )
6169       return dist2Nodes.begin()->second;
6170     list<const SMDS_MeshNode*> nodes;
6171     //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
6172
6173     double minSqDist = DBL_MAX;
6174     if ( nodes.empty() )  // get all nodes of OctreeNode's closest to thePnt
6175     {
6176       // sort leafs by their distance from thePnt
6177       typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
6178       TDistTreeMap treeMap;
6179       list< SMESH_OctreeNode* > treeList;
6180       list< SMESH_OctreeNode* >::iterator trIt;
6181       treeList.push_back( myOctreeNode );
6182
6183       gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
6184       bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize );
6185       for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
6186       {
6187         SMESH_OctreeNode* tree = *trIt;
6188         if ( !tree->isLeaf() ) // put children to the queue
6189         {
6190           if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue;
6191           SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6192           while ( cIt->more() )
6193             treeList.push_back( cIt->next() );
6194         }
6195         else if ( tree->NbNodes() ) // put a tree to the treeMap
6196         {
6197           const Bnd_B3d& box = *tree->getBox();
6198           double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
6199           pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
6200           if ( !it_in.second ) // not unique distance to box center
6201             treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
6202         }
6203       }
6204       // find distance after which there is no sense to check tree's
6205       double sqLimit = DBL_MAX;
6206       TDistTreeMap::iterator sqDist_tree = treeMap.begin();
6207       if ( treeMap.size() > 5 ) {
6208         SMESH_OctreeNode* closestTree = sqDist_tree->second;
6209         const Bnd_B3d& box = *closestTree->getBox();
6210         double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
6211         sqLimit = limit * limit;
6212       }
6213       // get all nodes from trees
6214       for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
6215         if ( sqDist_tree->first > sqLimit )
6216           break;
6217         SMESH_OctreeNode* tree = sqDist_tree->second;
6218         tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
6219       }
6220     }
6221     // find closest among nodes
6222     minSqDist = DBL_MAX;
6223     const SMDS_MeshNode* closestNode = 0;
6224     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6225     for ( ; nIt != nodes.end(); ++nIt ) {
6226       double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) );
6227       if ( minSqDist > sqDist ) {
6228         closestNode = *nIt;
6229         minSqDist = sqDist;
6230       }
6231     }
6232     return closestNode;
6233   }
6234
6235   //---------------------------------------------------------------------
6236   /*!
6237    * \brief Destructor
6238    */
6239   ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
6240
6241   //---------------------------------------------------------------------
6242   /*!
6243    * \brief Return the node tree
6244    */
6245   const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
6246
6247 private:
6248   SMESH_OctreeNode* myOctreeNode;
6249   SMESHDS_Mesh*     myMesh;
6250   double            myHalfLeafSize; // max size of a leaf box
6251 };
6252
6253 //=======================================================================
6254 /*!
6255  * \brief Return SMESH_NodeSearcher
6256  */
6257 //=======================================================================
6258
6259 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher()
6260 {
6261   return new SMESH_NodeSearcherImpl( GetMeshDS() );
6262 }
6263
6264 // ========================================================================
6265 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
6266 {
6267   const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
6268   const int MaxLevel         = 7;  // maximal tree height -> nb terminal boxes: 8^7 = 2097152
6269   const double NodeRadius = 1e-9;  // to enlarge bnd box of element
6270
6271   //=======================================================================
6272   /*!
6273    * \brief Octal tree of bounding boxes of elements
6274    */
6275   //=======================================================================
6276
6277   class ElementBndBoxTree : public SMESH_Octree
6278   {
6279   public:
6280
6281     ElementBndBoxTree(const SMDS_Mesh&     mesh,
6282                       SMDSAbs_ElementType  elemType,
6283                       SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(),
6284                       double               tolerance = NodeRadius );
6285     void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems );
6286     void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
6287     void getElementsInSphere ( const gp_XYZ& center,
6288                                const double  radius, TIDSortedElemSet& foundElems);
6289     size_t getSize() { return std::max( _size, _elements.size() ); }
6290     ~ElementBndBoxTree();
6291
6292   protected:
6293     ElementBndBoxTree():_size(0) {}
6294     SMESH_Octree* newChild() const { return new ElementBndBoxTree; }
6295     void          buildChildrenData();
6296     Bnd_B3d*      buildRootBox();
6297   private:
6298     //!< Bounding box of element
6299     struct ElementBox : public Bnd_B3d
6300     {
6301       const SMDS_MeshElement* _element;
6302       int                     _refCount; // an ElementBox can be included in several tree branches
6303       ElementBox(const SMDS_MeshElement* elem, double tolerance);
6304     };
6305     vector< ElementBox* > _elements;
6306     size_t                _size;
6307   };
6308
6309   //================================================================================
6310   /*!
6311    * \brief ElementBndBoxTree creation
6312    */
6313   //================================================================================
6314
6315   ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
6316     :SMESH_Octree( new SMESH_TreeLimit( MaxLevel, /*minSize=*/0. ))
6317   {
6318     int nbElems = mesh.GetMeshInfo().NbElements( elemType );
6319     _elements.reserve( nbElems );
6320
6321     SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType );
6322     while ( elemIt->more() )
6323       _elements.push_back( new ElementBox( elemIt->next(),tolerance  ));
6324
6325     compute();
6326   }
6327
6328   //================================================================================
6329   /*!
6330    * \brief Destructor
6331    */
6332   //================================================================================
6333
6334   ElementBndBoxTree::~ElementBndBoxTree()
6335   {
6336     for ( int i = 0; i < _elements.size(); ++i )
6337       if ( --_elements[i]->_refCount <= 0 )
6338         delete _elements[i];
6339   }
6340
6341   //================================================================================
6342   /*!
6343    * \brief Return the maximal box
6344    */
6345   //================================================================================
6346
6347   Bnd_B3d* ElementBndBoxTree::buildRootBox()
6348   {
6349     Bnd_B3d* box = new Bnd_B3d;
6350     for ( int i = 0; i < _elements.size(); ++i )
6351       box->Add( *_elements[i] );
6352     return box;
6353   }
6354
6355   //================================================================================
6356   /*!
6357    * \brief Redistrubute element boxes among children
6358    */
6359   //================================================================================
6360
6361   void ElementBndBoxTree::buildChildrenData()
6362   {
6363     for ( int i = 0; i < _elements.size(); ++i )
6364     {
6365       for (int j = 0; j < 8; j++)
6366       {
6367         if ( !_elements[i]->IsOut( *myChildren[j]->getBox() ))
6368         {
6369           _elements[i]->_refCount++;
6370           ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
6371         }
6372       }
6373       _elements[i]->_refCount--;
6374     }
6375     _size = _elements.size();
6376     SMESHUtils::FreeVector( _elements ); // = _elements.clear() + free memory
6377
6378     for (int j = 0; j < 8; j++)
6379     {
6380       ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
6381       if ( child->_elements.size() <= MaxNbElemsInLeaf )
6382         child->myIsLeaf = true;
6383
6384       if ( child->_elements.capacity() - child->_elements.size() > 1000 )
6385         SMESHUtils::CompactVector( child->_elements );
6386     }
6387   }
6388
6389   //================================================================================
6390   /*!
6391    * \brief Return elements which can include the point
6392    */
6393   //================================================================================
6394
6395   void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt&     point,
6396                                                 TIDSortedElemSet& foundElems)
6397   {
6398     if ( getBox()->IsOut( point.XYZ() ))
6399       return;
6400
6401     if ( isLeaf() )
6402     {
6403       for ( int i = 0; i < _elements.size(); ++i )
6404         if ( !_elements[i]->IsOut( point.XYZ() ))
6405           foundElems.insert( _elements[i]->_element );
6406     }
6407     else
6408     {
6409       for (int i = 0; i < 8; i++)
6410         ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6411     }
6412   }
6413
6414   //================================================================================
6415   /*!
6416    * \brief Return elements which can be intersected by the line
6417    */
6418   //================================================================================
6419
6420   void ElementBndBoxTree::getElementsNearLine( const gp_Ax1&     line,
6421                                                TIDSortedElemSet& foundElems)
6422   {
6423     if ( getBox()->IsOut( line ))
6424       return;
6425
6426     if ( isLeaf() )
6427     {
6428       for ( int i = 0; i < _elements.size(); ++i )
6429         if ( !_elements[i]->IsOut( line ))
6430           foundElems.insert( _elements[i]->_element );
6431     }
6432     else
6433     {
6434       for (int i = 0; i < 8; i++)
6435         ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6436     }
6437   }
6438
6439   //================================================================================
6440   /*!
6441    * \brief Return elements from leaves intersecting the sphere
6442    */
6443   //================================================================================
6444
6445   void ElementBndBoxTree::getElementsInSphere ( const gp_XYZ&     center,
6446                                                 const double      radius,
6447                                                 TIDSortedElemSet& foundElems)
6448   {
6449     if ( getBox()->IsOut( center, radius ))
6450       return;
6451
6452     if ( isLeaf() )
6453     {
6454       for ( int i = 0; i < _elements.size(); ++i )
6455         if ( !_elements[i]->IsOut( center, radius ))
6456           foundElems.insert( _elements[i]->_element );
6457     }
6458     else
6459     {
6460       for (int i = 0; i < 8; i++)
6461         ((ElementBndBoxTree*) myChildren[i])->getElementsInSphere( center, radius, foundElems );
6462     }
6463   }
6464
6465   //================================================================================
6466   /*!
6467    * \brief Construct the element box
6468    */
6469   //================================================================================
6470
6471   ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance)
6472   {
6473     _element  = elem;
6474     _refCount = 1;
6475     SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6476     while ( nIt->more() )
6477       Add( SMESH_TNodeXYZ( nIt->next() ));
6478     Enlarge( tolerance );
6479   }
6480
6481 } // namespace
6482
6483 //=======================================================================
6484 /*!
6485  * \brief Implementation of search for the elements by point and
6486  *        of classification of point in 2D mesh
6487  */
6488 //=======================================================================
6489
6490 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6491 {
6492   SMESHDS_Mesh*                _mesh;
6493   SMDS_ElemIteratorPtr         _meshPartIt;
6494   ElementBndBoxTree*           _ebbTree;
6495   SMESH_NodeSearcherImpl*      _nodeSearcher;
6496   SMDSAbs_ElementType          _elementType;
6497   double                       _tolerance;
6498   bool                         _outerFacesFound;
6499   set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6500
6501   SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr())
6502     : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {}
6503   ~SMESH_ElementSearcherImpl()
6504   {
6505     if ( _ebbTree )      delete _ebbTree;      _ebbTree      = 0;
6506     if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6507   }
6508   virtual int FindElementsByPoint(const gp_Pnt&                      point,
6509                                   SMDSAbs_ElementType                type,
6510                                   vector< const SMDS_MeshElement* >& foundElements);
6511   virtual TopAbs_State GetPointState(const gp_Pnt& point);
6512   virtual const SMDS_MeshElement* FindClosestTo( const gp_Pnt&       point,
6513                                                  SMDSAbs_ElementType type );
6514
6515   void GetElementsNearLine( const gp_Ax1&                      line,
6516                             SMDSAbs_ElementType                type,
6517                             vector< const SMDS_MeshElement* >& foundElems);
6518   double getTolerance();
6519   bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6520                             const double tolerance, double & param);
6521   void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6522   bool isOuterBoundary(const SMDS_MeshElement* face) const
6523   {
6524     return _outerFaces.empty() || _outerFaces.count(face);
6525   }
6526   struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6527   {
6528     const SMDS_MeshElement* _face;
6529     gp_Vec                  _faceNorm;
6530     bool                    _coincides; //!< the line lays in face plane
6531     TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6532       : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6533   };
6534   struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6535   {
6536     SMESH_TLink      _link;
6537     TIDSortedElemSet _faces;
6538     TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6539       : _link( n1, n2 ), _faces( &face, &face + 1) {}
6540   };
6541 };
6542
6543 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6544 {
6545   return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6546              << ", _coincides="<<i._coincides << ")";
6547 }
6548
6549 //=======================================================================
6550 /*!
6551  * \brief define tolerance for search
6552  */
6553 //=======================================================================
6554
6555 double SMESH_ElementSearcherImpl::getTolerance()
6556 {
6557   if ( _tolerance < 0 )
6558   {
6559     const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6560
6561     _tolerance = 0;
6562     if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6563     {
6564       double boxSize = _nodeSearcher->getTree()->maxSize();
6565       _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6566     }
6567     else if ( _ebbTree && meshInfo.NbElements() > 0 )
6568     {
6569       double boxSize = _ebbTree->maxSize();
6570       _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6571     }
6572     if ( _tolerance == 0 )
6573     {
6574       // define tolerance by size of a most complex element
6575       int complexType = SMDSAbs_Volume;
6576       while ( complexType > SMDSAbs_All &&
6577               meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6578         --complexType;
6579       if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6580       double elemSize;
6581       if ( complexType == int( SMDSAbs_Node ))
6582       {
6583         SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6584         elemSize = 1;
6585         if ( meshInfo.NbNodes() > 2 )
6586           elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6587       }
6588       else
6589       {
6590         SMDS_ElemIteratorPtr elemIt =
6591             _mesh->elementsIterator( SMDSAbs_ElementType( complexType ));
6592         const SMDS_MeshElement* elem = elemIt->next();
6593         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6594         SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() ));
6595         elemSize = 0;
6596         while ( nodeIt->more() )
6597         {
6598           double dist = n1.Distance( cast2Node( nodeIt->next() ));
6599           elemSize = max( dist, elemSize );
6600         }
6601       }
6602       _tolerance = 1e-4 * elemSize;
6603     }
6604   }
6605   return _tolerance;
6606 }
6607
6608 //================================================================================
6609 /*!
6610  * \brief Find intersection of the line and an edge of face and return parameter on line
6611  */
6612 //================================================================================
6613
6614 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin&           line,
6615                                                      const SMDS_MeshElement* face,
6616                                                      const double            tol,
6617                                                      double &                param)
6618 {
6619   int nbInts = 0;
6620   param = 0;
6621
6622   GeomAPI_ExtremaCurveCurve anExtCC;
6623   Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6624
6625   int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6626   for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6627   {
6628     GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )),
6629                          SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) ));
6630     anExtCC.Init( lineCurve, edge);
6631     if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6632     {
6633       Quantity_Parameter pl, pe;
6634       anExtCC.LowerDistanceParameters( pl, pe );
6635       param += pl;
6636       if ( ++nbInts == 2 )
6637         break;
6638     }
6639   }
6640   if ( nbInts > 0 ) param /= nbInts;
6641   return nbInts > 0;
6642 }
6643 //================================================================================
6644 /*!
6645  * \brief Find all faces belonging to the outer boundary of mesh
6646  */
6647 //================================================================================
6648
6649 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6650 {
6651   if ( _outerFacesFound ) return;
6652
6653   // Collect all outer faces by passing from one outer face to another via their links
6654   // and BTW find out if there are internal faces at all.
6655
6656   // checked links and links where outer boundary meets internal one
6657   set< SMESH_TLink > visitedLinks, seamLinks;
6658
6659   // links to treat with already visited faces sharing them
6660   list < TFaceLink > startLinks;
6661
6662   // load startLinks with the first outerFace
6663   startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6664   _outerFaces.insert( outerFace );
6665
6666   TIDSortedElemSet emptySet;
6667   while ( !startLinks.empty() )
6668   {
6669     const SMESH_TLink& link  = startLinks.front()._link;
6670     TIDSortedElemSet&  faces = startLinks.front()._faces;
6671
6672     outerFace = *faces.begin();
6673     // find other faces sharing the link
6674     const SMDS_MeshElement* f;
6675     while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6676       faces.insert( f );
6677
6678     // select another outer face among the found
6679     const SMDS_MeshElement* outerFace2 = 0;
6680     if ( faces.size() == 2 )
6681     {
6682       outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6683     }
6684     else if ( faces.size() > 2 )
6685     {
6686       seamLinks.insert( link );
6687
6688       // link direction within the outerFace
6689       gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()),
6690                    SMESH_TNodeXYZ( link.node2()));
6691       int i1 = outerFace->GetNodeIndex( link.node1() );
6692       int i2 = outerFace->GetNodeIndex( link.node2() );
6693       bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6694       if ( rev ) n1n2.Reverse();
6695       // outerFace normal
6696       gp_XYZ ofNorm, fNorm;
6697       if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6698       {
6699         // direction from the link inside outerFace
6700         gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6701         // sort all other faces by angle with the dirInOF
6702         map< double, const SMDS_MeshElement* > angle2Face;
6703         set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6704         for ( ; face != faces.end(); ++face )
6705         {
6706           if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6707             continue;
6708           gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6709           double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6710           if ( angle < 0 ) angle += 2. * M_PI;
6711           angle2Face.insert( make_pair( angle, *face ));
6712         }
6713         if ( !angle2Face.empty() )
6714           outerFace2 = angle2Face.begin()->second;
6715       }
6716     }
6717     // store the found outer face and add its links to continue seaching from
6718     if ( outerFace2 )
6719     {
6720       _outerFaces.insert( outerFace );
6721       int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6722       for ( int i = 0; i < nbNodes; ++i )
6723       {
6724         SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6725         if ( visitedLinks.insert( link2 ).second )
6726           startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6727       }
6728     }
6729     startLinks.pop_front();
6730   }
6731   _outerFacesFound = true;
6732
6733   if ( !seamLinks.empty() )
6734   {
6735     // There are internal boundaries touching the outher one,
6736     // find all faces of internal boundaries in order to find
6737     // faces of boundaries of holes, if any.
6738
6739   }
6740   else
6741   {
6742     _outerFaces.clear();
6743   }
6744 }
6745
6746 //=======================================================================
6747 /*!
6748  * \brief Find elements of given type where the given point is IN or ON.
6749  *        Returns nb of found elements and elements them-selves.
6750  *
6751  * 'ALL' type means elements of any type excluding nodes, balls and 0D elements
6752  */
6753 //=======================================================================
6754
6755 int SMESH_ElementSearcherImpl::
6756 FindElementsByPoint(const gp_Pnt&                      point,
6757                     SMDSAbs_ElementType                type,
6758                     vector< const SMDS_MeshElement* >& foundElements)
6759 {
6760   foundElements.clear();
6761
6762   double tolerance = getTolerance();
6763
6764   // =================================================================================
6765   if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement || type == SMDSAbs_Ball)
6766   {
6767     if ( !_nodeSearcher )
6768       _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6769
6770     const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6771     if ( !closeNode ) return foundElements.size();
6772
6773     if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance )
6774       return foundElements.size(); // to far from any node
6775
6776     if ( type == SMDSAbs_Node )
6777     {
6778       foundElements.push_back( closeNode );
6779     }
6780     else
6781     {
6782       SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( type );
6783       while ( elemIt->more() )
6784         foundElements.push_back( elemIt->next() );
6785     }
6786   }
6787   // =================================================================================
6788   else // elements more complex than 0D
6789   {
6790     if ( !_ebbTree || _elementType != type )
6791     {
6792       if ( _ebbTree ) delete _ebbTree;
6793       _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance );
6794     }
6795     TIDSortedElemSet suspectElems;
6796     _ebbTree->getElementsNearPoint( point, suspectElems );
6797     TIDSortedElemSet::iterator elem = suspectElems.begin();
6798     for ( ; elem != suspectElems.end(); ++elem )
6799       if ( !SMESH_MeshEditor::IsOut( *elem, point, tolerance ))
6800         foundElements.push_back( *elem );
6801   }
6802   return foundElements.size();
6803 }
6804
6805 //=======================================================================
6806 /*!
6807  * \brief Find an element of given type most close to the given point
6808  *
6809  * WARNING: Only face search is implemeneted so far
6810  */
6811 //=======================================================================
6812
6813 const SMDS_MeshElement*
6814 SMESH_ElementSearcherImpl::FindClosestTo( const gp_Pnt&       point,
6815                                           SMDSAbs_ElementType type )
6816 {
6817   const SMDS_MeshElement* closestElem = 0;
6818
6819   if ( type == SMDSAbs_Face )
6820   {
6821     if ( !_ebbTree || _elementType != type )
6822     {
6823       if ( _ebbTree ) delete _ebbTree;
6824       _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
6825     }
6826     TIDSortedElemSet suspectElems;
6827     _ebbTree->getElementsNearPoint( point, suspectElems );
6828
6829     if ( suspectElems.empty() && _ebbTree->maxSize() > 0 )
6830     {
6831       gp_Pnt boxCenter = 0.5 * ( _ebbTree->getBox()->CornerMin() +
6832                                  _ebbTree->getBox()->CornerMax() );
6833       double radius;
6834       if ( _ebbTree->getBox()->IsOut( point.XYZ() ))
6835         radius = point.Distance( boxCenter ) - 0.5 * _ebbTree->maxSize();
6836       else
6837         radius = _ebbTree->maxSize() / pow( 2., _ebbTree->getHeight()) / 2;
6838       while ( suspectElems.empty() )
6839       {
6840         _ebbTree->getElementsInSphere( point.XYZ(), radius, suspectElems );
6841         radius *= 1.1;
6842       }
6843     }
6844     double minDist = std::numeric_limits<double>::max();
6845     multimap< double, const SMDS_MeshElement* > dist2face;
6846     TIDSortedElemSet::iterator elem = suspectElems.begin();
6847     for ( ; elem != suspectElems.end(); ++elem )
6848     {
6849       double dist = SMESH_MeshEditor::GetDistance( dynamic_cast<const SMDS_MeshFace*>(*elem),
6850                                                    point );
6851       if ( dist < minDist + 1e-10)
6852       {
6853         minDist = dist;
6854         dist2face.insert( dist2face.begin(), make_pair( dist, *elem ));
6855       }
6856     }
6857     if ( !dist2face.empty() )
6858     {
6859       multimap< double, const SMDS_MeshElement* >::iterator d2f = dist2face.begin();
6860       closestElem = d2f->second;
6861       // if there are several elements at the same distance, select one
6862       // with GC closest to the point
6863       typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
6864       double minDistToGC = 0;
6865       for ( ++d2f; d2f != dist2face.end() && fabs( d2f->first - minDist ) < 1e-10; ++d2f )
6866       {
6867         if ( minDistToGC == 0 )
6868         {
6869           gp_XYZ gc(0,0,0);
6870           gc = accumulate( TXyzIterator(closestElem->nodesIterator()),
6871                            TXyzIterator(), gc ) / closestElem->NbNodes();
6872           minDistToGC = point.SquareDistance( gc );
6873         }
6874         gp_XYZ gc(0,0,0);
6875         gc = accumulate( TXyzIterator( d2f->second->nodesIterator()),
6876                          TXyzIterator(), gc ) / d2f->second->NbNodes();
6877         double d = point.SquareDistance( gc );
6878         if ( d < minDistToGC )
6879         {
6880           minDistToGC = d;
6881           closestElem = d2f->second;
6882         }
6883       }
6884       // cout << "FindClosestTo( " <<point.X()<<", "<<point.Y()<<", "<<point.Z()<<" ) FACE "
6885       //      <<closestElem->GetID() << " DIST " << minDist << endl;
6886     }
6887   }
6888   else
6889   {
6890     // NOT IMPLEMENTED SO FAR
6891   }
6892   return closestElem;
6893 }
6894
6895
6896 //================================================================================
6897 /*!
6898  * \brief Classify the given point in the closed 2D mesh
6899  */
6900 //================================================================================
6901
6902 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6903 {
6904   double tolerance = getTolerance();
6905   if ( !_ebbTree || _elementType != SMDSAbs_Face )
6906   {
6907     if ( _ebbTree ) delete _ebbTree;
6908     _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt );
6909   }
6910   // Algo: analyse transition of a line starting at the point through mesh boundary;
6911   // try three lines parallel to axis of the coordinate system and perform rough
6912   // analysis. If solution is not clear perform thorough analysis.
6913
6914   const int nbAxes = 3;
6915   gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6916   map< double, TInters >   paramOnLine2TInters[ nbAxes ];
6917   list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6918   multimap< int, int > nbInt2Axis; // to find the simplest case
6919   for ( int axis = 0; axis < nbAxes; ++axis )
6920   {
6921     gp_Ax1 lineAxis( point, axisDir[axis]);
6922     gp_Lin line    ( lineAxis );
6923
6924     TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
6925     _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
6926
6927     // Intersect faces with the line
6928
6929     map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6930     TIDSortedElemSet::iterator face = suspectFaces.begin();
6931     for ( ; face != suspectFaces.end(); ++face )
6932     {
6933       // get face plane
6934       gp_XYZ fNorm;
6935       if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
6936       gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm );
6937
6938       // perform intersection
6939       IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
6940       if ( !intersection.IsDone() )
6941         continue;
6942       if ( intersection.IsInQuadric() )
6943       {
6944         tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
6945       }
6946       else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
6947       {
6948         gp_Pnt intersectionPoint = intersection.Point(1);
6949         if ( !SMESH_MeshEditor::IsOut( *face, intersectionPoint, tolerance ))
6950           u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
6951       }
6952     }
6953     // Analyse intersections roughly
6954
6955     int nbInter = u2inters.size();
6956     if ( nbInter == 0 )
6957       return TopAbs_OUT;
6958
6959     double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
6960     if ( nbInter == 1 ) // not closed mesh
6961       return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6962
6963     if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6964       return TopAbs_ON;
6965
6966     if ( (f<0) == (l<0) )
6967       return TopAbs_OUT;
6968
6969     int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
6970     int nbIntAfterPoint  = nbInter - nbIntBeforePoint;
6971     if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6972       return TopAbs_IN;
6973
6974     nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
6975
6976     if ( _outerFacesFound ) break; // pass to thorough analysis
6977
6978   } // three attempts - loop on CS axes
6979
6980   // Analyse intersections thoroughly.
6981   // We make two loops maximum, on the first one we only exclude touching intersections,
6982   // on the second, if situation is still unclear, we gather and use information on
6983   // position of faces (internal or outer). If faces position is already gathered,
6984   // we make the second loop right away.
6985
6986   for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
6987   {
6988     multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
6989     for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
6990     {
6991       int axis = nb_axis->second;
6992       map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6993
6994       gp_Ax1 lineAxis( point, axisDir[axis]);
6995       gp_Lin line    ( lineAxis );
6996
6997       // add tangent intersections to u2inters
6998       double param;
6999       list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
7000       for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
7001         if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
7002           u2inters.insert(make_pair( param, *tgtInt ));
7003       tangentInters[ axis ].clear();
7004
7005       // Count intersections before and after the point excluding touching ones.
7006       // If hasPositionInfo we count intersections of outer boundary only
7007
7008       int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
7009       double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
7010       map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
7011       bool ok = ! u_int1->second._coincides;
7012       while ( ok && u_int1 != u2inters.end() )
7013       {
7014         double u = u_int1->first;
7015         bool touchingInt = false;
7016         if ( ++u_int2 != u2inters.end() )
7017         {
7018           // skip intersections at the same point (if the line passes through edge or node)
7019           int nbSamePnt = 0;
7020           while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
7021           {
7022             ++nbSamePnt;
7023             ++u_int2;
7024           }
7025
7026           // skip tangent intersections
7027           int nbTgt = 0;
7028           const SMDS_MeshElement* prevFace = u_int1->second._face;
7029           while ( ok && u_int2->second._coincides )
7030           {
7031             if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
7032               ok = false;
7033             else
7034             {
7035               nbTgt++;
7036               u_int2++;
7037               ok = ( u_int2 != u2inters.end() );
7038             }
7039           }
7040           if ( !ok ) break;
7041
7042           // skip intersections at the same point after tangent intersections
7043           if ( nbTgt > 0 )
7044           {
7045             double u2 = u_int2->first;
7046             ++u_int2;
7047             while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
7048             {
7049               ++nbSamePnt;
7050               ++u_int2;
7051             }
7052           }
7053           // decide if we skipped a touching intersection
7054           if ( nbSamePnt + nbTgt > 0 )
7055           {
7056             double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
7057             map< double, TInters >::iterator u_int = u_int1;
7058             for ( ; u_int != u_int2; ++u_int )
7059             {
7060               if ( u_int->second._coincides ) continue;
7061               double dot = u_int->second._faceNorm * line.Direction();
7062               if ( dot > maxDot ) maxDot = dot;
7063               if ( dot < minDot ) minDot = dot;
7064             }
7065             touchingInt = ( minDot*maxDot < 0 );
7066           }
7067         }
7068         if ( !touchingInt )
7069         {
7070           if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
7071           {
7072             if ( u < 0 )
7073               ++nbIntBeforePoint;
7074             else
7075               ++nbIntAfterPoint;
7076           }
7077           if ( u < f ) f = u;
7078           if ( u > l ) l = u;
7079         }
7080
7081         u_int1 = u_int2; // to next intersection
7082
7083       } // loop on intersections with one line
7084
7085       if ( ok )
7086       {
7087         if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
7088           return TopAbs_ON;
7089
7090         if ( nbIntBeforePoint == 0  || nbIntAfterPoint == 0)
7091           return TopAbs_OUT;
7092
7093         if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
7094           return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7095
7096         if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7097           return TopAbs_IN;
7098
7099         if ( (f<0) == (l<0) )
7100           return TopAbs_OUT;
7101
7102         if ( hasPositionInfo )
7103           return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
7104       }
7105     } // loop on intersections of the tree lines - thorough analysis
7106
7107     if ( !hasPositionInfo )
7108     {
7109       // gather info on faces position - is face in the outer boundary or not
7110       map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
7111       findOuterBoundary( u2inters.begin()->second._face );
7112     }
7113
7114   } // two attempts - with and w/o faces position info in the mesh
7115
7116   return TopAbs_UNKNOWN;
7117 }
7118
7119 //=======================================================================
7120 /*!
7121  * \brief Return elements possibly intersecting the line
7122  */
7123 //=======================================================================
7124
7125 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1&                      line,
7126                                                      SMDSAbs_ElementType                type,
7127                                                      vector< const SMDS_MeshElement* >& foundElems)
7128 {
7129   if ( !_ebbTree || _elementType != type )
7130   {
7131     if ( _ebbTree ) delete _ebbTree;
7132     _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
7133   }
7134   TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
7135   _ebbTree->getElementsNearLine( line, suspectFaces );
7136   foundElems.assign( suspectFaces.begin(), suspectFaces.end());
7137 }
7138
7139 //=======================================================================
7140 /*!
7141  * \brief Return SMESH_ElementSearcher
7142  */
7143 //=======================================================================
7144
7145 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
7146 {
7147   return new SMESH_ElementSearcherImpl( *GetMeshDS() );
7148 }
7149
7150 //=======================================================================
7151 /*!
7152  * \brief Return SMESH_ElementSearcher acting on a sub-set of elements
7153  */
7154 //=======================================================================
7155
7156 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt)
7157 {
7158   return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt );
7159 }
7160
7161 //=======================================================================
7162 /*!
7163  * \brief Return true if the point is IN or ON of the element
7164  */
7165 //=======================================================================
7166
7167 bool SMESH_MeshEditor::IsOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
7168 {
7169   if ( element->GetType() == SMDSAbs_Volume)
7170   {
7171     return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
7172   }
7173
7174   // get ordered nodes
7175
7176   vector< gp_XYZ > xyz;
7177   vector<const SMDS_MeshNode*> nodeList;
7178
7179   SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
7180   if ( element->IsQuadratic() ) {
7181     if (const SMDS_VtkFace* f=dynamic_cast<const SMDS_VtkFace*>(element))
7182       nodeIt = f->interlacedNodesElemIterator();
7183     else if (const SMDS_VtkEdge*  e =dynamic_cast<const SMDS_VtkEdge*>(element))
7184       nodeIt = e->interlacedNodesElemIterator();
7185   }
7186   while ( nodeIt->more() )
7187     {
7188       const SMDS_MeshNode* node = cast2Node( nodeIt->next() );
7189       xyz.push_back( SMESH_TNodeXYZ(node) );
7190       nodeList.push_back(node);
7191     }
7192
7193   int i, nbNodes = element->NbNodes();
7194
7195   if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
7196   {
7197     // compute face normal
7198     gp_Vec faceNorm(0,0,0);
7199     xyz.push_back( xyz.front() );
7200     nodeList.push_back( nodeList.front() );
7201     for ( i = 0; i < nbNodes; ++i )
7202     {
7203       gp_Vec edge1( xyz[i+1], xyz[i]);
7204       gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
7205       faceNorm += edge1 ^ edge2;
7206     }
7207     double normSize = faceNorm.Magnitude();
7208     if ( normSize <= tol )
7209     {
7210       // degenerated face: point is out if it is out of all face edges
7211       for ( i = 0; i < nbNodes; ++i )
7212       {
7213         SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] );
7214         if ( !IsOut( &edge, point, tol ))
7215           return false;
7216       }
7217       return true;
7218     }
7219     faceNorm /= normSize;
7220
7221     // check if the point lays on face plane
7222     gp_Vec n2p( xyz[0], point );
7223     if ( fabs( n2p * faceNorm ) > tol )
7224       return true; // not on face plane
7225
7226     // check if point is out of face boundary:
7227     // define it by closest transition of a ray point->infinity through face boundary
7228     // on the face plane.
7229     // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
7230     // to find intersections of the ray with the boundary.
7231     gp_Vec ray = n2p;
7232     gp_Vec plnNorm = ray ^ faceNorm;
7233     normSize = plnNorm.Magnitude();
7234     if ( normSize <= tol ) return false; // point coincides with the first node
7235     plnNorm /= normSize;
7236     // for each node of the face, compute its signed distance to the plane
7237     vector<double> dist( nbNodes + 1);
7238     for ( i = 0; i < nbNodes; ++i )
7239     {
7240       gp_Vec n2p( xyz[i], point );
7241       dist[i] = n2p * plnNorm;
7242     }
7243     dist.back() = dist.front();
7244     // find the closest intersection
7245     int    iClosest = -1;
7246     double rClosest, distClosest = 1e100;;
7247     gp_Pnt pClosest;
7248     for ( i = 0; i < nbNodes; ++i )
7249     {
7250       double r;
7251       if ( fabs( dist[i]) < tol )
7252         r = 0.;
7253       else if ( fabs( dist[i+1]) < tol )
7254         r = 1.;
7255       else if ( dist[i] * dist[i+1] < 0 )
7256         r = dist[i] / ( dist[i] - dist[i+1] );
7257       else
7258         continue; // no intersection
7259       gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
7260       gp_Vec p2int ( point, pInt);
7261       if ( p2int * ray > -tol ) // right half-space
7262       {
7263         double intDist = p2int.SquareMagnitude();
7264         if ( intDist < distClosest )
7265         {
7266           iClosest = i;
7267           rClosest = r;
7268           pClosest = pInt;
7269           distClosest = intDist;
7270         }
7271       }
7272     }
7273     if ( iClosest < 0 )
7274       return true; // no intesections - out
7275
7276     // analyse transition
7277     gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
7278     gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
7279     gp_Vec p2int ( point, pClosest );
7280     bool out = (edgeNorm * p2int) < -tol;
7281     if ( rClosest > 0. && rClosest < 1. ) // not node intersection
7282       return out;
7283
7284     // ray pass through a face node; analyze transition through an adjacent edge
7285     gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
7286     gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
7287     gp_Vec edgeAdjacent( p1, p2 );
7288     gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
7289     bool out2 = (edgeNorm2 * p2int) < -tol;
7290
7291     bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
7292     return covexCorner ? (out || out2) : (out && out2);
7293   }
7294   if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
7295   {
7296     // point is out of edge if it is NOT ON any straight part of edge
7297     // (we consider quadratic edge as being composed of two straight parts)
7298     for ( i = 1; i < nbNodes; ++i )
7299     {
7300       gp_Vec edge( xyz[i-1], xyz[i]);
7301       gp_Vec n1p ( xyz[i-1], point);
7302       double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
7303       if ( dist > tol )
7304         continue;
7305       gp_Vec n2p( xyz[i], point );
7306       if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
7307         continue;
7308       return false; // point is ON this part
7309     }
7310     return true;
7311   }
7312   // Node or 0D element -------------------------------------------------------------------------
7313   {
7314     gp_Vec n2p ( xyz[0], point );
7315     return n2p.Magnitude() <= tol;
7316   }
7317   return true;
7318 }
7319
7320 //=======================================================================
7321
7322 namespace
7323 {
7324   // Position of a point relative to a segment
7325   //            .           .
7326   //            .  LEFT     .
7327   //            .           .
7328   //  VERTEX 1  o----ON----->  VERTEX 2
7329   //            .           .
7330   //            .  RIGHT    .
7331   //            .           .
7332   enum PositionName { POS_LEFT = 1, POS_VERTEX = 2, POS_RIGHT = 4, //POS_ON = 8,
7333                       POS_ALL = POS_LEFT | POS_RIGHT | POS_VERTEX };
7334   struct PointPos
7335   {
7336     PositionName _name;
7337     int          _index; // index of vertex or segment
7338
7339     PointPos( PositionName n, int i=-1 ): _name(n), _index(i) {}
7340     bool operator < (const PointPos& other ) const
7341     {
7342       if ( _name == other._name )
7343         return  ( _index < 0 || other._index < 0 ) ? false : _index < other._index;
7344       return _name < other._name;
7345     }
7346   };
7347
7348   //================================================================================
7349   /*!
7350    * \brief Return of a point relative to a segment
7351    *  \param point2D      - the point to analyze position of
7352    *  \param xyVec        - end points of segments
7353    *  \param index0       - 0-based index of the first point of segment
7354    *  \param posToFindOut - flags of positions to detect
7355    *  \retval PointPos - point position
7356    */
7357   //================================================================================
7358
7359   PointPos getPointPosition( const gp_XY& point2D,
7360                              const gp_XY* segEnds,
7361                              const int    index0 = 0,
7362                              const int    posToFindOut = POS_ALL)
7363   {
7364     const gp_XY& p1 = segEnds[ index0   ];
7365     const gp_XY& p2 = segEnds[ index0+1 ];
7366     const gp_XY grad = p2 - p1;
7367
7368     if ( posToFindOut & POS_VERTEX )
7369     {
7370       // check if the point2D is at "vertex 1" zone
7371       gp_XY pp1[2] = { p1, gp_XY( p1.X() - grad.Y(),
7372                                   p1.Y() + grad.X() ) };
7373       if ( getPointPosition( point2D, pp1, 0, POS_LEFT|POS_RIGHT )._name == POS_LEFT )
7374         return PointPos( POS_VERTEX, index0 );
7375
7376       // check if the point2D is at "vertex 2" zone
7377       gp_XY pp2[2] = { p2, gp_XY( p2.X() - grad.Y(),
7378                                   p2.Y() + grad.X() ) };
7379       if ( getPointPosition( point2D, pp2, 0, POS_LEFT|POS_RIGHT )._name == POS_RIGHT )
7380         return PointPos( POS_VERTEX, index0 + 1);
7381     }
7382     double edgeEquation =
7383       ( point2D.X() - p1.X() ) * grad.Y() - ( point2D.Y() - p1.Y() ) * grad.X();
7384     return PointPos( edgeEquation < 0 ? POS_LEFT : POS_RIGHT, index0 );
7385   }
7386 }
7387
7388 //=======================================================================
7389 /*!
7390  * \brief Return minimal distance from a point to a face
7391  *
7392  * Currently we ignore non-planarity and 2nd order of face
7393  */
7394 //=======================================================================
7395
7396 double SMESH_MeshEditor::GetDistance( const SMDS_MeshFace* face,
7397                                       const gp_Pnt&        point )
7398 {
7399   double badDistance = -1;
7400   if ( !face ) return badDistance;
7401
7402   // coordinates of nodes (medium nodes, if any, ignored)
7403   typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
7404   vector<gp_XYZ> xyz( TXyzIterator( face->nodesIterator()), TXyzIterator() );
7405   xyz.resize( face->NbCornerNodes()+1 );
7406
7407   // transformation to get xyz[0] lies on the origin, xyz[1] lies on the Z axis,
7408   // and xyz[2] lies in the XZ plane. This is to pass to 2D space on XZ plane.
7409   gp_Trsf trsf;
7410   gp_Vec OZ ( xyz[0], xyz[1] );
7411   gp_Vec OX ( xyz[0], xyz[2] );
7412   if ( OZ.Magnitude() < std::numeric_limits<double>::min() )
7413   {
7414     if ( xyz.size() < 4 ) return badDistance;
7415     OZ = gp_Vec ( xyz[0], xyz[2] );
7416     OX = gp_Vec ( xyz[0], xyz[3] );
7417   }
7418   gp_Ax3 tgtCS;
7419   try {
7420     tgtCS = gp_Ax3( xyz[0], OZ, OX );
7421   }
7422   catch ( Standard_Failure ) {
7423     return badDistance;
7424   }
7425   trsf.SetTransformation( tgtCS );
7426
7427   // move all the nodes to 2D
7428   vector<gp_XY> xy( xyz.size() );
7429   for ( size_t i = 0;i < xyz.size()-1; ++i )
7430   {
7431     gp_XYZ p3d = xyz[i];
7432     trsf.Transforms( p3d );
7433     xy[i].SetCoord( p3d.X(), p3d.Z() );
7434   }
7435   xyz.back() = xyz.front();
7436   xy.back() = xy.front();
7437
7438   // // move the point in 2D
7439   gp_XYZ tmpPnt = point.XYZ();
7440   trsf.Transforms( tmpPnt );
7441   gp_XY point2D( tmpPnt.X(), tmpPnt.Z() );
7442
7443   // loop on segments of the face to analyze point position ralative to the face
7444   set< PointPos > pntPosSet;
7445   for ( size_t i = 1; i < xy.size(); ++i )
7446   {
7447     PointPos pos = getPointPosition( point2D, &xy[0], i-1 );
7448     pntPosSet.insert( pos );
7449   }
7450
7451   // compute distance
7452   PointPos pos = *pntPosSet.begin();
7453   // cout << "Face " << face->GetID() << " DIST: ";
7454   switch ( pos._name )
7455   {
7456   case POS_LEFT: {
7457     // point is most close to a segment
7458     gp_Vec p0p1( point, xyz[ pos._index ] );
7459     gp_Vec p1p2( xyz[ pos._index ], xyz[ pos._index+1 ]); // segment vector
7460     p1p2.Normalize();
7461     double projDist = p0p1 * p1p2; // distance projected to the segment
7462     gp_Vec projVec = p1p2 * projDist;
7463     gp_Vec distVec = p0p1 - projVec;
7464     // cout << distVec.Magnitude()  << ", SEG " << face->GetNode(pos._index)->GetID()
7465     //      << " - " << face->GetNodeWrap(pos._index+1)->GetID() << endl;
7466     return distVec.Magnitude();
7467   }
7468   case POS_RIGHT: {
7469     // point is inside the face
7470     double distToFacePlane = tmpPnt.Y();
7471     // cout << distToFacePlane << ", INSIDE " << endl;
7472     return Abs( distToFacePlane );
7473   }
7474   case POS_VERTEX: {
7475     // point is most close to a node
7476     gp_Vec distVec( point, xyz[ pos._index ]);
7477     // cout << distVec.Magnitude()  << " VERTEX " << face->GetNode(pos._index)->GetID() << endl;
7478     return distVec.Magnitude();
7479   }
7480   }
7481   return badDistance;
7482 }
7483
7484 //=======================================================================
7485 //function : SimplifyFace
7486 //purpose  :
7487 //=======================================================================
7488 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
7489                                     vector<const SMDS_MeshNode *>&      poly_nodes,
7490                                     vector<int>&                        quantities) const
7491 {
7492   int nbNodes = faceNodes.size();
7493
7494   if (nbNodes < 3)
7495     return 0;
7496
7497   set<const SMDS_MeshNode*> nodeSet;
7498
7499   // get simple seq of nodes
7500   //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7501   vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7502   int iSimple = 0, nbUnique = 0;
7503
7504   simpleNodes[iSimple++] = faceNodes[0];
7505   nbUnique++;
7506   for (int iCur = 1; iCur < nbNodes; iCur++) {
7507     if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7508       simpleNodes[iSimple++] = faceNodes[iCur];
7509       if (nodeSet.insert( faceNodes[iCur] ).second)
7510         nbUnique++;
7511     }
7512   }
7513   int nbSimple = iSimple;
7514   if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7515     nbSimple--;
7516     iSimple--;
7517   }
7518
7519   if (nbUnique < 3)
7520     return 0;
7521
7522   // separate loops
7523   int nbNew = 0;
7524   bool foundLoop = (nbSimple > nbUnique);
7525   while (foundLoop) {
7526     foundLoop = false;
7527     set<const SMDS_MeshNode*> loopSet;
7528     for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7529       const SMDS_MeshNode* n = simpleNodes[iSimple];
7530       if (!loopSet.insert( n ).second) {
7531         foundLoop = true;
7532
7533         // separate loop
7534         int iC = 0, curLast = iSimple;
7535         for (; iC < curLast; iC++) {
7536           if (simpleNodes[iC] == n) break;
7537         }
7538         int loopLen = curLast - iC;
7539         if (loopLen > 2) {
7540           // create sub-element
7541           nbNew++;
7542           quantities.push_back(loopLen);
7543           for (; iC < curLast; iC++) {
7544             poly_nodes.push_back(simpleNodes[iC]);
7545           }
7546         }
7547         // shift the rest nodes (place from the first loop position)
7548         for (iC = curLast + 1; iC < nbSimple; iC++) {
7549           simpleNodes[iC - loopLen] = simpleNodes[iC];
7550         }
7551         nbSimple -= loopLen;
7552         iSimple -= loopLen;
7553       }
7554     } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7555   } // while (foundLoop)
7556
7557   if (iSimple > 2) {
7558     nbNew++;
7559     quantities.push_back(iSimple);
7560     for (int i = 0; i < iSimple; i++)
7561       poly_nodes.push_back(simpleNodes[i]);
7562   }
7563
7564   return nbNew;
7565 }
7566
7567 //=======================================================================
7568 //function : MergeNodes
7569 //purpose  : In each group, the cdr of nodes are substituted by the first one
7570 //           in all elements.
7571 //=======================================================================
7572
7573 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7574 {
7575   MESSAGE("MergeNodes");
7576   myLastCreatedElems.Clear();
7577   myLastCreatedNodes.Clear();
7578
7579   SMESHDS_Mesh* aMesh = GetMeshDS();
7580
7581   TNodeNodeMap nodeNodeMap; // node to replace - new node
7582   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7583   list< int > rmElemIds, rmNodeIds;
7584
7585   // Fill nodeNodeMap and elems
7586
7587   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7588   for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7589     list<const SMDS_MeshNode*>& nodes = *grIt;
7590     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7591     const SMDS_MeshNode* nToKeep = *nIt;
7592     //MESSAGE("node to keep " << nToKeep->GetID());
7593     for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7594       const SMDS_MeshNode* nToRemove = *nIt;
7595       nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7596       if ( nToRemove != nToKeep ) {
7597         //MESSAGE("  node to remove " << nToRemove->GetID());
7598         rmNodeIds.push_back( nToRemove->GetID() );
7599         AddToSameGroups( nToKeep, nToRemove, aMesh );
7600       }
7601
7602       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7603       while ( invElemIt->more() ) {
7604         const SMDS_MeshElement* elem = invElemIt->next();
7605         elems.insert(elem);
7606       }
7607     }
7608   }
7609   // Change element nodes or remove an element
7610
7611   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7612   for ( ; eIt != elems.end(); eIt++ ) {
7613     const SMDS_MeshElement* elem = *eIt;
7614     //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7615     int nbNodes = elem->NbNodes();
7616     int aShapeId = FindShape( elem );
7617
7618     set<const SMDS_MeshNode*> nodeSet;
7619     vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7620     int iUnique = 0, iCur = 0, nbRepl = 0;
7621     vector<int> iRepl( nbNodes );
7622
7623     // get new seq of nodes
7624     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7625     while ( itN->more() ) {
7626       const SMDS_MeshNode* n =
7627         static_cast<const SMDS_MeshNode*>( itN->next() );
7628
7629       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7630       if ( nnIt != nodeNodeMap.end() ) { // n sticks
7631         n = (*nnIt).second;
7632         // BUG 0020185: begin
7633         {
7634           bool stopRecur = false;
7635           set<const SMDS_MeshNode*> nodesRecur;
7636           nodesRecur.insert(n);
7637           while (!stopRecur) {
7638             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7639             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7640               n = (*nnIt_i).second;
7641               if (!nodesRecur.insert(n).second) {
7642                 // error: recursive dependancy
7643                 stopRecur = true;
7644               }
7645             }
7646             else
7647               stopRecur = true;
7648           }
7649         }
7650         // BUG 0020185: end
7651       }
7652       curNodes[ iCur ] = n;
7653       bool isUnique = nodeSet.insert( n ).second;
7654       if ( isUnique )
7655         uniqueNodes[ iUnique++ ] = n;
7656       else
7657         iRepl[ nbRepl++ ] = iCur;
7658       iCur++;
7659     }
7660
7661     // Analyse element topology after replacement
7662
7663     bool isOk = true;
7664     int nbUniqueNodes = nodeSet.size();
7665     //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7666     if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7667       // Polygons and Polyhedral volumes
7668       if (elem->IsPoly()) {
7669
7670         if (elem->GetType() == SMDSAbs_Face) {
7671           // Polygon
7672           vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7673           int inode = 0;
7674           for (; inode < nbNodes; inode++) {
7675             face_nodes[inode] = curNodes[inode];
7676           }
7677
7678           vector<const SMDS_MeshNode *> polygons_nodes;
7679           vector<int> quantities;
7680           int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7681           if (nbNew > 0) {
7682             inode = 0;
7683             for (int iface = 0; iface < nbNew; iface++) {
7684               int nbNodes = quantities[iface];
7685               vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7686               for (int ii = 0; ii < nbNodes; ii++, inode++) {
7687                 poly_nodes[ii] = polygons_nodes[inode];
7688               }
7689               SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7690               myLastCreatedElems.Append(newElem);
7691               if (aShapeId)
7692                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7693             }
7694
7695             MESSAGE("ChangeElementNodes MergeNodes Polygon");
7696             //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7697             vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7698             int quid =0;
7699             if (nbNew > 0) quid = nbNew - 1;
7700             vector<int> newquant(quantities.begin()+quid, quantities.end());
7701             const SMDS_MeshElement* newElem = 0;
7702             newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7703             myLastCreatedElems.Append(newElem);
7704             if ( aShapeId && newElem )
7705               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7706             rmElemIds.push_back(elem->GetID());
7707           }
7708           else {
7709             rmElemIds.push_back(elem->GetID());
7710           }
7711
7712         }
7713         else if (elem->GetType() == SMDSAbs_Volume) {
7714           // Polyhedral volume
7715           if (nbUniqueNodes < 4) {
7716             rmElemIds.push_back(elem->GetID());
7717           }
7718           else {
7719             // each face has to be analyzed in order to check volume validity
7720             const SMDS_VtkVolume* aPolyedre =
7721               dynamic_cast<const SMDS_VtkVolume*>( elem );
7722             if (aPolyedre) {
7723               int nbFaces = aPolyedre->NbFaces();
7724
7725               vector<const SMDS_MeshNode *> poly_nodes;
7726               vector<int> quantities;
7727
7728               for (int iface = 1; iface <= nbFaces; iface++) {
7729                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7730                 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7731
7732                 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7733                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7734                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7735                   if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7736                     faceNode = (*nnIt).second;
7737                   }
7738                   faceNodes[inode - 1] = faceNode;
7739                 }
7740
7741                 SimplifyFace(faceNodes, poly_nodes, quantities);
7742               }
7743
7744               if (quantities.size() > 3) {
7745                 // to be done: remove coincident faces
7746               }
7747
7748               if (quantities.size() > 3)
7749                 {
7750                   MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7751                   //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7752                   const SMDS_MeshElement* newElem = 0;
7753                   newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7754                   myLastCreatedElems.Append(newElem);
7755                   if ( aShapeId && newElem )
7756                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
7757                   rmElemIds.push_back(elem->GetID());
7758                 }
7759             }
7760             else {
7761               rmElemIds.push_back(elem->GetID());
7762             }
7763           }
7764         }
7765         else {
7766         }
7767
7768         continue;
7769       } // poly element
7770
7771       // Regular elements
7772       // TODO not all the possible cases are solved. Find something more generic?
7773       switch ( nbNodes ) {
7774       case 2: ///////////////////////////////////// EDGE
7775         isOk = false; break;
7776       case 3: ///////////////////////////////////// TRIANGLE
7777         isOk = false; break;
7778       case 4:
7779         if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7780           isOk = false;
7781         else { //////////////////////////////////// QUADRANGLE
7782           if ( nbUniqueNodes < 3 )
7783             isOk = false;
7784           else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7785             isOk = false; // opposite nodes stick
7786           //MESSAGE("isOk " << isOk);
7787         }
7788         break;
7789       case 6: ///////////////////////////////////// PENTAHEDRON
7790         if ( nbUniqueNodes == 4 ) {
7791           // ---------------------------------> tetrahedron
7792           if (nbRepl == 3 &&
7793               iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7794             // all top nodes stick: reverse a bottom
7795             uniqueNodes[ 0 ] = curNodes [ 1 ];
7796             uniqueNodes[ 1 ] = curNodes [ 0 ];
7797           }
7798           else if (nbRepl == 3 &&
7799                    iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7800             // all bottom nodes stick: set a top before
7801             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7802             uniqueNodes[ 0 ] = curNodes [ 3 ];
7803             uniqueNodes[ 1 ] = curNodes [ 4 ];
7804             uniqueNodes[ 2 ] = curNodes [ 5 ];
7805           }
7806           else if (nbRepl == 4 &&
7807                    iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7808             // a lateral face turns into a line: reverse a bottom
7809             uniqueNodes[ 0 ] = curNodes [ 1 ];
7810             uniqueNodes[ 1 ] = curNodes [ 0 ];
7811           }
7812           else
7813             isOk = false;
7814         }
7815         else if ( nbUniqueNodes == 5 ) {
7816           // PENTAHEDRON --------------------> 2 tetrahedrons
7817           if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7818             // a bottom node sticks with a linked top one
7819             // 1.
7820             SMDS_MeshElement* newElem =
7821               aMesh->AddVolume(curNodes[ 3 ],
7822                                curNodes[ 4 ],
7823                                curNodes[ 5 ],
7824                                curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7825             myLastCreatedElems.Append(newElem);
7826             if ( aShapeId )
7827               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7828             // 2. : reverse a bottom
7829             uniqueNodes[ 0 ] = curNodes [ 1 ];
7830             uniqueNodes[ 1 ] = curNodes [ 0 ];
7831             nbUniqueNodes = 4;
7832           }
7833           else
7834             isOk = false;
7835         }
7836         else
7837           isOk = false;
7838         break;
7839       case 8: {
7840         if(elem->IsQuadratic()) { // Quadratic quadrangle
7841           //   1    5    2
7842           //    +---+---+
7843           //    |       |
7844           //    |       |
7845           //   4+       +6
7846           //    |       |
7847           //    |       |
7848           //    +---+---+
7849           //   0    7    3
7850           isOk = false;
7851           if(nbRepl==2) {
7852             MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7853           }
7854           if(nbRepl==3) {
7855             MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2]);
7856             nbUniqueNodes = 6;
7857             if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7858               uniqueNodes[0] = curNodes[0];
7859               uniqueNodes[1] = curNodes[2];
7860               uniqueNodes[2] = curNodes[3];
7861               uniqueNodes[3] = curNodes[5];
7862               uniqueNodes[4] = curNodes[6];
7863               uniqueNodes[5] = curNodes[7];
7864               isOk = true;
7865             }
7866             if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7867               uniqueNodes[0] = curNodes[0];
7868               uniqueNodes[1] = curNodes[1];
7869               uniqueNodes[2] = curNodes[2];
7870               uniqueNodes[3] = curNodes[4];
7871               uniqueNodes[4] = curNodes[5];
7872               uniqueNodes[5] = curNodes[6];
7873               isOk = true;
7874             }
7875             if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7876               uniqueNodes[0] = curNodes[1];
7877               uniqueNodes[1] = curNodes[2];
7878               uniqueNodes[2] = curNodes[3];
7879               uniqueNodes[3] = curNodes[5];
7880               uniqueNodes[4] = curNodes[6];
7881               uniqueNodes[5] = curNodes[0];
7882               isOk = true;
7883             }
7884             if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7885               uniqueNodes[0] = curNodes[0];
7886               uniqueNodes[1] = curNodes[1];
7887               uniqueNodes[2] = curNodes[3];
7888               uniqueNodes[3] = curNodes[4];
7889               uniqueNodes[4] = curNodes[6];
7890               uniqueNodes[5] = curNodes[7];
7891               isOk = true;
7892             }
7893             if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7894               uniqueNodes[0] = curNodes[0];
7895               uniqueNodes[1] = curNodes[2];
7896               uniqueNodes[2] = curNodes[3];
7897               uniqueNodes[3] = curNodes[1];
7898               uniqueNodes[4] = curNodes[6];
7899               uniqueNodes[5] = curNodes[7];
7900               isOk = true;
7901             }
7902             if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7903               uniqueNodes[0] = curNodes[0];
7904               uniqueNodes[1] = curNodes[1];
7905               uniqueNodes[2] = curNodes[2];
7906               uniqueNodes[3] = curNodes[4];
7907               uniqueNodes[4] = curNodes[5];
7908               uniqueNodes[5] = curNodes[7];
7909               isOk = true;
7910             }
7911             if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7912               uniqueNodes[0] = curNodes[0];
7913               uniqueNodes[1] = curNodes[1];
7914               uniqueNodes[2] = curNodes[3];
7915               uniqueNodes[3] = curNodes[4];
7916               uniqueNodes[4] = curNodes[2];
7917               uniqueNodes[5] = curNodes[7];
7918               isOk = true;
7919             }
7920             if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7921               uniqueNodes[0] = curNodes[0];
7922               uniqueNodes[1] = curNodes[1];
7923               uniqueNodes[2] = curNodes[2];
7924               uniqueNodes[3] = curNodes[4];
7925               uniqueNodes[4] = curNodes[5];
7926               uniqueNodes[5] = curNodes[3];
7927               isOk = true;
7928             }
7929           }
7930           if(nbRepl==4) {
7931             MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3]);
7932           }
7933           if(nbRepl==5) {
7934             MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7935           }
7936           break;
7937         }
7938         //////////////////////////////////// HEXAHEDRON
7939         isOk = false;
7940         SMDS_VolumeTool hexa (elem);
7941         hexa.SetExternalNormal();
7942         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7943           //////////////////////// HEX ---> 1 tetrahedron
7944           for ( int iFace = 0; iFace < 6; iFace++ ) {
7945             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7946             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7947                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7948                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7949               // one face turns into a point ...
7950               int iOppFace = hexa.GetOppFaceIndex( iFace );
7951               ind = hexa.GetFaceNodesIndices( iOppFace );
7952               int nbStick = 0;
7953               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7954                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7955                   nbStick++;
7956               }
7957               if ( nbStick == 1 ) {
7958                 // ... and the opposite one - into a triangle.
7959                 // set a top node
7960                 ind = hexa.GetFaceNodesIndices( iFace );
7961                 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7962                 isOk = true;
7963               }
7964               break;
7965             }
7966           }
7967         }
7968         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7969           //////////////////////// HEX ---> 1 prism
7970           int nbTria = 0, iTria[3];
7971           const int *ind; // indices of face nodes
7972           // look for triangular faces
7973           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7974             ind = hexa.GetFaceNodesIndices( iFace );
7975             TIDSortedNodeSet faceNodes;
7976             for ( iCur = 0; iCur < 4; iCur++ )
7977               faceNodes.insert( curNodes[ind[iCur]] );
7978             if ( faceNodes.size() == 3 )
7979               iTria[ nbTria++ ] = iFace;
7980           }
7981           // check if triangles are opposite
7982           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7983           {
7984             isOk = true;
7985             // set nodes of the bottom triangle
7986             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7987             vector<int> indB;
7988             for ( iCur = 0; iCur < 4; iCur++ )
7989               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7990                 indB.push_back( ind[iCur] );
7991             if ( !hexa.IsForward() )
7992               std::swap( indB[0], indB[2] );
7993             for ( iCur = 0; iCur < 3; iCur++ )
7994               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7995             // set nodes of the top triangle
7996             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7997             for ( iCur = 0; iCur < 3; ++iCur )
7998               for ( int j = 0; j < 4; ++j )
7999                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
8000                 {
8001                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
8002                   break;
8003                 }
8004           }
8005           break;
8006         }
8007         else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
8008           //////////////////// HEXAHEDRON ---> 2 tetrahedrons
8009           for ( int iFace = 0; iFace < 6; iFace++ ) {
8010             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8011             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
8012                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
8013                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
8014               // one face turns into a point ...
8015               int iOppFace = hexa.GetOppFaceIndex( iFace );
8016               ind = hexa.GetFaceNodesIndices( iOppFace );
8017               int nbStick = 0;
8018               iUnique = 2;  // reverse a tetrahedron 1 bottom
8019               for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
8020                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
8021                   nbStick++;
8022                 else if ( iUnique >= 0 )
8023                   uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
8024               }
8025               if ( nbStick == 0 ) {
8026                 // ... and the opposite one is a quadrangle
8027                 // set a top node
8028                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
8029                 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
8030                 nbUniqueNodes = 4;
8031                 // tetrahedron 2
8032                 SMDS_MeshElement* newElem =
8033                   aMesh->AddVolume(curNodes[ind[ 0 ]],
8034                                    curNodes[ind[ 3 ]],
8035                                    curNodes[ind[ 2 ]],
8036                                    curNodes[indTop[ 0 ]]);
8037                 myLastCreatedElems.Append(newElem);
8038                 if ( aShapeId )
8039                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
8040                 isOk = true;
8041               }
8042               break;
8043             }
8044           }
8045         }
8046         else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
8047           ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
8048           // find indices of quad and tri faces
8049           int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
8050           for ( iFace = 0; iFace < 6; iFace++ ) {
8051             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8052             nodeSet.clear();
8053             for ( iCur = 0; iCur < 4; iCur++ )
8054               nodeSet.insert( curNodes[ind[ iCur ]] );
8055             nbUniqueNodes = nodeSet.size();
8056             if ( nbUniqueNodes == 3 )
8057               iTriFace[ nbTri++ ] = iFace;
8058             else if ( nbUniqueNodes == 4 )
8059               iQuadFace[ nbQuad++ ] = iFace;
8060           }
8061           if (nbQuad == 2 && nbTri == 4 &&
8062               hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
8063             // 2 opposite quadrangles stuck with a diagonal;
8064             // sample groups of merged indices: (0-4)(2-6)
8065             // --------------------------------------------> 2 tetrahedrons
8066             const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
8067             const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
8068             int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
8069             if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
8070                 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
8071               // stuck with 0-2 diagonal
8072               i0  = ind1[ 3 ];
8073               i1d = ind1[ 0 ];
8074               i2  = ind1[ 1 ];
8075               i3d = ind1[ 2 ];
8076               i0t = ind2[ 1 ];
8077               i2t = ind2[ 3 ];
8078             }
8079             else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
8080                      curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
8081               // stuck with 1-3 diagonal
8082               i0  = ind1[ 0 ];
8083               i1d = ind1[ 1 ];
8084               i2  = ind1[ 2 ];
8085               i3d = ind1[ 3 ];
8086               i0t = ind2[ 0 ];
8087               i2t = ind2[ 1 ];
8088             }
8089             else {
8090               ASSERT(0);
8091             }
8092             // tetrahedron 1
8093             uniqueNodes[ 0 ] = curNodes [ i0 ];
8094             uniqueNodes[ 1 ] = curNodes [ i1d ];
8095             uniqueNodes[ 2 ] = curNodes [ i3d ];
8096             uniqueNodes[ 3 ] = curNodes [ i0t ];
8097             nbUniqueNodes = 4;
8098             // tetrahedron 2
8099             SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
8100                                                          curNodes[ i2 ],
8101                                                          curNodes[ i3d ],
8102                                                          curNodes[ i2t ]);
8103             myLastCreatedElems.Append(newElem);
8104             if ( aShapeId )
8105               aMesh->SetMeshElementOnShape( newElem, aShapeId );
8106             isOk = true;
8107           }
8108           else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
8109                    ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
8110             // --------------------------------------------> prism
8111             // find 2 opposite triangles
8112             nbUniqueNodes = 6;
8113             for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
8114               if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
8115                 // find indices of kept and replaced nodes
8116                 // and fill unique nodes of 2 opposite triangles
8117                 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
8118                 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
8119                 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
8120                 // fill unique nodes
8121                 iUnique = 0;
8122                 isOk = true;
8123                 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
8124                   const SMDS_MeshNode* n     = curNodes[ind1[ iCur ]];
8125                   const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
8126                   if ( n == nInit ) {
8127                     // iCur of a linked node of the opposite face (make normals co-directed):
8128                     int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
8129                     // check that correspondent corners of triangles are linked
8130                     if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
8131                       isOk = false;
8132                     else {
8133                       uniqueNodes[ iUnique ] = n;
8134                       uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
8135                       iUnique++;
8136                     }
8137                   }
8138                 }
8139                 break;
8140               }
8141             }
8142           }
8143         } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
8144         else
8145         {
8146           MESSAGE("MergeNodes() removes hexahedron "<< elem);
8147         }
8148         break;
8149       } // HEXAHEDRON
8150
8151       default:
8152         isOk = false;
8153       } // switch ( nbNodes )
8154
8155     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
8156
8157     if ( isOk ) { // the elem remains valid after sticking nodes
8158       if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
8159       {
8160         // Change nodes of polyedre
8161         const SMDS_VtkVolume* aPolyedre =
8162           dynamic_cast<const SMDS_VtkVolume*>( elem );
8163         if (aPolyedre) {
8164           int nbFaces = aPolyedre->NbFaces();
8165
8166           vector<const SMDS_MeshNode *> poly_nodes;
8167           vector<int> quantities (nbFaces);
8168
8169           for (int iface = 1; iface <= nbFaces; iface++) {
8170             int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
8171             quantities[iface - 1] = nbFaceNodes;
8172
8173             for (inode = 1; inode <= nbFaceNodes; inode++) {
8174               const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
8175
8176               TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
8177               if (nnIt != nodeNodeMap.end()) { // curNode sticks
8178                 curNode = (*nnIt).second;
8179               }
8180               poly_nodes.push_back(curNode);
8181             }
8182           }
8183           aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
8184         }
8185       }
8186       else // replace non-polyhedron elements
8187       {
8188         const SMDSAbs_ElementType etyp = elem->GetType();
8189         const int elemId               = elem->GetID();
8190         const bool isPoly              = (elem->GetEntityType() == SMDSEntity_Polygon);
8191         uniqueNodes.resize(nbUniqueNodes);
8192
8193         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
8194
8195         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
8196         SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
8197         if ( sm && newElem )
8198           sm->AddElement( newElem );
8199         if ( elem != newElem )
8200           ReplaceElemInGroups( elem, newElem, aMesh );
8201       }
8202     }
8203     else {
8204       // Remove invalid regular element or invalid polygon
8205       rmElemIds.push_back( elem->GetID() );
8206     }
8207
8208   } // loop on elements
8209
8210   // Remove bad elements, then equal nodes (order important)
8211
8212   Remove( rmElemIds, false );
8213   Remove( rmNodeIds, true );
8214
8215 }
8216
8217
8218 // ========================================================
8219 // class   : SortableElement
8220 // purpose : allow sorting elements basing on their nodes
8221 // ========================================================
8222 class SortableElement : public set <const SMDS_MeshElement*>
8223 {
8224 public:
8225
8226   SortableElement( const SMDS_MeshElement* theElem )
8227   {
8228     myElem = theElem;
8229     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
8230     while ( nodeIt->more() )
8231       this->insert( nodeIt->next() );
8232   }
8233
8234   const SMDS_MeshElement* Get() const
8235   { return myElem; }
8236
8237   void Set(const SMDS_MeshElement* e) const
8238   { myElem = e; }
8239
8240
8241 private:
8242   mutable const SMDS_MeshElement* myElem;
8243 };
8244
8245 //=======================================================================
8246 //function : FindEqualElements
8247 //purpose  : Return list of group of elements built on the same nodes.
8248 //           Search among theElements or in the whole mesh if theElements is empty
8249 //=======================================================================
8250
8251 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
8252                                          TListOfListOfElementsID & theGroupsOfElementsID)
8253 {
8254   myLastCreatedElems.Clear();
8255   myLastCreatedNodes.Clear();
8256
8257   typedef map< SortableElement, int > TMapOfNodeSet;
8258   typedef list<int> TGroupOfElems;
8259
8260   if ( theElements.empty() )
8261   { // get all elements in the mesh
8262     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
8263     while ( eIt->more() )
8264       theElements.insert( theElements.end(), eIt->next());
8265   }
8266
8267   vector< TGroupOfElems > arrayOfGroups;
8268   TGroupOfElems groupOfElems;
8269   TMapOfNodeSet mapOfNodeSet;
8270
8271   TIDSortedElemSet::iterator elemIt = theElements.begin();
8272   for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
8273     const SMDS_MeshElement* curElem = *elemIt;
8274     SortableElement SE(curElem);
8275     int ind = -1;
8276     // check uniqueness
8277     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8278     if( !(pp.second) ) {
8279       TMapOfNodeSet::iterator& itSE = pp.first;
8280       ind = (*itSE).second;
8281       arrayOfGroups[ind].push_back(curElem->GetID());
8282     }
8283     else {
8284       groupOfElems.clear();
8285       groupOfElems.push_back(curElem->GetID());
8286       arrayOfGroups.push_back(groupOfElems);
8287       i++;
8288     }
8289   }
8290
8291   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8292   for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
8293     groupOfElems = *groupIt;
8294     if ( groupOfElems.size() > 1 ) {
8295       groupOfElems.sort();
8296       theGroupsOfElementsID.push_back(groupOfElems);
8297     }
8298   }
8299 }
8300
8301 //=======================================================================
8302 //function : MergeElements
8303 //purpose  : In each given group, substitute all elements by the first one.
8304 //=======================================================================
8305
8306 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8307 {
8308   myLastCreatedElems.Clear();
8309   myLastCreatedNodes.Clear();
8310
8311   typedef list<int> TListOfIDs;
8312   TListOfIDs rmElemIds; // IDs of elems to remove
8313
8314   SMESHDS_Mesh* aMesh = GetMeshDS();
8315
8316   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8317   while ( groupsIt != theGroupsOfElementsID.end() ) {
8318     TListOfIDs& aGroupOfElemID = *groupsIt;
8319     aGroupOfElemID.sort();
8320     int elemIDToKeep = aGroupOfElemID.front();
8321     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8322     aGroupOfElemID.pop_front();
8323     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8324     while ( idIt != aGroupOfElemID.end() ) {
8325       int elemIDToRemove = *idIt;
8326       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8327       // add the kept element in groups of removed one (PAL15188)
8328       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8329       rmElemIds.push_back( elemIDToRemove );
8330       ++idIt;
8331     }
8332     ++groupsIt;
8333   }
8334
8335   Remove( rmElemIds, false );
8336 }
8337
8338 //=======================================================================
8339 //function : MergeEqualElements
8340 //purpose  : Remove all but one of elements built on the same nodes.
8341 //=======================================================================
8342
8343 void SMESH_MeshEditor::MergeEqualElements()
8344 {
8345   TIDSortedElemSet aMeshElements; /* empty input ==
8346                                      to merge equal elements in the whole mesh */
8347   TListOfListOfElementsID aGroupsOfElementsID;
8348   FindEqualElements(aMeshElements, aGroupsOfElementsID);
8349   MergeElements(aGroupsOfElementsID);
8350 }
8351
8352 //=======================================================================
8353 //function : FindFaceInSet
8354 //purpose  : Return a face having linked nodes n1 and n2 and which is
8355 //           - not in avoidSet,
8356 //           - in elemSet provided that !elemSet.empty()
8357 //           i1 and i2 optionally returns indices of n1 and n2
8358 //=======================================================================
8359
8360 const SMDS_MeshElement*
8361 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode*    n1,
8362                                 const SMDS_MeshNode*    n2,
8363                                 const TIDSortedElemSet& elemSet,
8364                                 const TIDSortedElemSet& avoidSet,
8365                                 int*                    n1ind,
8366                                 int*                    n2ind)
8367
8368 {
8369   int i1, i2;
8370   const SMDS_MeshElement* face = 0;
8371
8372   SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
8373   //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
8374   while ( invElemIt->more() && !face ) // loop on inverse faces of n1
8375   {
8376     //MESSAGE("in while ( invElemIt->more() && !face )");
8377     const SMDS_MeshElement* elem = invElemIt->next();
8378     if (avoidSet.count( elem ))
8379       continue;
8380     if ( !elemSet.empty() && !elemSet.count( elem ))
8381       continue;
8382     // index of n1
8383     i1 = elem->GetNodeIndex( n1 );
8384     // find a n2 linked to n1
8385     int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
8386     for ( int di = -1; di < 2 && !face; di += 2 )
8387     {
8388       i2 = (i1+di+nbN) % nbN;
8389       if ( elem->GetNode( i2 ) == n2 )
8390         face = elem;
8391     }
8392     if ( !face && elem->IsQuadratic())
8393     {
8394       // analysis for quadratic elements using all nodes
8395       const SMDS_VtkFace* F =
8396         dynamic_cast<const SMDS_VtkFace*>(elem);
8397       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8398       // use special nodes iterator
8399       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8400       const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
8401       for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
8402       {
8403         const SMDS_MeshNode* n = cast2Node( anIter->next() );
8404         if ( n1 == prevN && n2 == n )
8405         {
8406           face = elem;
8407         }
8408         else if ( n2 == prevN && n1 == n )
8409         {
8410           face = elem; swap( i1, i2 );
8411         }
8412         prevN = n;
8413       }
8414     }
8415   }
8416   if ( n1ind ) *n1ind = i1;
8417   if ( n2ind ) *n2ind = i2;
8418   return face;
8419 }
8420
8421 //=======================================================================
8422 //function : findAdjacentFace
8423 //purpose  :
8424 //=======================================================================
8425
8426 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8427                                                 const SMDS_MeshNode* n2,
8428                                                 const SMDS_MeshElement* elem)
8429 {
8430   TIDSortedElemSet elemSet, avoidSet;
8431   if ( elem )
8432     avoidSet.insert ( elem );
8433   return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
8434 }
8435
8436 //=======================================================================
8437 //function : FindFreeBorder
8438 //purpose  :
8439 //=======================================================================
8440
8441 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8442
8443 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
8444                                        const SMDS_MeshNode*             theSecondNode,
8445                                        const SMDS_MeshNode*             theLastNode,
8446                                        list< const SMDS_MeshNode* > &   theNodes,
8447                                        list< const SMDS_MeshElement* >& theFaces)
8448 {
8449   if ( !theFirstNode || !theSecondNode )
8450     return false;
8451   // find border face between theFirstNode and theSecondNode
8452   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8453   if ( !curElem )
8454     return false;
8455
8456   theFaces.push_back( curElem );
8457   theNodes.push_back( theFirstNode );
8458   theNodes.push_back( theSecondNode );
8459
8460   //vector<const SMDS_MeshNode*> nodes;
8461   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8462   TIDSortedElemSet foundElems;
8463   bool needTheLast = ( theLastNode != 0 );
8464
8465   while ( nStart != theLastNode ) {
8466     if ( nStart == theFirstNode )
8467       return !needTheLast;
8468
8469     // find all free border faces sharing form nStart
8470
8471     list< const SMDS_MeshElement* > curElemList;
8472     list< const SMDS_MeshNode* > nStartList;
8473     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8474     while ( invElemIt->more() ) {
8475       const SMDS_MeshElement* e = invElemIt->next();
8476       if ( e == curElem || foundElems.insert( e ).second ) {
8477         // get nodes
8478         int iNode = 0, nbNodes = e->NbNodes();
8479         //const SMDS_MeshNode* nodes[nbNodes+1];
8480         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8481
8482         if(e->IsQuadratic()) {
8483           const SMDS_VtkFace* F =
8484             dynamic_cast<const SMDS_VtkFace*>(e);
8485           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8486           // use special nodes iterator
8487           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8488           while( anIter->more() ) {
8489             nodes[ iNode++ ] = cast2Node(anIter->next());
8490           }
8491         }
8492         else {
8493           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8494           while ( nIt->more() )
8495             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8496         }
8497         nodes[ iNode ] = nodes[ 0 ];
8498         // check 2 links
8499         for ( iNode = 0; iNode < nbNodes; iNode++ )
8500           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8501                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8502               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8503           {
8504             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8505             curElemList.push_back( e );
8506           }
8507       }
8508     }
8509     // analyse the found
8510
8511     int nbNewBorders = curElemList.size();
8512     if ( nbNewBorders == 0 ) {
8513       // no free border furthermore
8514       return !needTheLast;
8515     }
8516     else if ( nbNewBorders == 1 ) {
8517       // one more element found
8518       nIgnore = nStart;
8519       nStart = nStartList.front();
8520       curElem = curElemList.front();
8521       theFaces.push_back( curElem );
8522       theNodes.push_back( nStart );
8523     }
8524     else {
8525       // several continuations found
8526       list< const SMDS_MeshElement* >::iterator curElemIt;
8527       list< const SMDS_MeshNode* >::iterator nStartIt;
8528       // check if one of them reached the last node
8529       if ( needTheLast ) {
8530         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8531              curElemIt!= curElemList.end();
8532              curElemIt++, nStartIt++ )
8533           if ( *nStartIt == theLastNode ) {
8534             theFaces.push_back( *curElemIt );
8535             theNodes.push_back( *nStartIt );
8536             return true;
8537           }
8538       }
8539       // find the best free border by the continuations
8540       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8541       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8542       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8543            curElemIt!= curElemList.end();
8544            curElemIt++, nStartIt++ )
8545       {
8546         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8547         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8548         // find one more free border
8549         if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8550           cNL->clear();
8551           cFL->clear();
8552         }
8553         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8554           // choice: clear a worse one
8555           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8556           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8557           contNodes[ iWorse ].clear();
8558           contFaces[ iWorse ].clear();
8559         }
8560       }
8561       if ( contNodes[0].empty() && contNodes[1].empty() )
8562         return false;
8563
8564       // append the best free border
8565       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8566       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8567       theNodes.pop_back(); // remove nIgnore
8568       theNodes.pop_back(); // remove nStart
8569       theFaces.pop_back(); // remove curElem
8570       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8571       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8572       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8573       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8574       return true;
8575
8576     } // several continuations found
8577   } // while ( nStart != theLastNode )
8578
8579   return true;
8580 }
8581
8582 //=======================================================================
8583 //function : CheckFreeBorderNodes
8584 //purpose  : Return true if the tree nodes are on a free border
8585 //=======================================================================
8586
8587 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8588                                             const SMDS_MeshNode* theNode2,
8589                                             const SMDS_MeshNode* theNode3)
8590 {
8591   list< const SMDS_MeshNode* > nodes;
8592   list< const SMDS_MeshElement* > faces;
8593   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8594 }
8595
8596 //=======================================================================
8597 //function : SewFreeBorder
8598 //purpose  :
8599 //=======================================================================
8600
8601 SMESH_MeshEditor::Sew_Error
8602 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8603                                  const SMDS_MeshNode* theBordSecondNode,
8604                                  const SMDS_MeshNode* theBordLastNode,
8605                                  const SMDS_MeshNode* theSideFirstNode,
8606                                  const SMDS_MeshNode* theSideSecondNode,
8607                                  const SMDS_MeshNode* theSideThirdNode,
8608                                  const bool           theSideIsFreeBorder,
8609                                  const bool           toCreatePolygons,
8610                                  const bool           toCreatePolyedrs)
8611 {
8612   myLastCreatedElems.Clear();
8613   myLastCreatedNodes.Clear();
8614
8615   MESSAGE("::SewFreeBorder()");
8616   Sew_Error aResult = SEW_OK;
8617
8618   // ====================================
8619   //    find side nodes and elements
8620   // ====================================
8621
8622   list< const SMDS_MeshNode* > nSide[ 2 ];
8623   list< const SMDS_MeshElement* > eSide[ 2 ];
8624   list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8625   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8626
8627   // Free border 1
8628   // --------------
8629   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8630                       nSide[0], eSide[0])) {
8631     MESSAGE(" Free Border 1 not found " );
8632     aResult = SEW_BORDER1_NOT_FOUND;
8633   }
8634   if (theSideIsFreeBorder) {
8635     // Free border 2
8636     // --------------
8637     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8638                         nSide[1], eSide[1])) {
8639       MESSAGE(" Free Border 2 not found " );
8640       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8641     }
8642   }
8643   if ( aResult != SEW_OK )
8644     return aResult;
8645
8646   if (!theSideIsFreeBorder) {
8647     // Side 2
8648     // --------------
8649
8650     // -------------------------------------------------------------------------
8651     // Algo:
8652     // 1. If nodes to merge are not coincident, move nodes of the free border
8653     //    from the coord sys defined by the direction from the first to last
8654     //    nodes of the border to the correspondent sys of the side 2
8655     // 2. On the side 2, find the links most co-directed with the correspondent
8656     //    links of the free border
8657     // -------------------------------------------------------------------------
8658
8659     // 1. Since sewing may break if there are volumes to split on the side 2,
8660     //    we wont move nodes but just compute new coordinates for them
8661     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8662     TNodeXYZMap nBordXYZ;
8663     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8664     list< const SMDS_MeshNode* >::iterator nBordIt;
8665
8666     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8667     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8668     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8669     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8670     double tol2 = 1.e-8;
8671     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8672     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8673       // Need node movement.
8674
8675       // find X and Z axes to create trsf
8676       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8677       gp_Vec X = Zs ^ Zb;
8678       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8679         // Zb || Zs
8680         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8681
8682       // coord systems
8683       gp_Ax3 toBordAx( Pb1, Zb, X );
8684       gp_Ax3 fromSideAx( Ps1, Zs, X );
8685       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8686       // set trsf
8687       gp_Trsf toBordSys, fromSide2Sys;
8688       toBordSys.SetTransformation( toBordAx );
8689       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8690       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8691
8692       // move
8693       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8694         const SMDS_MeshNode* n = *nBordIt;
8695         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8696         toBordSys.Transforms( xyz );
8697         fromSide2Sys.Transforms( xyz );
8698         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8699       }
8700     }
8701     else {
8702       // just insert nodes XYZ in the nBordXYZ map
8703       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8704         const SMDS_MeshNode* n = *nBordIt;
8705         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8706       }
8707     }
8708
8709     // 2. On the side 2, find the links most co-directed with the correspondent
8710     //    links of the free border
8711
8712     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8713     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8714     sideNodes.push_back( theSideFirstNode );
8715
8716     bool hasVolumes = false;
8717     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8718     set<long> foundSideLinkIDs, checkedLinkIDs;
8719     SMDS_VolumeTool volume;
8720     //const SMDS_MeshNode* faceNodes[ 4 ];
8721
8722     const SMDS_MeshNode*    sideNode;
8723     const SMDS_MeshElement* sideElem;
8724     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8725     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8726     nBordIt = bordNodes.begin();
8727     nBordIt++;
8728     // border node position and border link direction to compare with
8729     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8730     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8731     // choose next side node by link direction or by closeness to
8732     // the current border node:
8733     bool searchByDir = ( *nBordIt != theBordLastNode );
8734     do {
8735       // find the next node on the Side 2
8736       sideNode = 0;
8737       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8738       long linkID;
8739       checkedLinkIDs.clear();
8740       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8741
8742       // loop on inverse elements of current node (prevSideNode) on the Side 2
8743       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8744       while ( invElemIt->more() )
8745       {
8746         const SMDS_MeshElement* elem = invElemIt->next();
8747         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8748         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8749         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8750         bool isVolume = volume.Set( elem );
8751         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8752         if ( isVolume ) // --volume
8753           hasVolumes = true;
8754         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8755           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8756           if(elem->IsQuadratic()) {
8757             const SMDS_VtkFace* F =
8758               dynamic_cast<const SMDS_VtkFace*>(elem);
8759             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8760             // use special nodes iterator
8761             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8762             while( anIter->more() ) {
8763               nodes[ iNode ] = cast2Node(anIter->next());
8764               if ( nodes[ iNode++ ] == prevSideNode )
8765                 iPrevNode = iNode - 1;
8766             }
8767           }
8768           else {
8769             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8770             while ( nIt->more() ) {
8771               nodes[ iNode ] = cast2Node( nIt->next() );
8772               if ( nodes[ iNode++ ] == prevSideNode )
8773                 iPrevNode = iNode - 1;
8774             }
8775           }
8776           // there are 2 links to check
8777           nbNodes = 2;
8778         }
8779         else // --edge
8780           continue;
8781         // loop on links, to be precise, on the second node of links
8782         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8783           const SMDS_MeshNode* n = nodes[ iNode ];
8784           if ( isVolume ) {
8785             if ( !volume.IsLinked( n, prevSideNode ))
8786               continue;
8787           }
8788           else {
8789             if ( iNode ) // a node before prevSideNode
8790               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8791             else         // a node after prevSideNode
8792               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8793           }
8794           // check if this link was already used
8795           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8796           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8797           if (!isJustChecked &&
8798               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8799           {
8800             // test a link geometrically
8801             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8802             bool linkIsBetter = false;
8803             double dot = 0.0, dist = 0.0;
8804             if ( searchByDir ) { // choose most co-directed link
8805               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8806               linkIsBetter = ( dot > maxDot );
8807             }
8808             else { // choose link with the node closest to bordPos
8809               dist = ( nextXYZ - bordPos ).SquareModulus();
8810               linkIsBetter = ( dist < minDist );
8811             }
8812             if ( linkIsBetter ) {
8813               maxDot = dot;
8814               minDist = dist;
8815               linkID = iLink;
8816               sideNode = n;
8817               sideElem = elem;
8818             }
8819           }
8820         }
8821       } // loop on inverse elements of prevSideNode
8822
8823       if ( !sideNode ) {
8824         MESSAGE(" Cant find path by links of the Side 2 ");
8825         return SEW_BAD_SIDE_NODES;
8826       }
8827       sideNodes.push_back( sideNode );
8828       sideElems.push_back( sideElem );
8829       foundSideLinkIDs.insert ( linkID );
8830       prevSideNode = sideNode;
8831
8832       if ( *nBordIt == theBordLastNode )
8833         searchByDir = false;
8834       else {
8835         // find the next border link to compare with
8836         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8837         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8838         // move to next border node if sideNode is before forward border node (bordPos)
8839         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8840           prevBordNode = *nBordIt;
8841           nBordIt++;
8842           bordPos = nBordXYZ[ *nBordIt ];
8843           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8844           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8845         }
8846       }
8847     }
8848     while ( sideNode != theSideSecondNode );
8849
8850     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8851       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8852       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8853     }
8854   } // end nodes search on the side 2
8855
8856   // ============================
8857   // sew the border to the side 2
8858   // ============================
8859
8860   int nbNodes[]  = { nSide[0].size(), nSide[1].size() };
8861   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8862
8863   TListOfListOfNodes nodeGroupsToMerge;
8864   if ( nbNodes[0] == nbNodes[1] ||
8865        ( theSideIsFreeBorder && !theSideThirdNode)) {
8866
8867     // all nodes are to be merged
8868
8869     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8870          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8871          nIt[0]++, nIt[1]++ )
8872     {
8873       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8874       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8875       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8876     }
8877   }
8878   else {
8879
8880     // insert new nodes into the border and the side to get equal nb of segments
8881
8882     // get normalized parameters of nodes on the borders
8883     //double param[ 2 ][ maxNbNodes ];
8884     double* param[ 2 ];
8885     param[0] = new double [ maxNbNodes ];
8886     param[1] = new double [ maxNbNodes ];
8887     int iNode, iBord;
8888     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8889       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8890       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8891       const SMDS_MeshNode* nPrev = *nIt;
8892       double bordLength = 0;
8893       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8894         const SMDS_MeshNode* nCur = *nIt;
8895         gp_XYZ segment (nCur->X() - nPrev->X(),
8896                         nCur->Y() - nPrev->Y(),
8897                         nCur->Z() - nPrev->Z());
8898         double segmentLen = segment.Modulus();
8899         bordLength += segmentLen;
8900         param[ iBord ][ iNode ] = bordLength;
8901         nPrev = nCur;
8902       }
8903       // normalize within [0,1]
8904       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8905         param[ iBord ][ iNode ] /= bordLength;
8906       }
8907     }
8908
8909     // loop on border segments
8910     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8911     int i[ 2 ] = { 0, 0 };
8912     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8913     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8914
8915     TElemOfNodeListMap insertMap;
8916     TElemOfNodeListMap::iterator insertMapIt;
8917     // insertMap is
8918     // key:   elem to insert nodes into
8919     // value: 2 nodes to insert between + nodes to be inserted
8920     do {
8921       bool next[ 2 ] = { false, false };
8922
8923       // find min adjacent segment length after sewing
8924       double nextParam = 10., prevParam = 0;
8925       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8926         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8927           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8928         if ( i[ iBord ] > 0 )
8929           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8930       }
8931       double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8932       double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8933       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8934
8935       // choose to insert or to merge nodes
8936       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8937       if ( Abs( du ) <= minSegLen * 0.2 ) {
8938         // merge
8939         // ------
8940         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8941         const SMDS_MeshNode* n0 = *nIt[0];
8942         const SMDS_MeshNode* n1 = *nIt[1];
8943         nodeGroupsToMerge.back().push_back( n1 );
8944         nodeGroupsToMerge.back().push_back( n0 );
8945         // position of node of the border changes due to merge
8946         param[ 0 ][ i[0] ] += du;
8947         // move n1 for the sake of elem shape evaluation during insertion.
8948         // n1 will be removed by MergeNodes() anyway
8949         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8950         next[0] = next[1] = true;
8951       }
8952       else {
8953         // insert
8954         // ------
8955         int intoBord = ( du < 0 ) ? 0 : 1;
8956         const SMDS_MeshElement* elem = *eIt[ intoBord ];
8957         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8958         const SMDS_MeshNode*    n2   = *nIt[ intoBord ];
8959         const SMDS_MeshNode*    nIns = *nIt[ 1 - intoBord ];
8960         if ( intoBord == 1 ) {
8961           // move node of the border to be on a link of elem of the side
8962           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8963           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8964           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8965           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8966           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8967         }
8968         insertMapIt = insertMap.find( elem );
8969         bool notFound = ( insertMapIt == insertMap.end() );
8970         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8971         if ( otherLink ) {
8972           // insert into another link of the same element:
8973           // 1. perform insertion into the other link of the elem
8974           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8975           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8976           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8977           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8978           // 2. perform insertion into the link of adjacent faces
8979           while (true) {
8980             const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8981             if ( adjElem )
8982               InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8983             else
8984               break;
8985           }
8986           if (toCreatePolyedrs) {
8987             // perform insertion into the links of adjacent volumes
8988             UpdateVolumes(n12, n22, nodeList);
8989           }
8990           // 3. find an element appeared on n1 and n2 after the insertion
8991           insertMap.erase( elem );
8992           elem = findAdjacentFace( n1, n2, 0 );
8993         }
8994         if ( notFound || otherLink ) {
8995           // add element and nodes of the side into the insertMap
8996           insertMapIt = insertMap.insert
8997             ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8998           (*insertMapIt).second.push_back( n1 );
8999           (*insertMapIt).second.push_back( n2 );
9000         }
9001         // add node to be inserted into elem
9002         (*insertMapIt).second.push_back( nIns );
9003         next[ 1 - intoBord ] = true;
9004       }
9005
9006       // go to the next segment
9007       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
9008         if ( next[ iBord ] ) {
9009           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
9010             eIt[ iBord ]++;
9011           nPrev[ iBord ] = *nIt[ iBord ];
9012           nIt[ iBord ]++; i[ iBord ]++;
9013         }
9014       }
9015     }
9016     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
9017
9018     // perform insertion of nodes into elements
9019
9020     for (insertMapIt = insertMap.begin();
9021          insertMapIt != insertMap.end();
9022          insertMapIt++ )
9023     {
9024       const SMDS_MeshElement* elem = (*insertMapIt).first;
9025       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
9026       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
9027       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
9028
9029       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
9030
9031       if ( !theSideIsFreeBorder ) {
9032         // look for and insert nodes into the faces adjacent to elem
9033         while (true) {
9034           const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
9035           if ( adjElem )
9036             InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
9037           else
9038             break;
9039         }
9040       }
9041       if (toCreatePolyedrs) {
9042         // perform insertion into the links of adjacent volumes
9043         UpdateVolumes(n1, n2, nodeList);
9044       }
9045     }
9046
9047     delete param[0];
9048     delete param[1];
9049   } // end: insert new nodes
9050
9051   MergeNodes ( nodeGroupsToMerge );
9052
9053   return aResult;
9054 }
9055
9056 //=======================================================================
9057 //function : InsertNodesIntoLink
9058 //purpose  : insert theNodesToInsert into theFace between theBetweenNode1
9059 //           and theBetweenNode2 and split theElement
9060 //=======================================================================
9061
9062 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theFace,
9063                                            const SMDS_MeshNode*        theBetweenNode1,
9064                                            const SMDS_MeshNode*        theBetweenNode2,
9065                                            list<const SMDS_MeshNode*>& theNodesToInsert,
9066                                            const bool                  toCreatePoly)
9067 {
9068   if ( theFace->GetType() != SMDSAbs_Face ) return;
9069
9070   // find indices of 2 link nodes and of the rest nodes
9071   int iNode = 0, il1, il2, i3, i4;
9072   il1 = il2 = i3 = i4 = -1;
9073   //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
9074   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
9075
9076   if(theFace->IsQuadratic()) {
9077     const SMDS_VtkFace* F =
9078       dynamic_cast<const SMDS_VtkFace*>(theFace);
9079     if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9080     // use special nodes iterator
9081     SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9082     while( anIter->more() ) {
9083       const SMDS_MeshNode* n = cast2Node(anIter->next());
9084       if ( n == theBetweenNode1 )
9085         il1 = iNode;
9086       else if ( n == theBetweenNode2 )
9087         il2 = iNode;
9088       else if ( i3 < 0 )
9089         i3 = iNode;
9090       else
9091         i4 = iNode;
9092       nodes[ iNode++ ] = n;
9093     }
9094   }
9095   else {
9096     SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9097     while ( nodeIt->more() ) {
9098       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9099       if ( n == theBetweenNode1 )
9100         il1 = iNode;
9101       else if ( n == theBetweenNode2 )
9102         il2 = iNode;
9103       else if ( i3 < 0 )
9104         i3 = iNode;
9105       else
9106         i4 = iNode;
9107       nodes[ iNode++ ] = n;
9108     }
9109   }
9110   if ( il1 < 0 || il2 < 0 || i3 < 0 )
9111     return ;
9112
9113   // arrange link nodes to go one after another regarding the face orientation
9114   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
9115   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
9116   if ( reverse ) {
9117     iNode = il1;
9118     il1 = il2;
9119     il2 = iNode;
9120     aNodesToInsert.reverse();
9121   }
9122   // check that not link nodes of a quadrangles are in good order
9123   int nbFaceNodes = theFace->NbNodes();
9124   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
9125     iNode = i3;
9126     i3 = i4;
9127     i4 = iNode;
9128   }
9129
9130   if (toCreatePoly || theFace->IsPoly()) {
9131
9132     iNode = 0;
9133     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
9134
9135     // add nodes of face up to first node of link
9136     bool isFLN = false;
9137
9138     if(theFace->IsQuadratic()) {
9139       const SMDS_VtkFace* F =
9140         dynamic_cast<const SMDS_VtkFace*>(theFace);
9141       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9142       // use special nodes iterator
9143       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9144       while( anIter->more()  && !isFLN ) {
9145         const SMDS_MeshNode* n = cast2Node(anIter->next());
9146         poly_nodes[iNode++] = n;
9147         if (n == nodes[il1]) {
9148           isFLN = true;
9149         }
9150       }
9151       // add nodes to insert
9152       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9153       for (; nIt != aNodesToInsert.end(); nIt++) {
9154         poly_nodes[iNode++] = *nIt;
9155       }
9156       // add nodes of face starting from last node of link
9157       while ( anIter->more() ) {
9158         poly_nodes[iNode++] = cast2Node(anIter->next());
9159       }
9160     }
9161     else {
9162       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9163       while ( nodeIt->more() && !isFLN ) {
9164         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9165         poly_nodes[iNode++] = n;
9166         if (n == nodes[il1]) {
9167           isFLN = true;
9168         }
9169       }
9170       // add nodes to insert
9171       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9172       for (; nIt != aNodesToInsert.end(); nIt++) {
9173         poly_nodes[iNode++] = *nIt;
9174       }
9175       // add nodes of face starting from last node of link
9176       while ( nodeIt->more() ) {
9177         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9178         poly_nodes[iNode++] = n;
9179       }
9180     }
9181
9182     // edit or replace the face
9183     SMESHDS_Mesh *aMesh = GetMeshDS();
9184
9185     if (theFace->IsPoly()) {
9186       aMesh->ChangePolygonNodes(theFace, poly_nodes);
9187     }
9188     else {
9189       int aShapeId = FindShape( theFace );
9190
9191       SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
9192       myLastCreatedElems.Append(newElem);
9193       if ( aShapeId && newElem )
9194         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9195
9196       aMesh->RemoveElement(theFace);
9197     }
9198     return;
9199   }
9200
9201   SMESHDS_Mesh *aMesh = GetMeshDS();
9202   if( !theFace->IsQuadratic() ) {
9203
9204     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
9205     int nbLinkNodes = 2 + aNodesToInsert.size();
9206     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
9207     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
9208     linkNodes[ 0 ] = nodes[ il1 ];
9209     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
9210     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9211     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9212       linkNodes[ iNode++ ] = *nIt;
9213     }
9214     // decide how to split a quadrangle: compare possible variants
9215     // and choose which of splits to be a quadrangle
9216     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
9217     if ( nbFaceNodes == 3 ) {
9218       iBestQuad = nbSplits;
9219       i4 = i3;
9220     }
9221     else if ( nbFaceNodes == 4 ) {
9222       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
9223       double aBestRate = DBL_MAX;
9224       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
9225         i1 = 0; i2 = 1;
9226         double aBadRate = 0;
9227         // evaluate elements quality
9228         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
9229           if ( iSplit == iQuad ) {
9230             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
9231                                    linkNodes[ i2++ ],
9232                                    nodes[ i3 ],
9233                                    nodes[ i4 ]);
9234             aBadRate += getBadRate( &quad, aCrit );
9235           }
9236           else {
9237             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
9238                                    linkNodes[ i2++ ],
9239                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
9240             aBadRate += getBadRate( &tria, aCrit );
9241           }
9242         }
9243         // choice
9244         if ( aBadRate < aBestRate ) {
9245           iBestQuad = iQuad;
9246           aBestRate = aBadRate;
9247         }
9248       }
9249     }
9250
9251     // create new elements
9252     int aShapeId = FindShape( theFace );
9253
9254     i1 = 0; i2 = 1;
9255     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
9256       SMDS_MeshElement* newElem = 0;
9257       if ( iSplit == iBestQuad )
9258         newElem = aMesh->AddFace (linkNodes[ i1++ ],
9259                                   linkNodes[ i2++ ],
9260                                   nodes[ i3 ],
9261                                   nodes[ i4 ]);
9262       else
9263         newElem = aMesh->AddFace (linkNodes[ i1++ ],
9264                                   linkNodes[ i2++ ],
9265                                   nodes[ iSplit < iBestQuad ? i4 : i3 ]);
9266       myLastCreatedElems.Append(newElem);
9267       if ( aShapeId && newElem )
9268         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9269     }
9270
9271     // change nodes of theFace
9272     const SMDS_MeshNode* newNodes[ 4 ];
9273     newNodes[ 0 ] = linkNodes[ i1 ];
9274     newNodes[ 1 ] = linkNodes[ i2 ];
9275     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9276     newNodes[ 3 ] = nodes[ i4 ];
9277     //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
9278     const SMDS_MeshElement* newElem = 0;
9279     if (iSplit == iBestQuad)
9280       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
9281     else
9282       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
9283     myLastCreatedElems.Append(newElem);
9284     if ( aShapeId && newElem )
9285       aMesh->SetMeshElementOnShape( newElem, aShapeId );
9286 } // end if(!theFace->IsQuadratic())
9287   else { // theFace is quadratic
9288     // we have to split theFace on simple triangles and one simple quadrangle
9289     int tmp = il1/2;
9290     int nbshift = tmp*2;
9291     // shift nodes in nodes[] by nbshift
9292     int i,j;
9293     for(i=0; i<nbshift; i++) {
9294       const SMDS_MeshNode* n = nodes[0];
9295       for(j=0; j<nbFaceNodes-1; j++) {
9296         nodes[j] = nodes[j+1];
9297       }
9298       nodes[nbFaceNodes-1] = n;
9299     }
9300     il1 = il1 - nbshift;
9301     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9302     //   n0      n1     n2    n0      n1     n2
9303     //     +-----+-----+        +-----+-----+
9304     //      \         /         |           |
9305     //       \       /          |           |
9306     //      n5+     +n3       n7+           +n3
9307     //         \   /            |           |
9308     //          \ /             |           |
9309     //           +              +-----+-----+
9310     //           n4           n6      n5     n4
9311
9312     // create new elements
9313     int aShapeId = FindShape( theFace );
9314
9315     int n1,n2,n3;
9316     if(nbFaceNodes==6) { // quadratic triangle
9317       SMDS_MeshElement* newElem =
9318         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9319       myLastCreatedElems.Append(newElem);
9320       if ( aShapeId && newElem )
9321         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9322       if(theFace->IsMediumNode(nodes[il1])) {
9323         // create quadrangle
9324         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
9325         myLastCreatedElems.Append(newElem);
9326         if ( aShapeId && newElem )
9327           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9328         n1 = 1;
9329         n2 = 2;
9330         n3 = 3;
9331       }
9332       else {
9333         // create quadrangle
9334         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
9335         myLastCreatedElems.Append(newElem);
9336         if ( aShapeId && newElem )
9337           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9338         n1 = 0;
9339         n2 = 1;
9340         n3 = 5;
9341       }
9342     }
9343     else { // nbFaceNodes==8 - quadratic quadrangle
9344       SMDS_MeshElement* newElem =
9345         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9346       myLastCreatedElems.Append(newElem);
9347       if ( aShapeId && newElem )
9348         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9349       newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
9350       myLastCreatedElems.Append(newElem);
9351       if ( aShapeId && newElem )
9352         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9353       newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
9354       myLastCreatedElems.Append(newElem);
9355       if ( aShapeId && newElem )
9356         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9357       if(theFace->IsMediumNode(nodes[il1])) {
9358         // create quadrangle
9359         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
9360         myLastCreatedElems.Append(newElem);
9361         if ( aShapeId && newElem )
9362           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9363         n1 = 1;
9364         n2 = 2;
9365         n3 = 3;
9366       }
9367       else {
9368         // create quadrangle
9369         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
9370         myLastCreatedElems.Append(newElem);
9371         if ( aShapeId && newElem )
9372           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9373         n1 = 0;
9374         n2 = 1;
9375         n3 = 7;
9376       }
9377     }
9378     // create needed triangles using n1,n2,n3 and inserted nodes
9379     int nbn = 2 + aNodesToInsert.size();
9380     //const SMDS_MeshNode* aNodes[nbn];
9381     vector<const SMDS_MeshNode*> aNodes(nbn);
9382     aNodes[0] = nodes[n1];
9383     aNodes[nbn-1] = nodes[n2];
9384     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9385     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9386       aNodes[iNode++] = *nIt;
9387     }
9388     for(i=1; i<nbn; i++) {
9389       SMDS_MeshElement* newElem =
9390         aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
9391       myLastCreatedElems.Append(newElem);
9392       if ( aShapeId && newElem )
9393         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9394     }
9395   }
9396   // remove old face
9397   aMesh->RemoveElement(theFace);
9398 }
9399
9400 //=======================================================================
9401 //function : UpdateVolumes
9402 //purpose  :
9403 //=======================================================================
9404 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
9405                                       const SMDS_MeshNode*        theBetweenNode2,
9406                                       list<const SMDS_MeshNode*>& theNodesToInsert)
9407 {
9408   myLastCreatedElems.Clear();
9409   myLastCreatedNodes.Clear();
9410
9411   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9412   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9413     const SMDS_MeshElement* elem = invElemIt->next();
9414
9415     // check, if current volume has link theBetweenNode1 - theBetweenNode2
9416     SMDS_VolumeTool aVolume (elem);
9417     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9418       continue;
9419
9420     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9421     int iface, nbFaces = aVolume.NbFaces();
9422     vector<const SMDS_MeshNode *> poly_nodes;
9423     vector<int> quantities (nbFaces);
9424
9425     for (iface = 0; iface < nbFaces; iface++) {
9426       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9427       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9428       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9429
9430       for (int inode = 0; inode < nbFaceNodes; inode++) {
9431         poly_nodes.push_back(faceNodes[inode]);
9432
9433         if (nbInserted == 0) {
9434           if (faceNodes[inode] == theBetweenNode1) {
9435             if (faceNodes[inode + 1] == theBetweenNode2) {
9436               nbInserted = theNodesToInsert.size();
9437
9438               // add nodes to insert
9439               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9440               for (; nIt != theNodesToInsert.end(); nIt++) {
9441                 poly_nodes.push_back(*nIt);
9442               }
9443             }
9444           }
9445           else if (faceNodes[inode] == theBetweenNode2) {
9446             if (faceNodes[inode + 1] == theBetweenNode1) {
9447               nbInserted = theNodesToInsert.size();
9448
9449               // add nodes to insert in reversed order
9450               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9451               nIt--;
9452               for (; nIt != theNodesToInsert.begin(); nIt--) {
9453                 poly_nodes.push_back(*nIt);
9454               }
9455               poly_nodes.push_back(*nIt);
9456             }
9457           }
9458           else {
9459           }
9460         }
9461       }
9462       quantities[iface] = nbFaceNodes + nbInserted;
9463     }
9464
9465     // Replace or update the volume
9466     SMESHDS_Mesh *aMesh = GetMeshDS();
9467
9468     if (elem->IsPoly()) {
9469       aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
9470
9471     }
9472     else {
9473       int aShapeId = FindShape( elem );
9474
9475       SMDS_MeshElement* newElem =
9476         aMesh->AddPolyhedralVolume(poly_nodes, quantities);
9477       myLastCreatedElems.Append(newElem);
9478       if (aShapeId && newElem)
9479         aMesh->SetMeshElementOnShape(newElem, aShapeId);
9480
9481       aMesh->RemoveElement(elem);
9482     }
9483   }
9484 }
9485
9486 namespace
9487 {
9488   //================================================================================
9489   /*!
9490    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9491    */
9492   //================================================================================
9493
9494   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9495                            vector<const SMDS_MeshNode *> & nodes,
9496                            vector<int> &                   nbNodeInFaces )
9497   {
9498     nodes.clear();
9499     nbNodeInFaces.clear();
9500     SMDS_VolumeTool vTool ( elem );
9501     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9502     {
9503       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9504       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9505       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9506     }
9507   }
9508 }
9509
9510 //=======================================================================
9511 /*!
9512  * \brief Convert elements contained in a submesh to quadratic
9513  * \return int - nb of checked elements
9514  */
9515 //=======================================================================
9516
9517 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9518                                              SMESH_MesherHelper& theHelper,
9519                                              const bool          theForce3d)
9520 {
9521   int nbElem = 0;
9522   if( !theSm ) return nbElem;
9523
9524   vector<int> nbNodeInFaces;
9525   vector<const SMDS_MeshNode *> nodes;
9526   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9527   while(ElemItr->more())
9528   {
9529     nbElem++;
9530     const SMDS_MeshElement* elem = ElemItr->next();
9531     if( !elem || elem->IsQuadratic() ) continue;
9532
9533     // get elem data needed to re-create it
9534     //
9535     const int id                        = elem->GetID();
9536     const int nbNodes                   = elem->NbNodes();
9537     const SMDSAbs_ElementType aType     = elem->GetType();
9538     const SMDSAbs_EntityType  aGeomType = elem->GetEntityType();
9539     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9540     if ( aGeomType == SMDSEntity_Polyhedra )
9541       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9542     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9543       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9544
9545     // remove a linear element
9546     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9547
9548     const SMDS_MeshElement* NewElem = 0;
9549
9550     switch( aType )
9551     {
9552     case SMDSAbs_Edge :
9553       {
9554         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9555         break;
9556       }
9557     case SMDSAbs_Face :
9558       {
9559         switch(nbNodes)
9560         {
9561         case 3:
9562           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9563           break;
9564         case 4:
9565           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9566           break;
9567         default:
9568           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9569           continue;
9570         }
9571         break;
9572       }
9573     case SMDSAbs_Volume :
9574       {
9575         switch( aGeomType )
9576         {
9577         case SMDSEntity_Tetra:
9578           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9579           break;
9580         case SMDSEntity_Pyramid:
9581           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9582           break;
9583         case SMDSEntity_Penta:
9584           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9585           break;
9586         case SMDSEntity_Hexa:
9587           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9588                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9589           break;
9590         case SMDSEntity_Hexagonal_Prism:
9591         default:
9592           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9593         }
9594         break;
9595       }
9596     default :
9597       continue;
9598     }
9599     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9600     if( NewElem )
9601       theSm->AddElement( NewElem );
9602   }
9603   return nbElem;
9604 }
9605
9606 //=======================================================================
9607 //function : ConvertToQuadratic
9608 //purpose  :
9609 //=======================================================================
9610
9611 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d)
9612 {
9613   SMESHDS_Mesh* meshDS = GetMeshDS();
9614
9615   SMESH_MesherHelper aHelper(*myMesh);
9616   aHelper.SetIsQuadratic( true );
9617
9618   int nbCheckedElems = 0;
9619   if ( myMesh->HasShapeToMesh() )
9620   {
9621     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9622     {
9623       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9624       while ( smIt->more() ) {
9625         SMESH_subMesh* sm = smIt->next();
9626         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9627           aHelper.SetSubShape( sm->GetSubShape() );
9628           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9629         }
9630       }
9631     }
9632   }
9633   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9634   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9635   {
9636     SMESHDS_SubMesh *smDS = 0;
9637     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9638     while(aEdgeItr->more())
9639     {
9640       const SMDS_MeshEdge* edge = aEdgeItr->next();
9641       if(edge && !edge->IsQuadratic())
9642       {
9643         int id = edge->GetID();
9644         //MESSAGE("edge->GetID() " << id);
9645         const SMDS_MeshNode* n1 = edge->GetNode(0);
9646         const SMDS_MeshNode* n2 = edge->GetNode(1);
9647
9648         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9649
9650         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9651         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9652       }
9653     }
9654     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9655     while(aFaceItr->more())
9656     {
9657       const SMDS_MeshFace* face = aFaceItr->next();
9658       if(!face || face->IsQuadratic() ) continue;
9659
9660       const int id = face->GetID();
9661       const SMDSAbs_EntityType type = face->GetEntityType();
9662       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9663
9664       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9665
9666       SMDS_MeshFace * NewFace = 0;
9667       switch( type )
9668       {
9669       case SMDSEntity_Triangle:
9670         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9671         break;
9672       case SMDSEntity_Quadrangle:
9673         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9674         break;
9675       default:
9676         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9677       }
9678       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9679     }
9680     vector<int> nbNodeInFaces;
9681     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9682     while(aVolumeItr->more())
9683     {
9684       const SMDS_MeshVolume* volume = aVolumeItr->next();
9685       if(!volume || volume->IsQuadratic() ) continue;
9686
9687       const int id = volume->GetID();
9688       const SMDSAbs_EntityType type = volume->GetEntityType();
9689       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9690       if ( type == SMDSEntity_Polyhedra )
9691         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9692       else if ( type == SMDSEntity_Hexagonal_Prism )
9693         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9694
9695       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9696
9697       SMDS_MeshVolume * NewVolume = 0;
9698       switch ( type )
9699       {
9700       case SMDSEntity_Tetra:
9701         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9702         break;
9703       case SMDSEntity_Hexa:
9704         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9705                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9706         break;
9707       case SMDSEntity_Pyramid:
9708         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9709                                       nodes[3], nodes[4], id, theForce3d);
9710         break;
9711       case SMDSEntity_Penta:
9712         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9713                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9714         break;
9715       case SMDSEntity_Hexagonal_Prism:
9716       default:
9717         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9718       }
9719       ReplaceElemInGroups(volume, NewVolume, meshDS);
9720     }
9721   }
9722
9723   if ( !theForce3d )
9724   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9725     aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9726     aHelper.FixQuadraticElements(myError);
9727   }
9728 }
9729
9730 //================================================================================
9731 /*!
9732  * \brief Makes given elements quadratic
9733  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9734  *  \param theElements - elements to make quadratic
9735  */
9736 //================================================================================
9737
9738 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9739                                           TIDSortedElemSet& theElements)
9740 {
9741   if ( theElements.empty() ) return;
9742
9743   // we believe that all theElements are of the same type
9744   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9745
9746   // get all nodes shared by theElements
9747   TIDSortedNodeSet allNodes;
9748   TIDSortedElemSet::iterator eIt = theElements.begin();
9749   for ( ; eIt != theElements.end(); ++eIt )
9750     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9751
9752   // complete theElements with elements of lower dim whose all nodes are in allNodes
9753
9754   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9755   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9756   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9757   for ( ; nIt != allNodes.end(); ++nIt )
9758   {
9759     const SMDS_MeshNode* n = *nIt;
9760     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9761     while ( invIt->more() )
9762     {
9763       const SMDS_MeshElement* e = invIt->next();
9764       if ( e->IsQuadratic() )
9765       {
9766         quadAdjacentElems[ e->GetType() ].insert( e );
9767         continue;
9768       }
9769       if ( e->GetType() >= elemType )
9770       {
9771         continue; // same type of more complex linear element
9772       }
9773
9774       if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9775         continue; // e is already checked
9776
9777       // check nodes
9778       bool allIn = true;
9779       SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9780       while ( nodeIt->more() && allIn )
9781         allIn = allNodes.count( cast2Node( nodeIt->next() ));
9782       if ( allIn )
9783         theElements.insert(e );
9784     }
9785   }
9786
9787   SMESH_MesherHelper helper(*myMesh);
9788   helper.SetIsQuadratic( true );
9789
9790   // add links of quadratic adjacent elements to the helper
9791
9792   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9793     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9794           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9795     {
9796       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9797     }
9798   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9799     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9800           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9801     {
9802       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9803     }
9804   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9805     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9806           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9807     {
9808       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9809     }
9810
9811   // make quadratic elements instead of linear ones
9812
9813   SMESHDS_Mesh* meshDS = GetMeshDS();
9814   SMESHDS_SubMesh* smDS = 0;
9815   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9816   {
9817     const SMDS_MeshElement* elem = *eIt;
9818     if( elem->IsQuadratic() || elem->NbNodes() < 2 || elem->IsPoly() )
9819       continue;
9820
9821     const int id                   = elem->GetID();
9822     const SMDSAbs_ElementType type = elem->GetType();
9823     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9824
9825     if ( !smDS || !smDS->Contains( elem ))
9826       smDS = meshDS->MeshElements( elem->getshapeId() );
9827     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9828
9829     SMDS_MeshElement * newElem = 0;
9830     switch( nodes.size() )
9831     {
9832     case 4: // cases for most frequently used element types go first (for optimization)
9833       if ( type == SMDSAbs_Volume )
9834         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9835       else
9836         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9837       break;
9838     case 8:
9839       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9840                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9841       break;
9842     case 3:
9843       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9844       break;
9845     case 2:
9846       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9847       break;
9848     case 5:
9849       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9850                                  nodes[4], id, theForce3d);
9851       break;
9852     case 6:
9853       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9854                                  nodes[4], nodes[5], id, theForce3d);
9855       break;
9856     default:;
9857     }
9858     ReplaceElemInGroups( elem, newElem, meshDS);
9859     if( newElem && smDS )
9860       smDS->AddElement( newElem );
9861   }
9862
9863   if ( !theForce3d  && !getenv("NO_FixQuadraticElements"))
9864   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9865     helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9866     helper.FixQuadraticElements( myError );
9867   }
9868 }
9869
9870 //=======================================================================
9871 /*!
9872  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9873  * \return int - nb of checked elements
9874  */
9875 //=======================================================================
9876
9877 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9878                                      SMDS_ElemIteratorPtr theItr,
9879                                      const int            theShapeID)
9880 {
9881   int nbElem = 0;
9882   SMESHDS_Mesh* meshDS = GetMeshDS();
9883
9884   while( theItr->more() )
9885   {
9886     const SMDS_MeshElement* elem = theItr->next();
9887     nbElem++;
9888     if( elem && elem->IsQuadratic())
9889     {
9890       int id                    = elem->GetID();
9891       int nbCornerNodes         = elem->NbCornerNodes();
9892       SMDSAbs_ElementType aType = elem->GetType();
9893
9894       vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9895
9896       //remove a quadratic element
9897       if ( !theSm || !theSm->Contains( elem ))
9898         theSm = meshDS->MeshElements( elem->getshapeId() );
9899       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9900
9901       // remove medium nodes
9902       for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9903         if ( nodes[i]->NbInverseElements() == 0 )
9904           meshDS->RemoveFreeNode( nodes[i], theSm );
9905
9906       // add a linear element
9907       nodes.resize( nbCornerNodes );
9908       SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9909       ReplaceElemInGroups(elem, newElem, meshDS);
9910       if( theSm && newElem )
9911         theSm->AddElement( newElem );
9912     }
9913   }
9914   return nbElem;
9915 }
9916
9917 //=======================================================================
9918 //function : ConvertFromQuadratic
9919 //purpose  :
9920 //=======================================================================
9921
9922 bool SMESH_MeshEditor::ConvertFromQuadratic()
9923 {
9924   int nbCheckedElems = 0;
9925   if ( myMesh->HasShapeToMesh() )
9926   {
9927     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9928     {
9929       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9930       while ( smIt->more() ) {
9931         SMESH_subMesh* sm = smIt->next();
9932         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9933           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9934       }
9935     }
9936   }
9937
9938   int totalNbElems =
9939     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9940   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9941   {
9942     SMESHDS_SubMesh *aSM = 0;
9943     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9944   }
9945
9946   return true;
9947 }
9948
9949 namespace
9950 {
9951   //================================================================================
9952   /*!
9953    * \brief Return true if all medium nodes of the element are in the node set
9954    */
9955   //================================================================================
9956
9957   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9958   {
9959     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9960       if ( !nodeSet.count( elem->GetNode(i) ))
9961         return false;
9962     return true;
9963   }
9964 }
9965
9966 //================================================================================
9967 /*!
9968  * \brief Makes given elements linear
9969  */
9970 //================================================================================
9971
9972 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9973 {
9974   if ( theElements.empty() ) return;
9975
9976   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9977   set<int> mediumNodeIDs;
9978   TIDSortedElemSet::iterator eIt = theElements.begin();
9979   for ( ; eIt != theElements.end(); ++eIt )
9980   {
9981     const SMDS_MeshElement* e = *eIt;
9982     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9983       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9984   }
9985
9986   // replace given elements by linear ones
9987   typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
9988   SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
9989   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9990
9991   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9992   // except those elements sharing medium nodes of quadratic element whose medium nodes
9993   // are not all in mediumNodeIDs
9994
9995   // get remaining medium nodes
9996   TIDSortedNodeSet mediumNodes;
9997   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9998   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9999     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
10000       mediumNodes.insert( mediumNodes.end(), n );
10001
10002   // find more quadratic elements to convert
10003   TIDSortedElemSet moreElemsToConvert;
10004   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
10005   for ( ; nIt != mediumNodes.end(); ++nIt )
10006   {
10007     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
10008     while ( invIt->more() )
10009     {
10010       const SMDS_MeshElement* e = invIt->next();
10011       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
10012       {
10013         // find a more complex element including e and
10014         // whose medium nodes are not in mediumNodes
10015         bool complexFound = false;
10016         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
10017         {
10018           SMDS_ElemIteratorPtr invIt2 =
10019             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
10020           while ( invIt2->more() )
10021           {
10022             const SMDS_MeshElement* eComplex = invIt2->next();
10023             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
10024             {
10025               int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
10026               if ( nbCommonNodes == e->NbNodes())
10027               {
10028                 complexFound = true;
10029                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
10030                 break;
10031               }
10032             }
10033           }
10034         }
10035         if ( !complexFound )
10036           moreElemsToConvert.insert( e );
10037       }
10038     }
10039   }
10040   elemIt = SMDS_ElemIteratorPtr
10041     (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
10042   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10043 }
10044
10045 //=======================================================================
10046 //function : SewSideElements
10047 //purpose  :
10048 //=======================================================================
10049
10050 SMESH_MeshEditor::Sew_Error
10051 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
10052                                    TIDSortedElemSet&    theSide2,
10053                                    const SMDS_MeshNode* theFirstNode1,
10054                                    const SMDS_MeshNode* theFirstNode2,
10055                                    const SMDS_MeshNode* theSecondNode1,
10056                                    const SMDS_MeshNode* theSecondNode2)
10057 {
10058   myLastCreatedElems.Clear();
10059   myLastCreatedNodes.Clear();
10060
10061   MESSAGE ("::::SewSideElements()");
10062   if ( theSide1.size() != theSide2.size() )
10063     return SEW_DIFF_NB_OF_ELEMENTS;
10064
10065   Sew_Error aResult = SEW_OK;
10066   // Algo:
10067   // 1. Build set of faces representing each side
10068   // 2. Find which nodes of the side 1 to merge with ones on the side 2
10069   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10070
10071   // =======================================================================
10072   // 1. Build set of faces representing each side:
10073   // =======================================================================
10074   // a. build set of nodes belonging to faces
10075   // b. complete set of faces: find missing faces whose nodes are in set of nodes
10076   // c. create temporary faces representing side of volumes if correspondent
10077   //    face does not exist
10078
10079   SMESHDS_Mesh* aMesh = GetMeshDS();
10080   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
10081   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
10082   TIDSortedElemSet             faceSet1, faceSet2;
10083   set<const SMDS_MeshElement*> volSet1,  volSet2;
10084   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
10085   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
10086   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
10087   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
10088   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
10089   int iSide, iFace, iNode;
10090
10091   list<const SMDS_MeshElement* > tempFaceList;
10092   for ( iSide = 0; iSide < 2; iSide++ ) {
10093     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
10094     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
10095     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
10096     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
10097     set<const SMDS_MeshElement*>::iterator vIt;
10098     TIDSortedElemSet::iterator eIt;
10099     set<const SMDS_MeshNode*>::iterator    nIt;
10100
10101     // check that given nodes belong to given elements
10102     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
10103     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
10104     int firstIndex = -1, secondIndex = -1;
10105     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10106       const SMDS_MeshElement* elem = *eIt;
10107       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
10108       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
10109       if ( firstIndex > -1 && secondIndex > -1 ) break;
10110     }
10111     if ( firstIndex < 0 || secondIndex < 0 ) {
10112       // we can simply return until temporary faces created
10113       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
10114     }
10115
10116     // -----------------------------------------------------------
10117     // 1a. Collect nodes of existing faces
10118     //     and build set of face nodes in order to detect missing
10119     //     faces corresponding to sides of volumes
10120     // -----------------------------------------------------------
10121
10122     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
10123
10124     // loop on the given element of a side
10125     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10126       //const SMDS_MeshElement* elem = *eIt;
10127       const SMDS_MeshElement* elem = *eIt;
10128       if ( elem->GetType() == SMDSAbs_Face ) {
10129         faceSet->insert( elem );
10130         set <const SMDS_MeshNode*> faceNodeSet;
10131         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10132         while ( nodeIt->more() ) {
10133           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10134           nodeSet->insert( n );
10135           faceNodeSet.insert( n );
10136         }
10137         setOfFaceNodeSet.insert( faceNodeSet );
10138       }
10139       else if ( elem->GetType() == SMDSAbs_Volume )
10140         volSet->insert( elem );
10141     }
10142     // ------------------------------------------------------------------------------
10143     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10144     // ------------------------------------------------------------------------------
10145
10146     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10147       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10148       while ( fIt->more() ) { // loop on faces sharing a node
10149         const SMDS_MeshElement* f = fIt->next();
10150         if ( faceSet->find( f ) == faceSet->end() ) {
10151           // check if all nodes are in nodeSet and
10152           // complete setOfFaceNodeSet if they are
10153           set <const SMDS_MeshNode*> faceNodeSet;
10154           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10155           bool allInSet = true;
10156           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10157             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10158             if ( nodeSet->find( n ) == nodeSet->end() )
10159               allInSet = false;
10160             else
10161               faceNodeSet.insert( n );
10162           }
10163           if ( allInSet ) {
10164             faceSet->insert( f );
10165             setOfFaceNodeSet.insert( faceNodeSet );
10166           }
10167         }
10168       }
10169     }
10170
10171     // -------------------------------------------------------------------------
10172     // 1c. Create temporary faces representing sides of volumes if correspondent
10173     //     face does not exist
10174     // -------------------------------------------------------------------------
10175
10176     if ( !volSet->empty() ) {
10177       //int nodeSetSize = nodeSet->size();
10178
10179       // loop on given volumes
10180       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10181         SMDS_VolumeTool vol (*vIt);
10182         // loop on volume faces: find free faces
10183         // --------------------------------------
10184         list<const SMDS_MeshElement* > freeFaceList;
10185         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10186           if ( !vol.IsFreeFace( iFace ))
10187             continue;
10188           // check if there is already a face with same nodes in a face set
10189           const SMDS_MeshElement* aFreeFace = 0;
10190           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10191           int nbNodes = vol.NbFaceNodes( iFace );
10192           set <const SMDS_MeshNode*> faceNodeSet;
10193           vol.GetFaceNodes( iFace, faceNodeSet );
10194           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10195           if ( isNewFace ) {
10196             // no such a face is given but it still can exist, check it
10197             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10198             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10199           }
10200           if ( !aFreeFace ) {
10201             // create a temporary face
10202             if ( nbNodes == 3 ) {
10203               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10204               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10205             }
10206             else if ( nbNodes == 4 ) {
10207               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10208               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10209             }
10210             else {
10211               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10212               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10213               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10214             }
10215             if ( aFreeFace )
10216               tempFaceList.push_back( aFreeFace );
10217           }
10218
10219           if ( aFreeFace )
10220             freeFaceList.push_back( aFreeFace );
10221
10222         } // loop on faces of a volume
10223
10224         // choose one of several free faces of a volume
10225         // --------------------------------------------
10226         if ( freeFaceList.size() > 1 ) {
10227           // choose a face having max nb of nodes shared by other elems of a side
10228           int maxNbNodes = -1;
10229           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10230           while ( fIt != freeFaceList.end() ) { // loop on free faces
10231             int nbSharedNodes = 0;
10232             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10233             while ( nodeIt->more() ) { // loop on free face nodes
10234               const SMDS_MeshNode* n =
10235                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10236               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10237               while ( invElemIt->more() ) {
10238                 const SMDS_MeshElement* e = invElemIt->next();
10239                 nbSharedNodes += faceSet->count( e );
10240                 nbSharedNodes += elemSet->count( e );
10241               }
10242             }
10243             if ( nbSharedNodes > maxNbNodes ) {
10244               maxNbNodes = nbSharedNodes;
10245               freeFaceList.erase( freeFaceList.begin(), fIt++ );
10246             }
10247             else if ( nbSharedNodes == maxNbNodes ) {
10248               fIt++;
10249             }
10250             else {
10251               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10252             }
10253           }
10254           if ( freeFaceList.size() > 1 )
10255           {
10256             // could not choose one face, use another way
10257             // choose a face most close to the bary center of the opposite side
10258             gp_XYZ aBC( 0., 0., 0. );
10259             set <const SMDS_MeshNode*> addedNodes;
10260             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10261             eIt = elemSet2->begin();
10262             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10263               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10264               while ( nodeIt->more() ) { // loop on free face nodes
10265                 const SMDS_MeshNode* n =
10266                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10267                 if ( addedNodes.insert( n ).second )
10268                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10269               }
10270             }
10271             aBC /= addedNodes.size();
10272             double minDist = DBL_MAX;
10273             fIt = freeFaceList.begin();
10274             while ( fIt != freeFaceList.end() ) { // loop on free faces
10275               double dist = 0;
10276               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10277               while ( nodeIt->more() ) { // loop on free face nodes
10278                 const SMDS_MeshNode* n =
10279                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10280                 gp_XYZ p( n->X(),n->Y(),n->Z() );
10281                 dist += ( aBC - p ).SquareModulus();
10282               }
10283               if ( dist < minDist ) {
10284                 minDist = dist;
10285                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10286               }
10287               else
10288                 fIt = freeFaceList.erase( fIt++ );
10289             }
10290           }
10291         } // choose one of several free faces of a volume
10292
10293         if ( freeFaceList.size() == 1 ) {
10294           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10295           faceSet->insert( aFreeFace );
10296           // complete a node set with nodes of a found free face
10297           //           for ( iNode = 0; iNode < ; iNode++ )
10298           //             nodeSet->insert( fNodes[ iNode ] );
10299         }
10300
10301       } // loop on volumes of a side
10302
10303       //       // complete a set of faces if new nodes in a nodeSet appeared
10304       //       // ----------------------------------------------------------
10305       //       if ( nodeSetSize != nodeSet->size() ) {
10306       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10307       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10308       //           while ( fIt->more() ) { // loop on faces sharing a node
10309       //             const SMDS_MeshElement* f = fIt->next();
10310       //             if ( faceSet->find( f ) == faceSet->end() ) {
10311       //               // check if all nodes are in nodeSet and
10312       //               // complete setOfFaceNodeSet if they are
10313       //               set <const SMDS_MeshNode*> faceNodeSet;
10314       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10315       //               bool allInSet = true;
10316       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10317       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10318       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10319       //                   allInSet = false;
10320       //                 else
10321       //                   faceNodeSet.insert( n );
10322       //               }
10323       //               if ( allInSet ) {
10324       //                 faceSet->insert( f );
10325       //                 setOfFaceNodeSet.insert( faceNodeSet );
10326       //               }
10327       //             }
10328       //           }
10329       //         }
10330       //       }
10331     } // Create temporary faces, if there are volumes given
10332   } // loop on sides
10333
10334   if ( faceSet1.size() != faceSet2.size() ) {
10335     // delete temporary faces: they are in reverseElements of actual nodes
10336 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10337 //    while ( tmpFaceIt->more() )
10338 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10339 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10340 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10341 //      aMesh->RemoveElement(*tmpFaceIt);
10342     MESSAGE("Diff nb of faces");
10343     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10344   }
10345
10346   // ============================================================
10347   // 2. Find nodes to merge:
10348   //              bind a node to remove to a node to put instead
10349   // ============================================================
10350
10351   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10352   if ( theFirstNode1 != theFirstNode2 )
10353     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10354   if ( theSecondNode1 != theSecondNode2 )
10355     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10356
10357   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10358   set< long > linkIdSet; // links to process
10359   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10360
10361   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10362   list< NLink > linkList[2];
10363   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10364   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10365   // loop on links in linkList; find faces by links and append links
10366   // of the found faces to linkList
10367   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10368   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10369   {
10370     NLink link[] = { *linkIt[0], *linkIt[1] };
10371     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10372     if ( !linkIdSet.count( linkID ) )
10373       continue;
10374
10375     // by links, find faces in the face sets,
10376     // and find indices of link nodes in the found faces;
10377     // in a face set, there is only one or no face sharing a link
10378     // ---------------------------------------------------------------
10379
10380     const SMDS_MeshElement* face[] = { 0, 0 };
10381     vector<const SMDS_MeshNode*> fnodes[2];
10382     int iLinkNode[2][2];
10383     TIDSortedElemSet avoidSet;
10384     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10385       const SMDS_MeshNode* n1 = link[iSide].first;
10386       const SMDS_MeshNode* n2 = link[iSide].second;
10387       //cout << "Side " << iSide << " ";
10388       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10389       // find a face by two link nodes
10390       face[ iSide ] = FindFaceInSet( n1, n2, *faceSetPtr[ iSide ], avoidSet,
10391                                      &iLinkNode[iSide][0], &iLinkNode[iSide][1] );
10392       if ( face[ iSide ])
10393       {
10394         //cout << " F " << face[ iSide]->GetID() <<endl;
10395         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10396         // put face nodes to fnodes
10397         if ( face[ iSide ]->IsQuadratic() )
10398         {
10399           // use interlaced nodes iterator
10400           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10401           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10402           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10403           while ( nIter->more() )
10404             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10405         }
10406         else
10407         {
10408           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10409                                   face[ iSide ]->end_nodes() );
10410         }
10411         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10412       }
10413     }
10414
10415     // check similarity of elements of the sides
10416     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10417       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10418       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10419         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10420       }
10421       else {
10422         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10423       }
10424       break; // do not return because it's necessary to remove tmp faces
10425     }
10426
10427     // set nodes to merge
10428     // -------------------
10429
10430     if ( face[0] && face[1] )  {
10431       const int nbNodes = face[0]->NbNodes();
10432       if ( nbNodes != face[1]->NbNodes() ) {
10433         MESSAGE("Diff nb of face nodes");
10434         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10435         break; // do not return because it s necessary to remove tmp faces
10436       }
10437       bool reverse[] = { false, false }; // order of nodes in the link
10438       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10439         // analyse link orientation in faces
10440         int i1 = iLinkNode[ iSide ][ 0 ];
10441         int i2 = iLinkNode[ iSide ][ 1 ];
10442         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10443       }
10444       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10445       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10446       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10447       {
10448         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10449                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10450       }
10451
10452       // add other links of the faces to linkList
10453       // -----------------------------------------
10454
10455       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10456         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10457         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10458         if ( !iter_isnew.second ) { // already in a set: no need to process
10459           linkIdSet.erase( iter_isnew.first );
10460         }
10461         else // new in set == encountered for the first time: add
10462         {
10463           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10464           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10465           linkList[0].push_back ( NLink( n1, n2 ));
10466           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10467         }
10468       }
10469     } // 2 faces found
10470
10471     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10472       break;
10473
10474   } // loop on link lists
10475
10476   if ( aResult == SEW_OK &&
10477        ( //linkIt[0] != linkList[0].end() ||
10478          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10479     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10480              " " << (faceSetPtr[1]->empty()));
10481     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10482   }
10483
10484   // ====================================================================
10485   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10486   // ====================================================================
10487
10488   // delete temporary faces
10489 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10490 //  while ( tmpFaceIt->more() )
10491 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10492   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10493   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10494     aMesh->RemoveElement(*tmpFaceIt);
10495
10496   if ( aResult != SEW_OK)
10497     return aResult;
10498
10499   list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10500   // loop on nodes replacement map
10501   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10502   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10503     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10504       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10505       nodeIDsToRemove.push_back( nToRemove->GetID() );
10506       // loop on elements sharing nToRemove
10507       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10508       while ( invElemIt->more() ) {
10509         const SMDS_MeshElement* e = invElemIt->next();
10510         // get a new suite of nodes: make replacement
10511         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10512         vector< const SMDS_MeshNode*> nodes( nbNodes );
10513         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10514         while ( nIt->more() ) {
10515           const SMDS_MeshNode* n =
10516             static_cast<const SMDS_MeshNode*>( nIt->next() );
10517           nnIt = nReplaceMap.find( n );
10518           if ( nnIt != nReplaceMap.end() ) {
10519             nbReplaced++;
10520             n = (*nnIt).second;
10521           }
10522           nodes[ i++ ] = n;
10523         }
10524         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10525         //         elemIDsToRemove.push_back( e->GetID() );
10526         //       else
10527         if ( nbReplaced )
10528           {
10529             SMDSAbs_ElementType etyp = e->GetType();
10530             SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10531             if (newElem)
10532               {
10533                 myLastCreatedElems.Append(newElem);
10534                 AddToSameGroups(newElem, e, aMesh);
10535                 int aShapeId = e->getshapeId();
10536                 if ( aShapeId )
10537                   {
10538                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
10539                   }
10540               }
10541             aMesh->RemoveElement(e);
10542           }
10543       }
10544     }
10545
10546   Remove( nodeIDsToRemove, true );
10547
10548   return aResult;
10549 }
10550
10551 //================================================================================
10552 /*!
10553  * \brief Find corresponding nodes in two sets of faces
10554  * \param theSide1 - first face set
10555  * \param theSide2 - second first face
10556  * \param theFirstNode1 - a boundary node of set 1
10557  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10558  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10559  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10560  * \param nReplaceMap - output map of corresponding nodes
10561  * \return bool  - is a success or not
10562  */
10563 //================================================================================
10564
10565 #ifdef _DEBUG_
10566 //#define DEBUG_MATCHING_NODES
10567 #endif
10568
10569 SMESH_MeshEditor::Sew_Error
10570 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10571                                     set<const SMDS_MeshElement*>& theSide2,
10572                                     const SMDS_MeshNode*          theFirstNode1,
10573                                     const SMDS_MeshNode*          theFirstNode2,
10574                                     const SMDS_MeshNode*          theSecondNode1,
10575                                     const SMDS_MeshNode*          theSecondNode2,
10576                                     TNodeNodeMap &                nReplaceMap)
10577 {
10578   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10579
10580   nReplaceMap.clear();
10581   if ( theFirstNode1 != theFirstNode2 )
10582     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10583   if ( theSecondNode1 != theSecondNode2 )
10584     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10585
10586   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10587   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10588
10589   list< NLink > linkList[2];
10590   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10591   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10592
10593   // loop on links in linkList; find faces by links and append links
10594   // of the found faces to linkList
10595   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10596   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10597     NLink link[] = { *linkIt[0], *linkIt[1] };
10598     if ( linkSet.find( link[0] ) == linkSet.end() )
10599       continue;
10600
10601     // by links, find faces in the face sets,
10602     // and find indices of link nodes in the found faces;
10603     // in a face set, there is only one or no face sharing a link
10604     // ---------------------------------------------------------------
10605
10606     const SMDS_MeshElement* face[] = { 0, 0 };
10607     list<const SMDS_MeshNode*> notLinkNodes[2];
10608     //bool reverse[] = { false, false }; // order of notLinkNodes
10609     int nbNodes[2];
10610     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10611     {
10612       const SMDS_MeshNode* n1 = link[iSide].first;
10613       const SMDS_MeshNode* n2 = link[iSide].second;
10614       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10615       set< const SMDS_MeshElement* > facesOfNode1;
10616       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10617       {
10618         // during a loop of the first node, we find all faces around n1,
10619         // during a loop of the second node, we find one face sharing both n1 and n2
10620         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10621         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10622         while ( fIt->more() ) { // loop on faces sharing a node
10623           const SMDS_MeshElement* f = fIt->next();
10624           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10625               ! facesOfNode1.insert( f ).second ) // f encounters twice
10626           {
10627             if ( face[ iSide ] ) {
10628               MESSAGE( "2 faces per link " );
10629               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10630             }
10631             face[ iSide ] = f;
10632             faceSet->erase( f );
10633
10634             // get not link nodes
10635             int nbN = f->NbNodes();
10636             if ( f->IsQuadratic() )
10637               nbN /= 2;
10638             nbNodes[ iSide ] = nbN;
10639             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10640             int i1 = f->GetNodeIndex( n1 );
10641             int i2 = f->GetNodeIndex( n2 );
10642             int iEnd = nbN, iBeg = -1, iDelta = 1;
10643             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10644             if ( reverse ) {
10645               std::swap( iEnd, iBeg ); iDelta = -1;
10646             }
10647             int i = i2;
10648             while ( true ) {
10649               i += iDelta;
10650               if ( i == iEnd ) i = iBeg + iDelta;
10651               if ( i == i1 ) break;
10652               nodes.push_back ( f->GetNode( i ) );
10653             }
10654           }
10655         }
10656       }
10657     }
10658     // check similarity of elements of the sides
10659     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10660       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10661       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10662         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10663       }
10664       else {
10665         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10666       }
10667     }
10668
10669     // set nodes to merge
10670     // -------------------
10671
10672     if ( face[0] && face[1] )  {
10673       if ( nbNodes[0] != nbNodes[1] ) {
10674         MESSAGE("Diff nb of face nodes");
10675         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10676       }
10677 #ifdef DEBUG_MATCHING_NODES
10678       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10679                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10680                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10681 #endif
10682       int nbN = nbNodes[0];
10683       {
10684         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10685         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10686         for ( int i = 0 ; i < nbN - 2; ++i ) {
10687 #ifdef DEBUG_MATCHING_NODES
10688           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10689 #endif
10690           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10691         }
10692       }
10693
10694       // add other links of the face 1 to linkList
10695       // -----------------------------------------
10696
10697       const SMDS_MeshElement* f0 = face[0];
10698       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10699       for ( int i = 0; i < nbN; i++ )
10700       {
10701         const SMDS_MeshNode* n2 = f0->GetNode( i );
10702         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10703           linkSet.insert( SMESH_TLink( n1, n2 ));
10704         if ( !iter_isnew.second ) { // already in a set: no need to process
10705           linkSet.erase( iter_isnew.first );
10706         }
10707         else // new in set == encountered for the first time: add
10708         {
10709 #ifdef DEBUG_MATCHING_NODES
10710           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10711                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10712 #endif
10713           linkList[0].push_back ( NLink( n1, n2 ));
10714           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10715         }
10716         n1 = n2;
10717       }
10718     } // 2 faces found
10719   } // loop on link lists
10720
10721   return SEW_OK;
10722 }
10723
10724 //================================================================================
10725 /*!
10726   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10727   \param theElems - the list of elements (edges or faces) to be replicated
10728   The nodes for duplication could be found from these elements
10729   \param theNodesNot - list of nodes to NOT replicate
10730   \param theAffectedElems - the list of elements (cells and edges) to which the
10731   replicated nodes should be associated to.
10732   \return TRUE if operation has been completed successfully, FALSE otherwise
10733 */
10734 //================================================================================
10735
10736 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10737                                     const TIDSortedElemSet& theNodesNot,
10738                                     const TIDSortedElemSet& theAffectedElems )
10739 {
10740   myLastCreatedElems.Clear();
10741   myLastCreatedNodes.Clear();
10742
10743   if ( theElems.size() == 0 )
10744     return false;
10745
10746   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10747   if ( !aMeshDS )
10748     return false;
10749
10750   bool res = false;
10751   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10752   // duplicate elements and nodes
10753   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10754   // replce nodes by duplications
10755   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10756   return res;
10757 }
10758
10759 //================================================================================
10760 /*!
10761   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10762   \param theMeshDS - mesh instance
10763   \param theElems - the elements replicated or modified (nodes should be changed)
10764   \param theNodesNot - nodes to NOT replicate
10765   \param theNodeNodeMap - relation of old node to new created node
10766   \param theIsDoubleElem - flag os to replicate element or modify
10767   \return TRUE if operation has been completed successfully, FALSE otherwise
10768 */
10769 //================================================================================
10770
10771 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh*     theMeshDS,
10772                                     const TIDSortedElemSet& theElems,
10773                                     const TIDSortedElemSet& theNodesNot,
10774                                     std::map< const SMDS_MeshNode*,
10775                                     const SMDS_MeshNode* >& theNodeNodeMap,
10776                                     const bool theIsDoubleElem )
10777 {
10778   MESSAGE("doubleNodes");
10779   // iterate on through element and duplicate them (by nodes duplication)
10780   bool res = false;
10781   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10782   for ( ;  elemItr != theElems.end(); ++elemItr )
10783   {
10784     const SMDS_MeshElement* anElem = *elemItr;
10785     if (!anElem)
10786       continue;
10787
10788     bool isDuplicate = false;
10789     // duplicate nodes to duplicate element
10790     std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10791     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10792     int ind = 0;
10793     while ( anIter->more() )
10794     {
10795
10796       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10797       SMDS_MeshNode* aNewNode = aCurrNode;
10798       if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10799         aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10800       else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10801       {
10802         // duplicate node
10803         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10804         theNodeNodeMap[ aCurrNode ] = aNewNode;
10805         myLastCreatedNodes.Append( aNewNode );
10806       }
10807       isDuplicate |= (aCurrNode != aNewNode);
10808       newNodes[ ind++ ] = aNewNode;
10809     }
10810     if ( !isDuplicate )
10811       continue;
10812
10813     if ( theIsDoubleElem )
10814       AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10815     else
10816       {
10817       MESSAGE("ChangeElementNodes");
10818       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10819       }
10820     res = true;
10821   }
10822   return res;
10823 }
10824
10825 //================================================================================
10826 /*!
10827   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10828   \param theNodes - identifiers of nodes to be doubled
10829   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10830          nodes. If list of element identifiers is empty then nodes are doubled but
10831          they not assigned to elements
10832   \return TRUE if operation has been completed successfully, FALSE otherwise
10833 */
10834 //================================================================================
10835
10836 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10837                                     const std::list< int >& theListOfModifiedElems )
10838 {
10839   MESSAGE("DoubleNodes");
10840   myLastCreatedElems.Clear();
10841   myLastCreatedNodes.Clear();
10842
10843   if ( theListOfNodes.size() == 0 )
10844     return false;
10845
10846   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10847   if ( !aMeshDS )
10848     return false;
10849
10850   // iterate through nodes and duplicate them
10851
10852   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10853
10854   std::list< int >::const_iterator aNodeIter;
10855   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10856   {
10857     int aCurr = *aNodeIter;
10858     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10859     if ( !aNode )
10860       continue;
10861
10862     // duplicate node
10863
10864     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10865     if ( aNewNode )
10866     {
10867       anOldNodeToNewNode[ aNode ] = aNewNode;
10868       myLastCreatedNodes.Append( aNewNode );
10869     }
10870   }
10871
10872   // Create map of new nodes for modified elements
10873
10874   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10875
10876   std::list< int >::const_iterator anElemIter;
10877   for ( anElemIter = theListOfModifiedElems.begin();
10878         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10879   {
10880     int aCurr = *anElemIter;
10881     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10882     if ( !anElem )
10883       continue;
10884
10885     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10886
10887     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10888     int ind = 0;
10889     while ( anIter->more() )
10890     {
10891       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10892       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10893       {
10894         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10895         aNodeArr[ ind++ ] = aNewNode;
10896       }
10897       else
10898         aNodeArr[ ind++ ] = aCurrNode;
10899     }
10900     anElemToNodes[ anElem ] = aNodeArr;
10901   }
10902
10903   // Change nodes of elements
10904
10905   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10906     anElemToNodesIter = anElemToNodes.begin();
10907   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10908   {
10909     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10910     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10911     if ( anElem )
10912       {
10913       MESSAGE("ChangeElementNodes");
10914       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10915       }
10916   }
10917
10918   return true;
10919 }
10920
10921 namespace {
10922
10923   //================================================================================
10924   /*!
10925   \brief Check if element located inside shape
10926   \return TRUE if IN or ON shape, FALSE otherwise
10927   */
10928   //================================================================================
10929
10930   template<class Classifier>
10931   bool isInside(const SMDS_MeshElement* theElem,
10932                 Classifier&             theClassifier,
10933                 const double            theTol)
10934   {
10935     gp_XYZ centerXYZ (0, 0, 0);
10936     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10937     while (aNodeItr->more())
10938       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10939
10940     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10941     theClassifier.Perform(aPnt, theTol);
10942     TopAbs_State aState = theClassifier.State();
10943     return (aState == TopAbs_IN || aState == TopAbs_ON );
10944   }
10945
10946   //================================================================================
10947   /*!
10948    * \brief Classifier of the 3D point on the TopoDS_Face
10949    *        with interaface suitable for isInside()
10950    */
10951   //================================================================================
10952
10953   struct _FaceClassifier
10954   {
10955     Extrema_ExtPS       _extremum;
10956     BRepAdaptor_Surface _surface;
10957     TopAbs_State        _state;
10958
10959     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10960     {
10961       _extremum.Initialize( _surface,
10962                             _surface.FirstUParameter(), _surface.LastUParameter(),
10963                             _surface.FirstVParameter(), _surface.LastVParameter(),
10964                             _surface.Tolerance(), _surface.Tolerance() );
10965     }
10966     void Perform(const gp_Pnt& aPnt, double theTol)
10967     {
10968       _state = TopAbs_OUT;
10969       _extremum.Perform(aPnt);
10970       if ( _extremum.IsDone() )
10971         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10972 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10973           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10974 #else
10975           _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10976 #endif
10977     }
10978     TopAbs_State State() const
10979     {
10980       return _state;
10981     }
10982   };
10983 }
10984
10985 //================================================================================
10986 /*!
10987   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed.
10988   This method is the first step of DoubleNodeElemGroupsInRegion.
10989   \param theElems - list of groups of elements (edges or faces) to be replicated
10990   \param theNodesNot - list of groups of nodes not to replicated
10991   \param theShape - shape to detect affected elements (element which geometric center
10992          located on or inside shape).
10993          The replicated nodes should be associated to affected elements.
10994   \return groups of affected elements
10995   \sa DoubleNodeElemGroupsInRegion()
10996  */
10997 //================================================================================
10998
10999 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11000                                                    const TIDSortedElemSet& theNodesNot,
11001                                                    const TopoDS_Shape&     theShape,
11002                                                    TIDSortedElemSet&       theAffectedElems)
11003 {
11004   if ( theShape.IsNull() )
11005     return false;
11006
11007   const double aTol = Precision::Confusion();
11008   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11009   auto_ptr<_FaceClassifier>              aFaceClassifier;
11010   if ( theShape.ShapeType() == TopAbs_SOLID )
11011   {
11012     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11013     bsc3d->PerformInfinitePoint(aTol);
11014   }
11015   else if (theShape.ShapeType() == TopAbs_FACE )
11016   {
11017     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11018   }
11019
11020   // iterates on indicated elements and get elements by back references from their nodes
11021   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11022   for ( ;  elemItr != theElems.end(); ++elemItr )
11023   {
11024     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11025     if (!anElem)
11026       continue;
11027
11028     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11029     while ( nodeItr->more() )
11030     {
11031       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11032       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11033         continue;
11034       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11035       while ( backElemItr->more() )
11036       {
11037         const SMDS_MeshElement* curElem = backElemItr->next();
11038         if ( curElem && theElems.find(curElem) == theElems.end() &&
11039              ( bsc3d.get() ?
11040                isInside( curElem, *bsc3d, aTol ) :
11041                isInside( curElem, *aFaceClassifier, aTol )))
11042           theAffectedElems.insert( curElem );
11043       }
11044     }
11045   }
11046   return true;
11047 }
11048
11049 //================================================================================
11050 /*!
11051   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11052   \param theElems - group of of elements (edges or faces) to be replicated
11053   \param theNodesNot - group of nodes not to replicate
11054   \param theShape - shape to detect affected elements (element which geometric center
11055   located on or inside shape).
11056   The replicated nodes should be associated to affected elements.
11057   \return TRUE if operation has been completed successfully, FALSE otherwise
11058 */
11059 //================================================================================
11060
11061 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11062                                             const TIDSortedElemSet& theNodesNot,
11063                                             const TopoDS_Shape&     theShape )
11064 {
11065   if ( theShape.IsNull() )
11066     return false;
11067
11068   const double aTol = Precision::Confusion();
11069   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11070   auto_ptr<_FaceClassifier>              aFaceClassifier;
11071   if ( theShape.ShapeType() == TopAbs_SOLID )
11072   {
11073     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11074     bsc3d->PerformInfinitePoint(aTol);
11075   }
11076   else if (theShape.ShapeType() == TopAbs_FACE )
11077   {
11078     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11079   }
11080
11081   // iterates on indicated elements and get elements by back references from their nodes
11082   TIDSortedElemSet anAffected;
11083   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11084   for ( ;  elemItr != theElems.end(); ++elemItr )
11085   {
11086     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11087     if (!anElem)
11088       continue;
11089
11090     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11091     while ( nodeItr->more() )
11092     {
11093       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11094       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11095         continue;
11096       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11097       while ( backElemItr->more() )
11098       {
11099         const SMDS_MeshElement* curElem = backElemItr->next();
11100         if ( curElem && theElems.find(curElem) == theElems.end() &&
11101              ( bsc3d.get() ?
11102                isInside( curElem, *bsc3d, aTol ) :
11103                isInside( curElem, *aFaceClassifier, aTol )))
11104           anAffected.insert( curElem );
11105       }
11106     }
11107   }
11108   return DoubleNodes( theElems, theNodesNot, anAffected );
11109 }
11110
11111 /*!
11112  *  \brief compute an oriented angle between two planes defined by four points.
11113  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11114  *  @param p0 base of the rotation axe
11115  *  @param p1 extremity of the rotation axe
11116  *  @param g1 belongs to the first plane
11117  *  @param g2 belongs to the second plane
11118  */
11119 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11120 {
11121 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11122 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11123 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11124 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11125   gp_Vec vref(p0, p1);
11126   gp_Vec v1(p0, g1);
11127   gp_Vec v2(p0, g2);
11128   gp_Vec n1 = vref.Crossed(v1);
11129   gp_Vec n2 = vref.Crossed(v2);
11130   return n2.AngleWithRef(n1, vref);
11131 }
11132
11133 /*!
11134  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11135  * The list of groups must describe a partition of the mesh volumes.
11136  * The nodes of the internal faces at the boundaries of the groups are doubled.
11137  * In option, the internal faces are replaced by flat elements.
11138  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11139  * The flat elements are stored in groups of volumes.
11140  * @param theElems - list of groups of volumes, where a group of volume is a set of
11141  * SMDS_MeshElements sorted by Id.
11142  * @param createJointElems - if TRUE, create the elements
11143  * @return TRUE if operation has been completed successfully, FALSE otherwise
11144  */
11145 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11146                                                      bool createJointElems)
11147 {
11148   MESSAGE("----------------------------------------------");
11149   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11150   MESSAGE("----------------------------------------------");
11151
11152   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11153   meshDS->BuildDownWardConnectivity(true);
11154   CHRONO(50);
11155   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11156
11157   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11158   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11159   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11160
11161   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11162   std::map<int,int>celldom; // cell vtkId --> domain
11163   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11164   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11165   faceDomains.clear();
11166   celldom.clear();
11167   cellDomains.clear();
11168   nodeDomains.clear();
11169   std::map<int,int> emptyMap;
11170   std::set<int> emptySet;
11171   emptyMap.clear();
11172
11173   for (int idom = 0; idom < theElems.size(); idom++)
11174     {
11175
11176       // --- build a map (face to duplicate --> volume to modify)
11177       //     with all the faces shared by 2 domains (group of elements)
11178       //     and corresponding volume of this domain, for each shared face.
11179       //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11180
11181       //MESSAGE("Domain " << idom);
11182       const TIDSortedElemSet& domain = theElems[idom];
11183       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11184       for (; elemItr != domain.end(); ++elemItr)
11185         {
11186           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11187           if (!anElem)
11188             continue;
11189           int vtkId = anElem->getVtkId();
11190           //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11191           int neighborsVtkIds[NBMAXNEIGHBORS];
11192           int downIds[NBMAXNEIGHBORS];
11193           unsigned char downTypes[NBMAXNEIGHBORS];
11194           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11195           for (int n = 0; n < nbNeighbors; n++)
11196             {
11197               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11198               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11199               if (! domain.count(elem)) // neighbor is in another domain : face is shared
11200                 {
11201                   DownIdType face(downIds[n], downTypes[n]);
11202                   if (!faceDomains.count(face))
11203                     faceDomains[face] = emptyMap; // create an empty entry for face
11204                   if (!faceDomains[face].count(idom))
11205                     {
11206                       faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11207                       celldom[vtkId] = idom;
11208                       //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11209                     }
11210                 }
11211             }
11212         }
11213     }
11214
11215   //MESSAGE("Number of shared faces " << faceDomains.size());
11216   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11217
11218   // --- explore the shared faces domain by domain,
11219   //     explore the nodes of the face and see if they belong to a cell in the domain,
11220   //     which has only a node or an edge on the border (not a shared face)
11221
11222   for (int idomain = 0; idomain < theElems.size(); idomain++)
11223     {
11224       //MESSAGE("Domain " << idomain);
11225       const TIDSortedElemSet& domain = theElems[idomain];
11226       itface = faceDomains.begin();
11227       for (; itface != faceDomains.end(); ++itface)
11228         {
11229           std::map<int, int> domvol = itface->second;
11230           if (!domvol.count(idomain))
11231             continue;
11232           DownIdType face = itface->first;
11233           //MESSAGE(" --- face " << face.cellId);
11234           std::set<int> oldNodes;
11235           oldNodes.clear();
11236           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11237           std::set<int>::iterator itn = oldNodes.begin();
11238           for (; itn != oldNodes.end(); ++itn)
11239             {
11240               int oldId = *itn;
11241               //MESSAGE("     node " << oldId);
11242               vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11243               for (int i=0; i<l.ncells; i++)
11244                 {
11245                   int vtkId = l.cells[i];
11246                   const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11247                   if (!domain.count(anElem))
11248                     continue;
11249                   int vtkType = grid->GetCellType(vtkId);
11250                   int downId = grid->CellIdToDownId(vtkId);
11251                   if (downId < 0)
11252                     {
11253                       MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11254                       continue; // not OK at this stage of the algorithm:
11255                                 //no cells created after BuildDownWardConnectivity
11256                     }
11257                   DownIdType aCell(downId, vtkType);
11258                   if (!cellDomains.count(aCell))
11259                     cellDomains[aCell] = emptyMap; // create an empty entry for cell
11260                   cellDomains[aCell][idomain] = vtkId;
11261                   celldom[vtkId] = idomain;
11262                   //MESSAGE("       cell " << vtkId << " domain " << idomain);
11263                 }
11264             }
11265         }
11266     }
11267
11268   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11269   //     for each shared face, get the nodes
11270   //     for each node, for each domain of the face, create a clone of the node
11271
11272   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11273   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11274   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11275
11276   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11277   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11278   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11279
11280   for (int idomain = 0; idomain < theElems.size(); idomain++)
11281     {
11282       itface = faceDomains.begin();
11283       for (; itface != faceDomains.end(); ++itface)
11284         {
11285           std::map<int, int> domvol = itface->second;
11286           if (!domvol.count(idomain))
11287             continue;
11288           DownIdType face = itface->first;
11289           //MESSAGE(" --- face " << face.cellId);
11290           std::set<int> oldNodes;
11291           oldNodes.clear();
11292           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11293           std::set<int>::iterator itn = oldNodes.begin();
11294           for (; itn != oldNodes.end(); ++itn)
11295             {
11296               int oldId = *itn;
11297               //MESSAGE("-+-+-a node " << oldId);
11298               if (!nodeDomains.count(oldId))
11299                 nodeDomains[oldId] = emptyMap; // create an empty entry for node
11300               if (nodeDomains[oldId].empty())
11301                 {
11302                   nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11303                   //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11304                 }
11305               std::map<int, int>::iterator itdom = domvol.begin();
11306               for (; itdom != domvol.end(); ++itdom)
11307                 {
11308                   int idom = itdom->first;
11309                   //MESSAGE("         domain " << idom);
11310                   if (!nodeDomains[oldId].count(idom)) // --- node to clone
11311                     {
11312                       if (nodeDomains[oldId].size() >= 2) // a multiple node
11313                         {
11314                           vector<int> orderedDoms;
11315                           //MESSAGE("multiple node " << oldId);
11316                           if (mutipleNodes.count(oldId))
11317                             orderedDoms = mutipleNodes[oldId];
11318                           else
11319                             {
11320                               map<int,int>::iterator it = nodeDomains[oldId].begin();
11321                               for (; it != nodeDomains[oldId].end(); ++it)
11322                                 orderedDoms.push_back(it->first);
11323                             }
11324                           orderedDoms.push_back(idom); // TODO order ==> push_front or back
11325                           //stringstream txt;
11326                           //for (int i=0; i<orderedDoms.size(); i++)
11327                           //  txt << orderedDoms[i] << " ";
11328                           //MESSAGE("orderedDoms " << txt.str());
11329                           mutipleNodes[oldId] = orderedDoms;
11330                         }
11331                       double *coords = grid->GetPoint(oldId);
11332                       SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11333                       int newId = newNode->getVtkId();
11334                       nodeDomains[oldId][idom] = newId; // cloned node for other domains
11335                       //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11336                     }
11337                 }
11338             }
11339         }
11340     }
11341
11342   for (int idomain = 0; idomain < theElems.size(); idomain++)
11343     {
11344       itface = faceDomains.begin();
11345       for (; itface != faceDomains.end(); ++itface)
11346         {
11347           std::map<int, int> domvol = itface->second;
11348           if (!domvol.count(idomain))
11349             continue;
11350           DownIdType face = itface->first;
11351           //MESSAGE(" --- face " << face.cellId);
11352           std::set<int> oldNodes;
11353           oldNodes.clear();
11354           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11355           int nbMultipleNodes = 0;
11356           std::set<int>::iterator itn = oldNodes.begin();
11357           for (; itn != oldNodes.end(); ++itn)
11358             {
11359               int oldId = *itn;
11360               if (mutipleNodes.count(oldId))
11361                 nbMultipleNodes++;
11362             }
11363           if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11364             {
11365               //MESSAGE("multiple Nodes detected on a shared face");
11366               int downId = itface->first.cellId;
11367               unsigned char cellType = itface->first.cellType;
11368               // --- shared edge or shared face ?
11369               if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11370                 {
11371                   int nodes[3];
11372                   int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11373                   for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11374                     if (mutipleNodes.count(nodes[i]))
11375                       if (!mutipleNodesToFace.count(nodes[i]))
11376                         mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11377                 }
11378               else // shared face (between two volumes)
11379                 {
11380                   int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11381                   const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11382                   const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11383                   for (int ie =0; ie < nbEdges; ie++)
11384                     {
11385                       int nodes[3];
11386                       int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11387                       if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11388                         {
11389                           vector<int> vn0 = mutipleNodes[nodes[0]];
11390                           vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11391                           vector<int> doms;
11392                           for (int i0 = 0; i0 < vn0.size(); i0++)
11393                             for (int i1 = 0; i1 < vn1.size(); i1++)
11394                               if (vn0[i0] == vn1[i1])
11395                                 doms.push_back(vn0[i0]);
11396                           if (doms.size() >2)
11397                             {
11398                               //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11399                               double *coords = grid->GetPoint(nodes[0]);
11400                               gp_Pnt p0(coords[0], coords[1], coords[2]);
11401                               coords = grid->GetPoint(nodes[nbNodes - 1]);
11402                               gp_Pnt p1(coords[0], coords[1], coords[2]);
11403                               gp_Pnt gref;
11404                               int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11405                               map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11406                               map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11407                               int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11408                               for (int id=0; id < doms.size(); id++)
11409                                 {
11410                                   int idom = doms[id];
11411                                   for (int ivol=0; ivol<nbvol; ivol++)
11412                                     {
11413                                       int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11414                                       SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11415                                       if (theElems[idom].count(elem))
11416                                         {
11417                                           SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11418                                           domvol[idom] = svol;
11419                                           //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11420                                           double values[3];
11421                                           vtkIdType npts = 0;
11422                                           vtkIdType* pts = 0;
11423                                           grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11424                                           SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11425                                           if (id ==0)
11426                                             {
11427                                               gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11428                                               angleDom[idom] = 0;
11429                                             }
11430                                           else
11431                                             {
11432                                               gp_Pnt g(values[0], values[1], values[2]);
11433                                               angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11434                                               //MESSAGE("  angle=" << angleDom[idom]);
11435                                             }
11436                                           break;
11437                                         }
11438                                     }
11439                                 }
11440                               map<double, int> sortedDom; // sort domains by angle
11441                               for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11442                                 sortedDom[ia->second] = ia->first;
11443                               vector<int> vnodes;
11444                               vector<int> vdom;
11445                               for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11446                                 {
11447                                   vdom.push_back(ib->second);
11448                                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11449                                 }
11450                               for (int ino = 0; ino < nbNodes; ino++)
11451                                 vnodes.push_back(nodes[ino]);
11452                               edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11453                             }
11454                         }
11455                     }
11456                 }
11457             }
11458         }
11459     }
11460
11461   // --- iterate on shared faces (volumes to modify, face to extrude)
11462   //     get node id's of the face (id SMDS = id VTK)
11463   //     create flat element with old and new nodes if requested
11464
11465   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11466   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11467
11468   std::map<int, std::map<long,int> > nodeQuadDomains;
11469   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11470
11471   if (createJointElems)
11472     {
11473       int idg;
11474       string joints2DName = "joints2D";
11475       mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11476       SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11477       string joints3DName = "joints3D";
11478       mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11479       SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11480
11481       itface = faceDomains.begin();
11482       for (; itface != faceDomains.end(); ++itface)
11483         {
11484           DownIdType face = itface->first;
11485           std::set<int> oldNodes;
11486           std::set<int>::iterator itn;
11487           oldNodes.clear();
11488           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11489
11490           std::map<int, int> domvol = itface->second;
11491           std::map<int, int>::iterator itdom = domvol.begin();
11492           int dom1 = itdom->first;
11493           int vtkVolId = itdom->second;
11494           itdom++;
11495           int dom2 = itdom->first;
11496           SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11497                                                              nodeQuadDomains);
11498           stringstream grpname;
11499           grpname << "j_";
11500           if (dom1 < dom2)
11501             grpname << dom1 << "_" << dom2;
11502           else
11503             grpname << dom2 << "_" << dom1;
11504           string namegrp = grpname.str();
11505           if (!mapOfJunctionGroups.count(namegrp))
11506             mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11507           SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11508           if (sgrp)
11509             sgrp->Add(vol->GetID());
11510           if (vol->GetType() == SMDSAbs_Volume)
11511             joints3DGrp->Add(vol->GetID());
11512           else if (vol->GetType() == SMDSAbs_Face)
11513             joints2DGrp->Add(vol->GetID());
11514         }
11515     }
11516
11517   // --- create volumes on multiple domain intersection if requested
11518   //     iterate on mutipleNodesToFace
11519   //     iterate on edgesMultiDomains
11520
11521   if (createJointElems)
11522     {
11523       // --- iterate on mutipleNodesToFace
11524
11525       std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11526       for (; itn != mutipleNodesToFace.end(); ++itn)
11527         {
11528           int node = itn->first;
11529           vector<int> orderDom = itn->second;
11530           vector<vtkIdType> orderedNodes;
11531           for (int idom = 0; idom <orderDom.size(); idom++)
11532             orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11533             SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11534
11535             stringstream grpname;
11536             grpname << "m2j_";
11537             grpname << 0 << "_" << 0;
11538             int idg;
11539             string namegrp = grpname.str();
11540             if (!mapOfJunctionGroups.count(namegrp))
11541               mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11542             SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11543             if (sgrp)
11544               sgrp->Add(face->GetID());
11545         }
11546
11547       // --- iterate on edgesMultiDomains
11548
11549       std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11550       for (; ite != edgesMultiDomains.end(); ++ite)
11551         {
11552           vector<int> nodes = ite->first;
11553           vector<int> orderDom = ite->second;
11554           vector<vtkIdType> orderedNodes;
11555           if (nodes.size() == 2)
11556             {
11557               //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11558               for (int ino=0; ino < nodes.size(); ino++)
11559                 if (orderDom.size() == 3)
11560                   for (int idom = 0; idom <orderDom.size(); idom++)
11561                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11562                 else
11563                   for (int idom = orderDom.size()-1; idom >=0; idom--)
11564                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11565               SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11566
11567               int idg;
11568               string namegrp = "jointsMultiples";
11569               if (!mapOfJunctionGroups.count(namegrp))
11570                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11571               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11572               if (sgrp)
11573                 sgrp->Add(vol->GetID());
11574             }
11575           else
11576             {
11577               INFOS("Quadratic multiple joints not implemented");
11578               // TODO quadratic nodes
11579             }
11580         }
11581     }
11582
11583   // --- list the explicit faces and edges of the mesh that need to be modified,
11584   //     i.e. faces and edges built with one or more duplicated nodes.
11585   //     associate these faces or edges to their corresponding domain.
11586   //     only the first domain found is kept when a face or edge is shared
11587
11588   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11589   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11590   faceOrEdgeDom.clear();
11591   feDom.clear();
11592
11593   for (int idomain = 0; idomain < theElems.size(); idomain++)
11594     {
11595       std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11596       for (; itnod != nodeDomains.end(); ++itnod)
11597         {
11598           int oldId = itnod->first;
11599           //MESSAGE("     node " << oldId);
11600           vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11601           for (int i = 0; i < l.ncells; i++)
11602             {
11603               int vtkId = l.cells[i];
11604               int vtkType = grid->GetCellType(vtkId);
11605               int downId = grid->CellIdToDownId(vtkId);
11606               if (downId < 0)
11607                 continue; // new cells: not to be modified
11608               DownIdType aCell(downId, vtkType);
11609               int volParents[1000];
11610               int nbvol = grid->GetParentVolumes(volParents, vtkId);
11611               for (int j = 0; j < nbvol; j++)
11612                 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11613                   if (!feDom.count(vtkId))
11614                     {
11615                       feDom[vtkId] = idomain;
11616                       faceOrEdgeDom[aCell] = emptyMap;
11617                       faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11618                       //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11619                       //        << " type " << vtkType << " downId " << downId);
11620                     }
11621             }
11622         }
11623     }
11624
11625   // --- iterate on shared faces (volumes to modify, face to extrude)
11626   //     get node id's of the face
11627   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11628
11629   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11630   for (int m=0; m<3; m++)
11631     {
11632       std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11633       itface = (*amap).begin();
11634       for (; itface != (*amap).end(); ++itface)
11635         {
11636           DownIdType face = itface->first;
11637           std::set<int> oldNodes;
11638           std::set<int>::iterator itn;
11639           oldNodes.clear();
11640           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11641           //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11642           std::map<int, int> localClonedNodeIds;
11643
11644           std::map<int, int> domvol = itface->second;
11645           std::map<int, int>::iterator itdom = domvol.begin();
11646           for (; itdom != domvol.end(); ++itdom)
11647             {
11648               int idom = itdom->first;
11649               int vtkVolId = itdom->second;
11650               //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11651               localClonedNodeIds.clear();
11652               for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11653                 {
11654                   int oldId = *itn;
11655                   if (nodeDomains[oldId].count(idom))
11656                     {
11657                       localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11658                       //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11659                     }
11660                 }
11661               meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11662             }
11663         }
11664     }
11665
11666   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11667   grid->BuildLinks();
11668
11669   CHRONOSTOP(50);
11670   counters::stats();
11671   return true;
11672 }
11673
11674 /*!
11675  * \brief Double nodes on some external faces and create flat elements.
11676  * Flat elements are mainly used by some types of mechanic calculations.
11677  *
11678  * Each group of the list must be constituted of faces.
11679  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11680  * @param theElems - list of groups of faces, where a group of faces is a set of
11681  * SMDS_MeshElements sorted by Id.
11682  * @return TRUE if operation has been completed successfully, FALSE otherwise
11683  */
11684 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11685 {
11686   MESSAGE("-------------------------------------------------");
11687   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11688   MESSAGE("-------------------------------------------------");
11689
11690   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11691
11692   // --- For each group of faces
11693   //     duplicate the nodes, create a flat element based on the face
11694   //     replace the nodes of the faces by their clones
11695
11696   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11697   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11698   clonedNodes.clear();
11699   intermediateNodes.clear();
11700   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11701   mapOfJunctionGroups.clear();
11702
11703   for (int idom = 0; idom < theElems.size(); idom++)
11704     {
11705       const TIDSortedElemSet& domain = theElems[idom];
11706       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11707       for (; elemItr != domain.end(); ++elemItr)
11708         {
11709           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11710           SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11711           if (!aFace)
11712             continue;
11713           // MESSAGE("aFace=" << aFace->GetID());
11714           bool isQuad = aFace->IsQuadratic();
11715           vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11716
11717           // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11718
11719           SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11720           while (nodeIt->more())
11721             {
11722               const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11723               bool isMedium = isQuad && (aFace->IsMediumNode(node));
11724               if (isMedium)
11725                 ln2.push_back(node);
11726               else
11727                 ln0.push_back(node);
11728
11729               const SMDS_MeshNode* clone = 0;
11730               if (!clonedNodes.count(node))
11731                 {
11732                   clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11733                   clonedNodes[node] = clone;
11734                 }
11735               else
11736                 clone = clonedNodes[node];
11737
11738               if (isMedium)
11739                 ln3.push_back(clone);
11740               else
11741                 ln1.push_back(clone);
11742
11743               const SMDS_MeshNode* inter = 0;
11744               if (isQuad && (!isMedium))
11745                 {
11746                   if (!intermediateNodes.count(node))
11747                     {
11748                       inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11749                       intermediateNodes[node] = inter;
11750                     }
11751                   else
11752                     inter = intermediateNodes[node];
11753                   ln4.push_back(inter);
11754                 }
11755             }
11756
11757           // --- extrude the face
11758
11759           vector<const SMDS_MeshNode*> ln;
11760           SMDS_MeshVolume* vol = 0;
11761           vtkIdType aType = aFace->GetVtkType();
11762           switch (aType)
11763           {
11764             case VTK_TRIANGLE:
11765               vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11766               // MESSAGE("vol prism " << vol->GetID());
11767               ln.push_back(ln1[0]);
11768               ln.push_back(ln1[1]);
11769               ln.push_back(ln1[2]);
11770               break;
11771             case VTK_QUAD:
11772               vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11773               // MESSAGE("vol hexa " << vol->GetID());
11774               ln.push_back(ln1[0]);
11775               ln.push_back(ln1[1]);
11776               ln.push_back(ln1[2]);
11777               ln.push_back(ln1[3]);
11778               break;
11779             case VTK_QUADRATIC_TRIANGLE:
11780               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11781                                       ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11782               // MESSAGE("vol quad prism " << vol->GetID());
11783               ln.push_back(ln1[0]);
11784               ln.push_back(ln1[1]);
11785               ln.push_back(ln1[2]);
11786               ln.push_back(ln3[0]);
11787               ln.push_back(ln3[1]);
11788               ln.push_back(ln3[2]);
11789               break;
11790             case VTK_QUADRATIC_QUAD:
11791 //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11792 //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11793 //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11794               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11795                                       ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11796                                       ln4[0], ln4[1], ln4[2], ln4[3]);
11797               // MESSAGE("vol quad hexa " << vol->GetID());
11798               ln.push_back(ln1[0]);
11799               ln.push_back(ln1[1]);
11800               ln.push_back(ln1[2]);
11801               ln.push_back(ln1[3]);
11802               ln.push_back(ln3[0]);
11803               ln.push_back(ln3[1]);
11804               ln.push_back(ln3[2]);
11805               ln.push_back(ln3[3]);
11806               break;
11807             case VTK_POLYGON:
11808               break;
11809             default:
11810               break;
11811           }
11812
11813           if (vol)
11814             {
11815               stringstream grpname;
11816               grpname << "jf_";
11817               grpname << idom;
11818               int idg;
11819               string namegrp = grpname.str();
11820               if (!mapOfJunctionGroups.count(namegrp))
11821                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11822               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11823               if (sgrp)
11824                 sgrp->Add(vol->GetID());
11825             }
11826
11827           // --- modify the face
11828
11829           aFace->ChangeNodes(&ln[0], ln.size());
11830         }
11831     }
11832   return true;
11833 }
11834
11835 /*!
11836  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11837  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11838  *  groups of faces to remove inside the object, (idem edges).
11839  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11840  */
11841 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11842                                       const TopoDS_Shape& theShape,
11843                                       SMESH_NodeSearcher* theNodeSearcher,
11844                                       const char* groupName,
11845                                       std::vector<double>&   nodesCoords,
11846                                       std::vector<std::vector<int> >& listOfListOfNodes)
11847 {
11848   MESSAGE("--------------------------------");
11849   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11850   MESSAGE("--------------------------------");
11851
11852   // --- zone of volumes to remove is given :
11853   //     1 either by a geom shape (one or more vertices) and a radius,
11854   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11855   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11856   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11857   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11858   //     defined by it's name.
11859
11860   SMESHDS_GroupBase* groupDS = 0;
11861   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11862   while ( groupIt->more() )
11863     {
11864       groupDS = 0;
11865       SMESH_Group * group = groupIt->next();
11866       if ( !group ) continue;
11867       groupDS = group->GetGroupDS();
11868       if ( !groupDS || groupDS->IsEmpty() ) continue;
11869       std::string grpName = group->GetName();
11870       //MESSAGE("grpName=" << grpName);
11871       if (grpName == groupName)
11872         break;
11873       else
11874         groupDS = 0;
11875     }
11876
11877   bool isNodeGroup = false;
11878   bool isNodeCoords = false;
11879   if (groupDS)
11880     {
11881       if (groupDS->GetType() != SMDSAbs_Node)
11882         return;
11883       isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11884     }
11885
11886   if (nodesCoords.size() > 0)
11887     isNodeCoords = true; // a list o nodes given by their coordinates
11888   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11889
11890   // --- define groups to build
11891
11892   int idg; // --- group of SMDS volumes
11893   string grpvName = groupName;
11894   grpvName += "_vol";
11895   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11896   if (!grp)
11897     {
11898       MESSAGE("group not created " << grpvName);
11899       return;
11900     }
11901   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11902
11903   int idgs; // --- group of SMDS faces on the skin
11904   string grpsName = groupName;
11905   grpsName += "_skin";
11906   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11907   if (!grps)
11908     {
11909       MESSAGE("group not created " << grpsName);
11910       return;
11911     }
11912   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11913
11914   int idgi; // --- group of SMDS faces internal (several shapes)
11915   string grpiName = groupName;
11916   grpiName += "_internalFaces";
11917   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11918   if (!grpi)
11919     {
11920       MESSAGE("group not created " << grpiName);
11921       return;
11922     }
11923   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11924
11925   int idgei; // --- group of SMDS faces internal (several shapes)
11926   string grpeiName = groupName;
11927   grpeiName += "_internalEdges";
11928   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11929   if (!grpei)
11930     {
11931       MESSAGE("group not created " << grpeiName);
11932       return;
11933     }
11934   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11935
11936   // --- build downward connectivity
11937
11938   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11939   meshDS->BuildDownWardConnectivity(true);
11940   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11941
11942   // --- set of volumes detected inside
11943
11944   std::set<int> setOfInsideVol;
11945   std::set<int> setOfVolToCheck;
11946
11947   std::vector<gp_Pnt> gpnts;
11948   gpnts.clear();
11949
11950   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11951     {
11952       MESSAGE("group of nodes provided");
11953       SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11954       while ( elemIt->more() )
11955         {
11956           const SMDS_MeshElement* elem = elemIt->next();
11957           if (!elem)
11958             continue;
11959           const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11960           if (!node)
11961             continue;
11962           SMDS_MeshElement* vol = 0;
11963           SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11964           while (volItr->more())
11965             {
11966               vol = (SMDS_MeshElement*)volItr->next();
11967               setOfInsideVol.insert(vol->getVtkId());
11968               sgrp->Add(vol->GetID());
11969             }
11970         }
11971     }
11972   else if (isNodeCoords)
11973     {
11974       MESSAGE("list of nodes coordinates provided");
11975       int i = 0;
11976       int k = 0;
11977       while (i < nodesCoords.size()-2)
11978         {
11979           double x = nodesCoords[i++];
11980           double y = nodesCoords[i++];
11981           double z = nodesCoords[i++];
11982           gp_Pnt p = gp_Pnt(x, y ,z);
11983           gpnts.push_back(p);
11984           MESSAGE("TopoDS_Vertex " << k++ << " " << p.X() << " " << p.Y() << " " << p.Z());
11985         }
11986     }
11987   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11988     {
11989       MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11990       TopTools_IndexedMapOfShape vertexMap;
11991       TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11992       gp_Pnt p = gp_Pnt(0,0,0);
11993       if (vertexMap.Extent() < 1)
11994         return;
11995
11996       for ( int i = 1; i <= vertexMap.Extent(); ++i )
11997         {
11998           const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11999           p = BRep_Tool::Pnt(vertex);
12000           gpnts.push_back(p);
12001           MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12002         }
12003     }
12004
12005   if (gpnts.size() > 0)
12006     {
12007       int nodeId = 0;
12008       const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12009       if (startNode)
12010         nodeId = startNode->GetID();
12011       MESSAGE("nodeId " << nodeId);
12012
12013       double radius2 = radius*radius;
12014       MESSAGE("radius2 " << radius2);
12015
12016       // --- volumes on start node
12017
12018       setOfVolToCheck.clear();
12019       SMDS_MeshElement* startVol = 0;
12020       SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12021       while (volItr->more())
12022         {
12023           startVol = (SMDS_MeshElement*)volItr->next();
12024           setOfVolToCheck.insert(startVol->getVtkId());
12025         }
12026       if (setOfVolToCheck.empty())
12027         {
12028           MESSAGE("No volumes found");
12029           return;
12030         }
12031
12032       // --- starting with central volumes then their neighbors, check if they are inside
12033       //     or outside the domain, until no more new neighbor volume is inside.
12034       //     Fill the group of inside volumes
12035
12036       std::map<int, double> mapOfNodeDistance2;
12037       mapOfNodeDistance2.clear();
12038       std::set<int> setOfOutsideVol;
12039       while (!setOfVolToCheck.empty())
12040         {
12041           std::set<int>::iterator it = setOfVolToCheck.begin();
12042           int vtkId = *it;
12043           MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12044           bool volInside = false;
12045           vtkIdType npts = 0;
12046           vtkIdType* pts = 0;
12047           grid->GetCellPoints(vtkId, npts, pts);
12048           for (int i=0; i<npts; i++)
12049             {
12050               double distance2 = 0;
12051               if (mapOfNodeDistance2.count(pts[i]))
12052                 {
12053                   distance2 = mapOfNodeDistance2[pts[i]];
12054                   MESSAGE("point " << pts[i] << " distance2 " << distance2);
12055                 }
12056               else
12057                 {
12058                   double *coords = grid->GetPoint(pts[i]);
12059                   gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12060                   distance2 = 1.E40;
12061                   for (int j=0; j<gpnts.size(); j++)
12062                     {
12063                       double d2 = aPoint.SquareDistance(gpnts[j]);
12064                       if (d2 < distance2)
12065                         {
12066                           distance2 = d2;
12067                           if (distance2 < radius2)
12068                             break;
12069                         }
12070                     }
12071                   mapOfNodeDistance2[pts[i]] = distance2;
12072                   MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12073                 }
12074               if (distance2 < radius2)
12075                 {
12076                   volInside = true; // one or more nodes inside the domain
12077                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12078                   break;
12079                 }
12080             }
12081           if (volInside)
12082             {
12083               setOfInsideVol.insert(vtkId);
12084               MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12085               int neighborsVtkIds[NBMAXNEIGHBORS];
12086               int downIds[NBMAXNEIGHBORS];
12087               unsigned char downTypes[NBMAXNEIGHBORS];
12088               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12089               for (int n = 0; n < nbNeighbors; n++)
12090                 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12091                   setOfVolToCheck.insert(neighborsVtkIds[n]);
12092             }
12093           else
12094             {
12095               setOfOutsideVol.insert(vtkId);
12096               MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12097             }
12098           setOfVolToCheck.erase(vtkId);
12099         }
12100     }
12101
12102   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12103   //     If yes, add the volume to the inside set
12104
12105   bool addedInside = true;
12106   std::set<int> setOfVolToReCheck;
12107   while (addedInside)
12108     {
12109       MESSAGE(" --------------------------- re check");
12110       addedInside = false;
12111       std::set<int>::iterator itv = setOfInsideVol.begin();
12112       for (; itv != setOfInsideVol.end(); ++itv)
12113         {
12114           int vtkId = *itv;
12115           int neighborsVtkIds[NBMAXNEIGHBORS];
12116           int downIds[NBMAXNEIGHBORS];
12117           unsigned char downTypes[NBMAXNEIGHBORS];
12118           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12119           for (int n = 0; n < nbNeighbors; n++)
12120             if (!setOfInsideVol.count(neighborsVtkIds[n]))
12121               setOfVolToReCheck.insert(neighborsVtkIds[n]);
12122         }
12123       setOfVolToCheck = setOfVolToReCheck;
12124       setOfVolToReCheck.clear();
12125       while  (!setOfVolToCheck.empty())
12126         {
12127           std::set<int>::iterator it = setOfVolToCheck.begin();
12128           int vtkId = *it;
12129           if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12130             {
12131               MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12132               int countInside = 0;
12133               int neighborsVtkIds[NBMAXNEIGHBORS];
12134               int downIds[NBMAXNEIGHBORS];
12135               unsigned char downTypes[NBMAXNEIGHBORS];
12136               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12137               for (int n = 0; n < nbNeighbors; n++)
12138                 if (setOfInsideVol.count(neighborsVtkIds[n]))
12139                   countInside++;
12140               MESSAGE("countInside " << countInside);
12141               if (countInside > 1)
12142                 {
12143                   MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12144                   setOfInsideVol.insert(vtkId);
12145                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12146                   addedInside = true;
12147                 }
12148               else
12149                 setOfVolToReCheck.insert(vtkId);
12150             }
12151           setOfVolToCheck.erase(vtkId);
12152         }
12153     }
12154
12155   // --- map of Downward faces at the boundary, inside the global volume
12156   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12157   //     fill group of SMDS faces inside the volume (when several volume shapes)
12158   //     fill group of SMDS faces on the skin of the global volume (if skin)
12159
12160   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12161   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12162   std::set<int>::iterator it = setOfInsideVol.begin();
12163   for (; it != setOfInsideVol.end(); ++it)
12164     {
12165       int vtkId = *it;
12166       //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12167       int neighborsVtkIds[NBMAXNEIGHBORS];
12168       int downIds[NBMAXNEIGHBORS];
12169       unsigned char downTypes[NBMAXNEIGHBORS];
12170       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12171       for (int n = 0; n < nbNeighbors; n++)
12172         {
12173           int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12174           if (neighborDim == 3)
12175             {
12176               if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12177                 {
12178                   DownIdType face(downIds[n], downTypes[n]);
12179                   boundaryFaces[face] = vtkId;
12180                 }
12181               // if the face between to volumes is in the mesh, get it (internal face between shapes)
12182               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12183               if (vtkFaceId >= 0)
12184                 {
12185                   sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12186                   // find also the smds edges on this face
12187                   int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12188                   const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12189                   const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12190                   for (int i = 0; i < nbEdges; i++)
12191                     {
12192                       int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12193                       if (vtkEdgeId >= 0)
12194                         sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12195                     }
12196                 }
12197             }
12198           else if (neighborDim == 2) // skin of the volume
12199             {
12200               DownIdType face(downIds[n], downTypes[n]);
12201               skinFaces[face] = vtkId;
12202               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12203               if (vtkFaceId >= 0)
12204                 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12205             }
12206         }
12207     }
12208
12209   // --- identify the edges constituting the wire of each subshape on the skin
12210   //     define polylines with the nodes of edges, equivalent to wires
12211   //     project polylines on subshapes, and partition, to get geom faces
12212
12213   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12214   std::set<int> emptySet;
12215   emptySet.clear();
12216   std::set<int> shapeIds;
12217
12218   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12219   while (itelem->more())
12220     {
12221       const SMDS_MeshElement *elem = itelem->next();
12222       int shapeId = elem->getshapeId();
12223       int vtkId = elem->getVtkId();
12224       if (!shapeIdToVtkIdSet.count(shapeId))
12225         {
12226           shapeIdToVtkIdSet[shapeId] = emptySet;
12227           shapeIds.insert(shapeId);
12228         }
12229       shapeIdToVtkIdSet[shapeId].insert(vtkId);
12230     }
12231
12232   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12233   std::set<DownIdType, DownIdCompare> emptyEdges;
12234   emptyEdges.clear();
12235
12236   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12237   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12238     {
12239       int shapeId = itShape->first;
12240       MESSAGE(" --- Shape ID --- "<< shapeId);
12241       shapeIdToEdges[shapeId] = emptyEdges;
12242
12243       std::vector<int> nodesEdges;
12244
12245       std::set<int>::iterator its = itShape->second.begin();
12246       for (; its != itShape->second.end(); ++its)
12247         {
12248           int vtkId = *its;
12249           MESSAGE("     " << vtkId);
12250           int neighborsVtkIds[NBMAXNEIGHBORS];
12251           int downIds[NBMAXNEIGHBORS];
12252           unsigned char downTypes[NBMAXNEIGHBORS];
12253           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12254           for (int n = 0; n < nbNeighbors; n++)
12255             {
12256               if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12257                 continue;
12258               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12259               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12260               if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12261                 {
12262                   DownIdType edge(downIds[n], downTypes[n]);
12263                   if (!shapeIdToEdges[shapeId].count(edge))
12264                     {
12265                       shapeIdToEdges[shapeId].insert(edge);
12266                       int vtkNodeId[3];
12267                       int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12268                       nodesEdges.push_back(vtkNodeId[0]);
12269                       nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12270                       MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12271                     }
12272                 }
12273             }
12274         }
12275
12276       std::list<int> order;
12277       order.clear();
12278       if (nodesEdges.size() > 0)
12279         {
12280           order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12281           nodesEdges[0] = -1;
12282           order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
12283           nodesEdges[1] = -1; // do not reuse this edge
12284           bool found = true;
12285           while (found)
12286             {
12287               int nodeTofind = order.back(); // try first to push back
12288               int i = 0;
12289               for (i = 0; i<nodesEdges.size(); i++)
12290                 if (nodesEdges[i] == nodeTofind)
12291                   break;
12292               if (i == nodesEdges.size())
12293                 found = false; // no follower found on back
12294               else
12295                 {
12296                   if (i%2) // odd ==> use the previous one
12297                     if (nodesEdges[i-1] < 0)
12298                       found = false;
12299                     else
12300                       {
12301                         order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
12302                         nodesEdges[i-1] = -1;
12303                       }
12304                   else // even ==> use the next one
12305                     if (nodesEdges[i+1] < 0)
12306                       found = false;
12307                     else
12308                       {
12309                         order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
12310                         nodesEdges[i+1] = -1;
12311                       }
12312                 }
12313               if (found)
12314                 continue;
12315               // try to push front
12316               found = true;
12317               nodeTofind = order.front(); // try to push front
12318               for (i = 0; i<nodesEdges.size(); i++)
12319                 if (nodesEdges[i] == nodeTofind)
12320                   break;
12321               if (i == nodesEdges.size())
12322                 {
12323                   found = false; // no predecessor found on front
12324                   continue;
12325                 }
12326               if (i%2) // odd ==> use the previous one
12327                 if (nodesEdges[i-1] < 0)
12328                   found = false;
12329                 else
12330                   {
12331                     order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
12332                     nodesEdges[i-1] = -1;
12333                   }
12334               else // even ==> use the next one
12335                 if (nodesEdges[i+1] < 0)
12336                   found = false;
12337                 else
12338                   {
12339                     order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
12340                     nodesEdges[i+1] = -1;
12341                   }
12342             }
12343         }
12344
12345
12346       std::vector<int> nodes;
12347       nodes.push_back(shapeId);
12348       std::list<int>::iterator itl = order.begin();
12349       for (; itl != order.end(); itl++)
12350         {
12351           nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12352           MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12353         }
12354       listOfListOfNodes.push_back(nodes);
12355     }
12356
12357   //     partition geom faces with blocFissure
12358   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12359   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12360
12361   return;
12362 }
12363
12364
12365 //================================================================================
12366 /*!
12367  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12368  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12369  * \return TRUE if operation has been completed successfully, FALSE otherwise
12370  */
12371 //================================================================================
12372
12373 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12374 {
12375   // iterates on volume elements and detect all free faces on them
12376   SMESHDS_Mesh* aMesh = GetMeshDS();
12377   if (!aMesh)
12378     return false;
12379   //bool res = false;
12380   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12381   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12382   while(vIt->more())
12383   {
12384     const SMDS_MeshVolume* volume = vIt->next();
12385     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12386     vTool.SetExternalNormal();
12387     //const bool isPoly = volume->IsPoly();
12388     const int iQuad = volume->IsQuadratic();
12389     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12390     {
12391       if (!vTool.IsFreeFace(iface))
12392         continue;
12393       nbFree++;
12394       vector<const SMDS_MeshNode *> nodes;
12395       int nbFaceNodes = vTool.NbFaceNodes(iface);
12396       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12397       int inode = 0;
12398       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12399         nodes.push_back(faceNodes[inode]);
12400       if (iQuad) { // add medium nodes
12401         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12402           nodes.push_back(faceNodes[inode]);
12403         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12404           nodes.push_back(faceNodes[8]);
12405       }
12406       // add new face based on volume nodes
12407       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12408         nbExisted++;
12409         continue; // face already exsist
12410       }
12411       AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12412       nbCreated++;
12413     }
12414   }
12415   return ( nbFree==(nbExisted+nbCreated) );
12416 }
12417
12418 namespace
12419 {
12420   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12421   {
12422     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12423       return n;
12424     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12425   }
12426 }
12427 //================================================================================
12428 /*!
12429  * \brief Creates missing boundary elements
12430  *  \param elements - elements whose boundary is to be checked
12431  *  \param dimension - defines type of boundary elements to create
12432  *  \param group - a group to store created boundary elements in
12433  *  \param targetMesh - a mesh to store created boundary elements in
12434  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12435  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12436  *                                boundary elements will be copied into the targetMesh
12437  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12438  *                                boundary elements will be added into the new group
12439  *  \param aroundElements - if true, elements will be created on boundary of given
12440  *                          elements else, on boundary of the whole mesh.
12441  * \return nb of added boundary elements
12442  */
12443 //================================================================================
12444
12445 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12446                                        Bnd_Dimension           dimension,
12447                                        SMESH_Group*            group/*=0*/,
12448                                        SMESH_Mesh*             targetMesh/*=0*/,
12449                                        bool                    toCopyElements/*=false*/,
12450                                        bool                    toCopyExistingBoundary/*=false*/,
12451                                        bool                    toAddExistingBondary/*= false*/,
12452                                        bool                    aroundElements/*= false*/)
12453 {
12454   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12455   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12456   // hope that all elements are of the same type, do not check them all
12457   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12458     throw SALOME_Exception(LOCALIZED("wrong element type"));
12459
12460   if ( !targetMesh )
12461     toCopyElements = toCopyExistingBoundary = false;
12462
12463   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12464   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12465   int nbAddedBnd = 0;
12466
12467   // editor adding present bnd elements and optionally holding elements to add to the group
12468   SMESH_MeshEditor* presentEditor;
12469   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12470   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12471
12472   SMESH_MesherHelper helper( *myMesh );
12473   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12474   SMDS_VolumeTool vTool;
12475   TIDSortedElemSet avoidSet;
12476   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12477   int inode;
12478
12479   typedef vector<const SMDS_MeshNode*> TConnectivity;
12480
12481   SMDS_ElemIteratorPtr eIt;
12482   if (elements.empty())
12483     eIt = aMesh->elementsIterator(elemType);
12484   else
12485     eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12486
12487   while (eIt->more())
12488   {
12489     const SMDS_MeshElement* elem = eIt->next();
12490     const int iQuad = elem->IsQuadratic();
12491
12492     // ------------------------------------------------------------------------------------
12493     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12494     // ------------------------------------------------------------------------------------
12495     vector<const SMDS_MeshElement*> presentBndElems;
12496     vector<TConnectivity>           missingBndElems;
12497     TConnectivity nodes;
12498     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12499     {
12500       vTool.SetExternalNormal();
12501       const SMDS_MeshElement* otherVol = 0;
12502       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12503       {
12504         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12505              ( !aroundElements || elements.count( otherVol )))
12506           continue;
12507         const int nbFaceNodes = vTool.NbFaceNodes(iface);
12508         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12509         if ( missType == SMDSAbs_Edge ) // boundary edges
12510         {
12511           nodes.resize( 2+iQuad );
12512           for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12513           {
12514             for ( int j = 0; j < nodes.size(); ++j )
12515               nodes[j] =nn[i+j];
12516             if ( const SMDS_MeshElement* edge =
12517                  aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12518               presentBndElems.push_back( edge );
12519             else
12520               missingBndElems.push_back( nodes );
12521           }
12522         }
12523         else // boundary face
12524         {
12525           nodes.clear();
12526           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12527             nodes.push_back( nn[inode] );
12528           if (iQuad) // add medium nodes
12529             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12530               nodes.push_back( nn[inode] );
12531           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12532           if ( iCenter > 0 )
12533             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12534
12535           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12536                                                                SMDSAbs_Face, /*noMedium=*/false ))
12537             presentBndElems.push_back( f );
12538           else
12539             missingBndElems.push_back( nodes );
12540
12541           if ( targetMesh != myMesh )
12542           {
12543             // add 1D elements on face boundary to be added to a new mesh
12544             const SMDS_MeshElement* edge;
12545             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12546             {
12547               if ( iQuad )
12548                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12549               else
12550                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12551               if ( edge && avoidSet.insert( edge ).second )
12552                 presentBndElems.push_back( edge );
12553             }
12554           }
12555         }
12556       }
12557     }
12558     else                     // elem is a face ------------------------------------------
12559     {
12560       avoidSet.clear(), avoidSet.insert( elem );
12561       int nbNodes = elem->NbCornerNodes();
12562       nodes.resize( 2 /*+ iQuad*/);
12563       for ( int i = 0; i < nbNodes; i++ )
12564       {
12565         nodes[0] = elem->GetNode(i);
12566         nodes[1] = elem->GetNode((i+1)%nbNodes);
12567         if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12568           continue; // not free link
12569
12570         //if ( iQuad )
12571         //nodes[2] = elem->GetNode( i + nbNodes );
12572         if ( const SMDS_MeshElement* edge =
12573              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
12574           presentBndElems.push_back( edge );
12575         else
12576           missingBndElems.push_back( nodes );
12577       }
12578     }
12579
12580     // ---------------------------------
12581     // 2. Add missing boundary elements
12582     // ---------------------------------
12583     if ( targetMesh != myMesh )
12584       // instead of making a map of nodes in this mesh and targetMesh,
12585       // we create nodes with same IDs.
12586       for ( int i = 0; i < missingBndElems.size(); ++i )
12587       {
12588         TConnectivity& srcNodes = missingBndElems[i];
12589         TConnectivity  nodes( srcNodes.size() );
12590         for ( inode = 0; inode < nodes.size(); ++inode )
12591           nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12592         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12593                                                                    missType,
12594                                                                    /*noMedium=*/false))
12595           continue;
12596         tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12597         ++nbAddedBnd;
12598       }
12599     else
12600       for ( int i = 0; i < missingBndElems.size(); ++i )
12601       {
12602         TConnectivity& nodes = missingBndElems[i];
12603         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12604                                                                    missType,
12605                                                                    /*noMedium=*/false))
12606           continue;
12607         SMDS_MeshElement* elem =
12608           tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12609         ++nbAddedBnd;
12610
12611         // try to set a new element to a shape
12612         if ( myMesh->HasShapeToMesh() )
12613         {
12614           bool ok = true;
12615           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12616           const int nbN = nodes.size() / (iQuad+1 );
12617           for ( inode = 0; inode < nbN && ok; ++inode )
12618           {
12619             pair<int, TopAbs_ShapeEnum> i_stype =
12620               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12621             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12622               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12623           }
12624           if ( ok && mediumShapes.size() > 1 )
12625           {
12626             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12627             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12628             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12629             {
12630               if (( ok = ( stype_i->first != stype_i_0.first )))
12631                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12632                                         aMesh->IndexToShape( stype_i_0.second ));
12633             }
12634           }
12635           if ( ok && mediumShapes.begin()->first == missShapeType )
12636             aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12637         }
12638       }
12639
12640     // ----------------------------------
12641     // 3. Copy present boundary elements
12642     // ----------------------------------
12643     if ( toCopyExistingBoundary )
12644       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12645       {
12646         const SMDS_MeshElement* e = presentBndElems[i];
12647         TConnectivity nodes( e->NbNodes() );
12648         for ( inode = 0; inode < nodes.size(); ++inode )
12649           nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12650         presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12651       }
12652     else // store present elements to add them to a group
12653       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12654       {
12655         presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12656       }
12657
12658   } // loop on given elements
12659
12660   // ---------------------------------------------
12661   // 4. Fill group with boundary elements
12662   // ---------------------------------------------
12663   if ( group )
12664   {
12665     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12666       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12667         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12668   }
12669   tgtEditor.myLastCreatedElems.Clear();
12670   tgtEditor2.myLastCreatedElems.Clear();
12671
12672   // -----------------------
12673   // 5. Copy given elements
12674   // -----------------------
12675   if ( toCopyElements && targetMesh != myMesh )
12676   {
12677     if (elements.empty())
12678       eIt = aMesh->elementsIterator(elemType);
12679     else
12680       eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12681     while (eIt->more())
12682     {
12683       const SMDS_MeshElement* elem = eIt->next();
12684       TConnectivity nodes( elem->NbNodes() );
12685       for ( inode = 0; inode < nodes.size(); ++inode )
12686         nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12687       tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12688
12689       tgtEditor.myLastCreatedElems.Clear();
12690     }
12691   }
12692   return nbAddedBnd;
12693 }